[automerger skipped] add dagger2-auto-common and guava libraries to dagger2-auto-service. am: 913c498b21 am: c4e59f861d
am: d34906b953 -s ours
am skip reason: change_id Ia1aabce46978b665e1f919cbda24a0da2ef58cc3 with SHA1 bbf741c5dd is in history
Change-Id: I761e00c092fcf453e6f499e3be0e2c86035d98b3
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..ecd54a5
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,8 @@
+# Include debug info in the compiled jars
+build --javacopt=-g
+build --host_javacopt=-g
+
+# Disable The Guava Beta Checker.
+# TODO(ronshapiro): explore how much work it would be to reenable this
+build --javacopt="-Xep:BetaApi:OFF"
+build --host_javacopt="-Xep:BetaApi:OFF"
diff --git a/.gitignore b/.gitignore
index 0c4de1d..fabb38f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,35 @@
-*.iml
-*.ipr
-*.iws
+*~
+\#*\#
+
+.classpath
+.factorypath
+.project
+.settings
+eclipsebin
+
+bin
+gen
+build
+out
+#lib
+
+target
+pom.xml.*
+release.properties
+build.log
+
.idea
+*.iml
+classes
+
+obj
+
+.DS_Store
+
+dependency-reduced-pom.xml
+
+gen-external-apklibs
+
+/bazel-*
+
+*.pyc
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..9015f2e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,65 @@
+language: android
+
+os: linux
+dist: trusty
+sudo: required
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - libstdc++-4.9-dev # https://github.com/nodegit/nodegit/issues/853
+ - gcc-4.8
+ - g++-4.8
+
+jdk:
+ - &jdk_for_publishing oraclejdk8
+
+android:
+ components:
+ - tools
+ - tools # Duplicated as per https://github.com/travis-ci/travis-ci/issues/6040#issuecomment-219367943
+ - build-tools-26.0.2
+ - android-26
+ - platform-tools
+ - extra-android-m2repository
+
+before_install:
+ - wget https://github.com/bazelbuild/bazel/releases/download/"${BAZEL_VERSION}"/bazel_"${BAZEL_VERSION}"-linux-x86_64.deb
+ - sudo dpkg -i bazel_"${BAZEL_VERSION}"-linux-x86_64.deb
+ - sudo rm -f /etc/mavenrc
+ - wget http://www.us.apache.org/dist/maven/maven-3/3.1.1/binaries/apache-maven-3.1.1-bin.tar.gz
+ - tar -zxf apache-maven-3.1.1-bin.tar.gz
+ - export PATH="$PWD/apache-maven-3.1.1/bin:$PATH"
+ - mkdir travis_bin
+ - ln -s $(which gcc-4.8) travis_bin/gcc
+ - ln -s $(which g++-4.8) travis_bin/g++
+ - export PATH="${PWD}/travis_bin:${PATH}"
+
+script:
+ - bazel test --test_output errors //...
+ - pushd examples && mvn compile && popd
+
+env:
+ global:
+ # Encrypted credentials for deploying snapshots.
+ - secure: eGc3LHBRIPmTnXLM1YoIqG1do9BkpFI2pJm3fz5Cd8UaXtf7Oefa+Ts3rcn4ipee5A+lf8kEouPshSoaQs81KZ2/qf8rSTCIqeFjHR8hzmOVYo/0zRfS/VSUT0yqN+jeRhuNk3+A49RTPlcfJqPv3tyddtrM1vF7axhCJPQIRJM=
+ - secure: LTzrlqcSNeZTOV52D3ibY9RBdxY4Yu8dUOYhAonrWLE+eDTzuoyCzcPw8pEcYVNUi1rG6Q7v3QBDTnBztsPoCbcN5tEGjw5cQEbfEzSTkWaNCFjncWn36cLwx9lgbF+5Db/L0mYJ36unDKUpKVC8AgOtxQibfv/ffugfxxj8ohY=
+
+ # Encrypted GitHub access token to allow util/generate-latest-docs.sh to
+ # push Javadoc to gh-pages.
+ # This uses an access token created by cgdecker and will need to be updated
+ # (see util/generate-latest-docs.sh for a link) if he no longer has
+ # permission to push to the repo.
+ - secure: "UpTUhCQzAGbr5JetRg2GZxp/dPDep/7Il3yGeyDECopciWdx41OPk/QNqAXBhNtKuEaMVsmASyoteuhgaTryQdV4qUIGVOMhES6kbOlYy3nwK44VdsNeeepwVospyDyZbxMtXq5LuHWuTADmAl1mdjNPNoziXc523zjnUzUx/EQ="
+ - JDK_FOR_PUBLISHING: *jdk_for_publishing
+ - BAZEL_VERSION="0.24.1"
+
+after_success:
+ - util/generate-latest-docs.sh
+ - util/publish-snapshot-on-commit.sh
+
+branches:
+ only:
+ - master
+ - /^release.*$/
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..f4a0fdd
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,8 @@
+# This is the list of Dagger authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder. To see the full list
+# of contributors, see the revision history in source control.
+Google Inc.
+Square Inc.
+and other contributors
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index f8e59ef..27a05c5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,12 +14,12 @@
java_import_host {
name: "dagger2-auto-common",
- jars: ["lib/auto-common-1.0-20151022.071545-39.jar"],
+ jars: ["lib/auto-common-0.10.jar"],
}
java_import_host {
name: "dagger2-auto-factory-jar",
- jars: ["lib/auto-factory-1.0-20150915.183854-35.jar"],
+ jars: ["lib/auto-factory-1.0-beta6.jar"],
}
java_plugin {
@@ -29,12 +29,19 @@
"dagger2-auto-factory-jar",
"dagger2-auto-common",
"guava",
+ "javapoet",
+ "dagger2-google-java-format",
],
}
java_import_host {
name: "dagger2-auto-service-jar",
- jars: ["lib/auto-service-1.0-rc2.jar"],
+ jars: ["lib/auto-service-1.0-rc5.jar"],
+}
+
+java_import_host {
+ name: "dagger2-auto-service-annotations",
+ jars: ["lib/auto-service-annotations-1.0-rc5.jar"],
}
java_plugin {
@@ -43,19 +50,28 @@
static_libs: [
"dagger2-auto-common",
"dagger2-auto-service-jar",
+ "dagger2-auto-service-annotations",
"guava",
],
}
java_import_host {
name: "dagger2-auto-value-jar",
- jars: ["lib/auto-value-1.4.1.jar"],
+ jars: ["lib/auto-value-1.6.5.jar"],
+}
+
+java_import_host {
+ name: "dagger2-auto-value-annotations",
+ jars: ["lib/auto-value-annotations-1.6.5.jar"],
}
java_plugin {
name: "dagger2-auto-value",
processor_class: "com.google.auto.value.processor.AutoValueProcessor",
- static_libs: ["dagger2-auto-value-jar"],
+ static_libs: [
+ "dagger2-auto-value-jar",
+ "dagger2-auto-value-annotations",
+ ],
}
java_plugin {
@@ -66,42 +82,64 @@
java_import_host {
name: "dagger2-google-java-format",
- jars: ["lib/google-java-format-0.1-20151017.042846-2.jar"],
+ jars: ["lib/google-java-format-1.7-all-deps.jar"],
}
java_import_host {
name: "dagger2-inject",
- jars: ["lib/javax-inject.jar"],
+ jars: ["lib/javax.inject-1.jar"],
+}
+
+java_import_host {
+ name: "dagger2-bootstrap-compiler-jar",
+ jars: ["java/dagger/internal/codegen/bootstrap_compiler_deploy.jar"],
+}
+
+java_plugin {
+ name: "dagger2-bootstrap-compiler",
+ processor_class: "dagger.internal.codegen.ComponentProcessor",
+ generates_api: true,
+ static_libs: ["dagger2-bootstrap-compiler-jar"],
}
java_library_host {
name: "dagger2",
- srcs: ["core/src/main/java/**/*.java"],
+ srcs: [
+ "java/dagger/*.java",
+ "java/dagger/internal/*.java",
+ "java/dagger/multibindings/*.java",
+ "java/dagger/releasablereferences/*.java",
+ ],
+ exclude_srcs: ["java/dagger/android/**/*.java"],
static_libs: ["dagger2-inject"],
libs: ["guava"],
- java_version: "1.7",
+ java_version: "1.8",
}
-// build dagger2 producers plugin
+// build dagger2 producers library
// ============================================================
-java_plugin {
+java_library_host {
name: "dagger2-producers",
- srcs: ["producers/src/main/java/**/*.java"],
+ srcs: ["java/dagger/producers/**/*.java"],
- static_libs: ["dagger2-inject"],
+ static_libs: [
+ "dagger2-inject",
+ "error_prone_annotations",
+ ],
libs: [
"dagger2",
+ "dagger2-android-annotation-stubs",
"guava",
],
- java_version: "1.7",
+ java_version: "1.8",
}
// build dagger2 compiler plugin
@@ -111,10 +149,20 @@
name: "dagger2-compiler",
processor_class: "dagger.internal.codegen.ComponentProcessor",
generates_api: true,
+ use_tools_jar: true,
- // Required for use of javax.annotation.Generated per http://b/62050818
- javacflags: ["-J--add-modules=java.xml.ws.annotation"],
- srcs: ["compiler/src/main/java/**/*.java"],
+ srcs: [
+ "java/dagger/internal/codegen/**/*.java",
+ "java/dagger/internal/codegen/**/*.proto",
+
+ "java/dagger/model/*.java",
+ "java/dagger/spi/*.java",
+ ],
+
+ exclude_srcs: [
+ "java/dagger/internal/codegen/BindingGraphStatisticsCollector.java",
+ "java/dagger/internal/codegen/DaggerKythePlugin.java",
+ ],
// Manually include META-INF/services/javax.annotation.processing.Processor
// as the AutoService processor doesn't work properly.
@@ -130,6 +178,14 @@
"dagger2-inject",
"dagger2-producers",
"guava",
+ "javapoet",
+ ],
+
+ // shade guava to avoid conflicts with guava embedded in Error Prone.
+ jarjar_rules: "jarjar-rules.txt",
+
+ libs: [
+ "dagger2-android-annotation-stubs",
],
plugins: [
@@ -137,7 +193,22 @@
"dagger2-auto-service",
"dagger2-auto-value",
"dagger2-auto-annotation",
+ "dagger2-bootstrap-compiler",
],
- java_version: "1.7",
+ proto: {
+ type: "full",
+ include_dirs: ["external/protobuf/src/"],
+ },
+
+ java_version: "1.8",
+}
+
+// Compile dummy implementations of annotations used by dagger2 but not
+// present in the Android tree.
+java_library {
+ name: "dagger2-android-annotation-stubs",
+ host_supported: true,
+ sdk_version: "core_current",
+ srcs: ["android-annotation-stubs/src/**/*.java"],
}
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..8becb3e
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,179 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package(default_visibility = ["//visibility:public"])
+
+package_group(
+ name = "src",
+ packages = ["//..."],
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+java_library(
+ name = "dagger_with_compiler",
+ exported_plugins = ["//java/dagger/internal/codegen:component-codegen"],
+ exports = ["//java/dagger:core"],
+)
+
+java_library(
+ name = "producers_with_compiler",
+ exports = [
+ ":dagger_with_compiler",
+ "//java/dagger/producers",
+ ],
+)
+
+android_library(
+ name = "android",
+ exported_plugins = ["//java/dagger/android/processor:plugin"],
+ exports = ["//java/dagger/android"],
+)
+
+android_library(
+ name = "android-support",
+ exports = [
+ ":android",
+ "//java/dagger/android/support",
+ ],
+)
+
+load("@google_bazel_common//tools/jarjar:jarjar.bzl", "jarjar_library")
+
+SHADE_RULES = ["rule com.google.auto.common.** dagger.shaded.auto.common.@1"]
+
+jarjar_library(
+ name = "shaded_compiler",
+ jars = [
+ "//java/dagger/internal/codegen:base",
+ "//java/dagger/internal/codegen:binding",
+ "//java/dagger/internal/codegen:binding_graph_validation",
+ "//java/dagger/internal/codegen:jdk-and-guava-extras",
+ "//java/dagger/internal/codegen:processor",
+ "//java/dagger/internal/codegen:validation",
+ "//java/dagger/internal/codegen:writing",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/serialization",
+ "//java/dagger/model:internal-proxies",
+ "//java/dagger/errorprone",
+ "@com_google_auto_auto_common//jar",
+ ],
+ rules = SHADE_RULES,
+)
+
+jarjar_library(
+ name = "shaded_compiler_src",
+ jars = [
+ "//java/dagger/internal/codegen:libbase-src.jar",
+ "//java/dagger/internal/codegen:libbinding-src.jar",
+ "//java/dagger/internal/codegen:libbinding_graph_validation-src.jar",
+ "//java/dagger/internal/codegen:libjdk-and-guava-extras-src.jar",
+ "//java/dagger/internal/codegen:libprocessor-src.jar",
+ "//java/dagger/internal/codegen:libvalidation-src.jar",
+ "//java/dagger/internal/codegen:libwriting-src.jar",
+ "//java/dagger/internal/codegen/javapoet:libjavapoet-src.jar",
+ "//java/dagger/internal/codegen/langmodel:liblangmodel-src.jar",
+ # TODO(ronshapiro): is there a generated src.jar for protos in Bazel?
+ "//java/dagger/errorprone:liberrorprone-src.jar",
+ ],
+)
+
+jarjar_library(
+ name = "shaded_spi",
+ jars = [
+ "//java/dagger/internal/codegen:jdk-and-guava-extras",
+ "//java/dagger/model",
+ "//java/dagger/spi",
+ "@com_google_auto_auto_common//jar",
+ ],
+ rules = SHADE_RULES,
+)
+
+jarjar_library(
+ name = "shaded_spi_src",
+ jars = [
+ "//java/dagger/internal/codegen:libjdk-and-guava-extras-src.jar",
+ "//java/dagger/model:libmodel-src.jar",
+ "//java/dagger/spi:libspi-src.jar",
+ ],
+)
+
+javadoc_library(
+ name = "spi-javadoc",
+ srcs = [
+ "//java/dagger/model:model-srcs",
+ "//java/dagger/spi:spi-srcs",
+ ],
+ root_packages = [
+ "dagger.model",
+ "dagger.spi",
+ ],
+ deps = [
+ "//java/dagger/model",
+ "//java/dagger/spi",
+ ],
+)
+
+jarjar_library(
+ name = "shaded_android_processor",
+ jars = [
+ "//java/dagger/android/processor",
+ "@com_google_auto_auto_common//jar",
+ ],
+ rules = SHADE_RULES,
+)
+
+jarjar_library(
+ name = "shaded_grpc_server_processor",
+ jars = [
+ "//java/dagger/grpc/server/processor",
+ "@com_google_auto_auto_common//jar",
+ ],
+ rules = SHADE_RULES,
+)
+
+# coalesced javadocs used for the gh-pages site
+javadoc_library(
+ name = "user-docs",
+ srcs = [
+ "//java/dagger:javadoc-srcs",
+ "//java/dagger/android:android-srcs",
+ "//java/dagger/android/support:support-srcs",
+ "//java/dagger/grpc/server:javadoc-srcs",
+ "//java/dagger/grpc/server/processor:javadoc-srcs",
+ "//java/dagger/model:model-srcs",
+ "//java/dagger/producers:producers-srcs",
+ "//java/dagger/spi:spi-srcs",
+ ],
+ android_api_level = 26,
+ # TODO(ronshapiro): figure out how to specify the version number for release builds
+ doctitle = "Dagger Dependency Injection API",
+ exclude_packages = [
+ "dagger.internal",
+ "dagger.producers.internal",
+ "dagger.producers.monitoring.internal",
+ ],
+ root_packages = ["dagger"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/android",
+ "//java/dagger/android/support",
+ "//java/dagger/grpc/server",
+ "//java/dagger/grpc/server/processor",
+ "//java/dagger/model",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ ],
+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a38bfc..09c4282 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,87 +1,6 @@
Change Log
==========
-Dagger 2 (Components)
----------------------
-
-### Version 2.0.2 *(2015-11-03)*
-
-A patch release, most crucially including:
-
- * A fix to the way processor validation of types is done that permits dagger to play
- more nicely with other processors, avoiding over-validating aspects that it doesn't
- need, which may yet not have been generated by other processors in a different round
- of processing.
- * Some improved error reporting for edge-cases
- * Fix to prevent incompatible versions of Guava on the classpath from blowing up processing
- * Support a more robust set of types for map keys in map bindings (primitive types, etc.)
-
-### Version 2.0.1 *(2015-05-28)*
-
-A maintenance release fixing immediate issues following the Dagger 2.0 release, including:
-
- * Speed up Graph Validation (reduce build times by 10s of seconds on sampled large projects)
- * Generate correct code for @MapKey annotation types (beta)
- * Fix to properly emit code for class literal values in @MapKey annotations.
- * Fix for injecting component dependencies
- * Fixes to generated code to account for differences in generics handling in ecg vs. javac.
- * Subcomponents can now be abstract classes.
- * Subcomponents now properly build the object graph in some cases involving explicit bindings
- and (sub)components without scope.
- * Improve runtime performance of SetFactory (set multibindings)
- * Other smaller fixes, refactorings, etc.
-
-### Version 2.0.0 *(2015-04-21)*
-
-The initial release of the 2.0 code-line, supporting:
-
- * `@Component` interfaces representing a custom API to access a graph of objects
- * JSR-330 injection automation using `@Inject` signals, `@Qualifiers`
- * Simple bindings of implementations to interfaces, custom provision of objects, and set-bindings
- * Compile-time validation of graph structure (cycles, missing bindings, duplicate bindings)
- * Generation of
- - backing implementations for components
- - factories for `@Inject` constructors and modules
- - members-injectors for `@Inject` methods and fields
- * Beta support for
- - Map bindings
- - [Producers](http://google.github.io/dagger/api/latest/dagger/producers/Producer.html)
-
-==============================================================
-
-Dagger 1 (ObjectGraph)
-----------------------
-
-### Version 1.2.0 *(2013-12-13)*
-
- * Numerous performance improvements in both the compiler and runtime.
- * Use more efficient `String` concatenation.
- * Module adapters are now stateless.
- * Use read/write locks over global locks.
- * Reflective constructor invocation is now cached with `Class.newInstance`.
- * Avoid re-linking all bindings when calling `.plus()`.
- * Set bindings are now unioned when calling `.plus()`.
- * Fix: Tolerate missing type information during compilation by deferring writing
- module adapters.
-
-
-### Version 1.1.0 *(2013-08-05)*
-
- * Module loading now requires code generation via the 'dagger-compiler' artifact.
- * Allow multiple contributions to Set binding via `Provides.Type.SET_VALUES`.
- * Request classloading from the classloader of the requesting object, not the current thread's
- context classloader.
- * Cache class loading at the root injector to reduce costs of loading adapters.
- * Fix: Primitive array types are no longer incorrectly changed to their boxed type.
- * Update JavaWriter to 2.1.1.
-
-
-### Version 1.0.1 *(2013-06-03)*
-
- * Explicitly forbid declaring `@Inject` on a class type (e.g., `@Inject class Foo {}`).
- * Update JavaWriter to 1.0.5.
-
-
-### Version 1.0.0 *(2013-05-07)*
-
-Initial release.
+- For Dagger 2 releases, please see https://github.com/google/dagger/releases
+- For Dagger 1 (`ObjectGraph`) releases, see
+ https://github.com/square/dagger/blob/master/CHANGELOG.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 087af22..45f15c4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,37 +1,69 @@
-Contributing
-============
+# How to contribute
-If you would like to contribute code to Dagger you can do so through GitHub by
-forking the repository and sending a pull request.
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
-When submitting code, please make every effort to follow existing conventions
-and style in order to keep the code as readable as possible.
+## Contributor License Agreement
-Where appropriate, please provide unit tests or integration tests. Unit tests
-should be JUnit based tests and can use either standard JUnit assertions or
-FEST assertions and be added to `<project>/src/test/java`. Changes to build-time
-behaviour (such as changes to code generation or graph validation) should go into
-small maven projects using the `maven-invoker-plugin`. Examples of this are in
-`core/src/it` and can include bean-shell verification scripts and other
-facilities provided by `maven-invoker-plugin`.
+Contributions to any Google project must be accompanied by a Contributor License
+Agreement. This is necessary because you own the copyright to your changes, even
+after your contribution becomes part of this project. So this agreement simply
+gives us permission to use and redistribute your contributions as part of the
+project. Head over to <https://cla.developers.google.com/> to see your current
+agreements on file or to sign a new one.
-Please make sure your code compiles by running `mvn clean verify` which will
-execute both unit and integration test phases. Additionally, consider using
-http://travis-ci.org to validate your branches before you even put them into
-pull requests. All pull requests will be validated by Travis-ci in any case
-and must pass before being merged.
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
-If you are adding or modifying files you may add your own copyright line, but
-please ensure that the form is consistent with the existing files, and please
-note that a Square, Inc. copyright line must appear in every copyright notice.
-All files are released with the Apache 2.0 license.
+## Code reviews
-Checkstyle failures during compilation indicate errors in your style and will
-be displayed in the console output of the build (including in Travis-CI output),
-or can be viewed in the `checkstyle-result.xml` file.
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult [GitHub Help] for more
+information on using pull requests.
-Before your code can be accepted into the project you must sign the
-[Individual Contributor License Agreement (CLA)][1].
+[GitHub Help]: https://help.github.com/articles/about-pull-requests/
+## Building Dagger
- [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1
+Dagger is built with [`bazel`](https://bazel.build).
+
+### Building Dagger from the command line
+
+* [Install Bazel](https://docs.bazel.build/versions/master/install.html)
+* Build the Dagger project with `bazel build <target>`
+ * Learn more about Bazel targets [here][bazel targets].
+ * If you see an error similar to `ERROR: missing input file
+ '@androidsdk//:build-tools/26.0.2/aapt'`, install the missing build
+ tools version with the android `sdkmanager` tool.
+* Run tests with `bazel test <target>`, or `bazel test //...` to run all
+ tests
+* You can install the Dagger libraries in your **local maven repository** by
+ running the `./util/install-local-snapshot.sh` script.
+ * It will build the libraries and install them with a `LOCAL-SNAPSHOT`
+ version.
+
+[bazel targets]: https://docs.bazel.build/versions/master/build-ref.html
+
+### Importing the Dagger project in IntelliJ/Android Studio
+
+* Visit `Preferences > Plugins` in the IDE menu.
+ * Search for `bazel` and install the plugin.
+ * If no result shows up, click on `Search in repositories`, search for
+ `bazel` and install the plugin.
+* Select `Import Bazel Project`.
+* Input the path to the Dagger project under `workspace`, click `Next`.
+* Select `Generate from BUILD file`, type `BUILD` in the `Build file` input,
+ click `Next`.
+* [Android Studio only] In the `Project View` form, uncomment one of the
+ `android_sdk_platform` lines. Pick one that you have installed, then click
+ `Finish`.
+* If you get an error on Bazel sync, `Cannot run program "bazel"`, then:
+ * In the command line, run `where bazel` and copy the output (e.g.
+ `/usr/local/bin/bazel`)
+ * In Android Studio, go to `Preferences > Bazel Settings` and replace
+ `Bazel binary location` with what you just copied.
+* Note that the first sync can take a long time. When build files are changed,
+ you can run partial syncs (which should be faster) from the file menu.
+* [Android Studio only] To view the Dagger project structure, open the
+ `Project` view and switch the top selector from `Android` to `Project`.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..59b9e25
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "dagger2"
+description:
+ "A fast dependency injector for Android and Java."
+
+third_party {
+ url {
+ type: HOMEPAGE
+ value: "https://dagger.dev"
+ }
+ url {
+ type: GIT
+ value: "https://github.com/google/dagger"
+ }
+ version: "dagger-2.23.1"
+ last_upgrade_date { year: 2019 month: 6 day: 14 }
+ license_type: PERMISSIVE
+}
diff --git a/OWNERS b/OWNERS
index 22cc91a..87a5dbe 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1 @@
-paulduffin@google.com
-nfuller@google.com
+include platform/libcore:/OWNERS
diff --git a/README.android b/README.android
deleted file mode 100644
index 1983221..0000000
--- a/README.android
+++ /dev/null
@@ -1,12 +0,0 @@
-URL: https://github.com/google/dagger.git
-License: Apache 2
-Description: "Dagger 2 - A fast dependency injector for Android and Java"
-
-Version: 91f7d8bda5b1ef2fdf768758c88d3e5f069210ea
-
-Upstream depends (slightly) on Guava v19 but we only have Guava v18 in
-Android at the moment so we have reverted those changes that introduced
-a dependency on Guava v19.
-
-Local Patches:
- Revert "Stop using deprecated Futures method." - upstream 0a2ca81c0f78c621968deb58f4b42117db43fec4
diff --git a/README.md b/README.md
index 3c0e8bc..a9ebc26 100644
--- a/README.md
+++ b/README.md
@@ -1,110 +1,204 @@
-Dagger 2
-========
+# Dagger 2
+
+[![Maven Central][mavenbadge-svg]][mavencentral]
A fast dependency injector for Android and Java.
-About Google's Fork
--------------
+## About Google's Fork
-Dagger 2 is a compile-time evolution approach to dependency injection. Taking the approach
-started in Dagger 1.x to its ultimate conclusion, Dagger 2.0 eliminates all reflection, and
-improves code clarity by removing the traditional ObjectGraph/Injector in favor of
-user-specified @Component interfaces.
+Dagger 2 is a compile-time evolution approach to dependency injection.
+Taking the approach started in Dagger 1.x to its ultimate conclusion,
+Dagger 2.x eliminates all reflection, and improves code clarity by
+removing the traditional ObjectGraph/Injector in favor of user-specified
+`@Component` interfaces.
-This github project represents the Dagger 2 development stream. The earlier
-[project page][square] (Square, Inc's repository) represents the earlier 1.0 development stream.
-Both versions have benefitted from strong involvement from Square, Google, and other contributors.
+This github project represents the Dagger 2 development stream. The earlier
+[project page][square] (Square, Inc's repository) represents the earlier 1.0
+development stream. Both versions have benefited from strong involvement from
+Square, Google, and other contributors.
-## [Dagger 2's main documentation website can be found here.][website]
+Dagger is currently in active development, primarily internally at Google,
+with regular pushes to the open-source community. Snapshot releases are
+auto-deployed to sonatype's central maven repository on every clean build with
+the version `HEAD-SNAPSHOT`.
-Status
-------
+> [Dagger 2's main documentation website can be found here.][website]
- - ***Release Version:* 2.0.1**
- - ***Snapshot Version:* 2.1-SNAPSHOT**
-
-Dagger is currently in active development, primarily internally at Google, with regular pushes
-to the open-source community. Snapshot releases are auto-deployed to sonatype's central maven
-repository on a clean build with the version `2.1-SNAPSHOT`.
-
-Documentation
--------------
+## Documentation
You can [find the dagger documentation here][website] which has extended usage
instructions and other useful information. Substantial usage information can be
found in the [API documentation][20api].
-You can also learn more from [the original proposal][proposal],
+You can also learn more from [the original proposal][proposal],
[this talk by Greg Kick][gaktalk], and on the dagger-discuss@googlegroups.com
-mailing list.
+mailing list.
-Installation
---------
+## Installation
-You will need to include the `dagger-2.0.1.jar` in your application's runtime.
+### Bazel
+
+If you build with `bazel`, follow the [`bazel` documentation for referencing
+external projects][bazel-external-deps] to include Dagger in your build.
+
+Given the following `WORKSPACE` definition, you can reference dagger via
+`@com_google_dagger//:dagger_with_compiler` in your deps.
+
+```python
+http_archive(
+ name = "com_google_dagger",
+ urls = ["https://github.com/google/dagger/archive/dagger-<version>.zip"],
+)
+```
+
+### Other build systems
+
+You will need to include the `dagger-2.x.jar` in your application's runtime.
In order to activate code generation and generate implementations to manage
-your graph you will need to include `dagger-compiler-2.0.1.jar` in your build
+your graph you will need to include `dagger-compiler-2.x.jar` in your build
at compile time.
+#### Maven
+
In a Maven project, include the `dagger` artifact in the dependencies section
-of your `pom.xml` and the `dagger-compiler` artifact as either an `optional` or
-`provided` dependency:
+of your `pom.xml` and the `dagger-compiler` artifact as an
+`annotationProcessorPaths` value of the `maven-compiler-plugin`:
```xml
<dependencies>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
- <version>2.0.1</version>
+ <version>2.x</version>
+ </dependency>
+</dependencies>
+<build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.6.1</version>
+ <configuration>
+ <annotationProcessorPaths>
+ <path>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger-compiler</artifactId>
+ <version>2.x</version>
+ </path>
+ </annotationProcessorPaths>
+ </configuration>
+ </plugin>
+ </plugins>
+</build>
+```
+
+If you are using a version of the `maven-compiler-plugin` lower than `3.5`, add
+the `dagger-compiler` artifact with the `provided` scope:
+
+```xml
+<dependencies>
+ <dependency>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger</artifactId>
+ <version>2.x</version>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
- <version>2.0.1</version>
- <optional>true</optional>
+ <version>2.x</version>
+ <scope>provided</scope>
</dependency>
</dependencies>
```
-If you use the beta `dagger-producers` extension (which supplies parallelizable execution graphs),
-then add this to your maven configuration:
+If you use the beta `dagger-producers` extension (which supplies
+parallelizable execution graphs), then add this to your maven configuration:
```xml
<dependencies>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-producers</artifactId>
- <version>2.0-beta</version>
+ <version>2.x</version>
</dependency>
</dependencies>
```
+#### Java Gradle
+```groovy
+// Add plugin https://plugins.gradle.org/plugin/net.ltgt.apt
+plugins {
+ id "net.ltgt.apt" version "0.10"
+}
-### Download
+// Add Dagger dependencies
+dependencies {
+ compile 'com.google.dagger:dagger:2.x'
+ apt 'com.google.dagger:dagger-compiler:2.x'
+}
+```
+
+#### Android Gradle
+```groovy
+// Add Dagger dependencies
+dependencies {
+ compile 'com.google.dagger:dagger:2.x'
+ annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
+}
+```
+
+If you're using classes in `dagger.android` you'll also want to include:
+
+```groovy
+compile 'com.google.dagger:dagger-android:2.x'
+compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
+annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
+```
+
+If you're using a version of the Android gradle plugin below `2.2`, see
+https://bitbucket.org/hvisser/android-apt.
+
+If you're using the [Android Databinding library][databinding], you may want to
+increase the number of errors that `javac` will print. When Dagger prints an
+error, databinding compilation will halt and sometimes print more than 100
+errors, which is the default amount for `javac`. For more information, see
+[Issue 306](https://github.com/google/dagger/issues/306).
+
+```groovy
+gradle.projectsEvaluated {
+ tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xmaxerrs" << "500" // or whatever number you want
+ }
+}
+```
+
+### Download
* 2.x (google/dagger)
* [Dagger 2.0 Documentation][website]
* [Dagger 2.0 Javadocs][20api]
- * [Dagger development Javadocs][latestapi] (from the `master` branch on GitHub)
+ * [Dagger development Javadocs][latestapi] (from the `master` branch
+ on GitHub)
* [Google's Dagger project site on GitHub][project]
- * <a href="https://plus.google.com/118328287768685565185" rel="publisher">Google+ Dagger Project Page</a>
- * [Google+ Dagger Users Community][community]
* 1.x (square/dagger)
* [Square's original Dagger project site on GitHub][square]
- * [Square Open Source Community][squarecommunity]
-If you do not use maven, gradle, ivy, or other build systems that consume maven-style binary
-artifacts, they can be downloaded directly via the [Maven Central Repository][mavensearch].
+If you do not use maven, gradle, ivy, or other build systems that consume
+maven-style binary artifacts, they can be downloaded directly via the
+[Maven Central Repository][mavencentral].
-Developer snapshots are available from [Sonatype's snapshot repository][dagger-snap], and
-are built on a clean build of the GitHub project's master branch.
+Developer snapshots are available from Sonatype's
+[snapshot repository][dagger-snap], and are built on a clean build of
+the GitHub project's master branch.
-License
--------
+## Building Dagger
- Copyright 2012 Square, Inc.
- Copyright 2012 Google, Inc.
+See [the CONTRIBUTING.md docs][Building Dagger].
+
+## License
+
+ Copyright 2012 The Dagger Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -118,17 +212,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-
-
- [mavensearch]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.google.dagger%22
- [dagger-snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/dagger/
- [website]: http://google.github.io/dagger
- [latestapi]: http://google.github.io/dagger/api/latest/
- [20api]: http://google.github.io/dagger/api/2.0/
- [gaktalk]: https://www.youtube.com/watch?v=oK_XtfXPkqw
- [proposal]: https://github.com/square/dagger/issues/366
- [project]: http://github.com/google/dagger/
- [community]: https://plus.google.com/communities/111933036769103367883
- [square]: http://github.com/square/dagger/
- [squarecommunity]: https://plus.google.com/communities/109244258569782858265
-
+[20api]: https://dagger.dev/api/2.0/
+[`bazel`]: https://bazel.build
+[bazel-external-deps]: https://docs.bazel.build/versions/master/external.html#depending-on-other-bazel-projects
+[Building Dagger]: CONTRIBUTING.md#building-dagger
+[dagger-snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/dagger/
+[databinding]: https://developer.android.com/topic/libraries/data-binding/
+[gaktalk]: https://www.youtube.com/watch?v=oK_XtfXPkqw
+[latestapi]: https://dagger.dev/api/latest/
+[mavenbadge-svg]: https://maven-badges.herokuapp.com/maven-central/com.google.dagger/dagger/badge.svg
+[mavencentral]: https://search.maven.org/artifact/com.google.dagger/dagger
+[project]: http://github.com/google/dagger/
+[proposal]: https://github.com/square/dagger/issues/366
+[square]: http://github.com/square/dagger/
+[website]: https://dagger.dev
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..8758b38
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+ name = "google_bazel_common",
+ strip_prefix = "bazel-common-26011657fee96a949c66500b1662c4c7288a4968",
+ urls = ["https://github.com/google/bazel-common/archive/26011657fee96a949c66500b1662c4c7288a4968.zip"],
+)
+
+load("@google_bazel_common//:workspace_defs.bzl", "google_common_workspace_rules")
+
+google_common_workspace_rules()
+
+# This fixes an issue with protobuf starting to use zlib by default in 3.7.0.
+# TODO(ronshapiro): Figure out if this is in fact necessary, or if proto can depend on the
+# @bazel_tools library directly. See discussion in
+# https://github.com/protocolbuffers/protobuf/pull/5389#issuecomment-481785716
+bind(
+ name = "zlib",
+ actual = "@bazel_tools//third_party/zlib",
+)
diff --git a/android-annotation-stubs/gen_annotations.sh b/android-annotation-stubs/gen_annotations.sh
new file mode 100755
index 0000000..21aeb46
--- /dev/null
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+declare -A INNER
+declare -A PARAMETER
+declare -A IMPORT
+
+ANNOTATIONS=(
+ org.checkerframework.checker.nullness.compatqual.NullableDecl
+ net.ltgt.gradle.incap.IncrementalAnnotationProcessor
+)
+
+PARAMETER["net.ltgt.gradle.incap.IncrementalAnnotationProcessor"]="IncrementalAnnotationProcessorType"
+IMPORT["net.ltgt.gradle.incap.IncrementalAnnotationProcessor"]="net.ltgt.gradle.incap.IncrementalAnnotationProcessorType"
+
+for a in ${ANNOTATIONS[@]}; do
+ package=${a%.*}
+ class=${a##*.}
+ dir=$(dirname $0)/src/${package//.//}
+ file=${class}.java
+ inner=${INNER[$a]}
+ parameter=${PARAMETER[$a]}
+ import=
+
+ if [ -n "${parameter}" ]; then
+ parameter="${parameter} value();"
+ fi
+
+ for i in ${IMPORT[$a]}; do
+ import="${import}import ${i};"
+ done
+
+ mkdir -p ${dir}
+ sed -e"s/__PACKAGE__/${package}/" \
+ -e"s/__CLASS__/${class}/" \
+ -e"s/__INNER__/${inner}/" \
+ -e"s/__PARAMETER__/${parameter}/" \
+ -e"s/__IMPORT__/${import}/" \
+ $(dirname $0)/tmpl.java > ${dir}/${file}
+ google-java-format -i ${dir}/${file}
+done
+
+f=$(dirname $0)/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
+cat > ${f} <<EOF
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.ltgt.gradle.incap;
+public enum IncrementalAnnotationProcessorType {
+ DYNAMIC
+}
+EOF
diff --git a/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessor.java b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessor.java
new file mode 100644
index 0000000..aa1ce76
--- /dev/null
+++ b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.ltgt.gradle.incap;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.PARAMETER,
+ ElementType.TYPE,
+ ElementType.TYPE_PARAMETER,
+ ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.SOURCE)
+public @interface IncrementalAnnotationProcessor {
+
+ IncrementalAnnotationProcessorType value();
+}
diff --git a/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
new file mode 100644
index 0000000..83e3590
--- /dev/null
+++ b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.ltgt.gradle.incap;
+public enum IncrementalAnnotationProcessorType {
+ DYNAMIC
+}
diff --git a/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java b/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
new file mode 100644
index 0000000..2d39b0f
--- /dev/null
+++ b/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.checkerframework.checker.nullness.compatqual;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.PARAMETER,
+ ElementType.TYPE,
+ ElementType.TYPE_PARAMETER,
+ ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.SOURCE)
+public @interface NullableDecl {}
diff --git a/android-annotation-stubs/tmpl.java b/android-annotation-stubs/tmpl.java
new file mode 100644
index 0000000..c4df609
--- /dev/null
+++ b/android-annotation-stubs/tmpl.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package __PACKAGE__;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+__IMPORT__
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.PARAMETER,
+ ElementType.TYPE,
+ ElementType.TYPE_PARAMETER,
+ ElementType.TYPE_USE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface __CLASS__ {
+ __INNER__
+ __PARAMETER__
+}
diff --git a/build_defs.bzl b/build_defs.bzl
new file mode 100644
index 0000000..0bd7402
--- /dev/null
+++ b/build_defs.bzl
@@ -0,0 +1,26 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""This file defines constants useful across the Dagger build."""
+
+DOCLINT_HTML_AND_SYNTAX = ["-Xdoclint:html,syntax"]
+
+DOCLINT_REFERENCES = ["-Xdoclint:reference"]
+
+SOURCE_7_TARGET_7 = [
+ "-source",
+ "1.7",
+ "-target",
+ "1.7",
+]
diff --git a/checkstyle.xml b/checkstyle.xml
deleted file mode 100644
index e7ffbc0..0000000
--- a/checkstyle.xml
+++ /dev/null
@@ -1,137 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE module PUBLIC
- "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
- "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
-
-<module name="Checker">
- <!--module name="NewlineAtEndOfFile"/-->
- <module name="FileLength"/>
- <module name="FileTabCharacter"/>
-
- <!-- Trailing spaces -->
- <module name="RegexpSingleline">
- <property name="format" value="\s+$"/>
- <property name="message" value="Line has trailing spaces."/>
- </module>
-
- <!-- Space after 'for' and 'if' -->
- <module name="RegexpSingleline">
- <property name="format" value="^\s*(for|if)[^ ]"/>
- <property name="message" value="Space needed before opening parenthesis."/>
- </module>
-
- <!-- For each spacing -->
- <module name="RegexpSingleline">
- <property name="format" value="^\s*for \(.*?([^ ]:|:[^ ])"/>
- <property name="message" value="Space needed around ':' character."/>
- </module>
-
- <module name="TreeWalker">
- <property name="cacheFile" value="${checkstyle.cache.file}"/>
-
- <!-- Checks for Javadoc comments. -->
- <!-- See http://checkstyle.sf.net/config_javadoc.html -->
- <!--module name="JavadocMethod"/-->
- <!--module name="JavadocType"/-->
- <!--module name="JavadocVariable"/-->
- <!--module name="JavadocStyle"/-->
-
-
- <!-- Checks for Naming Conventions. -->
- <!-- See http://checkstyle.sf.net/config_naming.html -->
- <!--<module name="ConstantName"/>-->
- <module name="LocalFinalVariableName"/>
- <module name="LocalVariableName"/>
- <module name="MemberName"/>
- <module name="MethodName"/>
- <module name="PackageName"/>
- <module name="ParameterName"/>
- <module name="StaticVariableName"/>
- <module name="TypeName"/>
-
-
- <!-- Checks for imports -->
- <!-- See http://checkstyle.sf.net/config_import.html -->
- <module name="AvoidStarImport"/>
- <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
- <module name="RedundantImport"/>
- <module name="UnusedImports">
- <property name="processJavadoc" value="true"/>
- </module>
-
- <!-- Checks for Size Violations. -->
- <!-- See http://checkstyle.sf.net/config_sizes.html -->
- <module name="LineLength">
- <property name="max" value="100"/>
- </module>
- <module name="MethodLength">
- <property name="max" value="200"/>
- </module>
- <!--module name="ParameterNumber"/-->
-
-
- <!-- Checks for whitespace -->
- <!-- See http://checkstyle.sf.net/config_whitespace.html -->
- <module name="GenericWhitespace"/>
- <module name="EmptyForIteratorPad"/>
- <module name="MethodParamPad"/>
- <module name="NoWhitespaceAfter"/>
- <module name="NoWhitespaceBefore"/>
- <module name="OperatorWrap"/>
- <module name="ParenPad"/>
- <module name="TypecastParenPad"/>
- <module name="WhitespaceAfter"/>
- <module name="WhitespaceAround">
- <property name="allowEmptyConstructors" value="true" />
- <property name="allowEmptyMethods" value="true" />
- </module>
-
-
- <!-- Modifier Checks -->
- <!-- See http://checkstyle.sf.net/config_modifiers.html -->
- <!--module name="ModifierOrder"/-->
- <module name="RedundantModifier"/>
-
-
- <!-- Checks for blocks. You know, those {}'s -->
- <!-- See http://checkstyle.sf.net/config_blocks.html -->
- <!--module name="AvoidNestedBlocks"/-->
- <!--module name="EmptyBlock"/-->
- <module name="LeftCurly"/>
- <!--module name="NeedBraces"/-->
- <module name="RightCurly"/>
-
-
- <!-- Checks for common coding problems -->
- <!-- See http://checkstyle.sf.net/config_coding.html -->
- <!--module name="AvoidInlineConditionals"/-->
- <module name="CovariantEquals"/>
- <module name="EmptyStatement"/>
- <!--<module name="EqualsAvoidNull"/>-->
- <module name="EqualsHashCode"/>
- <!--module name="HiddenField"/-->
- <module name="IllegalInstantiation"/>
- <!--<module name="InnerAssignment"/>-->
- <!--module name="MagicNumber"/-->
- <module name="MissingSwitchDefault"/>
- <module name="RedundantThrows"/>
- <module name="SimplifyBooleanExpression"/>
- <module name="SimplifyBooleanReturn"/>
-
- <!-- Checks for class design -->
- <!-- See http://checkstyle.sf.net/config_design.html -->
- <!--module name="DesignForExtension"/-->
- <!--module name="FinalClass"/-->
- <!--module name="HideUtilityClassConstructor"/-->
- <!--module name="InterfaceIsType"/-->
- <!--module name="VisibilityModifier"/-->
-
-
- <!-- Miscellaneous other checks. -->
- <!-- See http://checkstyle.sf.net/config_misc.html -->
- <!--module name="ArrayTypeStyle"/-->
- <!--module name="FinalParameters"/-->
- <!--module name="TodoComment"/-->
- <module name="UpperEll"/>
- </module>
-</module>
diff --git a/compiler/dependency-reduced-pom.xml b/compiler/dependency-reduced-pom.xml
deleted file mode 100644
index 504f9ff..0000000
--- a/compiler/dependency-reduced-pom.xml
+++ /dev/null
@@ -1,204 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <parent>
- <artifactId>dagger-parent</artifactId>
- <groupId>com.google.dagger</groupId>
- <version>2.1-SNAPSHOT</version>
- </parent>
- <modelVersion>4.0.0</modelVersion>
- <artifactId>dagger-compiler</artifactId>
- <name>Dagger Compiler</name>
- <description>Tools to generate Dagger injection and module adapters from annotated code and validate them.</description>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <executions>
- <execution>
- <id>default-compile</id>
- <goals>
- <goal>compile</goal>
- </goals>
- <configuration>
- <annotationProcessors>
- <annotationProcessor>com.google.auto.value.processor.AutoValueProcessor</annotationProcessor>
- <annotationProcessor>com.google.auto.service.processor.AutoServiceProcessor</annotationProcessor>
- </annotationProcessors>
- </configuration>
- </execution>
- <execution>
- <id>default-test-compile</id>
- <goals>
- <goal>testCompile</goal>
- </goals>
- <configuration>
- <annotationProcessors>
- <annotationProcessor>dagger.internal.codegen.ComponentProcessor</annotationProcessor>
- </annotationProcessors>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <artifactId>maven-invoker-plugin</artifactId>
- <executions>
- <execution>
- <id>integration-test</id>
- <goals>
- <goal>install</goal>
- <goal>run</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <addTestClassPath>true</addTestClassPath>
- <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
- <cloneClean>true</cloneClean>
- <profiles>
- <profile>!sonatype-oss-release</profile>
- </profiles>
- <pomIncludes>
- <pomInclude>*/pom.xml</pomInclude>
- </pomIncludes>
- <localRepositoryPath>${project.build.directory}/it-repo</localRepositoryPath>
- <filterProperties>
- <dagger.version>${project.version}</dagger.version>
- <dagger.groupId>${project.groupId}</dagger.groupId>
- </filterProperties>
- <streamLogs>true</streamLogs>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-shade-plugin</artifactId>
- <version>2.3</version>
- <executions>
- <execution>
- <phase>package</phase>
- <goals>
- <goal>shade</goal>
- </goals>
- <configuration>
- <minimizeJar>true</minimizeJar>
- <artifactSet>
- <excludes>
- <exclude>com.google.guava</exclude>
- <exclude>com.google.auto.service</exclude>
- <exclude>com.google.auto.value</exclude>
- <exclude>com.google.dagger:dagger</exclude>
- <exclude>com.google.dagger:dagger-producers</exclude>
- <exclude>javax.inject</exclude>
- </excludes>
- </artifactSet>
- <relocations>
- <relocation>
- <pattern>com.google.auto.common</pattern>
- <shadedPattern>dagger.shaded.auto.common</shadedPattern>
- </relocation>
- </relocations>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- <dependencies>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>2.1-SNAPSHOT</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-producers</artifactId>
- <version>2.1-SNAPSHOT</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.google.auto.service</groupId>
- <artifactId>auto-service</artifactId>
- <version>1.0-rc2</version>
- <scope>compile</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>18.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.google.auto.value</groupId>
- <artifactId>auto-value</artifactId>
- <version>1.0</version>
- <scope>compile</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.11</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <artifactId>hamcrest-core</artifactId>
- <groupId>org.hamcrest</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>2.1-SNAPSHOT</version>
- <classifier>tests</classifier>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.testing.compile</groupId>
- <artifactId>compile-testing</artifactId>
- <version>0.7</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <artifactId>tools</artifactId>
- <groupId>com.sun</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava-testlib</artifactId>
- <version>18.0</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <artifactId>jsr305</artifactId>
- <groupId>com.google.code.findbugs</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>1.9.5</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <artifactId>objenesis</artifactId>
- <groupId>org.objenesis</groupId>
- </exclusion>
- <exclusion>
- <artifactId>hamcrest-core</artifactId>
- <groupId>org.hamcrest</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.google.truth</groupId>
- <artifactId>truth</artifactId>
- <version>0.26</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-</project>
-
diff --git a/compiler/pom.xml b/compiler/pom.xml
deleted file mode 100644
index 72252f1..0000000
--- a/compiler/pom.xml
+++ /dev/null
@@ -1,208 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2012 Square, Inc.
- Copyright (C) 2012 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>dagger-compiler</artifactId>
- <name>Dagger Compiler</name>
- <description>
- Tools to generate Dagger injection and module adapters from annotated code and validate them.
- </description>
-
- <dependencies>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>dagger-producers</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.auto</groupId>
- <artifactId>auto-common</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>jsr305</artifactId>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>com.google.googlejavaformat</groupId>
- <artifactId>google-java-format</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.auto.service</groupId>
- <artifactId>auto-service</artifactId>
- <scope>provided</scope> <!-- to leave out of the all-deps jar -->
- </dependency>
- <dependency>
- <groupId>com.google.auto.value</groupId>
- <artifactId>auto-value</artifactId>
- <scope>provided</scope> <!-- to leave out of the all-deps jar -->
- <version>1.0</version>
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- <classifier>tests</classifier>
- </dependency>
- <dependency>
- <groupId>com.google.testing.compile</groupId>
- <artifactId>compile-testing</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava-testlib</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.truth</groupId>
- <artifactId>truth</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <executions>
- <execution>
- <id>default-compile</id>
- <goals><goal>compile</goal></goals>
- <configuration>
- <annotationProcessors>
- <annotationProcessor>com.google.auto.value.processor.AutoValueProcessor</annotationProcessor>
- <annotationProcessor>com.google.auto.service.processor.AutoServiceProcessor</annotationProcessor>
- </annotationProcessors>
- </configuration>
- </execution>
- <execution>
- <id>default-test-compile</id>
- <goals><goal>testCompile</goal></goals>
- <configuration>
- <annotationProcessors>
- <annotationProcessor>dagger.internal.codegen.ComponentProcessor</annotationProcessor>
- </annotationProcessors>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <artifactId>maven-invoker-plugin</artifactId>
- <configuration>
- <addTestClassPath>true</addTestClassPath>
- <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
- <cloneClean>true</cloneClean>
- <profiles>
- <profile>!sonatype-oss-release</profile>
- </profiles>
- <pomIncludes>
- <pomInclude>*/pom.xml</pomInclude>
- </pomIncludes>
- <localRepositoryPath>${project.build.directory}/it-repo</localRepositoryPath>
- <filterProperties>
- <dagger.version>${project.version}</dagger.version>
- <dagger.groupId>${project.groupId}</dagger.groupId>
- </filterProperties>
- <streamLogs>true</streamLogs>
- </configuration>
- <executions>
- <execution>
- <id>integration-test</id>
- <goals>
- <goal>install</goal>
- <goal>run</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <artifactId>maven-shade-plugin</artifactId>
- <version>2.3</version>
- <executions>
- <execution>
- <phase>package</phase>
- <goals>
- <goal>shade</goal>
- </goals>
- <configuration>
- <minimizeJar>true</minimizeJar>
- <artifactSet>
- <excludes>
- <!-- guava which has a consistent API and whose public types we vend in producers -->
- <exclude>com.google.guava</exclude>
- <!-- annotation processors dagger uses to be built, not to operate -->
- <exclude>com.google.auto.service</exclude>
- <exclude>com.google.auto.value</exclude>
- <!-- projects should depend on api projects directly -->
- <exclude>com.google.dagger:dagger</exclude>
- <exclude>com.google.dagger:dagger-producers</exclude>
- <exclude>javax.inject</exclude>
- </excludes>
- </artifactSet>
- <relocations>
- <relocation>
- <pattern>com.google.auto.common</pattern>
- <shadedPattern>dagger.shaded.auto.common</shadedPattern>
- </relocation>
- </relocations>
- <filters>
- <filter>
- <artifact>*:*</artifact>
- <excludes>
- <exclude>META-INF/*.SF</exclude>
- <exclude>META-INF/*.DSA</exclude>
- <exclude>META-INF/*.RSA</exclude>
- </excludes>
- </filter>
- </filters>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/compiler/src/it/functional-tests/pom.xml b/compiler/src/it/functional-tests/pom.xml
deleted file mode 100644
index ce3dd4e..0000000
--- a/compiler/src/it/functional-tests/pom.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2014 Google, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<project
- xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
- </parent>
- <groupId>dagger.tests</groupId>
- <artifactId>functional-tests</artifactId>
- <name>Functional Tests</name>
- <dependencies>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-compiler</artifactId>
- <version>${project.version}</version>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject-tck</artifactId>
- </dependency>
- <dependency>
- <!-- For map-bindings -->
- <groupId>com.google.auto.value</groupId>
- <artifactId>auto-value</artifactId>
- <version>${auto.value.version}</version>
- <scope>provided</scope> <!-- to leave out of the all-deps jar -->
- </dependency>
- <dependency>
- <!-- For map-bindings -->
- <groupId>com.google.auto.factory</groupId>
- <artifactId>auto-factory</artifactId>
- <version>${auto.factory.version}</version>
- <scope>provided</scope> <!-- to leave out of the all-deps jar -->
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.truth</groupId>
- <artifactId>truth</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.1</version>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>2.10</version>
- <configuration>
- <failsOnError>false</failsOnError>
- <consoleOutput>true</consoleOutput>
- <configLocation>../../../../checkstyle.xml</configLocation>
- </configuration>
- <executions>
- <execution>
- <phase>compile</phase>
- <goals>
- <goal>checkstyle</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/compiler/src/it/functional-tests/src/main/java/test/A.java b/compiler/src/it/functional-tests/src/main/java/test/A.java
deleted file mode 100644
index 030f855..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/A.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-class A {
- @Inject A() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/AbstractMembersInjectingBaseClass.java b/compiler/src/it/functional-tests/src/main/java/test/AbstractMembersInjectingBaseClass.java
deleted file mode 100644
index 4fb0f78..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/AbstractMembersInjectingBaseClass.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-abstract class AbstractMembersInjectingBaseClass {
- @Inject Thing thing;
-}
-
diff --git a/compiler/src/it/functional-tests/src/main/java/test/AbstractMiddleClassWithoutMembers.java b/compiler/src/it/functional-tests/src/main/java/test/AbstractMiddleClassWithoutMembers.java
deleted file mode 100644
index 89e94bd..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/AbstractMiddleClassWithoutMembers.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-abstract class AbstractMiddleClassWithoutMembers extends AbstractMembersInjectingBaseClass {
-}
-
diff --git a/compiler/src/it/functional-tests/src/main/java/test/B.java b/compiler/src/it/functional-tests/src/main/java/test/B.java
deleted file mode 100644
index dec8e2e..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/B.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-class B {
- @Inject B() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/BasicAbstractClassComponent.java b/compiler/src/it/functional-tests/src/main/java/test/BasicAbstractClassComponent.java
deleted file mode 100644
index 78f77df..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/BasicAbstractClassComponent.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.Component;
-
-/**
- * This component tests behavior equivalent to {@link BasicComponent}, but as an abstract class
- * rather than an interface.
- */
-@Component(modules = PrimitivesModule.class)
-abstract class BasicAbstractClassComponent implements BasicComponent {
- void throwAParty() {
- throw new RuntimeException("Paaarrrrrtaaaaaaaay!");
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/BasicComponent.java b/compiler/src/it/functional-tests/src/main/java/test/BasicComponent.java
deleted file mode 100644
index a04607d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/BasicComponent.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-* Copyright (C) 2014 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.Component;
-import dagger.Lazy;
-import dagger.MembersInjector;
-import javax.inject.Provider;
-
-@Component(modules = PrimitivesModule.class)
-interface BasicComponent extends Injector<Thing> {
- byte getByte();
- char getChar();
- short getShort();
- int getInt();
- long getLong();
- boolean getBoolean();
- float getFloat();
- double getDouble();
-
- Byte getBoxedByte();
- Character getBoxedChar();
- Short getBoxedShort();
- Integer getBoxedInt();
- Long getBoxedLong();
- Boolean getBoxedBoolean();
- Float getBoxedFloat();
- Double getBoxedDouble();
-
- Provider<Byte> getByteProvider();
- Provider<Character> getCharProvider();
- Provider<Short> getShortProvider();
- Provider<Integer> getIntProvider();
- Provider<Long> getLongProvider();
- Provider<Boolean> getBooleanProvider();
- Provider<Float> getFloatProvider();
- Provider<Double> getDoubleProvider();
-
- byte[] getByteArray();
- char[] getCharArray();
- short[] getShortArray();
- int[] getIntArray();
- long[] getLongArray();
- boolean[] getBooleanArray();
- float[] getFloatArray();
- double[] getDoubleArray();
-
- Provider<byte[]> getByteArrayProvider();
- Provider<char[]> getCharArrayProvider();
- Provider<short[]> getShortArrayProvider();
- Provider<int[]> getIntArrayProvider();
- Provider<long[]> getLongArrayProvider();
- Provider<boolean[]> getBooleanArrayProvider();
- Provider<float[]> getFloatArrayProvider();
- Provider<double[]> getDoubleArrayProvider();
-
- Object noOpMembersInjection(Object obviouslyDoesNotHaveMembersToInject);
-
- Thing thing();
- InjectedThing injectedThing();
- Provider<InjectedThing> injectedThingProvider();
- Lazy<InjectedThing> lazyInjectedThing();
- MembersInjector<InjectedThing> injectedThingMembersInjector();
-
- TypeWithInheritedMembersInjection typeWithInheritedMembersInjection();
- MembersInjector<TypeWithInheritedMembersInjection>
- typeWithInheritedMembersInjectionMembersInjector();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/BooleanKey.java b/compiler/src/it/functional-tests/src/main/java/test/BooleanKey.java
deleted file mode 100644
index 4cef79e..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/BooleanKey.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-
-@MapKey(unwrapValue = true)
-@interface BooleanKey {
- boolean value();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericComponent.java b/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericComponent.java
deleted file mode 100644
index b30522f..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericComponent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Component;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-@Component(modules = BoundedGenericModule.class)
-interface BoundedGenericComponent {
- BoundedGenerics<Integer, ArrayList<String>, LinkedList<CharSequence>, Integer, List<Integer>>
- bounds1();
- BoundedGenerics<Double, LinkedList<String>, LinkedList<Comparable<String>>, Double, Set<Double>>
- bounds2();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericModule.java b/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericModule.java
deleted file mode 100644
index 6bd7be4..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericModule.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-@Module
-class BoundedGenericModule {
-
- @Provides
- Integer provideInteger() {
- return 1;
- }
-
- @Provides
- Double provideDouble() {
- return 2d;
- }
-
- @Provides
- ArrayList<String> provideArrayListString() {
- ArrayList<String> list = new ArrayList<>();
- list.add("arrayListOfString");
- return list;
- }
-
- @Provides
- LinkedList<String> provideLinkedListString() {
- LinkedList<String> list = new LinkedList<>();
- list.add("linkedListOfString");
- return list;
- }
-
- @Provides
- LinkedList<CharSequence> provideLinkedListCharSeq() {
- LinkedList<CharSequence> list = new LinkedList<>();
- list.add("linkedListOfCharSeq");
- return list;
- }
-
- @Provides
- @SuppressWarnings("unchecked")
- LinkedList<Comparable<String>> provideArrayListOfComparableString() {
- LinkedList<Comparable<String>> list = new LinkedList<>();
- list.add("arrayListOfComparableOfString");
- return list;
- }
-
- @Provides
- List<Integer> provideListOfInteger() {
- LinkedList<Integer> list = new LinkedList<>();
- list.add(3);
- return list;
- }
-
- @Provides
- Set<Double> provideSetOfDouble() {
- Set<Double> set = new HashSet<>();
- set.add(4d);
- return set;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/BoundedGenerics.java b/compiler/src/it/functional-tests/src/main/java/test/BoundedGenerics.java
deleted file mode 100644
index e26d643..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/BoundedGenerics.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import java.util.List;
-import javax.inject.Inject;
-
-class BoundedGenerics<A extends Number & Comparable<? super A>,
- B extends List<? extends CharSequence>,
- C extends List<? super String>,
- D extends A,
- E extends Iterable<D>> {
-
- final A a;
- final B b;
- final C c;
- final D d;
- final E e;
-
- @Inject BoundedGenerics(A a, B b, C c, D d, E e) {
- this.a = a;
- this.b = b;
- this.c = c;
- this.d = d;
- this.e = e;
- }
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ByteKey.java b/compiler/src/it/functional-tests/src/main/java/test/ByteKey.java
deleted file mode 100644
index 8e739bd..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ByteKey.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-
-@MapKey(unwrapValue = true)
-@interface ByteKey {
- byte value();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/CharKey.java b/compiler/src/it/functional-tests/src/main/java/test/CharKey.java
deleted file mode 100644
index a4f4e29..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/CharKey.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-
-@MapKey(unwrapValue = true)
-@interface CharKey {
- char value();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ChildDoubleModule.java b/compiler/src/it/functional-tests/src/main/java/test/ChildDoubleModule.java
deleted file mode 100644
index 09a1e6b..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ChildDoubleModule.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package test;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.ArrayList;
-import java.util.List;
-
-@Module
-class ChildDoubleModule extends ParentModule<Double, String, List<Double>> {
-
- @Provides Double provideDouble() {
- return 3d;
- }
-
- @Provides List<Double> provideListOfDouble() {
- List<Double> list = new ArrayList<>();
- list.add(4d);
- return list;
- }
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ChildIntegerModule.java b/compiler/src/it/functional-tests/src/main/java/test/ChildIntegerModule.java
deleted file mode 100644
index ac9c612..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ChildIntegerModule.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package test;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.ArrayList;
-import java.util.List;
-
-@Module
-class ChildIntegerModule extends ParentModule<Integer, String, List<Integer>> {
-
- @Provides Integer provideInteger() {
- return 1;
- }
-
- @Provides List<Integer> provideListOfInteger() {
- List<Integer> list = new ArrayList<>();
- list.add(2);
- return list;
- }
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ComplexGenerics.java b/compiler/src/it/functional-tests/src/main/java/test/ComplexGenerics.java
deleted file mode 100644
index e2e3274..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ComplexGenerics.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Lazy;
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-class ComplexGenerics {
-
- final Generic2<Generic<A>> g2ga;
- final Lazy<Generic2<Generic<A>>> g2gaLazy;
- final Provider<Generic2<Generic<A>>> g2gaProvider;
- final Generic2<Generic<B>> g2gb;
- final Lazy<Generic2<Generic<B>>> g2gbLazy;
- final Provider<Generic2<Generic<B>>> g2gbProvider;
- final Generic2<A> g2a;
- final Generic<Generic2<A>> gg2a;
- final Generic<Generic2<B>> gg2b;
-
- @Inject ComplexGenerics(
- Generic2<Generic<A>> g2ga,
- Lazy<Generic2<Generic<A>>> g2gaLazy,
- Provider<Generic2<Generic<A>>> g2gaProvider,
- Generic2<Generic<B>> g2gb,
- Lazy<Generic2<Generic<B>>> g2gbLazy,
- Provider<Generic2<Generic<B>>> g2gbProvider,
- Generic2<A> g2a,
- Generic<Generic2<A>> gg2a,
- Generic<Generic2<B>> gg2b) {
- this.g2ga = g2ga;
- this.g2gaLazy = g2gaLazy;
- this.g2gaProvider = g2gaProvider;
- this.g2gb = g2gb;
- this.g2gbLazy = g2gbLazy;
- this.g2gbProvider = g2gbProvider;
- this.g2a = g2a;
- this.gg2a = gg2a;
- this.gg2b = gg2b;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ComponentDependsOnGeneratedCode.java b/compiler/src/it/functional-tests/src/main/java/test/ComponentDependsOnGeneratedCode.java
deleted file mode 100644
index 6ffe1e0..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ComponentDependsOnGeneratedCode.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.Component;
-
-@Component
-interface ComponentDependsOnGeneratedCode {
- NeedsFactory needsFactory();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCode.java b/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCode.java
deleted file mode 100644
index f7460c9..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCode.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Component;
-
-@Component
-interface ComponentSupertypeDependsOnGeneratedCode
- extends ComponentSupertypeDependsOnGeneratedCodeInterface {}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCodeInterface.java b/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCodeInterface.java
deleted file mode 100644
index fca90e0..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCodeInterface.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-interface ComponentSupertypeDependsOnGeneratedCodeInterface {
- NeedsFactory_SomethingFactory somethingFactory();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/Generic.java b/compiler/src/it/functional-tests/src/main/java/test/Generic.java
deleted file mode 100644
index ee1aa09..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/Generic.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-public class Generic<T> {
- final T t;
-
- @Inject public Generic(T t) {
- this.t = t;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/Generic2.java b/compiler/src/it/functional-tests/src/main/java/test/Generic2.java
deleted file mode 100644
index 4a56df3..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/Generic2.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-public class Generic2<T> {
- final T t;
-
- @Inject Generic2(T t) {
- this.t = t;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/GenericChild.java b/compiler/src/it/functional-tests/src/main/java/test/GenericChild.java
deleted file mode 100644
index 5c65dc0..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/GenericChild.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-class GenericChild<T> extends GenericParent<T, B> {
-
- A registeredA;
- T registeredT;
-
- @Inject GenericChild() {}
-
- @Inject A a;
- @Inject T t;
-
- @Inject void registerA(A a) { this.registeredA = a; }
- @Inject void registerT(T t) { this.registeredT = t; }
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/GenericComponent.java b/compiler/src/it/functional-tests/src/main/java/test/GenericComponent.java
deleted file mode 100644
index da5b9b5..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/GenericComponent.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Component;
-import test.sub.Exposed;
-import test.sub.PublicSubclass;
-
-@Component(modules = {ChildDoubleModule.class, ChildIntegerModule.class})
-interface GenericComponent {
- ReferencesGeneric referencesGeneric();
- GenericDoubleReferences<A> doubleGenericA();
- GenericDoubleReferences<B> doubleGenericB();
- ComplexGenerics complexGenerics();
- GenericNoDeps<A> noDepsA();
- GenericNoDeps<B> noDepsB();
-
- void injectA(GenericChild<A> childA);
- void injectB(GenericChild<B> childB);
-
- Exposed exposed();
- PublicSubclass publicSubclass();
-
- Iterable<Integer> iterableInt();
- Iterable<Double> iterableDouble();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/GenericDoubleReferences.java b/compiler/src/it/functional-tests/src/main/java/test/GenericDoubleReferences.java
deleted file mode 100644
index 6785c7c..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/GenericDoubleReferences.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-class GenericDoubleReferences<T> {
- final T t;
- final T t2;
- final Thing a;
- final Thing a2;
-
- @Inject GenericDoubleReferences(T t, Thing a, T t2, Thing a2) {
- this.t = t;
- this.a = a;
- this.t2 = t2;
- this.a2 = a2;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/GenericNoDeps.java b/compiler/src/it/functional-tests/src/main/java/test/GenericNoDeps.java
deleted file mode 100644
index e065f79..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/GenericNoDeps.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-class GenericNoDeps<T> {
-
- @Inject GenericNoDeps() {}
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/GenericParent.java b/compiler/src/it/functional-tests/src/main/java/test/GenericParent.java
deleted file mode 100644
index 0e01f5f..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/GenericParent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-class GenericParent<X, Y> {
-
- X registeredX;
- Y registeredY;
- B registeredB;
-
-
- @Inject GenericParent() {}
-
- @Inject X x;
- @Inject Y y;
- @Inject B b;
-
- @Inject void registerX(X x) { this.registeredX = x; }
- @Inject void registerY(Y y) { this.registeredY = y; }
- @Inject void registerB(B b) { this.registeredB = b; }
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/InjectedThing.java b/compiler/src/it/functional-tests/src/main/java/test/InjectedThing.java
deleted file mode 100644
index 73a46e8..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/InjectedThing.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Lazy;
-import dagger.MembersInjector;
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-@SuppressWarnings("unused")
-final class InjectedThing {
- @Inject byte primitiveByte;
- @Inject char primitiveChar;
- @Inject short primitiveShort;
- @Inject int primitiveInt;
- @Inject long primitiveLong;
- @Inject boolean primitiveBoolean;
- @Inject float primitiveFloat;
- @Inject double primitiveDouble;
-
- @Inject Provider<Byte> byteProvider;
- @Inject Provider<Character> charProvider;
- @Inject Provider<Short> shortProvider;
- @Inject Provider<Integer> intProvider;
- @Inject Provider<Long> longProvider;
- @Inject Provider<Boolean> booleanProvider;
- @Inject Provider<Float> floatProvider;
- @Inject Provider<Double> doubleProvider;
-
- @Inject Lazy<Byte> lazyByte;
- @Inject Lazy<Character> lazyChar;
- @Inject Lazy<Short> lazyShort;
- @Inject Lazy<Integer> lazyInt;
- @Inject Lazy<Long> lazyLong;
- @Inject Lazy<Boolean> lazyBoolean;
- @Inject Lazy<Float> lazyFloat;
- @Inject Lazy<Double> lazyDouble;
-
- @Inject Byte boxedBype;
- @Inject Character boxedChar;
- @Inject Short boxedShort;
- @Inject Integer boxedInt;
- @Inject Long boxedLong;
- @Inject Boolean boxedBoolean;
- @Inject Float boxedFloat;
- @Inject Double boxedDouble;
-
- @Inject byte[] byteArray;
- @Inject char[] charArray;
- @Inject short[] shortArray;
- @Inject int[] intArray;
- @Inject long[] longArray;
- @Inject boolean[] booleanArray;
- @Inject float[] floatArray;
- @Inject double[] doubleArray;
-
- @Inject Provider<byte[]> byteArrayProvider;
- @Inject Provider<char[]> charArrayProvider;
- @Inject Provider<short[]> shortArrayProvider;
- @Inject Provider<int[]> intArrayProvider;
- @Inject Provider<long[]> longArrayProvider;
- @Inject Provider<boolean[]> booleanArrayProvider;
- @Inject Provider<float[]> floatArrayProvider;
- @Inject Provider<double[]> doubleArrayProvider;
-
- @Inject Lazy<byte[]> lazyByteArray;
- @Inject Lazy<char[]> lazyCharArray;
- @Inject Lazy<short[]> lazyShortArray;
- @Inject Lazy<int[]> lazyIntArray;
- @Inject Lazy<long[]> lazyLongArray;
- @Inject Lazy<boolean[]> lazyBooleanArray;
- @Inject Lazy<float[]> lazy;
- @Inject Lazy<double[]> lazyDoubleArray;
-
- @Inject Thing thing;
- @Inject Provider<Thing> thingProvider;
- @Inject Lazy<Thing> lazyThing;
- @Inject MembersInjector<Thing> thingMembersInjector;
-
- @Inject InjectedThing(
- byte primitiveByte,
- char primitiveChar,
- short primitiveShort,
- int primitiveInt,
- long primitiveLong,
- boolean primitiveBoolean,
- float primitiveFloat,
- double primitiveDouble,
-
- Provider<Byte> byteProvider,
- Provider<Character> charProvider,
- Provider<Short> shortProvider,
- Provider<Integer> intProvider,
- Provider<Long> longProvider,
- Provider<Boolean> booleanProvider,
- Provider<Float> floatProvider,
- Provider<Double> doubleProvider,
-
- Lazy<Byte> lazyByte,
- Lazy<Character> lazyChar,
- Lazy<Short> lazyShort,
- Lazy<Integer> lazyInt,
- Lazy<Long> lazyLong,
- Lazy<Boolean> lazyBoolean,
- Lazy<Float> lazyFloat,
- Lazy<Double> lazyDouble,
-
- Byte boxedBype,
- Character boxedChar,
- Short boxedShort,
- Integer boxedInt,
- Long boxedLong,
- Boolean boxedBoolean,
- Float boxedFloat,
- Double boxedDouble,
-
- byte[] byteArray,
- char[] charArray,
- short[] shortArray,
- int[] intArray,
- long[] longArray,
- boolean[] booleanArray,
- float[] floatArray,
- double[] doubleArray,
-
- Provider<byte[]> byteArrayProvider,
- Provider<char[]> charArrayProvider,
- Provider<short[]> shortArrayProvider,
- Provider<int[]> intArrayProvider,
- Provider<long[]> longArrayProvider,
- Provider<boolean[]> booleanArrayProvider,
- Provider<float[]> floatArrayProvider,
- Provider<double[]> doubleArrayProvider,
-
- Lazy<byte[]> lazyByteArray,
- Lazy<char[]> lazyCharArray,
- Lazy<short[]> lazyShortArray,
- Lazy<int[]> lazyIntArray,
- Lazy<long[]> lazyLongArray,
- Lazy<boolean[]> lazyBooleanArray,
- Lazy<float[]> lazy,
- Lazy<double[]> lazyDoubleArray,
-
- Thing thing,
- Provider<Thing> thingProvider,
- Lazy<Thing> lazyThing,
- MembersInjector<Thing> thingMembersInjector) {}
-
- @Inject void primitiveByte(byte primitiveByte) {}
- @Inject void primitiveChar(char primitiveChar) {}
- @Inject void primitiveShort(short primitiveShort) {}
- @Inject void primitiveInt(int primitiveInt) {}
- @Inject void primitiveLong(long primitiveLong) {}
- @Inject void primitiveBoolean(boolean primitiveBoolean) {}
- @Inject void primitiveFloat(float primitiveFloat) {}
- @Inject void primitiveDouble(double primitiveDouble) {}
-
- @Inject void byteProvider(Provider<Byte> byteProvider) {}
- @Inject void charProvider(Provider<Character> charProvider) {}
- @Inject void shortProvider(Provider<Short> shortProvider) {}
- @Inject void intProvider(Provider<Integer> intProvider) {}
- @Inject void longProvider(Provider<Long> longProvider) {}
- @Inject void booleanProvider(Provider<Boolean> booleanProvider) {}
- @Inject void floatProvider(Provider<Float> floatProvider) {}
- @Inject void doubleProvider(Provider<Double> doubleProvider) {}
-
- @Inject void lazyByte(Lazy<Byte> lazyByte) {}
- @Inject void lazyChar(Lazy<Character> lazyChar) {}
- @Inject void lazyShort(Lazy<Short> lazyShort) {}
- @Inject void lazyInt(Lazy<Integer> lazyInt) {}
- @Inject void lazyLong(Lazy<Long> lazyLong) {}
- @Inject void lazyBoolean(Lazy<Boolean> lazyBoolean) {}
- @Inject void lazyFloat(Lazy<Float> lazyFloat) {}
- @Inject void lazyDouble(Lazy<Double> lazyDouble) {}
-
- @Inject void boxedBype(Byte boxedBype) {}
- @Inject void boxedChar(Character boxedChar) {}
- @Inject void boxedShort(Short boxedShort) {}
- @Inject void boxedInt(Integer boxedInt) {}
- @Inject void boxedLong(Long boxedLong) {}
- @Inject void boxedBoolean(Boolean boxedBoolean) {}
- @Inject void boxedFloat(Float boxedFloat) {}
- @Inject void boxedDouble(Double boxedDouble) {}
-
- @Inject void byteArray(byte[] byteArray) {}
- @Inject void charArray(char[] charArray) {}
- @Inject void shortArray(short[] shortArray) {}
- @Inject void intArray(int[] intArray) {}
- @Inject void longArray(long[] longArray) {}
- @Inject void booleanArray(boolean[] booleanArray) {}
- @Inject void floatArray(float[] floatArray) {}
- @Inject void doubleArray(double[] doubleArray) {}
-
- @Inject void byteArrayProvider(Provider<byte[]> byteArrayProvider) {}
- @Inject void charArrayProvider(Provider<char[]> charArrayProvider) {}
- @Inject void shortArrayProvider(Provider<short[]> shortArrayProvider) {}
- @Inject void intArrayProvider(Provider<int[]> intArrayProvider) {}
- @Inject void longArrayProvider(Provider<long[]> longArrayProvider) {}
- @Inject void booleanArrayProvider(Provider<boolean[]> booleanArrayProvider) {}
- @Inject void floatArrayProvider(Provider<float[]> floatArrayProvider) {}
- @Inject void doubleArrayProvider(Provider<double[]> doubleArrayProvider) {}
-
- @Inject void lazyByteArray(Lazy<byte[]> lazyByteArray) {}
- @Inject void lazyCharArray(Lazy<char[]> lazyCharArray) {}
- @Inject void lazyShortArray(Lazy<short[]> lazyShortArray) {}
- @Inject void lazyIntArray(Lazy<int[]> lazyIntArray) {}
- @Inject void lazyLongArray(Lazy<long[]> lazyLongArray) {}
- @Inject void lazyBooleanArray(Lazy<boolean[]> lazyBooleanArray) {}
- @Inject void lazy(Lazy<float[]> lazy) {}
- @Inject void lazyDoubleArray(Lazy<double[]> lazyDoubleArray) {}
-
- @Inject void thing(Thing thing) {}
- @Inject void thingProvider(Provider<Thing> thingProvider) {}
- @Inject void lazyThing(Lazy<Thing> lazyThing) {}
- @Inject void thingMembersInjector(MembersInjector<Thing> thingMembersInjector) {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/Injector.java b/compiler/src/it/functional-tests/src/main/java/test/Injector.java
deleted file mode 100644
index 2a5798a..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/Injector.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Lazy;
-import dagger.MembersInjector;
-import javax.inject.Provider;
-
-/**
- * A simple interface that exercises all forms of injection for a given type.
- */
-interface Injector<T> {
- T instance();
- Provider<T> provider();
- Lazy<T> lazy();
- MembersInjector<T> membersInjector();
- void injectMembers(T t);
- T injectMembersAndReturn(T t);
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java b/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java
deleted file mode 100644
index ac0624f..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.Component;
-import dagger.mapkeys.StringKey;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Named;
-import javax.inject.Provider;
-import test.sub.ContributionsModule;
-
-@Component(
- modules = {
- MultibindingModule.class,
- ContributionsModule.class
- },
- dependencies = MultibindingDependency.class
-)
-interface MultibindingComponent {
- Map<String, String> map();
- Map<String, String[]> mapOfArrays();
- Map<String, Provider<String>> mapOfProviders();
- Set<String> mapKeys();
- Collection<String> mapValues();
- Set<Integer> set();
- Map<NestedAnnotationContainer.NestedWrappedKey, String> nestedKeyMap();
- Map<Class<? extends Number>, String> numberClassKeyMap();
- Map<Class<?>, String> classKeyMap();
- Map<Long, String> longKeyMap();
- Map<Integer, String> integerKeyMap();
- Map<Short, String> shortKeyMap();
- Map<Byte, String> byteKeyMap();
- Map<Boolean, String> booleanKeyMap();
- Map<Character, String> characterKeyMap();
- Map<StringKey, String> unwrappedAnnotationKeyMap();
- Map<WrappedAnnotationKey, String> wrappedAnnotationKeyMap();
- @Named("complexQualifier") Set<String> complexQualifierStringSet();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/MultibindingDependency.java b/compiler/src/it/functional-tests/src/main/java/test/MultibindingDependency.java
deleted file mode 100644
index a92e029..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/MultibindingDependency.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-interface MultibindingDependency {
- double doubleDependency();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java b/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java
deleted file mode 100644
index 4a7577e..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.mapkeys.ClassKey;
-import dagger.mapkeys.IntKey;
-import dagger.mapkeys.LongKey;
-import dagger.mapkeys.StringKey;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Named;
-import javax.inject.Provider;
-
-import static dagger.Provides.Type.MAP;
-import static dagger.Provides.Type.SET;
-
-@Module
-class MultibindingModule {
- @Provides(type = MAP)
- @StringKey("foo")
- static String provideFooKey(double doubleDependency) {
- return "foo value";
- }
-
- @Provides(type = MAP)
- @StringKey("bar")
- static String provideBarKey() {
- return "bar value";
- }
-
- @Provides(type = MAP)
- @StringKey("foo")
- static String[] provideFooArrayValue(double doubleDependency) {
- return new String[] {"foo1", "foo2"};
- }
-
- @Provides(type = MAP)
- @StringKey("bar")
- static String[] provideBarArrayValue() {
- return new String[] {"bar1", "bar2"};
- }
-
- @Provides(type = SET)
- static int provideFiveToSet() {
- return 5;
- }
-
- @Provides(type = SET)
- static int provideSixToSet() {
- return 6;
- }
-
- @Provides
- static Set<String> provideMapKeys(Map<String, Provider<String>> map) {
- return map.keySet();
- }
-
- @Provides
- static Collection<String> provideMapValues(Map<String, String> map) {
- return map.values();
- }
-
- @Provides(type = MAP)
- @NestedAnnotationContainer.NestedWrappedKey(Integer.class)
- static String valueForInteger() {
- return "integer";
- }
-
- @Provides(type = MAP)
- @NestedAnnotationContainer.NestedWrappedKey(Long.class)
- static String valueForLong() {
- return "long";
- }
-
- @Provides(type = MAP)
- @ClassKey(Integer.class)
- static String valueForClassInteger() {
- return "integer";
- }
-
- @Provides(type = MAP)
- @ClassKey(Long.class)
- static String valueForClassLong() {
- return "long";
- }
-
- @Provides(type = MAP)
- @NumberClassKey(BigDecimal.class)
- static String valueForNumberClassBigDecimal() {
- return "bigdecimal";
- }
-
- @Provides(type = MAP)
- @NumberClassKey(BigInteger.class)
- static String valueForNumberClassBigInteger() {
- return "biginteger";
- }
-
- @Provides(type = MAP)
- @LongKey(100)
- static String valueFor100Long() {
- return "100 long";
- }
-
- @Provides(type = MAP)
- @IntKey(100)
- static String valueFor100Int() {
- return "100 int";
- }
-
- @Provides(type = MAP)
- @ShortKey(100)
- static String valueFor100Short() {
- return "100 short";
- }
-
- @Provides(type = MAP)
- @ByteKey(100)
- static String valueFor100Byte() {
- return "100 byte";
- }
-
- @Provides(type = MAP)
- @BooleanKey(true)
- static String valueForTrue() {
- return "true";
- }
-
- @Provides(type = MAP)
- @CharKey('a')
- static String valueForA() {
- return "a char";
- }
-
- @Provides(type = MAP)
- @CharKey('\n')
- static String valueForNewline() {
- return "newline char";
- }
-
- @Provides(type = MAP)
- @UnwrappedAnnotationKey(@StringKey("foo\n"))
- static String valueForUnwrappedAnnotationKeyFoo() {
- return "foo annotation";
- }
-
- @Provides(type = MAP)
- @WrappedAnnotationKey(
- value = @StringKey("foo"),
- integers = {1, 2, 3},
- annotations = {},
- classes = {Long.class, Integer.class}
- )
- static String valueForWrappedAnnotationKeyFoo() {
- return "wrapped foo annotation";
- }
-
- @Provides(type = SET)
- @Named("complexQualifier")
- static String valueForComplexQualifierSet() {
- return "foo";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/NeedsFactory.java b/compiler/src/it/functional-tests/src/main/java/test/NeedsFactory.java
deleted file mode 100644
index b789073..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/NeedsFactory.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import com.google.auto.factory.AutoFactory;
-import javax.inject.Inject;
-
-class NeedsFactory {
- @Inject NeedsFactory(NeedsFactory_SomethingFactory somethingFactory) {}
-
- @AutoFactory
- static class Something {}
-}
-
diff --git a/compiler/src/it/functional-tests/src/main/java/test/NestedAnnotationContainer.java b/compiler/src/it/functional-tests/src/main/java/test/NestedAnnotationContainer.java
deleted file mode 100644
index c57b4ec..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/NestedAnnotationContainer.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-
-public final class NestedAnnotationContainer {
-
- @MapKey(unwrapValue = false)
- @interface NestedWrappedKey {
- Class<?> value();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/NonComponentDependencyComponent.java b/compiler/src/it/functional-tests/src/main/java/test/NonComponentDependencyComponent.java
deleted file mode 100644
index 43a088c..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/NonComponentDependencyComponent.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.Component;
-import javax.inject.Inject;
-import test.sub.OtherThing;
-
-@Component(dependencies = {NonComponentDependencyComponent.ThingComponent.class})
-interface NonComponentDependencyComponent {
- ThingTwo thingTwo();
-
- static class ThingTwo {
- @SuppressWarnings("unused")
- @Inject
- ThingTwo(
- Thing thing,
- NonComponentDependencyComponent nonComponentDependencyComponent,
- NonComponentDependencyComponent.ThingComponent thingComponent) {}
- }
-
- // A non-component interface which this interface depends upon.
- interface ThingComponent {
- Thing thing();
- }
-
- // The implementation for that interface.
- static class ThingComponentImpl implements ThingComponent {
- @Override
- public Thing thing() {
- return new Thing(new OtherThing(1));
- }
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/NumberClassKey.java b/compiler/src/it/functional-tests/src/main/java/test/NumberClassKey.java
deleted file mode 100644
index 4164ae5..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/NumberClassKey.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-
-@MapKey(unwrapValue = true)
-@interface NumberClassKey {
- Class<? extends Number> value();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/OuterClassBar.java b/compiler/src/it/functional-tests/src/main/java/test/OuterClassBar.java
deleted file mode 100644
index c7fabdb..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/OuterClassBar.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Component;
-
-final class OuterClassBar {
- @Component(modules = PrimitivesModule.class)
- interface NestedComponent {
- InjectedThing injectedThing();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/OuterClassFoo.java b/compiler/src/it/functional-tests/src/main/java/test/OuterClassFoo.java
deleted file mode 100644
index 86f963f..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/OuterClassFoo.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import dagger.Component;
-
-final class OuterClassFoo {
- @Component(modules = PrimitivesModule.class)
- interface NestedComponent {
- Thing thing();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ParentModule.java b/compiler/src/it/functional-tests/src/main/java/test/ParentModule.java
deleted file mode 100644
index a161aba..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ParentModule.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package test;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.ArrayList;
-import java.util.List;
-
-@Module
-abstract class ParentModule<A extends Number & Comparable<A>, B, C extends Iterable<A>> {
- @Provides Iterable<A> provideIterableOfAWithC(A a, C c) {
- List<A> list = new ArrayList<>();
- list.add(a);
- for (A elt : c) {
- list.add(elt);
- }
- return list;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/PrimitivesModule.java b/compiler/src/it/functional-tests/src/main/java/test/PrimitivesModule.java
deleted file mode 100644
index acbf271..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/PrimitivesModule.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package test;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class PrimitivesModule {
- static final byte BOUND_BYTE = -41;
- static final char BOUND_CHAR = 'g';
- static final short BOUND_SHORT = 21840;
- static final int BOUND_INT = 1894833693;
- static final long BOUND_LONG = -4369839828653523584L;
- static final boolean BOUND_BOOLEAN = true;
- static final float BOUND_FLOAT = (float) 0.9964542;
- static final double BOUND_DOUBLE = 0.12681322049667765;
-
- /*
- * While we can't ensure that these constants stay constant, this is a test so we're just going to
- * keep our fingers crossed that we're not going to be jerks.
- */
- static final byte[] BOUND_BYTE_ARRAY = {1, 2, 3};
- static final char[] BOUND_CHAR_ARRAY = {'g', 'a', 'k'};
- static final short[] BOUND_SHORT_ARRAY = {2, 4};
- static final int[] BOUND_INT_ARRAY = {3, 1, 2};
- static final long[] BOUND_LONG_ARRAY = {1, 1, 2, 3, 5};
- static final boolean[] BOUND_BOOLEAN_ARRAY = {false, true, false, false};
- static final float[] BOUND_FLOAT_ARRAY = {(float) 0.1, (float) 0.01, (float) 0.001};
- static final double[] BOUND_DOUBLE_ARRAY = {0.2, 0.02, 0.002};
-
- @Provides static byte provideByte() {
- return BOUND_BYTE;
- }
-
- @Provides static char provideChar() {
- return BOUND_CHAR;
- }
-
- @Provides static short provideShort() {
- return BOUND_SHORT;
- }
-
- @Provides static int provideInt() {
- return BOUND_INT;
- }
-
- @Provides static long provideLong() {
- return BOUND_LONG;
- }
-
- @Provides static boolean provideBoolean() {
- return BOUND_BOOLEAN;
- }
-
- @Provides static float provideFloat() {
- return BOUND_FLOAT;
- }
-
- @Provides static double boundDouble() {
- return BOUND_DOUBLE;
- }
-
- @Provides static byte[] provideByteArray() {
- return BOUND_BYTE_ARRAY;
- }
-
- @Provides static char[] provideCharArray() {
- return BOUND_CHAR_ARRAY;
- }
-
- @Provides static short[] provideShortArray() {
- return BOUND_SHORT_ARRAY;
- }
-
- @Provides static int[] provideIntArray() {
- return BOUND_INT_ARRAY;
- }
-
- @Provides static long[] provideLongArray() {
- return BOUND_LONG_ARRAY;
- }
-
- @Provides static boolean[] provideBooleanArray() {
- return BOUND_BOOLEAN_ARRAY;
- }
-
- @Provides static float[] provideFloatArray() {
- return BOUND_FLOAT_ARRAY;
- }
-
- @Provides static double[] boundDoubleArray() {
- return BOUND_DOUBLE_ARRAY;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ReferencesGeneric.java b/compiler/src/it/functional-tests/src/main/java/test/ReferencesGeneric.java
deleted file mode 100644
index 812c45d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ReferencesGeneric.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-class ReferencesGeneric {
- final Generic<A> genericA;
-
- @Inject ReferencesGeneric(Generic<A> genericA) {
- this.genericA = genericA;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ScopedGeneric.java b/compiler/src/it/functional-tests/src/main/java/test/ScopedGeneric.java
deleted file mode 100644
index 37d68e0..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ScopedGeneric.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package test;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-@Singleton
-class ScopedGeneric<T> {
- final T t;
- @Inject ScopedGeneric(T t) {
- this.t = t;
- }
-}
\ No newline at end of file
diff --git a/compiler/src/it/functional-tests/src/main/java/test/ShortKey.java b/compiler/src/it/functional-tests/src/main/java/test/ShortKey.java
deleted file mode 100644
index 01b3aa9..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/ShortKey.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-
-@MapKey(unwrapValue = true)
-@interface ShortKey {
- short value();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/SingletonGenericComponent.java b/compiler/src/it/functional-tests/src/main/java/test/SingletonGenericComponent.java
deleted file mode 100644
index 44a2cb5..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/SingletonGenericComponent.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package test;
-
-import dagger.Component;
-import javax.inject.Singleton;
-
-@Singleton
-@Component
-interface SingletonGenericComponent {
-
- ScopedGeneric<A> scopedGenericA();
- ScopedGeneric<B> scopedGenericB();
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/Thing.java b/compiler/src/it/functional-tests/src/main/java/test/Thing.java
deleted file mode 100644
index 46cbdc9..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/Thing.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* Copyright (C) 2014 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import javax.inject.Inject;
-import test.sub.OtherThing;
-
-final class Thing {
- @Inject Thing(@SuppressWarnings("unused") OtherThing unused) {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/TypeWithInheritedMembersInjection.java b/compiler/src/it/functional-tests/src/main/java/test/TypeWithInheritedMembersInjection.java
deleted file mode 100644
index 587baad..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/TypeWithInheritedMembersInjection.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import javax.inject.Inject;
-
-final class TypeWithInheritedMembersInjection extends AbstractMiddleClassWithoutMembers {
- @Inject TypeWithInheritedMembersInjection() {}
-}
-
diff --git a/compiler/src/it/functional-tests/src/main/java/test/UnwrappedAnnotationKey.java b/compiler/src/it/functional-tests/src/main/java/test/UnwrappedAnnotationKey.java
deleted file mode 100644
index 21ed958..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/UnwrappedAnnotationKey.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-import dagger.mapkeys.StringKey;
-
-@MapKey(unwrapValue = true)
-@interface UnwrappedAnnotationKey {
- StringKey value();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/WrappedAnnotationKey.java b/compiler/src/it/functional-tests/src/main/java/test/WrappedAnnotationKey.java
deleted file mode 100644
index 5d6e86d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/WrappedAnnotationKey.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import dagger.MapKey;
-import dagger.mapkeys.ClassKey;
-import dagger.mapkeys.StringKey;
-
-@MapKey(unwrapValue = false)
-@interface WrappedAnnotationKey {
- StringKey value();
- int[] integers();
- ClassKey[] annotations();
- Class<? extends Number>[] classes();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/ByteModule.java b/compiler/src/it/functional-tests/src/main/java/test/builder/ByteModule.java
deleted file mode 100644
index 8b85d60..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/ByteModule.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class ByteModule {
- final byte b;
-
- ByteModule(byte b) {
- this.b = b;
- }
-
- @Provides byte b() { return b; }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/DepComponent.java b/compiler/src/it/functional-tests/src/main/java/test/builder/DepComponent.java
deleted file mode 100644
index 93fd59d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/DepComponent.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Component;
-
-@Component
-interface DepComponent {
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/DoubleModule.java b/compiler/src/it/functional-tests/src/main/java/test/builder/DoubleModule.java
deleted file mode 100644
index 2dec4a7..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/DoubleModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class DoubleModule {
- @Provides
- double d() {
- return 4.2d;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/FloatModule.java b/compiler/src/it/functional-tests/src/main/java/test/builder/FloatModule.java
deleted file mode 100644
index 309e7ee..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/FloatModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class FloatModule {
- @Provides
- float f() {
- return 5.5f;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/GenericParent.java b/compiler/src/it/functional-tests/src/main/java/test/builder/GenericParent.java
deleted file mode 100644
index af196ee..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/GenericParent.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-interface GenericParent<B> {
- B subcomponentBuilder();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/Grandchild.java b/compiler/src/it/functional-tests/src/main/java/test/builder/Grandchild.java
deleted file mode 100644
index 8cbf67b..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/Grandchild.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Subcomponent;
-
-@Subcomponent(modules = IntModuleIncludingDoubleAndFloat.class)
-interface Grandchild {
- int i();
- String s();
-
- @Subcomponent.Builder
- interface Builder {
- Grandchild build();
- Builder set(IntModuleIncludingDoubleAndFloat intModule);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/IntModuleIncludingDoubleAndFloat.java b/compiler/src/it/functional-tests/src/main/java/test/builder/IntModuleIncludingDoubleAndFloat.java
deleted file mode 100644
index 5e3a928..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/IntModuleIncludingDoubleAndFloat.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module(includes = { DoubleModule.class, FloatModule.class })
-class IntModuleIncludingDoubleAndFloat {
- final int integer;
-
- IntModuleIncludingDoubleAndFloat(int integer) {
- this.integer = integer;
- }
-
- @Provides
- int integer() {
- return integer;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/LongModule.java b/compiler/src/it/functional-tests/src/main/java/test/builder/LongModule.java
deleted file mode 100644
index c16c9c7..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/LongModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class LongModule {
- @Provides
- long l() {
- return 6L;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java b/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java
deleted file mode 100644
index 690c91a..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Subcomponent;
-
-@MiddleScope
-@Subcomponent(modules = StringModule.class)
-interface MiddleChild {
- String s();
-
- Grandchild.Builder grandchildBuilder();
-
- RequiresSubcomponentBuilder<Grandchild.Builder> requiresGrandchildBuilder();
-
- @Subcomponent.Builder
- interface Builder {
- MiddleChild build();
- Builder set(StringModule stringModule);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleScope.java b/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleScope.java
deleted file mode 100644
index e2fbcaa..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleScope.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import java.lang.annotation.Retention;
-import javax.inject.Scope;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Scope
-@Retention(RUNTIME)
-@interface MiddleScope {
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/OtherMiddleChild.java b/compiler/src/it/functional-tests/src/main/java/test/builder/OtherMiddleChild.java
deleted file mode 100644
index 28e43ba..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/OtherMiddleChild.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Subcomponent;
-
-@MiddleScope
-@Subcomponent(modules = {StringModule.class, LongModule.class})
-interface OtherMiddleChild {
- long l();
- String s();
-
- Grandchild.Builder grandchildBuilder();
-
- @Subcomponent.Builder
- interface Builder {
- OtherMiddleChild build();
- Builder set(StringModule stringModule);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java b/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java
deleted file mode 100644
index 584eff6..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Component;
-import javax.inject.Singleton;
-
-@Singleton
-@Component
-interface ParentComponent {
- TestChildComponentWithBuilderAbstractClass.Builder childAbstractClassBuilder();
- TestChildComponentWithBuilderInterface.Builder childInterfaceBuilder();
-
- MiddleChild.Builder middleBuilder();
- OtherMiddleChild.Builder otherBuilder();
-
- RequiresSubcomponentBuilder<MiddleChild.Builder> requiresMiddleChildBuilder();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/ParentOfGenericComponent.java b/compiler/src/it/functional-tests/src/main/java/test/builder/ParentOfGenericComponent.java
deleted file mode 100644
index 474c617..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/ParentOfGenericComponent.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Component;
-import javax.inject.Singleton;
-
-@Component(modules = StringModule.class)
-@Singleton
-interface ParentOfGenericComponent extends GenericParent<Grandchild.Builder> {}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/RequiresSubcomponentBuilder.java b/compiler/src/it/functional-tests/src/main/java/test/builder/RequiresSubcomponentBuilder.java
deleted file mode 100644
index ee99632..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/RequiresSubcomponentBuilder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-class RequiresSubcomponentBuilder<B> {
- private final Provider<B> subcomponentBuilderProvider;
- private final B subcomponentBuilder;
-
- @Inject
- RequiresSubcomponentBuilder(Provider<B> subcomponentBuilderProvider, B subcomponentBuilder) {
- this.subcomponentBuilderProvider = subcomponentBuilderProvider;
- this.subcomponentBuilder = subcomponentBuilder;
- }
-
- Provider<B> subcomponentBuilderProvider() {
- return subcomponentBuilderProvider;
- }
-
- B subcomponentBuilder() {
- return subcomponentBuilder;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/StringModule.java b/compiler/src/it/functional-tests/src/main/java/test/builder/StringModule.java
deleted file mode 100644
index 3b979a5..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/StringModule.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class StringModule {
- final String string;
-
- StringModule(String string) {
- this.string = string;
- }
-
- @Provides
- String string() {
- return string;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderAbstractClass.java b/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderAbstractClass.java
deleted file mode 100644
index 8f39c14..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderAbstractClass.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Subcomponent;
-
-@Subcomponent(modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class,
- LongModule.class, ByteModule.class})
-interface TestChildComponentWithBuilderAbstractClass {
- String s();
- int i();
- long l();
- float f();
- double d();
- byte b();
-
- abstract class SharedBuilder<B, C, M1, M2> {
- abstract C build(); // Test resolving return type of build()
- abstract B setM1(M1 m1); // Test resolving return type & param of setter
- abstract SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
- abstract void setM3(DoubleModule doubleModule); // Test being overridden
- abstract SharedBuilder<B, C, M1, M2> set(FloatModule floatModule); // Test returning supertype.
- }
-
- @Subcomponent.Builder
- abstract class Builder extends SharedBuilder<Builder, TestChildComponentWithBuilderAbstractClass,
- StringModule, IntModuleIncludingDoubleAndFloat> {
- @Override abstract Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariance
- @Override abstract void setM3(DoubleModule doubleModule); // Test simple overrides allowed
- abstract void set(ByteModule byteModule);
-
- // Note we're missing LongModule -- it's implicit
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderInterface.java b/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderInterface.java
deleted file mode 100644
index 2add34e..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderInterface.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Subcomponent;
-
-@Subcomponent(modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class,
- LongModule.class, ByteModule.class})
-interface TestChildComponentWithBuilderInterface {
- String s();
- int i();
- long l();
- float f();
- double d();
- byte b();
-
- interface SharedBuilder<B, C, M1, M2> {
- C build(); // Test resolving return type of build()
- B setM1(M1 m1); // Test resolving return type & param of setter
- SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
- void setM3(DoubleModule doubleModule); // Test being overridden
- SharedBuilder<B, C, M1, M2> set(FloatModule floatModule); // Test return type is supertype.
- }
-
- @Subcomponent.Builder
- interface Builder extends SharedBuilder<Builder, TestChildComponentWithBuilderInterface,
- StringModule, IntModuleIncludingDoubleAndFloat> {
- @Override Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariant overrides
- @Override void setM3(DoubleModule doubleModule); // Test simple overrides allowed
- void set(ByteModule byteModule);
-
- // Note we're missing LongModule -- it's implicit
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderAbstractClass.java b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderAbstractClass.java
deleted file mode 100644
index 5eef53f..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderAbstractClass.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Component;
-
-@Component(
- modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
- dependencies = DepComponent.class)
-abstract class TestComponentWithBuilderAbstractClass {
-
- static Builder builder() {
- return DaggerTestComponentWithBuilderAbstractClass.builder();
- }
-
- abstract String s();
- abstract int i();
- abstract long l();
- abstract float f();
- abstract double d();
-
-
- static abstract class SharedBuilder {
- // Make sure we use the overriding signature.
- abstract Object build();
-
- Object stringModule(@SuppressWarnings("unused") StringModule stringModule) {
- return null;
- }
-
- SharedBuilder ignoredLongModule(@SuppressWarnings("unused") LongModule longModule) {
- return null;
- }
-
- }
-
- @Component.Builder
- static abstract class Builder extends SharedBuilder {
- @Override abstract TestComponentWithBuilderAbstractClass build(); // Narrowing return type
- @Override abstract Builder stringModule(StringModule stringModule); // Make abstract & narrow
- abstract Builder intModule(IntModuleIncludingDoubleAndFloat intModule);
- abstract void doubleModule(DoubleModule doubleModule); // Module w/o args
- abstract void depComponent(DepComponent depComponent);
-
- Builder ignoredIntModule(
- @SuppressWarnings("unused") IntModuleIncludingDoubleAndFloat intModule) {
- return null;
- }
-
- // Note we're missing LongModule & FloatModule -- they/re implicit
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderInterface.java b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderInterface.java
deleted file mode 100644
index 55214f8..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderInterface.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Component;
-
-@Component(
- modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
- dependencies = DepComponent.class)
-interface TestComponentWithBuilderInterface {
- String s();
- int i();
- long l();
- float f();
- double d();
-
- interface SharedBuilder {
- // Make sure we use the overriding signature.
- Object build();
- Object stringModule(StringModule m1);
- }
-
- @Component.Builder
- interface Builder extends SharedBuilder {
- @Override TestComponentWithBuilderInterface build(); // Narrowing return type
- @Override Builder stringModule(StringModule stringModule); // Narrowing return type
- Builder intModule(IntModuleIncludingDoubleAndFloat intModule);
- void doubleModule(DoubleModule doubleModule); // Module w/o args
- void depComponent(DepComponent depComponent);
-
- // Note we're missing LongModule & FloatModule -- they/re implicit
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderAbstractClass.java b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderAbstractClass.java
deleted file mode 100644
index 8032185..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderAbstractClass.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Component;
-
-@Component(
- modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
- dependencies = DepComponent.class)
-interface TestComponentWithGenericBuilderAbstractClass {
- String s();
- int i();
- long l();
- float f();
- double d();
-
- static abstract class SharedBuilder<B, C, M1, M2> {
- abstract C build(); // Test resolving return type of build()
- abstract B setM1(M1 m1); // Test resolving return type & param of setter
- abstract SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
- abstract void doubleModule(DoubleModule doubleModule); // Test being overridden
- abstract SharedBuilder<B, C, M1, M2> depComponent(FloatModule floatModule); // Test return type
- }
-
- @Component.Builder
- static abstract class Builder extends SharedBuilder<Builder,
- TestComponentWithGenericBuilderAbstractClass, StringModule,
- IntModuleIncludingDoubleAndFloat> {
- @Override abstract Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariant overrides
- @Override abstract void doubleModule(DoubleModule module3); // Test simple overrides allowed
- abstract void depComponent(DepComponent depComponent);
-
- // Note we're missing LongModule & FloatModule -- they're implicit
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderInterface.java b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderInterface.java
deleted file mode 100644
index f63e3ec..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderInterface.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import dagger.Component;
-
-@Component(
- modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
- dependencies = DepComponent.class)
-interface TestComponentWithGenericBuilderInterface {
- String s();
- int i();
- long l();
- float f();
- double d();
-
- interface SharedBuilder<B, C, M1, M2> {
- C build(); // Test resolving return type of build()
- B setM1(M1 m1); // Test resolving return type & param of setter
- SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
- void doubleModule(DoubleModule doubleModule); // Test being overridden
- SharedBuilder<B, C, M1, M2> set(FloatModule floatModule); // Test return type is supertype.
- }
-
- @Component.Builder
- interface Builder extends SharedBuilder<Builder, TestComponentWithGenericBuilderInterface,
- StringModule, IntModuleIncludingDoubleAndFloat> {
- @Override Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariant overrides allowed
- @Override void doubleModule(DoubleModule module3); // Test simple overrides allowed
- void depComponent(DepComponent depComponent);
-
- // Note we're missing M5 -- that's implicit.
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/cycle/Cycles.java b/compiler/src/it/functional-tests/src/main/java/test/cycle/Cycles.java
deleted file mode 100644
index 8d67d92..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/cycle/Cycles.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.cycle;
-
-import dagger.Component;
-import dagger.Lazy;
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-import dagger.mapkeys.StringKey;
-import java.util.Map;
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-import static dagger.Provides.Type.MAP;
-
-/**
- * Cycle classes used for testing cyclic dependencies.
- * A <- (E <- D <- B <- C <- Provider<A>, Lazy<A>), (B <- C <- Provider<A>, Lazy<A>)
- * S <- Provider<S>, Lazy<S>
- *
- * @author Tony Bentancur
- * @since 2.0
- */
-
-final class Cycles {
- private Cycles() {}
-
- static class A {
- public final B b;
- public final E e;
-
- @Inject
- A(E e, B b) {
- this.e = e;
- this.b = b;
- }
- }
-
- static class B {
- public final C c;
-
- @Inject
- B(C c) {
- this.c = c;
- }
- }
-
- static class C {
- public final Provider<A> aProvider;
- @Inject public Lazy<A> aLazy;
-
- @Inject
- C(Provider<A> aProvider) {
- this.aProvider = aProvider;
- }
- }
-
- static class D {
- public final B b;
-
- @Inject
- D(B b) {
- this.b = b;
- }
- }
-
- static class E {
- public final D d;
-
- @Inject
- E(D d) {
- this.d = d;
- }
- }
-
- static class S {
- public final Provider<S> sProvider;
- @Inject public Lazy<S> sLazy;
-
- @Inject
- S(Provider<S> sProvider) {
- this.sProvider = sProvider;
- }
- }
-
- static class X {
- public final Y y;
-
- @Inject
- X(Y y) {
- this.y = y;
- }
- }
-
- static class Y {
- public final Map<String, Provider<X>> mapOfProvidersOfX;
- public final Map<String, Provider<Y>> mapOfProvidersOfY;
-
- @Inject
- Y(Map<String, Provider<X>> mapOfProvidersOfX, Map<String, Provider<Y>> mapOfProvidersOfY) {
- this.mapOfProvidersOfX = mapOfProvidersOfX;
- this.mapOfProvidersOfY = mapOfProvidersOfY;
- }
- }
-
- @Module
- static class CycleMapModule {
- @Provides(type = MAP)
- @StringKey("X")
- static X x(X x) {
- return x;
- }
-
- @Provides(type = MAP)
- @StringKey("Y")
- static Y y(Y y) {
- return y;
- }
- }
-
- @SuppressWarnings("dependency-cycle")
- @Component(modules = CycleMapModule.class)
- interface CycleMapComponent {
- Y y();
- }
-
- @SuppressWarnings("dependency-cycle")
- @Component
- interface CycleComponent {
- A a();
-
- C c();
-
- ChildCycleComponent child();
- }
-
- @SuppressWarnings("dependency-cycle")
- @Component
- interface SelfCycleComponent {
- S s();
- }
-
- @Subcomponent
- interface ChildCycleComponent {
- @SuppressWarnings("dependency-cycle")
- A a();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/cycle/LongCycle.java b/compiler/src/it/functional-tests/src/main/java/test/cycle/LongCycle.java
deleted file mode 100644
index b4f61e0..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/cycle/LongCycle.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.cycle;
-
-import dagger.Component;
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-final class LongCycle {
- static class Class1 { @Inject Class1(Class2 class2) {} }
- static class Class2 { @Inject Class2(Class3 class3) {} }
- static class Class3 { @Inject Class3(Class4 class4) {} }
- static class Class4 { @Inject Class4(Class5 class5) {} }
- static class Class5 { @Inject Class5(Class6 class6) {} }
- static class Class6 { @Inject Class6(Class7 class7) {} }
- static class Class7 { @Inject Class7(Class8 class8) {} }
- static class Class8 { @Inject Class8(Class9 class9) {} }
- static class Class9 { @Inject Class9(Class10 class10) {} }
- static class Class10 { @Inject Class10(Class11 class11) {} }
- static class Class11 { @Inject Class11(Class12 class12) {} }
- static class Class12 { @Inject Class12(Class13 class13) {} }
- static class Class13 { @Inject Class13(Class14 class14) {} }
- static class Class14 { @Inject Class14(Class15 class15) {} }
- static class Class15 { @Inject Class15(Class16 class16) {} }
- static class Class16 { @Inject Class16(Class17 class17) {} }
- static class Class17 { @Inject Class17(Class18 class18) {} }
- static class Class18 { @Inject Class18(Class19 class19) {} }
- static class Class19 { @Inject Class19(Class20 class20) {} }
- static class Class20 { @Inject Class20(Class21 class21) {} }
- static class Class21 { @Inject Class21(Class22 class22) {} }
- static class Class22 { @Inject Class22(Class23 class23) {} }
- static class Class23 { @Inject Class23(Class24 class24) {} }
- static class Class24 { @Inject Class24(Class25 class25) {} }
- static class Class25 { @Inject Class25(Class26 class26) {} }
- static class Class26 { @Inject Class26(Class27 class27) {} }
- static class Class27 { @Inject Class27(Class28 class28) {} }
- static class Class28 { @Inject Class28(Class29 class29) {} }
- static class Class29 { @Inject Class29(Class30 class30) {} }
- static class Class30 { @Inject Class30(Class31 class31) {} }
- static class Class31 { @Inject Class31(Class32 class32) {} }
- static class Class32 { @Inject Class32(Class33 class33) {} }
- static class Class33 { @Inject Class33(Class34 class34) {} }
- static class Class34 { @Inject Class34(Class35 class35) {} }
- static class Class35 { @Inject Class35(Class36 class36) {} }
- static class Class36 { @Inject Class36(Class37 class37) {} }
- static class Class37 { @Inject Class37(Class38 class38) {} }
- static class Class38 { @Inject Class38(Class39 class39) {} }
- static class Class39 { @Inject Class39(Class40 class40) {} }
- static class Class40 { @Inject Class40(Class41 class41) {} }
- static class Class41 { @Inject Class41(Class42 class42) {} }
- static class Class42 { @Inject Class42(Class43 class43) {} }
- static class Class43 { @Inject Class43(Class44 class44) {} }
- static class Class44 { @Inject Class44(Class45 class45) {} }
- static class Class45 { @Inject Class45(Class46 class46) {} }
- static class Class46 { @Inject Class46(Class47 class47) {} }
- static class Class47 { @Inject Class47(Class48 class48) {} }
- static class Class48 { @Inject Class48(Class49 class49) {} }
- static class Class49 { @Inject Class49(Class50 class50) {} }
- static class Class50 { @Inject Class50(Class51 class51) {} }
- static class Class51 { @Inject Class51(Class52 class52) {} }
- static class Class52 { @Inject Class52(Class53 class53) {} }
- static class Class53 { @Inject Class53(Class54 class54) {} }
- static class Class54 { @Inject Class54(Class55 class55) {} }
- static class Class55 { @Inject Class55(Class56 class56) {} }
- static class Class56 { @Inject Class56(Class57 class57) {} }
- static class Class57 { @Inject Class57(Class58 class58) {} }
- static class Class58 { @Inject Class58(Class59 class59) {} }
- static class Class59 { @Inject Class59(Class60 class60) {} }
- static class Class60 { @Inject Class60(Class61 class61) {} }
- static class Class61 { @Inject Class61(Class62 class62) {} }
- static class Class62 { @Inject Class62(Class63 class63) {} }
- static class Class63 { @Inject Class63(Class64 class64) {} }
- static class Class64 { @Inject Class64(Class65 class65) {} }
- static class Class65 { @Inject Class65(Class66 class66) {} }
- static class Class66 { @Inject Class66(Class67 class67) {} }
- static class Class67 { @Inject Class67(Class68 class68) {} }
- static class Class68 { @Inject Class68(Class69 class69) {} }
- static class Class69 { @Inject Class69(Class70 class70) {} }
- static class Class70 { @Inject Class70(Class71 class71) {} }
- static class Class71 { @Inject Class71(Class72 class72) {} }
- static class Class72 { @Inject Class72(Class73 class73) {} }
- static class Class73 { @Inject Class73(Class74 class74) {} }
- static class Class74 { @Inject Class74(Class75 class75) {} }
- static class Class75 { @Inject Class75(Class76 class76) {} }
- static class Class76 { @Inject Class76(Class77 class77) {} }
- static class Class77 { @Inject Class77(Class78 class78) {} }
- static class Class78 { @Inject Class78(Class79 class79) {} }
- static class Class79 { @Inject Class79(Class80 class80) {} }
- static class Class80 { @Inject Class80(Class81 class81) {} }
- static class Class81 { @Inject Class81(Class82 class82) {} }
- static class Class82 { @Inject Class82(Class83 class83) {} }
- static class Class83 { @Inject Class83(Class84 class84) {} }
- static class Class84 { @Inject Class84(Class85 class85) {} }
- static class Class85 { @Inject Class85(Class86 class86) {} }
- static class Class86 { @Inject Class86(Class87 class87) {} }
- static class Class87 { @Inject Class87(Class88 class88) {} }
- static class Class88 { @Inject Class88(Class89 class89) {} }
- static class Class89 { @Inject Class89(Class90 class90) {} }
- static class Class90 { @Inject Class90(Class91 class91) {} }
- static class Class91 { @Inject Class91(Class92 class92) {} }
- static class Class92 { @Inject Class92(Class93 class93) {} }
- static class Class93 { @Inject Class93(Class94 class94) {} }
- static class Class94 { @Inject Class94(Class95 class95) {} }
- static class Class95 { @Inject Class95(Class96 class96) {} }
- static class Class96 { @Inject Class96(Class97 class97) {} }
- static class Class97 { @Inject Class97(Class98 class98) {} }
- static class Class98 { @Inject Class98(Class99 class99) {} }
- static class Class99 { @Inject Class99(Class100 class100) {} }
- static class Class100 { @Inject Class100(Class101 class101) {} }
- static class Class101 { @Inject Class101(Provider<Class1> class1Provider) {} }
-
- @SuppressWarnings("dependency-cycle")
- @Component
- interface LongCycleComponent {
- Class1 class1();
- }
-
- private LongCycle() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfArrayOfParentOfStringArray.java b/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfArrayOfParentOfStringArray.java
deleted file mode 100644
index 22efcf1..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfArrayOfParentOfStringArray.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-class ChildOfArrayOfParentOfStringArray extends
- MembersInjectGenericParent<MembersInjectGenericParent<String[]>[]> {
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfPrimitiveIntArray.java b/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfPrimitiveIntArray.java
deleted file mode 100644
index e01c1c2..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfPrimitiveIntArray.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-class ChildOfPrimitiveIntArray extends MembersInjectGenericParent<int[]> {
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfStringArray.java b/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfStringArray.java
deleted file mode 100644
index 8ec943b..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfStringArray.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-class ChildOfStringArray extends MembersInjectGenericParent<String[]> {
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectComponent.java b/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectComponent.java
deleted file mode 100644
index 9ab8c19..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectComponent.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-import dagger.Component;
-
-@Component(modules = {MembersInjectModule.class})
-interface MembersInjectComponent {
-
- void inject(ChildOfStringArray subfoo);
- void inject(ChildOfArrayOfParentOfStringArray subfoo);
- void inject(ChildOfPrimitiveIntArray subfoo);
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectGenericParent.java b/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectGenericParent.java
deleted file mode 100644
index 064b886..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectGenericParent.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-import javax.inject.Inject;
-
-class MembersInjectGenericParent<T> {
-
- @Inject T t;
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectModule.java b/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectModule.java
deleted file mode 100644
index a6c1fad..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectModule.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class MembersInjectModule {
-
- @Provides String[] provideStringArray() { return new String[10]; }
-
- @Provides int[] provideIntArray() { return new int[10]; }
-
- @SuppressWarnings("unchecked")
- @Provides MembersInjectGenericParent<String[]>[] provideFooArrayOfStringArray() { return new MembersInjectGenericParent[10]; }
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/membersinject/NonRequestedChild.java b/compiler/src/it/functional-tests/src/main/java/test/membersinject/NonRequestedChild.java
deleted file mode 100644
index 108a1b5..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/membersinject/NonRequestedChild.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-import javax.inject.Inject;
-
-/**
- * A class that should not be requested by any component, to ensure that we still generate a members
- * injector for it.
- */
-class NonRequestedChild extends MembersInjectGenericParent<String> {
- @Inject
- NonRequestedChild() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/FooComponent.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/FooComponent.java
deleted file mode 100644
index 3c88415..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/FooComponent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage;
-
-import dagger.Component;
-import java.util.Set;
-import test.multipackage.a.AModule;
-import test.multipackage.sub.FooChildComponent;
-
-/**
- * A component that tests the interaction between subcomponents, multiple packages, and
- * multibindings. Specifically, we want:
- * <ul>
- * <li>A set binding with some contributions in the parent component, and some in the subcomponent.
- * <li>The contributions come from different packages, but not the package of either component.
- * <li>The set binding is requested in the subcomponent through a binding from a separate package.
- * <li>No binding in the subcomponent, that's in the subcomponent's package, directly uses any
- * binding from the component's package.
- * </ul>
- */
-// NOTE(beder): Be careful about changing any bindings in either this component or the subcomponent.
-// Even adding a binding might stop this test from testing what it's supposed to test.
-@Component(modules = {AModule.class})
-interface FooComponent {
- Set<String> setOfString();
-
- FooChildComponent fooChildComponent();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/MembersInjectionVisibilityComponent.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/MembersInjectionVisibilityComponent.java
deleted file mode 100644
index 85ce40a..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/MembersInjectionVisibilityComponent.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.multipackage;
-
-import dagger.Component;
-import test.multipackage.a.AGrandchild;
-import test.multipackage.a.AModule;
-import test.multipackage.a.AParent;
-import test.multipackage.b.BChild;
-
-/**
- * A component that tests members injection across packages and subclasses.
- */
-@Component(modules = {AModule.class})
-public interface MembersInjectionVisibilityComponent {
- void inject(AParent aParent);
-
- void inject(BChild aChild);
-
- void inject(AGrandchild aGrandchild);
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AGrandchild.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AGrandchild.java
deleted file mode 100644
index 8f0f1f3..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AGrandchild.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.multipackage.a;
-
-import javax.inject.Inject;
-import test.multipackage.b.BChild;
-
-public class AGrandchild extends BChild {
-
- @Inject APackagePrivateObject aGrandchildField;
-
- private APackagePrivateObject aGrandchildMethod;
-
- @Inject
- void aGrandchildMethod(APackagePrivateObject aGrandchildMethod) {
- this.aGrandchildMethod = aGrandchildMethod;
- }
-
- @Override
- @Inject
- protected void aParentMethod(APublicObject aParentMethod) {
- super.aParentMethod(aParentMethod);
- }
-
- @Override
- protected void aChildMethod(APublicObject aChildMethod) {
- super.aChildMethod(aChildMethod);
- }
-
- public APackagePrivateObject aGrandchildField() {
- return aGrandchildField;
- }
-
- public APackagePrivateObject aGrandchildMethod() {
- return aGrandchildMethod;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AModule.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AModule.java
deleted file mode 100644
index d62506a..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AModule.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage.a;
-
-import dagger.Module;
-import dagger.Provides;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-public final class AModule {
- @Provides(type = SET) String provideString() {
- return "a";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APackagePrivateObject.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APackagePrivateObject.java
deleted file mode 100644
index d604133..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APackagePrivateObject.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.multipackage.a;
-
-import javax.inject.Inject;
-
-class APackagePrivateObject {
-
- @Inject
- APackagePrivateObject() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AParent.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AParent.java
deleted file mode 100644
index 4c91a6f..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AParent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.multipackage.a;
-
-import javax.inject.Inject;
-
-public class AParent {
-
- @Inject APackagePrivateObject aParentField;
-
- private APublicObject aParentMethod;
-
- @Inject
- protected void aParentMethod(APublicObject aParentMethod) {
- this.aParentMethod = aParentMethod;
- }
-
- public APackagePrivateObject aParentField() {
- return aParentField;
- }
-
- public APublicObject aParentMethod() {
- return aParentMethod;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APublicObject.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APublicObject.java
deleted file mode 100644
index 90357f6..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APublicObject.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.multipackage.a;
-
-import javax.inject.Inject;
-
-public class APublicObject {
-
- @Inject
- APublicObject() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BChild.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BChild.java
deleted file mode 100644
index 188d120..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BChild.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.multipackage.b;
-
-import javax.inject.Inject;
-import test.multipackage.a.AParent;
-import test.multipackage.a.APublicObject;
-
-public class BChild extends AParent {
-
- @Inject BPackagePrivateObject aChildField;
-
- private APublicObject aChildMethod;
-
- @Inject
- protected void aChildMethod(APublicObject aChildMethod) {
- this.aChildMethod = aChildMethod;
- }
-
- @Override
- protected void aParentMethod(APublicObject aParentMethod) {
- super.aParentMethod(aParentMethod);
- }
-
- public BPackagePrivateObject aChildField() {
- return aChildField;
- }
-
- public APublicObject aChildMethod() {
- return aChildMethod;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BModule.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BModule.java
deleted file mode 100644
index 4d817f1..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BModule.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage.b;
-
-import dagger.Module;
-import dagger.Provides;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-public final class BModule {
- @Provides(type = SET) String provideString() {
- return "b";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BPackagePrivateObject.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BPackagePrivateObject.java
deleted file mode 100644
index c397a02..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BPackagePrivateObject.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.multipackage.b;
-
-import javax.inject.Inject;
-
-class BPackagePrivateObject {
-
- @Inject
- BPackagePrivateObject() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/c/CModule.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/c/CModule.java
deleted file mode 100644
index e608afb..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/c/CModule.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage.c;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.Set;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-public final class CModule {
- @Provides(type = SET) String provideString() {
- return "c";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/d/DModule.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/d/DModule.java
deleted file mode 100644
index 51f8ace..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/d/DModule.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage.d;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.Set;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-public final class DModule {
- @Provides(type = SET) String provideString() {
- return "d";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/foo/Foo.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/foo/Foo.java
deleted file mode 100644
index 35f5862..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/foo/Foo.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage.foo;
-
-import java.util.Set;
-import javax.inject.Inject;
-
-public final class Foo<T> {
- public final Set<String> strings;
-
- @Inject Foo(Set<String> strings) {
- this.strings = strings;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/grandsub/FooGrandchildComponent.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/grandsub/FooGrandchildComponent.java
deleted file mode 100644
index 16a61dd..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/grandsub/FooGrandchildComponent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage.grandsub;
-
-import dagger.Subcomponent;
-import test.multipackage.d.DModule;
-import test.multipackage.foo.Foo;
-
-@Subcomponent(modules = DModule.class)
-public interface FooGrandchildComponent {
- Foo<FooGrandchildComponent> foo();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/multipackage/sub/FooChildComponent.java b/compiler/src/it/functional-tests/src/main/java/test/multipackage/sub/FooChildComponent.java
deleted file mode 100644
index 9050fcd..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/multipackage/sub/FooChildComponent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package test.multipackage.sub;
-
-import dagger.Subcomponent;
-import test.multipackage.b.BModule;
-import test.multipackage.c.CModule;
-import test.multipackage.foo.Foo;
-import test.multipackage.grandsub.FooGrandchildComponent;
-
-@Subcomponent(modules = {BModule.class, CModule.class})
-public interface FooChildComponent {
- Foo<FooChildComponent> foo();
-
- FooGrandchildComponent fooGrandchildComponent();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponent.java b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponent.java
deleted file mode 100644
index a8a5724..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponent.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test.nullables;
-
-import javax.inject.Provider;
-
-import dagger.Component;
-
-@Component(modules = NullModule.class)
-interface NullComponent {
- NullFoo nullFoo();
- @Nullable String string();
- Provider<String> stringProvider();
- Number number();
- Provider<Number> numberProvider();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponentWithDependency.java b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponentWithDependency.java
deleted file mode 100644
index 05093ed..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponentWithDependency.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test.nullables;
-
-import javax.inject.Provider;
-
-import dagger.Component;
-
-@Component(dependencies = NullComponent.class)
-interface NullComponentWithDependency {
- @Nullable String string();
- Provider<String> stringProvider();
- Number number();
- Provider<Number> numberProvider();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullFoo.java b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullFoo.java
deleted file mode 100644
index 9ed4b5d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullFoo.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test.nullables;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-class NullFoo {
- final String string;
- final Provider<String> stringProvider;
- final Number number;
- final Provider<Number> numberProvider;
-
- @Inject
- NullFoo(@Nullable String string,
- Provider<String> stringProvider,
- Number number,
- Provider<Number> numberProvider) {
- this.string = string;
- this.stringProvider = stringProvider;
- this.number = number;
- this.numberProvider = numberProvider;
- }
-
- String methodInjectedString;
- Provider<String> methodInjectedStringProvider;
- Number methodInjectedNumber;
- Provider<Number> methodInjectedNumberProvider;
- @Inject void inject(@Nullable String string,
- Provider<String> stringProvider,
- Number number,
- Provider<Number> numberProvider) {
- this.methodInjectedString = string;
- this.methodInjectedStringProvider = stringProvider;
- this.methodInjectedNumber = number;
- this.methodInjectedNumberProvider = numberProvider;
- }
-
- @Nullable @Inject String fieldInjectedString;
- @Inject Provider<String> fieldInjectedStringProvider;
- @Inject Number fieldInjectedNumber;
- @Inject Provider<Number> fieldInjectedNumberProvider;
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullModule.java b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullModule.java
deleted file mode 100644
index 652d5eb..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/nullables/NullModule.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test.nullables;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class NullModule {
- Number numberValue = null;
-
- @Nullable
- @Provides
- String provideNullableString() {
- return null;
- }
-
- @Provides
- Number provideNumber() {
- return numberValue;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/nullables/Nullable.java b/compiler/src/it/functional-tests/src/main/java/test/nullables/Nullable.java
deleted file mode 100644
index 8677640..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/nullables/Nullable.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package test.nullables;
-
-@interface Nullable {}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/AllStaticModule.java b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/AllStaticModule.java
deleted file mode 100644
index f47d36c..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/AllStaticModule.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.staticprovides;
-
-import static dagger.Provides.Type.SET;
-import static dagger.Provides.Type.SET_VALUES;
-import static java.util.Collections.emptySet;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.Set;
-
-@Module
-final class AllStaticModule {
- @Provides(type = SET) static String contributeString() {
- return AllStaticModule.class + ".contributeString";
- }
-
- @Provides(type = SET_VALUES) static Set<Integer> contibuteEmptyIntegerSet() {
- return emptySet();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/SomeStaticModule.java b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/SomeStaticModule.java
deleted file mode 100644
index 53ee14d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/SomeStaticModule.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.staticprovides;
-
-import static dagger.Provides.Type.SET;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class SomeStaticModule {
- @Provides(type = SET) static String contributeStringFromAStaticMethod() {
- return SomeStaticModule.class + ".contributeStringFromAStaticMethod";
- }
-
- @Provides(type = SET) String contributeStringFromAnInstanceMethod() {
- return SomeStaticModule.class + ".contributeStringFromAnInstanceMethod";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponent.java b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponent.java
deleted file mode 100644
index 4be51ed..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.staticprovides;
-
-import dagger.Component;
-import java.util.Set;
-
-/**
- * A simple component that demonstrates both static and non-static provides methods.
- */
-@Component(modules = {AllStaticModule.class, SomeStaticModule.class})
-interface StaticTestComponent {
- Set<String> getMultiboundStrings();
- Set<Integer> getMultiboundIntegers();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponentWithBuilder.java b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponentWithBuilder.java
deleted file mode 100644
index d778fc5..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponentWithBuilder.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.staticprovides;
-
-import dagger.Component;
-
-/**
- * A simple component that demonstrates both static and non-static provides methods with a builder.
- */
-@Component(modules = {AllStaticModule.class, SomeStaticModule.class})
-interface StaticTestComponentWithBuilder extends StaticTestComponent {
- @Component.Builder
- interface Builder {
- Builder allStaticModule(AllStaticModule allStaticModule);
- Builder someStaticModule(SomeStaticModule someStaticModule);
- StaticTestComponentWithBuilder build();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/sub/ContributionsModule.java b/compiler/src/it/functional-tests/src/main/java/test/sub/ContributionsModule.java
deleted file mode 100644
index b10ac45..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/sub/ContributionsModule.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.sub;
-
-import dagger.Module;
-import dagger.Provides;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-import static dagger.Provides.Type.SET;
-import static dagger.Provides.Type.SET_VALUES;
-
-@Module
-public final class ContributionsModule {
- @Provides(type = SET) int contributeAnInt(double doubleDependency) {
- return 1742;
- }
-
- @Provides(type = SET) int contributeAnotherInt() {
- return 832;
- }
-
- @Provides(type = SET_VALUES) Set<Integer> contributeSomeInts() {
- return Collections.unmodifiableSet(new LinkedHashSet<Integer>(Arrays.asList(-1, -90, -17)));
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/sub/Exposed.java b/compiler/src/it/functional-tests/src/main/java/test/sub/Exposed.java
deleted file mode 100644
index 9195b33..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/sub/Exposed.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package test.sub;
-
-import javax.inject.Inject;
-import test.Generic;
-import test.Generic2;
-
-public class Exposed {
-
- @Inject public Generic2<PackagePrivate> gpp2;
- @Inject public Generic2<PackagePrivateContainer.PublicEnclosed> gppc2;
-
- public Generic<PackagePrivate> gpp;
- public Generic<PackagePrivateContainer.PublicEnclosed> gppc;
-
- @Inject Exposed(Generic<PackagePrivate> gpp, Generic<PackagePrivateContainer.PublicEnclosed> gppc) {
- this.gpp = gpp;
- this.gppc = gppc;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/sub/OtherThing.java b/compiler/src/it/functional-tests/src/main/java/test/sub/OtherThing.java
deleted file mode 100644
index 9493517..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/sub/OtherThing.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.sub;
-
-import javax.inject.Inject;
-
-public final class OtherThing {
- @Inject public OtherThing(int i) {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivate.java b/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivate.java
deleted file mode 100644
index 9af646a..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivate.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package test.sub;
-
-import javax.inject.Inject;
-
-class PackagePrivate {
- @Inject PackagePrivate() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivateContainer.java b/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivateContainer.java
deleted file mode 100644
index 765b015..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivateContainer.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package test.sub;
-
-import javax.inject.Inject;
-
-class PackagePrivateContainer {
- public static class PublicEnclosed {
- @Inject PublicEnclosed() {}
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass.java b/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass.java
deleted file mode 100644
index 586d55d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package test.sub;
-
-import javax.inject.Inject;
-import test.Generic;
-
-public class PublicSubclass extends Generic<PackagePrivate> {
- @Inject public PublicSubclass(PackagePrivate pp) {
- super(pp);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass2.java b/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass2.java
deleted file mode 100644
index c356fa8..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass2.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package test.sub;
-
-import javax.inject.Inject;
-import test.Generic;
-
-public class PublicSubclass2 extends Generic<PackagePrivateContainer.PublicEnclosed> {
- @Inject public PublicSubclass2(PackagePrivateContainer.PublicEnclosed pp) {
- super(pp);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/AnInterface.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/AnInterface.java
deleted file mode 100644
index 8aaa015..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/AnInterface.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-interface AnInterface {
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/BoundAsSingleton.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/BoundAsSingleton.java
deleted file mode 100644
index 8ae1474..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/BoundAsSingleton.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Documented
-@Retention(RUNTIME)
-@Qualifier
-@interface BoundAsSingleton {}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java
deleted file mode 100644
index 2529433..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Subcomponent;
-
-@Subcomponent(modules = {ChildModule.class, StaticChildModule.class})
-abstract class ChildAbstractClassComponent implements ChildComponent {
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java
deleted file mode 100644
index d3c28f2..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Subcomponent;
-import java.util.Set;
-import javax.inject.Provider;
-
-@Subcomponent(modules = {ChildModule.class, StaticChildModule.class})
-interface ChildComponent {
- Provider<UnscopedType> getUnscopedTypeProvider();
-
- RequiresSingletons requiresSingleton();
-
- Set<Object> objectSet();
-
- GrandchildComponent newGrandchildComponent();
-
- Object object();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentRequiringModules.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentRequiringModules.java
deleted file mode 100644
index 905c689..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentRequiringModules.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Subcomponent;
-
-@Subcomponent(modules = {
- ChildModule.class,
- ChildModuleWithParameters.class,
- ChildModuleWithState.class})
-interface ChildComponentRequiringModules {
- int getInt();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentWithMultibindings.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentWithMultibindings.java
deleted file mode 100644
index 9ed266a..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentWithMultibindings.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Subcomponent;
-
-@Subcomponent(modules = ChildMultibindingModule.class)
-interface ChildComponentWithMultibindings {
- RequiresMultibindingsInChild requiresMultibindingsInChild();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModule.java
deleted file mode 100644
index ef28bd4..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModule.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-import dagger.Provides;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-final class ChildModule {
- @Provides(type = SET) Object provideUnscopedObject() {
- return new Object() {
- @Override public String toString() {
- return "unscoped in child";
- }
- };
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithParameters.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithParameters.java
deleted file mode 100644
index e18b4a6..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithParameters.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-
-/**
- * This is a module that can't be constructed with a default constructor.
- */
-@Module
-final class ChildModuleWithParameters {
- public ChildModuleWithParameters(@SuppressWarnings("unused") Object whatever) {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithState.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithState.java
deleted file mode 100644
index 5908a00..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithState.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * This is a module that can be constructed with a default constructor, but has state, so callers
- * might want to pass a reference anyway.
- */
-@Module
-final class ChildModuleWithState {
- private int i = 0;
-
- @Provides int provideInt() {
- return i++;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildMultibindingModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildMultibindingModule.java
deleted file mode 100644
index ae02b9e..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildMultibindingModule.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.mapkeys.StringKey;
-
-import static dagger.Provides.Type.MAP;
-import static dagger.Provides.Type.SET;
-
-@Module
-class ChildMultibindingModule {
-
- @Provides(type = SET)
- static Object childObject() {
- return "object provided by child";
- }
-
- @Provides(type = MAP)
- @StringKey("child key")
- static Object childKeyObject() {
- return "object in child";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GenericParentComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GenericParentComponent.java
deleted file mode 100644
index 5580ab8..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GenericParentComponent.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-interface GenericParentComponent<B> {
- B subcomponent();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildComponent.java
deleted file mode 100644
index 9f724ed..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildComponent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Subcomponent;
-import java.util.Set;
-import javax.inject.Provider;
-
-@Subcomponent(modules = GrandchildModule.class)
-interface GrandchildComponent {
- Provider<UnscopedType> getUnscopedTypeProvider();
-
- RequiresSingletons requiresSingleton();
-
- Set<Object> objectSet();
-
- NeedsAnInterface needsAnInterface();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildModule.java
deleted file mode 100644
index b288541..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildModule.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-import dagger.Provides;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-final class GrandchildModule {
- @Provides(type = SET) Object provideUnscopedObject() {
- return new Object() {
- @Override public String toString() {
- return "unscoped in grandchild";
- }
- };
- }
-
- @Provides AnInterface provideAnInterface(ImplementsAnInterface implementsAnInterface) {
- return implementsAnInterface;
- }
-
- @Provides NeedsAnInterface provideNeedsAnInterface(AnInterface anInterface) {
- return new NeedsAnInterface(anInterface);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ImplementsAnInterface.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ImplementsAnInterface.java
deleted file mode 100644
index ff3170c..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ImplementsAnInterface.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import javax.inject.Inject;
-
-class ImplementsAnInterface implements AnInterface {
- @Inject ImplementsAnInterface() {}
-}
-
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/NeedsAnInterface.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/NeedsAnInterface.java
deleted file mode 100644
index bccde85..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/NeedsAnInterface.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-class NeedsAnInterface {
- NeedsAnInterface(AnInterface anInterface) {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponent.java
deleted file mode 100644
index ebb067d..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Component;
-import javax.inject.Singleton;
-
-@Component(modules = ParentModule.class)
-@Singleton
-interface ParentComponent extends ParentGetters {
- ChildComponent newChildComponent();
-
- ChildAbstractClassComponent newChildAbstractClassComponent();
-
- ChildComponentRequiringModules newChildComponentRequiringModules(
- ChildModuleWithParameters cmwp,
- ChildModuleWithState childModuleWithState);
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithMultibindings.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithMultibindings.java
deleted file mode 100644
index 46fe883..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithMultibindings.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Component;
-
-@Component(modules = ParentMultibindingModule.class)
-interface ParentComponentWithMultibindings extends ParentComponentWithoutMultibindings {
- RequiresMultibindingsInParent requiresMultibindingsInParent();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithoutMultibindings.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithoutMultibindings.java
deleted file mode 100644
index 3d4431c..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithoutMultibindings.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Component;
-
-@Component(modules = ParentMultibindingModule.class)
-interface ParentComponentWithoutMultibindings {
- ChildComponentWithMultibindings childComponent();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentGetters.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentGetters.java
deleted file mode 100644
index 3ff855a..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentGetters.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import java.util.Set;
-import javax.inject.Provider;
-
-interface ParentGetters {
- Provider<UnscopedType> getUnscopedTypeProvider();
-
- Set<Object> objectSet();
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentModule.java
deleted file mode 100644
index dbe1a53..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentModule.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-import dagger.Provides;
-import javax.inject.Singleton;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-final class ParentModule {
- @Provides(type = SET) Object provideUnscopedObject() {
- return new Object() {
- @Override public String toString() {
- return "unscoped in parent";
- }
- };
- }
-
- @Provides(type = SET) @Singleton Object provideSingletonObject() {
- return new Object() {
- @Override public String toString() {
- return "singleton";
- }
- };
- }
-
- @Provides @Singleton @BoundAsSingleton UnscopedType provideUnscopedTypeBoundAsSingleton(
- UnscopedType unscopedType) {
- return unscopedType;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentMultibindingModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentMultibindingModule.java
deleted file mode 100644
index e4ec173..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentMultibindingModule.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.mapkeys.StringKey;
-
-import static dagger.Provides.Type.MAP;
-import static dagger.Provides.Type.SET;
-
-@Module
-class ParentMultibindingModule {
-
- @Provides(type = SET)
- static Object provideObject() {
- return "object provided by parent";
- }
-
- @Provides(type = SET)
- static String provideString() {
- return "string provided by parent";
- }
-
- @Provides(type = SET)
- static RequiresMultiboundObjects requiresMultiboundObjects(
- RequiresMultiboundObjects requiresMultiboundObjects) {
- return requiresMultiboundObjects;
- }
-
- @Provides(type = MAP)
- @StringKey("parent key")
- static String parentKeyString() {
- return "string in parent";
- }
-
- @Provides(type = MAP)
- @StringKey("parent key")
- static Object parentKeyObject() {
- return "object in parent";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentOfGenericComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentOfGenericComponent.java
deleted file mode 100644
index bf85537..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentOfGenericComponent.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Component;
-import javax.inject.Singleton;
-
-@Component(modules = ParentModule.class)
-@Singleton
-interface ParentOfGenericComponent extends GenericParentComponent<ChildComponent>, ParentGetters {
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInChild.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInChild.java
deleted file mode 100644
index 4ec0469..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInChild.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import java.util.Set;
-import javax.inject.Inject;
-
-class RequiresMultibindingsInChild extends RequiresMultibindingsInParent {
-
- @Inject
- RequiresMultibindingsInChild(
- RequiresMultiboundObjects requiresMultiboundObjects,
- RequiresMultiboundStrings requiresMultiboundStrings,
- Set<RequiresMultiboundObjects> setOfRequiresMultiboundObjects) {
- super(requiresMultiboundObjects, requiresMultiboundStrings, setOfRequiresMultiboundObjects);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInParent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInParent.java
deleted file mode 100644
index a48d38b..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInParent.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import java.util.Set;
-import javax.inject.Inject;
-
-class RequiresMultibindingsInParent {
- private final RequiresMultiboundObjects requiresMultiboundObjects;
- private final RequiresMultiboundStrings requiresMultiboundStrings;
- private final Set<RequiresMultiboundObjects> setOfRequiresMultiboundObjects;
-
- @Inject
- RequiresMultibindingsInParent(
- RequiresMultiboundObjects requiresMultiboundObjects,
- RequiresMultiboundStrings requiresMultiboundStrings,
- Set<RequiresMultiboundObjects> setOfRequiresMultiboundObjects) {
- this.requiresMultiboundObjects = requiresMultiboundObjects;
- this.requiresMultiboundStrings = requiresMultiboundStrings;
- this.setOfRequiresMultiboundObjects = setOfRequiresMultiboundObjects;
- }
-
- RequiresMultiboundObjects requiresMultiboundObjects() {
- return requiresMultiboundObjects;
- }
-
- RequiresMultiboundStrings requiresMultiboundStrings() {
- return requiresMultiboundStrings;
- }
-
- Set<RequiresMultiboundObjects> setOfRequiresMultiboundObjects() {
- return setOfRequiresMultiboundObjects;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundObjects.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundObjects.java
deleted file mode 100644
index d787153..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundObjects.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Inject;
-
-class RequiresMultiboundObjects {
- private final Set<Object> setOfObjects;
- private final Map<String, Object> mapOfObjects;
-
- @Inject
- RequiresMultiboundObjects(Set<Object> setOfObjects, Map<String, Object> mapOfObjects) {
- this.setOfObjects = setOfObjects;
- this.mapOfObjects = mapOfObjects;
- }
-
- Set<Object> setOfObjects() {
- return setOfObjects;
- }
-
- Map<String, Object> mapOfObjects() {
- return mapOfObjects;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundStrings.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundStrings.java
deleted file mode 100644
index 410bdf2..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundStrings.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Inject;
-
-class RequiresMultiboundStrings {
- private final Set<String> setOfStrings;
- private final Map<String, String> mapOfStrings;
-
- @Inject
- RequiresMultiboundStrings(Set<String> setOfStrings, Map<String, String> mapOfStrings) {
- this.setOfStrings = setOfStrings;
- this.mapOfStrings = mapOfStrings;
- }
-
- Set<String> setOfStrings() {
- return setOfStrings;
- }
-
- Map<String, String> mapOfStrings() {
- return mapOfStrings;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresSingletons.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresSingletons.java
deleted file mode 100644
index 2d40538..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresSingletons.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import javax.inject.Inject;
-
-final class RequiresSingletons {
- private final SingletonType singletonType;
- private final UnscopedType unscopedTypeBoundAsSingleton;
-
- @Inject RequiresSingletons(SingletonType singletonType,
- @BoundAsSingleton UnscopedType unscopedTypeBoundAsSingleton) {
- this.singletonType = singletonType;
- this.unscopedTypeBoundAsSingleton = unscopedTypeBoundAsSingleton;
- }
-
- SingletonType singletonType() {
- return singletonType;
- }
-
- UnscopedType unscopedTypeBoundAsSingleton() {
- return unscopedTypeBoundAsSingleton;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/SingletonType.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/SingletonType.java
deleted file mode 100644
index 663e858..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/SingletonType.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-@Singleton
-final class SingletonType {
- @Inject SingletonType() {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/StaticChildModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/StaticChildModule.java
deleted file mode 100644
index f7fd490..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/StaticChildModule.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class StaticChildModule {
- private StaticChildModule() {}
-
- @Provides static Object provideStaticObject() {
- return "static";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/UnscopedType.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/UnscopedType.java
deleted file mode 100644
index 89c0085..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/UnscopedType.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import javax.inject.Inject;
-
-final class UnscopedType {
- @Inject UnscopedType(@SuppressWarnings("unused") SingletonType singletonType) {}
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ChildComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ChildComponent.java
deleted file mode 100644
index b95502c..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ChildComponent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.hiding;
-
-import dagger.Subcomponent;
-
-@Subcomponent(modules = test.subcomponent.hiding.b.CommonModuleName.class)
-interface ChildComponent {
- //ensure that t.s.h.a.CommonName gets bound in this component
- test.subcomponent.hiding.a.CommonName aCommonName();
- //ensure that t.s.h.b.CommonName gets bound in this component
- test.subcomponent.hiding.b.CommonName bCommonName();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ParentComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ParentComponent.java
deleted file mode 100644
index d7c66a6..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ParentComponent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.hiding;
-
-import dagger.Component;
-import javax.inject.Singleton;
-
-@Component(modules = test.subcomponent.hiding.a.CommonModuleName.class)
-@Singleton
-interface ParentComponent {
- // ensure that t.s.h.a.CommonName gets bound in this component
- test.subcomponent.hiding.a.CommonName aCommonName();
-
- ChildComponent newChildComponent();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonModuleName.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonModuleName.java
deleted file mode 100644
index ad69289..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonModuleName.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.hiding.a;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-public class CommonModuleName {
- @Provides String provideString() {
- return "a";
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonName.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonName.java
deleted file mode 100644
index b2aefda..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonName.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.hiding.a;
-
-import javax.inject.Inject;
-
-public final class CommonName {
- private final String s;
-
- @Inject CommonName(String s) {
- this.s = s;
- }
-
- @Override
- public String toString() {
- return s;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonModuleName.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonModuleName.java
deleted file mode 100644
index 66deab5..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonModuleName.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.hiding.b;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-public class CommonModuleName {
- @Provides int provideString() {
- return 1;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonName.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonName.java
deleted file mode 100644
index 023cbdb..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonName.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.hiding.b;
-
-import javax.inject.Inject;
-
-public final class CommonName {
- private final int i;
-
- @Inject CommonName(int i) {
- this.i = i;
- }
-
- @Override
- public String toString() {
- return Integer.toString(i);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInChild.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInChild.java
deleted file mode 100644
index 2dd8d20..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInChild.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package test.subcomponent.repeat;
-
-abstract class OnlyUsedInChild {
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInParent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInParent.java
deleted file mode 100644
index cc22b1e..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInParent.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package test.subcomponent.repeat;
-
-abstract class OnlyUsedInParent {
-
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/ParentComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/ParentComponent.java
deleted file mode 100644
index f0af002..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/ParentComponent.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.repeat;
-
-import dagger.Component;
-import java.util.Set;
-
-@Component(modules = RepeatedModule.class)
-interface ParentComponent {
- Object state();
-
- String getString();
- Set<String> getMultiboundStrings();
- OnlyUsedInParent getOnlyUsedInParent();
-
- SubcomponentWithRepeatedModule.Builder newChildComponentBuilder();
-
- SubcomponentWithoutRepeatedModule newChildComponentWithoutRepeatedModule();
-
- @Component.Builder
- interface Builder {
- Builder repeatedModule(RepeatedModule repeatedModule);
-
- ParentComponent build();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/RepeatedModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/RepeatedModule.java
deleted file mode 100644
index d099751..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/RepeatedModule.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.repeat;
-
-import dagger.Module;
-import dagger.Provides;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-final class RepeatedModule {
- private final Object state = new Object();
-
- @Provides
- Object state() {
- return state;
- }
-
- @Provides
- static String provideString() {
- return "a string";
- }
-
- @Provides(type = SET)
- static String contributeString() {
- return "a string in a set";
- }
-
- @Provides
- static OnlyUsedInParent provideOnlyUsedInParent() {
- return new OnlyUsedInParent() {};
- }
-
- @Provides
- static OnlyUsedInChild provideOnlyUsedInChild() {
- return new OnlyUsedInChild() {};
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithRepeatedModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithRepeatedModule.java
deleted file mode 100644
index 279bc95..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithRepeatedModule.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.repeat;
-
-import dagger.Subcomponent;
-import java.util.Set;
-
-@Subcomponent(modules = RepeatedModule.class)
-interface SubcomponentWithRepeatedModule {
- Object state();
-
- String getString();
-
- Set<String> getMultiboundStrings();
-
- OnlyUsedInChild getOnlyUsedInChild();
-
- @Subcomponent.Builder
- interface Builder {
- Builder repeatedModule(RepeatedModule repeatedModule);
-
- SubcomponentWithRepeatedModule build();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithoutRepeatedModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithoutRepeatedModule.java
deleted file mode 100644
index e63c9a0..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithoutRepeatedModule.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.repeat;
-
-import dagger.Subcomponent;
-
-@Subcomponent
-interface SubcomponentWithoutRepeatedModule {
- SubcomponentWithRepeatedModule.Builder newGrandchildBuilder();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/tck/CarModule.java b/compiler/src/it/functional-tests/src/main/java/test/tck/CarModule.java
deleted file mode 100644
index bc78517..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/tck/CarModule.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.tck;
-
-import dagger.Module;
-import dagger.Provides;
-import org.atinject.tck.auto.Car;
-import org.atinject.tck.auto.Convertible;
-
-@Module
-class CarModule {
- @Provides
- Car provideConvertible(Convertible convertible) {
- return convertible;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/tck/CarShop.java b/compiler/src/it/functional-tests/src/main/java/test/tck/CarShop.java
deleted file mode 100644
index e42532e..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/tck/CarShop.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.tck;
-
-import dagger.Component;
-import org.atinject.tck.auto.Car;
-import javax.inject.Singleton;
-
-@Singleton
-@Component(
- modules = {
- CarModule.class,
- TireModule.class,
- SeatModule.class,
- EngineModule.class,
- FuelTankModule.class
- }
-)
-public interface CarShop {
- @SuppressWarnings("dependency-cycle")
- Car make();
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/tck/EngineModule.java b/compiler/src/it/functional-tests/src/main/java/test/tck/EngineModule.java
deleted file mode 100644
index 577fb5b..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/tck/EngineModule.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.tck;
-
-import dagger.MembersInjector;
-import dagger.Module;
-import dagger.Provides;
-import org.atinject.tck.auto.Engine;
-import org.atinject.tck.auto.V8Engine;
-
-@Module
-public class EngineModule {
- @Provides
- Engine provideEngine(MembersInjector<V8Engine> injector) {
- // This is provided because V8Engine has no @Inject constructor and Dagger requires an @Inject
- // constructor, however this is a TCK supplied class that we prefer to leave unmodified.
- V8Engine engine = new V8Engine();
- injector.injectMembers(engine);
- return engine;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/tck/FuelTankModule.java b/compiler/src/it/functional-tests/src/main/java/test/tck/FuelTankModule.java
deleted file mode 100644
index 931556c..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/tck/FuelTankModule.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.tck;
-
-import dagger.Module;
-import dagger.Provides;
-import org.atinject.tck.auto.FuelTank;
-
-@Module
-class FuelTankModule {
- @Provides
- FuelTank provideFuelTank() {
- return new FuelTank();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/tck/SeatModule.java b/compiler/src/it/functional-tests/src/main/java/test/tck/SeatModule.java
deleted file mode 100644
index 5c6b729..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/tck/SeatModule.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.tck;
-
-import dagger.Module;
-import dagger.Provides;
-import org.atinject.tck.auto.Drivers;
-import org.atinject.tck.auto.DriversSeat;
-import org.atinject.tck.auto.Seat;
-
-@Module
-class SeatModule {
- @Provides
- @Drivers
- Seat provideSeat(DriversSeat seat) {
- return seat;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/tck/TireModule.java b/compiler/src/it/functional-tests/src/main/java/test/tck/TireModule.java
deleted file mode 100644
index 914a6d6..0000000
--- a/compiler/src/it/functional-tests/src/main/java/test/tck/TireModule.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.tck;
-
-import dagger.Module;
-import dagger.Provides;
-import org.atinject.tck.auto.Tire;
-import org.atinject.tck.auto.accessories.SpareTire;
-import javax.inject.Named;
-
-@Module
-class TireModule {
- @Provides
- @Named("spare")
- Tire provideTire(SpareTire sparetire) {
- return sparetire;
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/BasicTest.java b/compiler/src/it/functional-tests/src/test/java/test/BasicTest.java
deleted file mode 100644
index fe9c6af..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/BasicTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-* Copyright (C) 2014 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import org.junit.experimental.theories.DataPoint;
-import org.junit.experimental.theories.Theories;
-import org.junit.experimental.theories.Theory;
-import org.junit.runner.RunWith;
-
-import static com.google.common.truth.Truth.assertThat;
-import static test.PrimitivesModule.BOUND_BOOLEAN;
-import static test.PrimitivesModule.BOUND_BOOLEAN_ARRAY;
-import static test.PrimitivesModule.BOUND_BYTE;
-import static test.PrimitivesModule.BOUND_BYTE_ARRAY;
-import static test.PrimitivesModule.BOUND_CHAR;
-import static test.PrimitivesModule.BOUND_CHAR_ARRAY;
-import static test.PrimitivesModule.BOUND_DOUBLE;
-import static test.PrimitivesModule.BOUND_DOUBLE_ARRAY;
-import static test.PrimitivesModule.BOUND_FLOAT;
-import static test.PrimitivesModule.BOUND_FLOAT_ARRAY;
-import static test.PrimitivesModule.BOUND_INT;
-import static test.PrimitivesModule.BOUND_INT_ARRAY;
-import static test.PrimitivesModule.BOUND_LONG;
-import static test.PrimitivesModule.BOUND_LONG_ARRAY;
-import static test.PrimitivesModule.BOUND_SHORT;
-import static test.PrimitivesModule.BOUND_SHORT_ARRAY;
-
-@RunWith(Theories.class)
-public class BasicTest {
- @DataPoint
- public static final BasicComponent basicComponent = DaggerBasicComponent.create();
- @DataPoint
- public static final BasicComponent abstractClassBasicComponent =
- DaggerBasicAbstractClassComponent.create();
-
- @Theory public void primitives(BasicComponent basicComponent) {
- assertThat(basicComponent.getByte()).isEqualTo(BOUND_BYTE);
- assertThat(basicComponent.getChar()).isEqualTo(BOUND_CHAR);
- assertThat(basicComponent.getShort()).isEqualTo(BOUND_SHORT);
- assertThat(basicComponent.getInt()).isEqualTo(BOUND_INT);
- assertThat(basicComponent.getLong()).isEqualTo(BOUND_LONG);
- assertThat(basicComponent.getBoolean()).isEqualTo(BOUND_BOOLEAN);
- assertThat(basicComponent.getFloat()).isEqualTo(BOUND_FLOAT);
- assertThat(basicComponent.getDouble()).isEqualTo(BOUND_DOUBLE);
- }
-
- @Theory public void boxedPrimitives(BasicComponent basicComponent) {
- assertThat(basicComponent.getBoxedByte()).isEqualTo(new Byte(BOUND_BYTE));
- assertThat(basicComponent.getBoxedChar()).isEqualTo(new Character(BOUND_CHAR));
- assertThat(basicComponent.getBoxedShort()).isEqualTo(new Short(BOUND_SHORT));
- assertThat(basicComponent.getBoxedInt()).isEqualTo(new Integer(BOUND_INT));
- assertThat(basicComponent.getBoxedLong()).isEqualTo(new Long(BOUND_LONG));
- assertThat(basicComponent.getBoxedBoolean()).isEqualTo(new Boolean(BOUND_BOOLEAN));
- assertThat(basicComponent.getBoxedFloat()).isEqualTo(new Float(BOUND_FLOAT));
- assertThat(basicComponent.getBoxedDouble()).isEqualTo(new Double(BOUND_DOUBLE));
- }
-
- @Theory public void boxedPrimitiveProviders(BasicComponent basicComponent) {
- assertThat(basicComponent.getByteProvider().get()).isEqualTo(new Byte(BOUND_BYTE));
- assertThat(basicComponent.getCharProvider().get()).isEqualTo(new Character(BOUND_CHAR));
- assertThat(basicComponent.getShortProvider().get()).isEqualTo(new Short(BOUND_SHORT));
- assertThat(basicComponent.getIntProvider().get()).isEqualTo(new Integer(BOUND_INT));
- assertThat(basicComponent.getLongProvider().get()).isEqualTo(new Long(BOUND_LONG));
- assertThat(basicComponent.getBooleanProvider().get()).isEqualTo(new Boolean(BOUND_BOOLEAN));
- assertThat(basicComponent.getFloatProvider().get()).isEqualTo(new Float(BOUND_FLOAT));
- assertThat(basicComponent.getDoubleProvider().get()).isEqualTo(new Double(BOUND_DOUBLE));
- }
-
- @Theory public void primitiveArrays(BasicComponent basicComponent) {
- assertThat(basicComponent.getByteArray()).isSameAs(BOUND_BYTE_ARRAY);
- assertThat(basicComponent.getCharArray()).isSameAs(BOUND_CHAR_ARRAY);
- assertThat(basicComponent.getShortArray()).isSameAs(BOUND_SHORT_ARRAY);
- assertThat(basicComponent.getIntArray()).isSameAs(BOUND_INT_ARRAY);
- assertThat(basicComponent.getLongArray()).isSameAs(BOUND_LONG_ARRAY);
- assertThat(basicComponent.getBooleanArray()).isSameAs(BOUND_BOOLEAN_ARRAY);
- assertThat(basicComponent.getFloatArray()).isSameAs(BOUND_FLOAT_ARRAY);
- assertThat(basicComponent.getDoubleArray()).isSameAs(BOUND_DOUBLE_ARRAY);
- }
-
- @Theory public void primitiveArrayProviders(BasicComponent basicComponent) {
- assertThat(basicComponent.getByteArrayProvider().get()).isSameAs(BOUND_BYTE_ARRAY);
- assertThat(basicComponent.getCharArrayProvider().get()).isSameAs(BOUND_CHAR_ARRAY);
- assertThat(basicComponent.getShortArrayProvider().get()).isSameAs(BOUND_SHORT_ARRAY);
- assertThat(basicComponent.getIntArrayProvider().get()).isSameAs(BOUND_INT_ARRAY);
- assertThat(basicComponent.getLongArrayProvider().get()).isSameAs(BOUND_LONG_ARRAY);
- assertThat(basicComponent.getBooleanArrayProvider().get()).isSameAs(BOUND_BOOLEAN_ARRAY);
- assertThat(basicComponent.getFloatArrayProvider().get()).isSameAs(BOUND_FLOAT_ARRAY);
- assertThat(basicComponent.getDoubleArrayProvider().get()).isSameAs(BOUND_DOUBLE_ARRAY);
- }
-
- @Theory public void noOpMembersInjection(BasicComponent basicComponent) {
- Object object = new Object();
- assertThat(basicComponent.noOpMembersInjection(object)).isSameAs(object);
- }
-
- @Theory public void basicObject_noDeps(BasicComponent basicComponent) {
- assertThat(basicComponent.thing()).isNotNull();
- }
-
- @Theory public void inheritedMembersInjection(BasicComponent basicComponent) {
- assertThat(basicComponent.typeWithInheritedMembersInjection().thing).isNotNull();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/DependsOnGeneratedCodeTest.java b/compiler/src/it/functional-tests/src/test/java/test/DependsOnGeneratedCodeTest.java
deleted file mode 100644
index 0310df6..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/DependsOnGeneratedCodeTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class DependsOnGeneratedCodeTest {
- @Test public void testComponentDependsOnGeneratedCode() {
- assertThat(DaggerComponentDependsOnGeneratedCode.create().needsFactory()).isNotNull();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/GenericTest.java b/compiler/src/it/functional-tests/src/test/java/test/GenericTest.java
deleted file mode 100644
index f1c981f..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/GenericTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import test.sub.Exposed;
-import test.sub.PublicSubclass;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertEquals;
-
-@RunWith(JUnit4.class)
-public class GenericTest {
-
- @Test public void testGenericComponentCreate() {
- GenericComponent component = DaggerGenericComponent.create();
- assertThat(component).isNotNull();
- }
-
- @Test public void testGenericSimpleReferences() {
- GenericComponent component = DaggerGenericComponent.create();
- assertThat(component.referencesGeneric().genericA.t).isNotNull();
- }
-
- @Test public void testGenericDoubleReferences() {
- GenericComponent component = DaggerGenericComponent.create();
- GenericDoubleReferences<A> doubleA = component.doubleGenericA();
- assertThat(doubleA.a).isNotNull();
- assertThat(doubleA.a2).isNotNull();
- assertThat(doubleA.t).isNotNull();
- assertThat(doubleA.t2).isNotNull();
-
- GenericDoubleReferences<B> doubleB = component.doubleGenericB();
- assertThat(doubleB.a).isNotNull();
- assertThat(doubleB.a2).isNotNull();
- assertThat(doubleB.t).isNotNull();
- assertThat(doubleB.t2).isNotNull();
- }
-
- @Test public void complexGenerics() {
- GenericComponent component = DaggerGenericComponent.create();
- // validate these can be called w/o exceptions.
- component.complexGenerics();
- }
-
- @Test public void noDepsGenerics() {
- GenericComponent component = DaggerGenericComponent.create();
- // validate these can be called w/o exceptions.
- component.noDepsA();
- component.noDepsB();
- }
-
- @Test public void boundedGenerics() {
- BoundedGenericModule expected = new BoundedGenericModule();
- BoundedGenericComponent component = DaggerBoundedGenericComponent.create();
- BoundedGenerics<Integer, ArrayList<String>, LinkedList<CharSequence>, Integer, List<Integer>>
- b1 = component.bounds1();
- assertEquals(expected.provideInteger(), b1.a);
- assertEquals(expected.provideArrayListString(), b1.b);
- assertEquals(expected.provideLinkedListCharSeq(), b1.c);
- assertEquals(expected.provideInteger(), b1.d);
- assertEquals(expected.provideListOfInteger(), b1.e);
-
- BoundedGenerics<Double, LinkedList<String>, LinkedList<Comparable<String>>, Double, Set<Double>>
- b2 = component.bounds2();
- assertEquals(expected.provideDouble(), b2.a);
- assertEquals(expected.provideLinkedListString(), b2.b);
- assertEquals(expected.provideArrayListOfComparableString(), b2.c);
- assertEquals(expected.provideDouble(), b2.d);
- assertEquals(expected.provideSetOfDouble(), b2.e);
- }
-
- @Test public void membersInjections() {
- GenericComponent component = DaggerGenericComponent.create();
- GenericChild<A> childA = new GenericChild<A>();
- component.injectA(childA);
- assertThat(childA.a).isNotNull();
- assertThat(childA.b).isNotNull();
- assertThat(childA.registeredA).isNotNull();
- assertThat(childA.registeredB).isNotNull();
- assertThat(childA.registeredT).isNotNull();
- assertThat(childA.registeredX).isNotNull();
- assertThat(childA.registeredY).isNotNull();
-
- GenericChild<B> childB = new GenericChild<B>();
- component.injectB(childB);
- assertThat(childB.a).isNotNull();
- assertThat(childB.b).isNotNull();
- assertThat(childB.registeredA).isNotNull();
- assertThat(childB.registeredB).isNotNull();
- assertThat(childB.registeredT).isNotNull();
- assertThat(childB.registeredX).isNotNull();
- assertThat(childB.registeredY).isNotNull();
- }
-
- @Test public void packagePrivateTypeParameterDependencies() {
- GenericComponent component = DaggerGenericComponent.create();
- Exposed exposed = component.exposed();
- assertThat(exposed.gpp.t).isNotNull();
- assertThat(exposed.gpp2).isNotNull();
- }
-
- @SuppressWarnings("rawtypes")
- @Test public void publicSubclassWithPackagePrivateTypeParameterOfSuperclass() {
- GenericComponent component = DaggerGenericComponent.create();
- PublicSubclass publicSubclass = component.publicSubclass();
- assertThat(((Generic)publicSubclass).t).isNotNull();
- }
-
- @Test public void singletonScopesAppliesToEachResolvedType() {
- SingletonGenericComponent component = DaggerSingletonGenericComponent.create();
- ScopedGeneric<A> a = component.scopedGenericA();
- assertThat(a).isSameAs(component.scopedGenericA());
- assertThat(a.t).isNotNull();
-
- ScopedGeneric<B> b = component.scopedGenericB();
- assertThat(b).isSameAs(component.scopedGenericB());
- assertThat(b.t).isNotNull();
-
- assertThat(a).isNotSameAs(b);
- }
-
- @Test public void genericModules() {
- GenericComponent component = DaggerGenericComponent.create();
- assertThat(component.iterableInt()).containsExactly(1, 2).inOrder();
- assertThat(component.iterableDouble()).containsExactly(3d, 4d).inOrder();
-
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java b/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java
deleted file mode 100644
index 1b7e302..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License
- * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing permissions and limitations under
- * the License.
- */
-package test;
-
-import com.google.auto.value.AutoAnnotation;
-import com.google.common.collect.ImmutableMap;
-import dagger.mapkeys.ClassKey;
-import dagger.mapkeys.StringKey;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Map;
-import javax.inject.Provider;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class MultibindingTest {
- private MultibindingComponent multibindingComponent;
-
- @Before public void setUp() {
- multibindingComponent = DaggerMultibindingComponent.builder()
- .multibindingDependency(new MultibindingDependency() {
- @Override public double doubleDependency() {
- return 0.0;
- }
- })
- .build();
- }
-
- @Test public void map() {
- Map<String, String> map = multibindingComponent.map();
- assertThat(map).hasSize(2);
- assertThat(map).containsEntry("foo", "foo value");
- assertThat(map).containsEntry("bar", "bar value");
- }
-
- @Test public void mapOfArrays() {
- Map<String, String[]> map = multibindingComponent.mapOfArrays();
- assertThat(map).hasSize(2);
- assertThat(map).containsKey("foo");
- assertThat(map.get("foo")).asList().containsExactly("foo1", "foo2").inOrder();
- assertThat(map).containsKey("bar");
- assertThat(map.get("bar")).asList().containsExactly("bar1", "bar2").inOrder();
- }
-
- @Test public void mapOfProviders() {
- Map<String, Provider<String>> mapOfProviders = multibindingComponent.mapOfProviders();
- assertThat(mapOfProviders).hasSize(2);
- assertThat(mapOfProviders.get("foo").get()).isEqualTo("foo value");
- assertThat(mapOfProviders.get("bar").get()).isEqualTo("bar value");
- }
-
- @Test public void mapKeysAndValues() {
- assertThat(multibindingComponent.mapKeys()).containsExactly("foo", "bar");
- assertThat(multibindingComponent.mapValues()).containsExactly("foo value", "bar value");
- }
-
- @Test public void nestedKeyMap() {
- assertThat(multibindingComponent.nestedKeyMap()).isEqualTo(
- ImmutableMap.of(
- nestedWrappedKey(Integer.class), "integer",
- nestedWrappedKey(Long.class), "long"));
- }
-
- @Test
- public void unwrappedAnnotationKeyMap() {
- assertThat(multibindingComponent.unwrappedAnnotationKeyMap())
- .isEqualTo(ImmutableMap.of(testStringKey("foo\n"), "foo annotation"));
- }
-
- @Test
- public void wrappedAnnotationKeyMap() {
- @SuppressWarnings("unchecked")
- Class<? extends Number>[] classes = new Class[] {Long.class, Integer.class};
- assertThat(multibindingComponent.wrappedAnnotationKeyMap())
- .isEqualTo(
- ImmutableMap.of(
- testWrappedAnnotationKey(
- testStringKey("foo"), new int[] {1, 2, 3}, new ClassKey[] {}, classes),
- "wrapped foo annotation"));
- }
-
- @Test
- public void booleanKeyMap() {
- assertThat(multibindingComponent.booleanKeyMap()).isEqualTo(ImmutableMap.of(true, "true"));
- }
-
- @Test
- public void byteKeyMap() {
- assertThat(multibindingComponent.byteKeyMap())
- .isEqualTo(ImmutableMap.of((byte) 100, "100 byte"));
- }
-
- @Test
- public void charKeyMap() {
- assertThat(multibindingComponent.characterKeyMap())
- .isEqualTo(ImmutableMap.of('a', "a char", '\n', "newline char"));
- }
-
- @Test
- public void classKeyMap() {
- assertThat(multibindingComponent.classKeyMap())
- .isEqualTo(
- ImmutableMap.of(
- Integer.class, "integer",
- Long.class, "long"));
- }
-
- @Test
- public void numberClassKeyMap() {
- assertThat(multibindingComponent.numberClassKeyMap())
- .isEqualTo(
- ImmutableMap.of(
- BigDecimal.class, "bigdecimal",
- BigInteger.class, "biginteger"));
- }
-
- @Test
- public void intKeyMap() {
- assertThat(multibindingComponent.integerKeyMap()).isEqualTo(ImmutableMap.of(100, "100 int"));
- }
-
- @Test
- public void longKeyMap() {
- assertThat(multibindingComponent.longKeyMap())
- .isEqualTo(ImmutableMap.of((long) 100, "100 long"));
- }
-
- @Test
- public void shortKeyMap() {
- assertThat(multibindingComponent.shortKeyMap())
- .isEqualTo(ImmutableMap.of((short) 100, "100 short"));
- }
-
- @Test public void setBindings() {
- assertThat(multibindingComponent.set()).containsExactly(-90, -17, -1, 5, 6, 832, 1742);
- }
-
- @Test public void complexQualifierSet() {
- assertThat(multibindingComponent.complexQualifierStringSet()).containsExactly("foo");
- }
-
- @AutoAnnotation
- static StringKey testStringKey(String value) {
- return new AutoAnnotation_MultibindingTest_testStringKey(value);
- }
-
- @AutoAnnotation
- static NestedAnnotationContainer.NestedWrappedKey nestedWrappedKey(Class<?> value) {
- return new AutoAnnotation_MultibindingTest_nestedWrappedKey(value);
- }
-
- @AutoAnnotation
- static WrappedAnnotationKey testWrappedAnnotationKey(
- StringKey value, int[] integers, ClassKey[] annotations, Class<? extends Number>[] classes) {
- return new AutoAnnotation_MultibindingTest_testWrappedAnnotationKey(
- value, integers, annotations, classes);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/NestedTest.java b/compiler/src/it/functional-tests/src/test/java/test/NestedTest.java
deleted file mode 100644
index 14c3e53..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/NestedTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class NestedTest {
- @Test public void nestedFoo() {
- OuterClassFoo.NestedComponent nestedFoo = DaggerOuterClassFoo_NestedComponent.create();
- assertThat(nestedFoo.thing()).isNotNull();
- }
-
- @Test public void nestedBar() {
- OuterClassBar.NestedComponent nestedBar = DaggerOuterClassBar_NestedComponent.create();
- assertThat(nestedBar.injectedThing()).isNotNull();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/NonComponentDependencyTest.java b/compiler/src/it/functional-tests/src/test/java/test/NonComponentDependencyTest.java
deleted file mode 100644
index 37d3f7a..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/NonComponentDependencyTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class NonComponentDependencyTest {
- @Test public void testThing() {
- NonComponentDependencyComponent component =
- DaggerNonComponentDependencyComponent.builder()
- .thingComponent(new NonComponentDependencyComponent.ThingComponentImpl())
- .build();
- assertThat(component).isNotNull();
- assertThat(component.thingTwo()).isNotNull();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java b/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java
deleted file mode 100644
index 46f5388..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.builder;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-@RunWith(JUnit4.class)
-public class BuilderTest {
-
- @Test public void interfaceBuilder() {
- TestComponentWithBuilderInterface.Builder builder =
- DaggerTestComponentWithBuilderInterface.builder();
-
- // Make sure things fail if we don't set our required modules.
- try {
- builder.build();
- fail();
- } catch(IllegalStateException expected) {}
-
- builder.intModule(new IntModuleIncludingDoubleAndFloat(1))
- .stringModule(new StringModule("sam"))
- .depComponent(new DepComponent() {});
- builder.doubleModule(new DoubleModule());
- // Don't set other modules -- make sure it works.
-
- TestComponentWithBuilderInterface component = builder.build();
- assertThat(component.s()).isEqualTo("sam");
- assertThat(component.i()).isEqualTo(1);
- assertThat(component.d()).isWithin(0).of(4.2d);
- assertThat(component.f()).isEqualTo(5.5f);
- assertThat(component.l()).isEqualTo(6L);
- }
-
- @Test public void abstractClassBuilder() {
- TestComponentWithBuilderAbstractClass.Builder builder =
- TestComponentWithBuilderAbstractClass.builder();
-
- // Make sure things fail if we don't set our required modules.
- try {
- builder.build();
- fail();
- } catch(IllegalStateException expected) {}
-
- builder.intModule(new IntModuleIncludingDoubleAndFloat(1))
- .stringModule(new StringModule("sam"))
- .depComponent(new DepComponent() {});
- builder.doubleModule(new DoubleModule());
- // Don't set other modules -- make sure it works.
-
- TestComponentWithBuilderAbstractClass component = builder.build();
- assertThat(component.s()).isEqualTo("sam");
- assertThat(component.i()).isEqualTo(1);
- assertThat(component.d()).isWithin(0).of(4.2d);
- assertThat(component.f()).isEqualTo(5.5f);
- assertThat(component.l()).isEqualTo(6L);
- }
-
- @Test public void interfaceGenericBuilder() {
- TestComponentWithGenericBuilderInterface.Builder builder =
- DaggerTestComponentWithGenericBuilderInterface.builder();
-
- // Make sure things fail if we don't set our required modules.
- try {
- builder.build();
- fail();
- } catch(IllegalStateException expected) {}
-
- builder.setM2(new IntModuleIncludingDoubleAndFloat(1))
- .setM1(new StringModule("sam"))
- .depComponent(new DepComponent() {});
- builder.doubleModule(new DoubleModule());
- // Don't set other modules -- make sure it works.
-
- TestComponentWithGenericBuilderInterface component = builder.build();
- assertThat(component.s()).isEqualTo("sam");
- assertThat(component.i()).isEqualTo(1);
- assertThat(component.d()).isWithin(0).of(4.2d);
- assertThat(component.f()).isEqualTo(5.5f);
- assertThat(component.l()).isEqualTo(6L);
- }
-
- @Test public void abstractClassGenericBuilder() {
- TestComponentWithGenericBuilderAbstractClass.Builder builder =
- DaggerTestComponentWithGenericBuilderAbstractClass.builder();
-
- // Make sure things fail if we don't set our required modules.
- try {
- builder.build();
- fail();
- } catch(IllegalStateException expected) {}
-
- builder.setM2(new IntModuleIncludingDoubleAndFloat(1))
- .setM1(new StringModule("sam"))
- .depComponent(new DepComponent() {});
- builder.doubleModule(new DoubleModule());
- // Don't set other modules -- make sure it works.
-
- TestComponentWithGenericBuilderAbstractClass component = builder.build();
- assertThat(component.s()).isEqualTo("sam");
- assertThat(component.i()).isEqualTo(1);
- assertThat(component.d()).isWithin(0).of(4.2d);
- assertThat(component.f()).isEqualTo(5.5f);
- assertThat(component.l()).isEqualTo(6L);
- }
-
- @Test public void subcomponents_interface() {
- ParentComponent parent = DaggerParentComponent.create();
- TestChildComponentWithBuilderInterface.Builder builder1 = parent.childInterfaceBuilder();
- try {
- builder1.build();
- fail();
- } catch(IllegalStateException expected) {}
-
- builder1.setM2(new IntModuleIncludingDoubleAndFloat(1))
- .setM1(new StringModule("sam"))
- .set(new ByteModule((byte)7));
- builder1.set(new FloatModule());
- TestChildComponentWithBuilderInterface child1 = builder1.build();
- assertThat(child1.s()).isEqualTo("sam");
- assertThat(child1.i()).isEqualTo(1);
- assertThat(child1.d()).isWithin(0).of(4.2d);
- assertThat(child1.f()).isEqualTo(5.5f);
- assertThat(child1.l()).isEqualTo(6L);
- assertThat(child1.b()).isEqualTo((byte)7);
- }
-
- @Test public void subcomponents_abstractclass() {
- ParentComponent parent = DaggerParentComponent.create();
- TestChildComponentWithBuilderAbstractClass.Builder builder2 =
- parent.childAbstractClassBuilder();
- try {
- builder2.build();
- fail();
- } catch(IllegalStateException expected) {}
-
- builder2.setM2(new IntModuleIncludingDoubleAndFloat(10))
- .setM1(new StringModule("tara"))
- .set(new ByteModule((byte)70));
- builder2.set(new FloatModule());
- TestChildComponentWithBuilderAbstractClass child2 = builder2.build();
- assertThat(child2.s()).isEqualTo("tara");
- assertThat(child2.i()).isEqualTo(10);
- assertThat(child2.d()).isWithin(0).of(4.2d);
- assertThat(child2.f()).isEqualTo(5.5f);
- assertThat(child2.l()).isEqualTo(6L);
- assertThat(child2.b()).isEqualTo((byte)70);
- }
-
- @Test
- public void grandchildren() {
- ParentComponent parent = DaggerParentComponent.create();
- MiddleChild middle1 = parent.middleBuilder().set(new StringModule("sam")).build();
- Grandchild grandchild1 =
- middle1.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(21)).build();
- Grandchild grandchild2 =
- middle1.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(22)).build();
-
- assertThat(middle1.s()).isEqualTo("sam");
- assertThat(grandchild1.i()).isEqualTo(21);
- assertThat(grandchild1.s()).isEqualTo("sam");
- assertThat(grandchild2.i()).isEqualTo(22);
- assertThat(grandchild2.s()).isEqualTo("sam");
-
- // Make sure grandchildren from newer children have no relation to the older ones.
- MiddleChild middle2 = parent.middleBuilder().set(new StringModule("tara")).build();
- Grandchild grandchild3 =
- middle2.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(23)).build();
- Grandchild grandchild4 =
- middle2.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(24)).build();
-
- assertThat(middle2.s()).isEqualTo("tara");
- assertThat(grandchild3.i()).isEqualTo(23);
- assertThat(grandchild3.s()).isEqualTo("tara");
- assertThat(grandchild4.i()).isEqualTo(24);
- assertThat(grandchild4.s()).isEqualTo("tara");
- }
-
- @Test
- public void diamondGrandchildren() {
- ParentComponent parent = DaggerParentComponent.create();
- MiddleChild middle = parent.middleBuilder().set(new StringModule("sam")).build();
- OtherMiddleChild other = parent.otherBuilder().set(new StringModule("tara")).build();
-
- Grandchild middlegrand =
- middle.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(21)).build();
- Grandchild othergrand =
- other.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(22)).build();
-
- assertThat(middle.s()).isEqualTo("sam");
- assertThat(other.s()).isEqualTo("tara");
- assertThat(middlegrand.s()).isEqualTo("sam");
- assertThat(othergrand.s()).isEqualTo("tara");
- assertThat(middlegrand.i()).isEqualTo(21);
- assertThat(othergrand.i()).isEqualTo(22);
- }
-
- @Test
- public void genericSubcomponentMethod() {
- ParentOfGenericComponent parent =
- DaggerParentOfGenericComponent.builder().stringModule(new StringModule("sam")).build();
- Grandchild.Builder builder = parent.subcomponentBuilder();
- Grandchild child = builder.set(new IntModuleIncludingDoubleAndFloat(21)).build();
- assertThat(child.s()).isEqualTo("sam");
- assertThat(child.i()).isEqualTo(21);
- }
-
- @Test
- public void requireSubcomponentBuilderProviders() {
- ParentComponent parent = DaggerParentComponent.create();
- MiddleChild middle =
- parent
- .requiresMiddleChildBuilder()
- .subcomponentBuilderProvider()
- .get()
- .set(new StringModule("sam"))
- .build();
- Grandchild grandchild =
- middle
- .requiresGrandchildBuilder()
- .subcomponentBuilderProvider()
- .get()
- .set(new IntModuleIncludingDoubleAndFloat(12))
- .build();
- assertThat(middle.s()).isEqualTo("sam");
- assertThat(grandchild.i()).isEqualTo(12);
- assertThat(grandchild.s()).isEqualTo("sam");
- }
-
- @Test
- public void requireSubcomponentBuilders() {
- ParentComponent parent = DaggerParentComponent.create();
- MiddleChild middle =
- parent
- .requiresMiddleChildBuilder()
- .subcomponentBuilder()
- .set(new StringModule("sam"))
- .build();
- Grandchild grandchild =
- middle
- .requiresGrandchildBuilder()
- .subcomponentBuilder()
- .set(new IntModuleIncludingDoubleAndFloat(12))
- .build();
- assertThat(middle.s()).isEqualTo("sam");
- assertThat(grandchild.i()).isEqualTo(12);
- assertThat(grandchild.s()).isEqualTo("sam");
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/cycle/CycleTest.java b/compiler/src/it/functional-tests/src/test/java/test/cycle/CycleTest.java
deleted file mode 100644
index d3bc2cb..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/cycle/CycleTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.cycle;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import test.cycle.Cycles.A;
-import test.cycle.Cycles.C;
-import test.cycle.Cycles.ChildCycleComponent;
-import test.cycle.Cycles.CycleComponent;
-import test.cycle.Cycles.CycleMapComponent;
-import test.cycle.Cycles.S;
-import test.cycle.Cycles.SelfCycleComponent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class CycleTest {
- @Test
- public void providerIndirectionSelfCycle() {
- SelfCycleComponent selfCycleComponent = DaggerCycles_SelfCycleComponent.create();
- S s = selfCycleComponent.s();
- assertThat(s.sProvider.get()).isNotNull();
- }
-
- @Test
- public void providerIndirectionCycle() {
- CycleComponent cycleComponent = DaggerCycles_CycleComponent.create();
- A a = cycleComponent.a();
- C c = cycleComponent.c();
- assertThat(c.aProvider.get()).isNotNull();
- assertThat(a.b.c.aProvider.get()).isNotNull();
- assertThat(a.e.d.b.c.aProvider.get()).isNotNull();
- }
-
- @Test
- public void lazyIndirectionSelfCycle() {
- SelfCycleComponent selfCycleComponent = DaggerCycles_SelfCycleComponent.create();
- S s = selfCycleComponent.s();
- assertThat(s.sLazy.get()).isNotNull();
- }
-
- @Test
- public void lazyIndirectionCycle() {
- CycleComponent cycleComponent = DaggerCycles_CycleComponent.create();
- A a = cycleComponent.a();
- C c = cycleComponent.c();
- assertThat(c.aLazy.get()).isNotNull();
- assertThat(a.b.c.aLazy.get()).isNotNull();
- assertThat(a.e.d.b.c.aLazy.get()).isNotNull();
- }
-
- @Test
- public void subcomponentIndirectionCycle() {
- ChildCycleComponent childCycleComponent = DaggerCycles_CycleComponent.create().child();
- A a = childCycleComponent.a();
- assertThat(a.b.c.aProvider.get()).isNotNull();
- assertThat(a.e.d.b.c.aProvider.get()).isNotNull();
- }
-
- @Test
- public void providerMapIndirectionCycle() {
- CycleMapComponent cycleMapComponent = DaggerCycles_CycleMapComponent.create();
- assertThat(cycleMapComponent.y()).isNotNull();
- assertThat(cycleMapComponent.y().mapOfProvidersOfX).containsKey("X");
- assertThat(cycleMapComponent.y().mapOfProvidersOfX.get("X")).isNotNull();
- assertThat(cycleMapComponent.y().mapOfProvidersOfX.get("X").get()).isNotNull();
- assertThat(cycleMapComponent.y().mapOfProvidersOfX.get("X").get().y).isNotNull();
- assertThat(cycleMapComponent.y().mapOfProvidersOfX).hasSize(1);
- assertThat(cycleMapComponent.y().mapOfProvidersOfY).containsKey("Y");
- assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y")).isNotNull();
- assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y").get()).isNotNull();
- assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y").get().mapOfProvidersOfX).hasSize(1);
- assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y").get().mapOfProvidersOfY).hasSize(1);
- assertThat(cycleMapComponent.y().mapOfProvidersOfY).hasSize(1);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/cycle/LongCycleTest.java b/compiler/src/it/functional-tests/src/test/java/test/cycle/LongCycleTest.java
deleted file mode 100644
index e50eaee..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/cycle/LongCycleTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.cycle;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import test.cycle.LongCycle.LongCycleComponent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class LongCycleTest {
-
- /**
- * Tests a cycle long enough that the real factory is created in a separate initialize method from
- * the delegate factory.
- */
- @Test
- public void longCycle() {
- LongCycleComponent longCycleComponent = DaggerLongCycle_LongCycleComponent.create();
- assertThat(longCycleComponent.class1()).isNotNull();
- }
-
- /**
- * Fails if {@link LongCycleComponent} doesn't have a long enough cycle to make sure the real
- * factory is created in a separate method from the delegate factory.
- */
- @Test
- public void longCycleHasMoreThanOneInitializeMethod() throws NoSuchMethodException {
- DaggerLongCycle_LongCycleComponent.class
- .getDeclaredMethod("initialize1", DaggerLongCycle_LongCycleComponent.Builder.class);
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/membersinject/MembersInjectTest.java b/compiler/src/it/functional-tests/src/test/java/test/membersinject/MembersInjectTest.java
deleted file mode 100644
index 411ecb1..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/membersinject/MembersInjectTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.membersinject;
-
-import dagger.MembersInjector;
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import test.multipackage.DaggerMembersInjectionVisibilityComponent;
-import test.multipackage.MembersInjectionVisibilityComponent;
-import test.multipackage.a.AGrandchild;
-import test.multipackage.a.AParent;
-import test.multipackage.b.BChild;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class MembersInjectTest {
- @Test public void testMembersInject_arrays() {
- MembersInjectComponent component = DaggerMembersInjectComponent.builder().build();
-
- ChildOfStringArray childOfStringArray = new ChildOfStringArray();
- component.inject(childOfStringArray);
- }
-
- @Test public void testMembersInject_nestedArrays() {
- MembersInjectComponent component = DaggerMembersInjectComponent.builder().build();
-
- ChildOfArrayOfParentOfStringArray childOfArrayOfParentOfStringArray =
- new ChildOfArrayOfParentOfStringArray();
- component.inject(childOfArrayOfParentOfStringArray);
- }
-
- @Test public void testMembersInject_primitives() {
- MembersInjectComponent component = DaggerMembersInjectComponent.builder().build();
-
- ChildOfPrimitiveIntArray childOfPrimitiveIntArray = new ChildOfPrimitiveIntArray();
- component.inject(childOfPrimitiveIntArray);
- }
-
- @Test
- public void testMembersInject_overrides() {
- MembersInjectionVisibilityComponent component =
- DaggerMembersInjectionVisibilityComponent.create();
- AParent aParent = new AParent();
- component.inject(aParent);
- assertThat(aParent.aParentField()).isNotNull();
- assertThat(aParent.aParentMethod()).isNotNull();
-
- BChild aChild = new BChild();
- component.inject(aChild);
- assertThat(aChild.aParentField()).isNotNull();
- assertThat(aChild.aParentMethod()).isNull();
- assertThat(aChild.aChildField()).isNotNull();
- assertThat(aChild.aChildMethod()).isNotNull();
-
- AGrandchild aGrandchild = new AGrandchild();
- component.inject(aGrandchild);
- assertThat(aGrandchild.aParentField()).isNotNull();
- assertThat(aGrandchild.aParentMethod()).isNotNull();
- assertThat(aGrandchild.aChildField()).isNotNull();
- assertThat(aGrandchild.aChildMethod()).isNull();
- assertThat(aGrandchild.aGrandchildField()).isNotNull();
- assertThat(aGrandchild.aGrandchildMethod()).isNotNull();
- }
-
- @Test
- public void testNonRequestedMembersInjector() {
- NonRequestedChild child = new NonRequestedChild();
- Provider<String> provider =
- new Provider<String>() {
- @Override
- public String get() {
- return "field!";
- }
- };
- MembersInjector<NonRequestedChild> injector = new NonRequestedChild_MembersInjector(provider);
- injector.injectMembers(child);
- assertThat(child.t).isEqualTo("field!");
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/nullables/NullabilityTest.java b/compiler/src/it/functional-tests/src/test/java/test/nullables/NullabilityTest.java
deleted file mode 100644
index a0e1e22..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/nullables/NullabilityTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test.nullables;
-
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-@RunWith(JUnit4.class)
-public class NullabilityTest {
- @Test public void testNullability_provides() {
- NullModule module = new NullModule();
- NullComponent component = DaggerNullComponent.builder().nullModule(module).build();
-
- // Can't construct NullFoo because it depends on Number, and Number was null.
- try {
- component.nullFoo();
- fail();
- } catch (NullPointerException npe) {
- assertThat(npe).hasMessage("Cannot return null from a non-@Nullable @Provides method");
- }
-
- // set number to non-null so we can create
- module.numberValue = 1;
- NullFoo nullFoo = component.nullFoo();
-
- // Then set it back to null so we can test its providers.
- module.numberValue = null;
- validate(true, nullFoo.string, nullFoo.stringProvider, nullFoo.numberProvider);
- validate(true, nullFoo.methodInjectedString, nullFoo.methodInjectedStringProvider,
- nullFoo.methodInjectedNumberProvider);
- validate(true, nullFoo.fieldInjectedString, nullFoo.fieldInjectedStringProvider,
- nullFoo.fieldInjectedNumberProvider);
- }
-
- @Test public void testNullability_components() {
- NullComponent nullComponent = new NullComponent() {
- @Override public Provider<String> stringProvider() {
- return new Provider<String>() {
- @Override public String get() {
- return null;
- }
- };
- }
-
- @Override public String string() {
- return null;
- }
-
- @Override public Provider<Number> numberProvider() {
- return new Provider<Number>() {
- @Override public Number get() {
- return null;
- }
- };
- }
-
- @Override public Number number() {
- return null;
- }
-
- @Override public NullFoo nullFoo() {
- return null;
- }
- };
- NullComponentWithDependency component =
- DaggerNullComponentWithDependency.builder().nullComponent(nullComponent).build();
- validate(false, component.string(), component.stringProvider(), component.numberProvider());
-
- // Also validate that the component's number() method fails
- try {
- component.number();
- fail();
- } catch (NullPointerException npe) {
- assertThat(npe).hasMessage("Cannot return null from a non-@Nullable component method");
- }
- }
-
- private void validate(boolean fromProvides,
- String string,
- Provider<String> stringProvider,
- Provider<Number> numberProvider) {
- assertThat(string).isNull();
- assertThat(numberProvider).isNotNull();
- try {
- numberProvider.get();
- fail();
- } catch(NullPointerException npe) {
- assertThat(npe).hasMessage("Cannot return null from a non-@Nullable "
- + (fromProvides ? "@Provides" : "component") + " method");
- }
- assertThat(stringProvider.get()).isNull();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/staticprovides/StaticProvidesTest.java b/compiler/src/it/functional-tests/src/test/java/test/staticprovides/StaticProvidesTest.java
deleted file mode 100644
index 3972594..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/staticprovides/StaticProvidesTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.staticprovides;
-
-import com.google.common.collect.ImmutableSet;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collection;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-@RunWith(Parameterized.class)
-public class StaticProvidesTest {
- @Parameters
- public static Collection<Object[]> components() {
- return Arrays.asList(new Object[][] {
- {DaggerStaticTestComponent.create()},
- {DaggerStaticTestComponentWithBuilder.builder().build()},
- {DaggerStaticTestComponentWithBuilder.builder()
- .allStaticModule(new AllStaticModule())
- .someStaticModule(new SomeStaticModule())
- .build()}});
- }
-
- @Parameter
- public StaticTestComponent component;
-
- @Test public void setMultibinding() {
- assertThat(component.getMultiboundStrings()).isEqualTo(ImmutableSet.of(
- AllStaticModule.class + ".contributeString",
- SomeStaticModule.class + ".contributeStringFromAStaticMethod",
- SomeStaticModule.class + ".contributeStringFromAnInstanceMethod"));
- }
-
- @Test public void allStaticProvidesModules_noFieldInComponentBuilder() {
- for (Field field : DaggerStaticTestComponent.Builder.class.getDeclaredFields()) {
- assertWithMessage(field.getName())
- .that(field.getType()).isNotEqualTo(AllStaticModule.class);
- }
- }
-
- @Test public void allStaticProvidesModules_deprecatedMethodInComponentBuilder() {
- for (Method method : DaggerStaticTestComponent.Builder.class.getDeclaredMethods()) {
- if (Arrays.asList(method.getParameterTypes()).contains(AllStaticModule.class)) {
- assertWithMessage(method.getName())
- .that(method.isAnnotationPresent(Deprecated.class))
- .isTrue();
- }
- }
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentMultibindingsTest.java b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentMultibindingsTest.java
deleted file mode 100644
index f57a778..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentMultibindingsTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import java.util.Collection;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-@RunWith(Parameterized.class)
-public class SubcomponentMultibindingsTest {
-
- @Parameters(name = "{0}")
- public static Collection<Object[]> parameters() {
- return ImmutableList.of(
- new Object[] {DaggerParentComponentWithMultibindings.create()},
- new Object[] {DaggerParentComponentWithoutMultibindings.create()});
- }
-
- private ParentComponentWithoutMultibindings parent;
-
- public SubcomponentMultibindingsTest(ParentComponentWithoutMultibindings parentComponent) {
- this.parent = parentComponent;
- }
-
- @Test
- public void testMultibindingsInSubcomponents() {
- RequiresMultibindingsInChild requiresMultibindingsInChild =
- parent.childComponent().requiresMultibindingsInChild();
-
- assertWithMessage("requiresMultiboundObjects.setOfObjects")
- .that(requiresMultibindingsInChild.requiresMultiboundObjects().setOfObjects())
- .containsExactly("object provided by parent", "object provided by child");
-
- assertWithMessage("requiresMultiboundObjects.mapOfObjects")
- .that(requiresMultibindingsInChild.requiresMultiboundObjects().mapOfObjects())
- .isEqualTo(
- ImmutableMap.of("parent key", "object in parent", "child key", "object in child"));
-
- assertWithMessage("requiresMultiboundStrings")
- .that(requiresMultibindingsInChild.requiresMultiboundStrings().setOfStrings())
- .containsExactly("string provided by parent");
-
- assertWithMessage("requiresMultiboundStrings.mapOfStrings")
- .that(requiresMultibindingsInChild.requiresMultiboundStrings().mapOfStrings())
- .isEqualTo(ImmutableMap.of("parent key", "string in parent"));
- }
-
- @Test
- public void testOverriddenMultibindingsInSubcomponents() {
- RequiresMultibindingsInChild requiresMultibindingsInChild =
- parent.childComponent().requiresMultibindingsInChild();
-
- assertWithMessage("setOfRequiresMultiboundObjects")
- .that(requiresMultibindingsInChild.setOfRequiresMultiboundObjects())
- .hasSize(1);
-
- RequiresMultiboundObjects onlyElementInMultiboundRequiresMultiboundObjects =
- getOnlyElement(requiresMultibindingsInChild.setOfRequiresMultiboundObjects());
-
- assertWithMessage("setOfRequiresMultiboundObjects[only].setOfObjects")
- .that(onlyElementInMultiboundRequiresMultiboundObjects.setOfObjects())
- .containsExactly("object provided by parent", "object provided by child");
-
- assertWithMessage("setOfRequiresMultiboundObjects[only].mapOfObjects")
- .that(onlyElementInMultiboundRequiresMultiboundObjects.mapOfObjects())
- .isEqualTo(
- ImmutableMap.of("parent key", "object in parent", "child key", "object in child"));
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentTest.java b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentTest.java
deleted file mode 100644
index cb62925..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Set;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import static com.google.common.collect.Sets.intersection;
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(Parameterized.class)
-public class SubcomponentTest {
- private static final ParentComponent parentComponent = DaggerParentComponent.create();
- private static final ParentOfGenericComponent parentOfGenericComponent =
- DaggerParentOfGenericComponent.create();
-
- @Parameters
- public static Collection<Object[]> parameters() {
- return Arrays.asList(new Object[][] {
- { parentComponent, parentComponent.newChildComponent() },
- { parentComponent, parentComponent.newChildAbstractClassComponent() },
- { parentOfGenericComponent, parentOfGenericComponent.subcomponent() }});
- }
-
- private final ParentGetters parentGetters;
- private final ChildComponent childComponent;
-
- public SubcomponentTest(ParentGetters parentGetters, ChildComponent childComponent) {
- this.parentGetters = parentGetters;
- this.childComponent = childComponent;
- }
-
-
- @Test
- public void scopePropagatesUpward_class() {
- assertThat(childComponent.requiresSingleton().singletonType())
- .isSameAs(childComponent.requiresSingleton().singletonType());
- assertThat(childComponent.requiresSingleton().singletonType())
- .isSameAs(childComponent.newGrandchildComponent().requiresSingleton().singletonType());
- }
-
- @Test
- public void scopePropagatesUpward_provides() {
- assertThat(childComponent
- .requiresSingleton().unscopedTypeBoundAsSingleton())
- .isSameAs(childComponent
- .requiresSingleton().unscopedTypeBoundAsSingleton());
- assertThat(childComponent
- .requiresSingleton().unscopedTypeBoundAsSingleton())
- .isSameAs(childComponent.newGrandchildComponent()
- .requiresSingleton().unscopedTypeBoundAsSingleton());
- }
-
- @Test
- public void multibindingContributions() {
- Set<Object> parentObjectSet = parentGetters.objectSet();
- assertThat(parentObjectSet).hasSize(2);
- Set<Object> childObjectSet = childComponent.objectSet();
- assertThat(childObjectSet).hasSize(3);
- Set<Object> grandchildObjectSet =
- childComponent.newGrandchildComponent().objectSet();
- assertThat(grandchildObjectSet).hasSize(4);
- assertThat(intersection(parentObjectSet, childObjectSet)).hasSize(1);
- assertThat(intersection(parentObjectSet, grandchildObjectSet)).hasSize(1);
- assertThat(intersection(childObjectSet, grandchildObjectSet)).hasSize(1);
- }
-
- @Test
- public void unscopedProviders() {
- assertThat(parentGetters.getUnscopedTypeProvider())
- .isSameAs(childComponent.getUnscopedTypeProvider());
- assertThat(parentGetters.getUnscopedTypeProvider())
- .isSameAs(childComponent
- .newGrandchildComponent()
- .getUnscopedTypeProvider());
- }
-
- @Test
- public void passedModules() {
- ChildModuleWithState childModuleWithState = new ChildModuleWithState();
- ChildComponentRequiringModules childComponent1 =
- parentComponent.newChildComponentRequiringModules(
- new ChildModuleWithParameters(new Object()),
- childModuleWithState);
- ChildComponentRequiringModules childComponent2 =
- parentComponent.newChildComponentRequiringModules(
- new ChildModuleWithParameters(new Object()),
- childModuleWithState);
- assertThat(childComponent1.getInt()).isEqualTo(0);
- assertThat(childComponent2.getInt()).isEqualTo(1);
- }
-
- @Test
- public void dependenceisInASubcomponent() {
- assertThat(childComponent.newGrandchildComponent().needsAnInterface()).isNotNull();
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/hiding/SubcomponentHidingTest.java b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/hiding/SubcomponentHidingTest.java
deleted file mode 100644
index 27dcbb6..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/hiding/SubcomponentHidingTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.hiding;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class SubcomponentHidingTest {
- @Test public void moduleNameHiding() {
- ParentComponent parent = DaggerParentComponent.create();
- assertThat(parent.aCommonName().toString()).isEqualTo("a");
- assertThat(parent.newChildComponent().aCommonName().toString()).isEqualTo("a");
- assertThat(parent.newChildComponent().bCommonName().toString()).isEqualTo("1");
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/repeat/RepeatedModuleTest.java b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/repeat/RepeatedModuleTest.java
deleted file mode 100644
index 7e92371..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/subcomponent/repeat/RepeatedModuleTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.subcomponent.repeat;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-@RunWith(JUnit4.class)
-public final class RepeatedModuleTest {
- private ParentComponent parentComponent;
-
- @Before
- public void initializeParentComponent() {
- this.parentComponent = DaggerParentComponent.builder().build();
- }
-
- @Test
- public void repeatedModuleHasSameStateInSubcomponent() {
- SubcomponentWithRepeatedModule childComponent =
- parentComponent.newChildComponentBuilder().build();
- assertThat(parentComponent.state()).isSameAs(childComponent.state());
- }
-
- @Test
- public void repeatedModuleHasSameStateInGrandchildSubcomponent() {
- SubcomponentWithoutRepeatedModule childComponent =
- parentComponent.newChildComponentWithoutRepeatedModule();
- SubcomponentWithRepeatedModule grandchildComponent =
- childComponent.newGrandchildBuilder().build();
- assertThat(parentComponent.state()).isSameAs(grandchildComponent.state());
- }
-
- @Test
- public void repeatedModuleBuilderThrowsInSubcomponent() {
- SubcomponentWithRepeatedModule.Builder childComponentBuilder =
- parentComponent.newChildComponentBuilder();
- try {
- childComponentBuilder.repeatedModule(new RepeatedModule());
- fail();
- } catch (UnsupportedOperationException expected) {
- assertThat(expected)
- .hasMessage(
- "test.subcomponent.repeat.RepeatedModule cannot be set "
- + "because it is inherited from the enclosing component");
- }
- }
-
- @Test
- public void repeatedModuleBuilderThrowsInGrandchildSubcomponent() {
- SubcomponentWithoutRepeatedModule childComponent =
- parentComponent.newChildComponentWithoutRepeatedModule();
- SubcomponentWithRepeatedModule.Builder grandchildComponentBuilder =
- childComponent.newGrandchildBuilder();
- try {
- grandchildComponentBuilder.repeatedModule(new RepeatedModule());
- fail();
- } catch (UnsupportedOperationException expected) {
- assertThat(expected)
- .hasMessage(
- "test.subcomponent.repeat.RepeatedModule cannot be set "
- + "because it is inherited from the enclosing component");
- }
- }
-}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/tck/TckTest.java b/compiler/src/it/functional-tests/src/test/java/test/tck/TckTest.java
deleted file mode 100644
index d79b06b..0000000
--- a/compiler/src/it/functional-tests/src/test/java/test/tck/TckTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package test.tck;
-
-import junit.framework.Test;
-import org.atinject.tck.Tck;
-import org.atinject.tck.auto.Car;
-import org.atinject.tck.auto.Convertible;
-
-/**
- * Test suite to execute the JSR-330 TCK in JUnit.
- */
-public class TckTest {
- public static Test suite() {
- CarShop carShopComponent = DaggerCarShop.create();
- Car car = carShopComponent.make();
- Convertible.localConvertible.set((Convertible) car);
- return Tck.testsFor(car, false, false);
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/pom.xml b/compiler/src/it/producers-functional-tests/pom.xml
deleted file mode 100644
index a0d1649..0000000
--- a/compiler/src/it/producers-functional-tests/pom.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2014 Google, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<project
- xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
- </parent>
- <groupId>dagger.tests</groupId>
- <artifactId>producers-functional-tests</artifactId>
- <name>Producers Functional Tests</name>
- <dependencies>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-producers</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-compiler</artifactId>
- <version>${project.version}</version>
- <optional>true</optional>
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.truth</groupId>
- <artifactId>truth</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.1</version>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>2.10</version>
- <configuration>
- <failsOnError>false</failsOnError>
- <consoleOutput>true</consoleOutput>
- <configLocation>../../../../checkstyle.xml</configLocation>
- </configuration>
- <executions>
- <execution>
- <phase>compile</phase>
- <goals>
- <goal>checkstyle</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedComponent.java
deleted file mode 100644
index a80ea49..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedComponent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Component;
-
-@Component(modules = DependedModule.class)
-interface DependedComponent {
- String getGreeting();
-}
-
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedModule.java
deleted file mode 100644
index dc62612..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class DependedModule {
- @Provides
- String provideGreeting() {
- return "Hello world!";
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProducerModule.java
deleted file mode 100644
index fe47c99..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProducerModule.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-
-import java.util.List;
-
-@ProducerModule
-final class DependedProducerModule {
-
- @Produces
- int produceNumberOfGreetings() {
- return 2;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProductionComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProductionComponent.java
deleted file mode 100644
index ba98e36..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProductionComponent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProductionComponent;
-
-@ProductionComponent(modules = DependedProducerModule.class)
-interface DependedProductionComponent {
- ListenableFuture<Integer> numGreetings();
-}
-
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentComponent.java
deleted file mode 100644
index 85709f0..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentComponent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProductionComponent;
-
-import java.util.List;
-
-@ProductionComponent(
- modules = DependentProducerModule.class,
- dependencies = {DependedComponent.class, DependedProductionComponent.class})
-interface DependentComponent {
- ListenableFuture<List<String>> greetings();
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentProducerModule.java
deleted file mode 100644
index e16c822..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentProducerModule.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-
-import java.util.List;
-
-@ProducerModule
-final class DependentProducerModule {
- @Produces
- ListenableFuture<List<String>> greetings(Integer numGreetings, String greeting) {
- List<String> greetings = ImmutableList.of(
- String.valueOf(numGreetings), greeting, Ascii.toUpperCase(greeting));
- return Futures.immediateFuture(greetings);
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingComponent.java
deleted file mode 100644
index 6277e62..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingComponent.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Produced;
-import dagger.producers.ProductionComponent;
-import java.util.Set;
-import producerstest.MultibindingProducerModule.PossiblyThrowingSet;
-
-@ProductionComponent(modules = MultibindingProducerModule.class)
-interface MultibindingComponent {
- ListenableFuture<Set<String>> strs();
- ListenableFuture<Integer> strCount();
-
- ListenableFuture<Set<Produced<String>>> successfulSet();
-
- @PossiblyThrowingSet
- ListenableFuture<Set<Produced<String>>> possiblyThrowingSet();
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingProducerModule.java
deleted file mode 100644
index 09cd5bd..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingProducerModule.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package producerstest;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.util.Set;
-import javax.inject.Qualifier;
-
-import static dagger.producers.Produces.Type.SET;
-import static dagger.producers.Produces.Type.SET_VALUES;
-
-@ProducerModule
-final class MultibindingProducerModule {
- @Qualifier
- @interface PossiblyThrowingSet {}
-
- @Produces(type = SET)
- static ListenableFuture<String> futureStr() {
- return Futures.immediateFuture("foo");
- }
-
- @Produces(type = SET)
- static String str() {
- return "bar";
- }
-
- @Produces(type = SET_VALUES)
- static ListenableFuture<Set<String>> futureStrs() {
- return Futures.<Set<String>>immediateFuture(ImmutableSet.of("foo1", "foo2"));
- }
-
- @Produces(type = SET_VALUES)
- static Set<String> strs() {
- return ImmutableSet.of("bar1", "bar2");
- }
-
- @Produces
- static int strCount(Set<String> strs) {
- return strs.size();
- }
-
- @Produces(type = SET)
- @PossiblyThrowingSet
- static String successfulStringForSet() {
- return "singleton";
- }
-
- @Produces(type = SET_VALUES)
- @PossiblyThrowingSet
- static Set<String> successfulStringsForSet() {
- return ImmutableSet.of("double", "ton");
- }
-
- @Produces(type = SET)
- @PossiblyThrowingSet
- static String throwingStringForSet() {
- throw new RuntimeException("monkey");
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Request.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Request.java
deleted file mode 100644
index 0227be6..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Request.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import javax.inject.Inject;
-
-final class Request {
- private final String name;
-
- @Inject
- Request() {
- this.name = "Request";
- }
-
- String name() {
- return this.name;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Response.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Response.java
deleted file mode 100644
index 8618ff5..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Response.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-final class Response {
- private final String data;
-
- Response(String data) {
- this.data = data;
- }
-
- String data() {
- return this.data;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseModule.java
deleted file mode 100644
index 1edbe8a..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class ResponseModule {
- @Provides
- static int requestNumber() {
- return 5;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseProducerModule.java
deleted file mode 100644
index 0ef2ae8..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseProducerModule.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-
-@ProducerModule(includes = ResponseModule.class)
-final class ResponseProducerModule {
- @Produces
- static ListenableFuture<String> greeting() {
- return Futures.immediateFuture("Hello");
- }
-
- @Produces
- static Response response(String greeting, Request request, int requestNumber) {
- return new Response(String.format("%s, %s #%d!", greeting, request.name(), requestNumber));
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleComponent.java
deleted file mode 100644
index 1d1e492..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleComponent.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProductionComponent;
-
-@ProductionComponent(modules = ResponseProducerModule.class)
-interface SimpleComponent {
- ListenableFuture<Response> response();
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleProducerModule.java
deleted file mode 100644
index 2d831ed..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleProducerModule.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package producerstest;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.io.IOException;
-import java.util.Set;
-import javax.inject.Provider;
-import javax.inject.Qualifier;
-
-import static dagger.producers.Produces.Type.SET;
-import static dagger.producers.Produces.Type.SET_VALUES;
-
-/**
- * A module that contains various signatures of produces methods. This is not used in any
- * components.
- */
-@ProducerModule
-final class SimpleProducerModule {
- @Qualifier @interface Qual {
- int value();
- }
-
- // Unique bindings.
-
- @Produces
- @Qual(-2)
- static ListenableFuture<String> throwingProducer() {
- throw new RuntimeException("monkey");
- }
-
- @Produces
- @Qual(-1)
- static ListenableFuture<String> settableFutureStr(SettableFuture<String> future) {
- return future;
- }
-
- @Produces
- @Qual(0)
- static String str() {
- return "str";
- }
-
- @Produces
- @Qual(1)
- static ListenableFuture<String> futureStr() {
- return Futures.immediateFuture("future str");
- }
-
- @Produces
- @Qual(2)
- static String strWithArg(int i) {
- return "str with arg";
- }
-
- @Produces
- @Qual(3)
- static ListenableFuture<String> futureStrWithArg(int i) {
- return Futures.immediateFuture("future str with arg");
- }
-
- @Produces
- @Qual(4)
- static String strThrowingException() throws IOException {
- return "str throwing exception";
- }
-
- @Produces
- @Qual(5)
- static ListenableFuture<String> futureStrThrowingException() throws IOException {
- return Futures.immediateFuture("future str throwing exception");
- }
-
- @Produces
- @Qual(6)
- static String strWithArgThrowingException(int i) throws IOException {
- return "str with arg throwing exception";
- }
-
- @Produces
- @Qual(7)
- static ListenableFuture<String> futureStrWithArgThrowingException(int i) throws IOException {
- return Futures.immediateFuture("future str with arg throwing exception");
- }
-
- @Produces
- @Qual(8)
- static String strWithArgs(int i, Produced<Double> b, Producer<Object> c, Provider<Boolean> d) {
- return "str with args";
- }
-
- @Produces
- @Qual(9)
- static String strWithArgsThrowingException(
- int i, Produced<Double> b, Producer<Object> c, Provider<Boolean> d) throws IOException {
- return "str with args throwing exception";
- }
-
- @Produces
- @Qual(10)
- static ListenableFuture<String> futureStrWithArgs(
- int i, Produced<Double> b, Producer<Object> c, Provider<Boolean> d) {
- return Futures.immediateFuture("future str with args");
- }
-
- @Produces
- @Qual(11)
- static ListenableFuture<String> futureStrWithArgsThrowingException(
- int i, Produced<Double> b, Producer<Object> c, Provider<Boolean> d) throws IOException {
- return Futures.immediateFuture("str with args throwing exception");
- }
-
- // Set bindings.
-
- @Produces(type = SET)
- static String setOfStrElement() {
- return "set of str element";
- }
-
- @Produces(type = SET)
- static String setOfStrElementThrowingException() throws IOException {
- return "set of str element throwing exception";
- }
-
- @Produces(type = SET)
- static ListenableFuture<String> setOfStrFutureElement() {
- return Futures.immediateFuture("set of str element");
- }
-
- @Produces(type = SET)
- static ListenableFuture<String> setOfStrFutureElementThrowingException() throws IOException {
- return Futures.immediateFuture("set of str element throwing exception");
- }
-
- @Produces(type = SET)
- static String setOfStrElementWithArg(int i) {
- return "set of str element with arg";
- }
-
- @Produces(type = SET)
- static String setOfStrElementWithArgThrowingException(int i) throws IOException {
- return "set of str element with arg throwing exception";
- }
-
- @Produces(type = SET)
- static ListenableFuture<String> setOfStrFutureElementWithArg(int i) {
- return Futures.immediateFuture("set of str element with arg");
- }
-
- @Produces(type = SET)
- static ListenableFuture<String> setOfStrFutureElementWithArgThrowingException(int i)
- throws IOException {
- return Futures.immediateFuture("set of str element with arg throwing exception");
- }
-
- @Produces(type = SET_VALUES)
- static Set<String> setOfStrValues() {
- return ImmutableSet.of("set of str 1", "set of str 2");
- }
-
- @Produces(type = SET_VALUES)
- static Set<String> setOfStrValuesThrowingException() throws IOException {
- return ImmutableSet.of("set of str 1", "set of str 2 throwing exception");
- }
-
- @Produces(type = SET_VALUES)
- static ListenableFuture<Set<String>> setOfStrFutureValues() {
- return Futures.<Set<String>>immediateFuture(ImmutableSet.of("set of str 1", "set of str 2"));
- }
-
- @Produces(type = SET_VALUES)
- static ListenableFuture<Set<String>> setOfStrFutureValuesThrowingException() throws IOException {
- return Futures.<Set<String>>immediateFuture(
- ImmutableSet.of("set of str 1", "set of str 2 throwing exception"));
- }
-
- @Produces(type = SET_VALUES)
- static Set<String> setOfStrValuesWithArg(int i) {
- return ImmutableSet.of("set of str with arg 1", "set of str with arg 2");
- }
-
- @Produces(type = SET_VALUES)
- static Set<String> setOfStrValuesWithArgThrowingException(int i) throws IOException {
- return ImmutableSet.of("set of str with arg 1", "set of str with arg 2 throwing exception");
- }
-
- @Produces(type = SET_VALUES)
- static ListenableFuture<Set<String>> setOfStrFutureValuesWithArg(int i) {
- return Futures.<Set<String>>immediateFuture(
- ImmutableSet.of("set of str with arg 1", "set of str with arg 2"));
- }
-
- @Produces(type = SET_VALUES)
- static ListenableFuture<Set<String>> setOfStrFutureValuesWithArgThrowingException(int i)
- throws IOException {
- return Futures.<Set<String>>immediateFuture(
- ImmutableSet.of("set of str with arg 1", "set of str with arg 2 throwing exception"));
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/ComponentDependency.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/ComponentDependency.java
deleted file mode 100644
index 7bba4ea..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/ComponentDependency.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.badexecutor;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-interface ComponentDependency {
- ListenableFuture<Double> doubleDep();
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleComponent.java
deleted file mode 100644
index 6b3536e..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleComponent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.badexecutor;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProductionComponent;
-
-/**
- * A component that contains entry points that exercise different execution paths, for verifying the
- * behavior when the executor throws a {@link java.util.concurrent.RejectedExecutionException}.
- */
-@ProductionComponent(dependencies = ComponentDependency.class, modules = SimpleProducerModule.class)
-interface SimpleComponent {
- /** An entry point exposing a producer method with no args. */
- ListenableFuture<String> noArgStr();
-
- /** An entry point exposing a producer method that depends on another producer method. */
- ListenableFuture<Integer> singleArgInt();
-
- /** An entry point exposing a producer method that depends on a component dependency method. */
- ListenableFuture<Boolean> singleArgBool();
-
- /** An entry point exposing a component dependency method. */
- ListenableFuture<Double> doubleDep();
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleProducerModule.java
deleted file mode 100644
index 00ab037..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleProducerModule.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.badexecutor;
-
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-
-@ProducerModule
-final class SimpleProducerModule {
- @Produces
- static String noArgStr() {
- return "no arg string";
- }
-
- @Produces
- static int singleArgInt(String arg) {
- return arg.length();
- }
-
- @Produces
- static boolean singleArgBool(double arg) {
- return arg > 0.0;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/DepComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/DepComponent.java
deleted file mode 100644
index dadde7b..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/DepComponent.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.builder;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-interface DepComponent {
- ListenableFuture<Double> d();
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/IntModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/IntModule.java
deleted file mode 100644
index 7f99836..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/IntModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.builder;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class IntModule {
- @Provides
- static int i() {
- return 42;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/StringModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/StringModule.java
deleted file mode 100644
index cdf0793..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/StringModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.builder;
-
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-
-@ProducerModule
-final class StringModule {
- @Produces
- static String str(int i) {
- return "arg: " + i;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/TestComponentWithBuilder.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/TestComponentWithBuilder.java
deleted file mode 100644
index 16dc9ba..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/TestComponentWithBuilder.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.builder;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProductionComponent;
-import java.util.concurrent.Executor;
-
-@ProductionComponent(
- modules = {StringModule.class, IntModule.class},
- dependencies = DepComponent.class
-)
-interface TestComponentWithBuilder {
- ListenableFuture<String> s();
- ListenableFuture<Double> d();
-
- @ProductionComponent.Builder
- interface Builder {
- Builder depComponent(DepComponent depComponent);
- Builder strModule(StringModule strModule);
- Builder executor(Executor executor);
- TestComponentWithBuilder build();
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoredComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoredComponent.java
deleted file mode 100644
index 48acbab..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoredComponent.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.monitoring;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProductionComponent;
-
-@ProductionComponent(modules = {MonitoringModule.class, StubModule.class, ServingModule.class})
-interface MonitoredComponent {
- ListenableFuture<String> output();
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoringModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoringModule.java
deleted file mode 100644
index 0c42090..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoringModule.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.monitoring;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-
-import static dagger.Provides.Type.SET;
-
-@Module
-final class MonitoringModule {
- private final ProductionComponentMonitor.Factory monitorFactory;
-
- MonitoringModule(ProductionComponentMonitor.Factory monitorFactory) {
- this.monitorFactory = monitorFactory;
- }
-
- @Provides(type = SET)
- ProductionComponentMonitor.Factory monitorFactory() {
- return monitorFactory;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/ServingModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/ServingModule.java
deleted file mode 100644
index d5bec22..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/ServingModule.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.monitoring;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import javax.inject.Qualifier;
-import producerstest.monitoring.StubModule.ForServer1;
-import producerstest.monitoring.StubModule.ForServer2;
-
-@ProducerModule
-final class ServingModule {
- @Qualifier
- @interface RequestData {}
-
- @Qualifier
- @interface IntermediateData {}
-
- @Produces
- @RequestData
- static String requestData() {
- return "Hello, World!";
- }
-
- @Produces
- @IntermediateData
- static ListenableFuture<String> callServer1(
- @RequestData String data, @ForServer1 StringStub stub) {
- return stub.run(data);
- }
-
- @Produces
- static ListenableFuture<String> callServer2(
- @IntermediateData String data, @ForServer2 StringStub stub) {
- return stub.run(data);
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StringStub.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StringStub.java
deleted file mode 100644
index 195dd28..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StringStub.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.monitoring;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-interface StringStub {
- ListenableFuture<String> run(String input);
-}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StubModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StubModule.java
deleted file mode 100644
index dc8ab62..0000000
--- a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StubModule.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.monitoring;
-
-import dagger.Module;
-import dagger.Provides;
-import javax.inject.Qualifier;
-
-@Module
-final class StubModule {
- @Qualifier
- @interface ForServer1 {}
-
- @Qualifier
- @interface ForServer2 {}
-
- private final StringStub server1;
- private final StringStub server2;
-
- StubModule(StringStub server1, StringStub server2) {
- this.server1 = server1;
- this.server2 = server2;
- }
-
- @Provides
- @ForServer1
- StringStub server1() {
- return server1;
- }
-
- @Provides
- @ForServer2
- StringStub server2() {
- return server2;
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/DependentTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/DependentTest.java
deleted file mode 100644
index b2533d7..0000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/DependentTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package producerstest;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class DependentTest {
- @Test public void dependentComponent() throws Exception {
- DependentComponent dependentComponent = DaggerDependentComponent
- .builder()
- .dependedProductionComponent(DaggerDependedProductionComponent.builder()
- .executor(MoreExecutors.directExecutor())
- .build())
- .dependedComponent(DaggerDependedComponent.create())
- .executor(MoreExecutors.directExecutor())
- .build();
- assertThat(dependentComponent).isNotNull();
- assertThat(dependentComponent.greetings().get()).containsExactly(
- "2", "Hello world!", "HELLO WORLD!");
- }
-
- @Test public void reuseBuilderWithDependentComponent() throws Exception {
- DaggerDependentComponent.Builder dependentComponentBuilder = DaggerDependentComponent
- .builder()
- .executor(MoreExecutors.directExecutor());
-
- DependentComponent componentUsingComponents = dependentComponentBuilder
- .dependedProductionComponent(DaggerDependedProductionComponent.builder()
- .executor(MoreExecutors.directExecutor())
- .build())
- .dependedComponent(DaggerDependedComponent.create())
- .build();
-
- DependentComponent componentUsingJavaImpls = dependentComponentBuilder
- .dependedProductionComponent(new DependedProductionComponent() {
- @Override public ListenableFuture<Integer> numGreetings() {
- return Futures.immediateFuture(3);
- }
- })
- .dependedComponent(new DependedComponent() {
- @Override public String getGreeting() {
- return "Goodbye world!";
- }
- })
- .build();
-
- assertThat(componentUsingJavaImpls.greetings().get()).containsExactly(
- "3", "Goodbye world!", "GOODBYE WORLD!");
- assertThat(componentUsingComponents.greetings().get()).containsExactly(
- "2", "Hello world!", "HELLO WORLD!");
-
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/MultibindingTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/MultibindingTest.java
deleted file mode 100644
index 4ddc7c6..0000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/MultibindingTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package producerstest;
-
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.MoreExecutors;
-import dagger.producers.Produced;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class MultibindingTest {
- @Test
- public void setBinding() throws Exception {
- MultibindingComponent multibindingComponent =
- DaggerMultibindingComponent.builder().executor(MoreExecutors.directExecutor()).build();
- assertThat(multibindingComponent.strs().get())
- .containsExactly("foo", "foo1", "foo2", "bar", "bar1", "bar2");
- assertThat(multibindingComponent.strCount().get()).isEqualTo(6);
- }
-
- @Test
- public void setBindingOfProduced() throws Exception {
- MultibindingComponent multibindingComponent =
- DaggerMultibindingComponent.builder().executor(MoreExecutors.directExecutor()).build();
- assertThat(multibindingComponent.successfulSet().get())
- .containsExactly(
- Produced.successful("foo"),
- Produced.successful("foo1"),
- Produced.successful("foo2"),
- Produced.successful("bar"),
- Produced.successful("bar1"),
- Produced.successful("bar2"));
- }
-
- @Test
- public void setBindingOfProducedWithFailures() throws Exception {
- MultibindingComponent multibindingComponent =
- DaggerMultibindingComponent.builder().executor(MoreExecutors.directExecutor()).build();
- Set<Produced<String>> possiblyThrowingSet = multibindingComponent.possiblyThrowingSet().get();
- Set<String> successes = new HashSet<>();
- Set<ExecutionException> failures = new HashSet<>();
- for (Produced<String> str : possiblyThrowingSet) {
- try {
- successes.add(str.get());
- } catch (ExecutionException e) {
- failures.add(e);
- }
- }
- assertThat(successes).containsExactly("singleton", "double", "ton");
- assertThat(failures).hasSize(1);
- assertThat(Iterables.getOnlyElement(failures).getCause()).hasMessage("monkey");
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/ProducerFactoryTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/ProducerFactoryTest.java
deleted file mode 100644
index 9a56029..0000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/ProducerFactoryTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package producerstest;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.SettableFuture;
-import dagger.producers.Producer;
-import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import java.util.concurrent.ExecutionException;
-import javax.inject.Provider;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.when;
-
-@RunWith(JUnit4.class)
-public class ProducerFactoryTest {
- @Mock private ProductionComponentMonitor componentMonitor;
- private ProducerMonitor monitor;
- private Provider<ProductionComponentMonitor> componentMonitorProvider;
-
- @Before
- public void setUpMocks() {
- MockitoAnnotations.initMocks(this);
- monitor = Mockito.mock(ProducerMonitor.class, Mockito.CALLS_REAL_METHODS);
- when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor);
- componentMonitorProvider =
- new Provider<ProductionComponentMonitor>() {
- @Override
- public ProductionComponentMonitor get() {
- return componentMonitor;
- }
- };
- }
-
- @Test
- public void noArgMethod() throws Exception {
- ProducerToken token = ProducerToken.create(SimpleProducerModule_StrFactory.class);
- Producer<String> producer =
- new SimpleProducerModule_StrFactory(
- MoreExecutors.directExecutor(), componentMonitorProvider);
- assertThat(producer.get().get()).isEqualTo("str");
- InOrder order = inOrder(componentMonitor, monitor);
- order.verify(componentMonitor).producerMonitorFor(token);
- order.verify(monitor).methodStarting();
- order.verify(monitor).methodFinished();
- order.verify(monitor).succeeded("str");
- order.verifyNoMoreInteractions();
- }
-
- @Test public void singleArgMethod() throws Exception {
- SettableFuture<Integer> intFuture = SettableFuture.create();
- Producer<Integer> intProducer = producerOfFuture(intFuture);
- Producer<String> producer =
- new SimpleProducerModule_StrWithArgFactory(
- MoreExecutors.directExecutor(), componentMonitorProvider, intProducer);
- assertThat(producer.get().isDone()).isFalse();
- intFuture.set(42);
- assertThat(producer.get().get()).isEqualTo("str with arg");
- }
-
- @Test
- public void successMonitor() throws Exception {
- ProducerToken token = ProducerToken.create(SimpleProducerModule_SettableFutureStrFactory.class);
-
- SettableFuture<String> strFuture = SettableFuture.create();
- SettableFuture<SettableFuture<String>> strFutureFuture = SettableFuture.create();
- Producer<SettableFuture<String>> strFutureProducer = producerOfFuture(strFutureFuture);
- Producer<String> producer =
- new SimpleProducerModule_SettableFutureStrFactory(
- MoreExecutors.directExecutor(), componentMonitorProvider, strFutureProducer);
- assertThat(producer.get().isDone()).isFalse();
-
- InOrder order = inOrder(componentMonitor, monitor);
- order.verify(componentMonitor).producerMonitorFor(token);
-
- strFutureFuture.set(strFuture);
- order.verify(monitor).methodStarting();
- order.verify(monitor).methodFinished();
- assertThat(producer.get().isDone()).isFalse();
-
- strFuture.set("monkey");
- assertThat(producer.get().get()).isEqualTo("monkey");
- order.verify(monitor).succeeded("monkey");
-
- order.verifyNoMoreInteractions();
- }
-
- @Test
- public void failureMonitor() throws Exception {
- ProducerToken token = ProducerToken.create(SimpleProducerModule_SettableFutureStrFactory.class);
-
- SettableFuture<String> strFuture = SettableFuture.create();
- SettableFuture<SettableFuture<String>> strFutureFuture = SettableFuture.create();
- Producer<SettableFuture<String>> strFutureProducer = producerOfFuture(strFutureFuture);
- Producer<String> producer =
- new SimpleProducerModule_SettableFutureStrFactory(
- MoreExecutors.directExecutor(), componentMonitorProvider, strFutureProducer);
- assertThat(producer.get().isDone()).isFalse();
-
- InOrder order = inOrder(componentMonitor, monitor);
- order.verify(componentMonitor).producerMonitorFor(token);
-
- strFutureFuture.set(strFuture);
- order.verify(monitor).methodStarting();
- order.verify(monitor).methodFinished();
- assertThat(producer.get().isDone()).isFalse();
-
- Throwable t = new RuntimeException("monkey");
- strFuture.setException(t);
- try {
- producer.get().get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isSameAs(t);
- order.verify(monitor).failed(t);
- }
-
- order.verifyNoMoreInteractions();
- }
-
- @Test
- public void failureMonitorDueToThrowingProducer() throws Exception {
- ProducerToken token = ProducerToken.create(SimpleProducerModule_ThrowingProducerFactory.class);
-
- Producer<String> producer =
- new SimpleProducerModule_ThrowingProducerFactory(
- MoreExecutors.directExecutor(), componentMonitorProvider);
- assertThat(producer.get().isDone()).isTrue();
-
- InOrder order = inOrder(componentMonitor, monitor);
- order.verify(componentMonitor).producerMonitorFor(token);
-
- order.verify(monitor).methodStarting();
- order.verify(monitor).methodFinished();
-
- try {
- producer.get().get();
- fail();
- } catch (ExecutionException e) {
- order.verify(monitor).failed(e.getCause());
- }
-
- order.verifyNoMoreInteractions();
- }
-
- @Test(expected = NullPointerException.class)
- public void nullComponentMonitorProvider() throws Exception {
- new SimpleProducerModule_StrFactory(MoreExecutors.directExecutor(), null);
- }
-
- private static <T> Producer<T> producerOfFuture(final ListenableFuture<T> future) {
- return new Producer<T>() {
- @Override public ListenableFuture<T> get() {
- return future;
- }
- };
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/SimpleTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/SimpleTest.java
deleted file mode 100644
index cacc0f1..0000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/SimpleTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package producerstest;
-
-import com.google.common.util.concurrent.MoreExecutors;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class SimpleTest {
- @Test public void testSimpleComponent() throws Exception {
- SimpleComponent simpleComponent = DaggerSimpleComponent
- .builder()
- .executor(MoreExecutors.directExecutor())
- .build();
- assertThat(simpleComponent).isNotNull();
- assertThat(simpleComponent.response().get().data()).isEqualTo("Hello, Request #5!");
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/badexecutor/BadExecutorTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/badexecutor/BadExecutorTest.java
deleted file mode 100644
index 8a49797..0000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/badexecutor/BadExecutorTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package producerstest.badexecutor;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.RejectedExecutionException;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-/** This test verifies behavior when the executor throws {@link RejectedExecutionException}. */
-@RunWith(JUnit4.class)
-public final class BadExecutorTest {
- private SimpleComponent component;
-
- @Before
- public void setUpComponent() {
- ComponentDependency dependency =
- new ComponentDependency() {
- @Override
- public ListenableFuture<Double> doubleDep() {
- return Futures.immediateFuture(42.0);
- }
- };
- ListeningExecutorService executorService = MoreExecutors.newDirectExecutorService();
- component =
- DaggerSimpleComponent.builder()
- .executor(executorService)
- .componentDependency(dependency)
- .build();
- executorService.shutdown();
- }
-
- @Test
- public void rejectNoArgMethod() throws Exception {
- try {
- component.noArgStr().get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isInstanceOf(RejectedExecutionException.class);
- }
- }
-
- @Test
- public void rejectSingleArgMethod() throws Exception {
- try {
- component.singleArgInt().get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isInstanceOf(RejectedExecutionException.class);
- }
- }
-
- @Test
- public void rejectSingleArgFromComponentDepMethod() throws Exception {
- try {
- component.singleArgBool().get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isInstanceOf(RejectedExecutionException.class);
- }
- }
-
- @Test
- public void doNotRejectComponentDepMethod() throws Exception {
- assertThat(component.doubleDep().get()).isEqualTo(42.0);
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/builder/ProductionComponentBuilderTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/builder/ProductionComponentBuilderTest.java
deleted file mode 100644
index 715761d..0000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/builder/ProductionComponentBuilderTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.builder;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-/** Tests for {@link dagger.producers.ProductionComponent.Builder}. */
-@RunWith(JUnit4.class)
-public final class ProductionComponentBuilderTest {
-
- @Test
- public void successfulBuild() throws Exception {
- TestComponentWithBuilder component =
- DaggerTestComponentWithBuilder.builder()
- .executor(MoreExecutors.directExecutor())
- .depComponent(depComponent(15.3))
- .strModule(new StringModule())
- .build();
- assertThat(component.s().get()).isEqualTo("arg: 42");
- assertThat(component.d().get()).isEqualTo(15.3);
- }
-
- @Test
- public void successfulBuild_withMissingZeroArgModule() throws Exception {
- TestComponentWithBuilder component =
- DaggerTestComponentWithBuilder.builder()
- .executor(MoreExecutors.directExecutor())
- .depComponent(depComponent(15.3))
- .build();
- assertThat(component.s().get()).isEqualTo("arg: 42");
- assertThat(component.d().get()).isEqualTo(15.3);
- }
-
- @Test(expected = IllegalStateException.class)
- public void missingExecutor() {
- DaggerTestComponentWithBuilder.builder()
- .depComponent(depComponent(15.3))
- .strModule(new StringModule())
- .build();
- }
-
- @Test(expected = IllegalStateException.class)
- public void missingDepComponent() {
- DaggerTestComponentWithBuilder.builder()
- .executor(MoreExecutors.directExecutor())
- .strModule(new StringModule())
- .build();
- }
-
- private static DepComponent depComponent(final double value) {
- return new DepComponent() {
- @Override
- public ListenableFuture<Double> d() {
- return Futures.immediateFuture(value);
- }
- };
- }
-}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/monitoring/MonitoringTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/monitoring/MonitoringTest.java
deleted file mode 100644
index 3efb2b5..0000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/monitoring/MonitoringTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package producerstest.monitoring;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.SettableFuture;
-import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-/** Tests for production components using monitoring. */
-@RunWith(JUnit4.class)
-public final class MonitoringTest {
- @Mock private ProductionComponentMonitor.Factory componentMonitorFactory;
- @Mock private StringStub server1;
- @Mock private StringStub server2;
- private SettableFuture<String> server1Future;
- private SettableFuture<String> server2Future;
- private FakeProductionComponentMonitor componentMonitor;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- componentMonitor = new FakeProductionComponentMonitor();
- when(componentMonitorFactory.create(any())).thenReturn(componentMonitor);
- server1Future = SettableFuture.create();
- server2Future = SettableFuture.create();
- when(server1.run(any(String.class))).thenReturn(server1Future);
- when(server2.run(any(String.class))).thenReturn(server2Future);
- }
-
- @Test
- public void basicMonitoring() throws Exception {
- MonitoredComponent component =
- DaggerMonitoredComponent.builder()
- .executor(MoreExecutors.directExecutor())
- .monitoringModule(new MonitoringModule(componentMonitorFactory))
- .stubModule(new StubModule(server1, server2))
- .build();
- ListenableFuture<String> output = component.output();
- assertThat(componentMonitor.monitors).hasSize(3);
- ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
- ImmutableList.copyOf(componentMonitor.monitors.entrySet());
- assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
- assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
- assertThat(entries.get(2).getKey().toString()).contains("RequestData");
-
- ProducerMonitor callServer2Monitor = entries.get(0).getValue();
- ProducerMonitor callServer1Monitor = entries.get(1).getValue();
- ProducerMonitor requestDataMonitor = entries.get(2).getValue();
-
- InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
- inOrder.verify(requestDataMonitor).methodStarting();
- inOrder.verify(requestDataMonitor).methodFinished();
- inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
- inOrder.verify(callServer1Monitor).methodStarting();
- inOrder.verify(callServer1Monitor).methodFinished();
- verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
-
- server1Future.set("server 1 response");
- inOrder.verify(callServer1Monitor).succeeded("server 1 response");
- inOrder.verify(callServer2Monitor).methodStarting();
- inOrder.verify(callServer2Monitor).methodFinished();
- verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
-
- server2Future.set("server 2 response");
- inOrder.verify(callServer2Monitor).succeeded("server 2 response");
- verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
- assertThat(output.get()).isEqualTo("server 2 response");
- }
-
- @Test
- public void basicMonitoringWithFailure() throws Exception {
- MonitoredComponent component =
- DaggerMonitoredComponent.builder()
- .executor(MoreExecutors.directExecutor())
- .monitoringModule(new MonitoringModule(componentMonitorFactory))
- .stubModule(new StubModule(server1, server2))
- .build();
- ListenableFuture<String> output = component.output();
- assertThat(componentMonitor.monitors).hasSize(3);
- ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
- ImmutableList.copyOf(componentMonitor.monitors.entrySet());
- assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
- assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
- assertThat(entries.get(2).getKey().toString()).contains("RequestData");
-
- ProducerMonitor callServer2Monitor = entries.get(0).getValue();
- ProducerMonitor callServer1Monitor = entries.get(1).getValue();
- ProducerMonitor requestDataMonitor = entries.get(2).getValue();
-
- InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
- inOrder.verify(requestDataMonitor).methodStarting();
- inOrder.verify(requestDataMonitor).methodFinished();
- inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
- inOrder.verify(callServer1Monitor).methodStarting();
- inOrder.verify(callServer1Monitor).methodFinished();
- verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
-
- RuntimeException cause = new RuntimeException("monkey");
- server1Future.setException(cause);
- inOrder.verify(callServer1Monitor).failed(cause);
- inOrder.verify(callServer2Monitor).failed(any(Throwable.class));
- verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
- try {
- output.get();
- fail();
- } catch (ExecutionException e) {
- assertThat(Throwables.getRootCause(e)).isSameAs(cause);
- }
- }
-
- private static final class FakeProductionComponentMonitor implements ProductionComponentMonitor {
- final Map<ProducerToken, ProducerMonitor> monitors = new LinkedHashMap<>();
-
- @Override
- public ProducerMonitor producerMonitorFor(ProducerToken token) {
- ProducerMonitor monitor = mock(ProducerMonitor.class);
- monitors.put(token, monitor);
- return monitor;
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/AbstractComponentProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/AbstractComponentProcessingStep.java
deleted file mode 100644
index 578fb5f..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/AbstractComponentProcessingStep.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
-import java.lang.annotation.Annotation;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/**
- * A {@link ProcessingStep} that is responsible for dealing with a component or production component
- * as part of the {@link ComponentProcessor}.
- */
-abstract class AbstractComponentProcessingStep implements ProcessingStep {
-
- private final Class<? extends Annotation> componentAnnotation;
- private final Messager messager;
- private final ComponentHierarchyValidator componentHierarchyValidator;
- private final BindingGraphValidator bindingGraphValidator;
- private final ComponentDescriptor.Factory componentDescriptorFactory;
- private final BindingGraph.Factory bindingGraphFactory;
- private final ComponentGenerator componentGenerator;
-
- AbstractComponentProcessingStep(
- Class<? extends Annotation> componentAnnotation,
- Messager messager,
- ComponentHierarchyValidator componentHierarchyValidator,
- BindingGraphValidator bindingGraphValidator,
- ComponentDescriptor.Factory componentDescriptorFactory,
- BindingGraph.Factory bindingGraphFactory,
- ComponentGenerator componentGenerator) {
- this.componentAnnotation = componentAnnotation;
- this.messager = messager;
- this.componentHierarchyValidator = componentHierarchyValidator;
- this.bindingGraphValidator = bindingGraphValidator;
- this.componentDescriptorFactory = componentDescriptorFactory;
- this.bindingGraphFactory = bindingGraphFactory;
- this.componentGenerator = componentGenerator;
- }
-
- @Override
- public final ImmutableSet<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
- ComponentElementValidator componentElementValidator =
- componentElementValidator(elementsByAnnotation);
- for (Element element : elementsByAnnotation.get(componentAnnotation)) {
- TypeElement componentTypeElement = MoreElements.asType(element);
- try {
- if (componentElementValidator.validateComponent(componentTypeElement, messager)) {
- ComponentDescriptor componentDescriptor =
- componentDescriptorFactory.forComponent(componentTypeElement);
- ValidationReport<TypeElement> hierarchyReport =
- componentHierarchyValidator.validate(componentDescriptor);
- hierarchyReport.printMessagesTo(messager);
- if (hierarchyReport.isClean()) {
- BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor);
- ValidationReport<TypeElement> graphReport =
- bindingGraphValidator.validate(bindingGraph);
- graphReport.printMessagesTo(messager);
- if (graphReport.isClean()) {
- generateComponent(bindingGraph);
- }
- }
- }
- } catch (TypeNotPresentException e) {
- rejectedElements.add(componentTypeElement);
- }
- }
- return rejectedElements.build();
- }
-
- private void generateComponent(BindingGraph bindingGraph) {
- try {
- componentGenerator.generate(bindingGraph);
- } catch (SourceFileGenerationException e) {
- e.printMessageTo(messager);
- }
- }
-
- /**
- * Returns an object that can validate a type element annotated with the component type.
- */
- protected abstract ComponentElementValidator componentElementValidator(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation);
-
- /**
- * Validates a component type element.
- */
- protected static abstract class ComponentElementValidator {
- /**
- * Validates a component type element. Prints any messages about the element to
- * {@code messager}.
- *
- * @throws TypeNotPresentException if any type required to validate the component cannot be
- * found
- */
- abstract boolean validateComponent(TypeElement componentTypeElement, Messager messager);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java b/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java
deleted file mode 100644
index db890b2..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java
+++ /dev/null
@@ -1,1165 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.MembersInjector;
-import dagger.internal.DelegateFactory;
-import dagger.internal.Factory;
-import dagger.internal.InstanceFactory;
-import dagger.internal.MapFactory;
-import dagger.internal.MapProviderFactory;
-import dagger.internal.MembersInjectors;
-import dagger.internal.ScopedProvider;
-import dagger.internal.SetFactory;
-import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ComponentGenerator.MemberSelect;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.ConstructorWriter;
-import dagger.internal.codegen.writer.FieldWriter;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.StringLiteral;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import dagger.internal.codegen.writer.VoidName;
-import dagger.producers.Producer;
-import dagger.producers.internal.Producers;
-import dagger.producers.internal.SetOfProducedProducer;
-import dagger.producers.internal.SetProducer;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Provider;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Name;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic;
-import javax.tools.Diagnostic.Kind;
-
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.any;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.DELEGATED;
-import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.INITIALIZED;
-import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.UNINITIALIZED;
-import static dagger.internal.codegen.Binding.bindingPackageFor;
-import static dagger.internal.codegen.ComponentGenerator.MemberSelect.staticMethodInvocationWithCast;
-import static dagger.internal.codegen.ComponentGenerator.MemberSelect.staticSelect;
-import static dagger.internal.codegen.ContributionBinding.contributionTypeFor;
-import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.ENUM_INSTANCE;
-import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
-import static dagger.internal.codegen.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD;
-import static dagger.internal.codegen.MapKeys.getMapKeySnippet;
-import static dagger.internal.codegen.MembersInjectionBinding.Strategy.NO_OP;
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.internal.codegen.SourceFiles.indexDependenciesByUnresolvedKey;
-import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
-import static dagger.internal.codegen.Util.getKeyTypeOfMap;
-import static dagger.internal.codegen.Util.getProvidedValueTypeOfMap;
-import static dagger.internal.codegen.Util.isMapWithNonProvidedValues;
-import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
-import static dagger.internal.codegen.writer.Snippet.memberSelectSnippet;
-import static dagger.internal.codegen.writer.Snippet.nullCheck;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.VOID;
-
-/**
- * Creates the implementation class for a component or subcomponent.
- */
-abstract class AbstractComponentWriter {
- // TODO(dpb): Make all these fields private after refactoring is complete.
- protected final Elements elements;
- protected final Types types;
- protected final Key.Factory keyFactory;
- protected final Kind nullableValidationType;
- protected final Set<JavaWriter> javaWriters = new LinkedHashSet<>();
- protected final ClassName name;
- protected final BindingGraph graph;
- private final Map<BindingKey, InitializationState> initializationStates = new HashMap<>();
- private final Map<Binding, InitializationState> contributionInitializationStates =
- new HashMap<>();
- protected ClassWriter componentWriter;
- private final Map<BindingKey, MemberSelect> memberSelectSnippets = new HashMap<>();
- private final Map<ContributionBinding, MemberSelect> multibindingContributionSnippets =
- new HashMap<>();
- protected ConstructorWriter constructorWriter;
- protected Optional<ClassName> builderName = Optional.absent();
-
- /**
- * For each component requirement, the builder field. This map is empty for subcomponents that do
- * not use a builder.
- */
- private ImmutableMap<TypeElement, FieldWriter> builderFields = ImmutableMap.of();
-
- /**
- * For each component requirement, the snippet for the component field that holds it.
- *
- * <p>Fields are written for all requirements for subcomponents that do not use a builder, and for
- * any requirement that is reused from a subcomponent of this component.
- */
- protected final Map<TypeElement, MemberSelect> componentContributionFields = Maps.newHashMap();
-
- AbstractComponentWriter(
- Types types,
- Elements elements,
- Key.Factory keyFactory,
- Diagnostic.Kind nullableValidationType,
- ClassName name,
- BindingGraph graph) {
- this.types = types;
- this.elements = elements;
- this.keyFactory = keyFactory;
- this.nullableValidationType = nullableValidationType;
- this.name = name;
- this.graph = graph;
- }
-
- protected final TypeElement componentDefinitionType() {
- return graph.componentDescriptor().componentDefinitionType();
- }
-
- protected final ClassName componentDefinitionTypeName() {
- return ClassName.fromTypeElement(componentDefinitionType());
- }
-
- /**
- * Returns an expression snippet that evaluates to an instance of the contribution, looking for
- * either a builder field or a component field.
- */
- private Snippet getComponentContributionSnippet(TypeElement contributionType) {
- if (builderFields.containsKey(contributionType)) {
- return Snippet.format("builder.%s", builderFields.get(contributionType).name());
- } else {
- Optional<Snippet> snippet = getOrCreateComponentContributionFieldSnippet(contributionType);
- checkState(snippet.isPresent(), "no builder or component field for %s", contributionType);
- return snippet.get();
- }
- }
-
- /**
- * Returns a snippet for a component contribution field. Adds a field the first time one is
- * requested for a contribution type if this component's builder has a field for it.
- */
- protected Optional<Snippet> getOrCreateComponentContributionFieldSnippet(
- TypeElement contributionType) {
- MemberSelect fieldSelect = componentContributionFields.get(contributionType);
- if (fieldSelect == null) {
- if (!builderFields.containsKey(contributionType)) {
- return Optional.absent();
- }
- FieldWriter componentField =
- componentWriter.addField(contributionType, simpleVariableName(contributionType));
- componentField.addModifiers(PRIVATE, FINAL);
- constructorWriter
- .body()
- .addSnippet(
- "this.%s = builder.%s;",
- componentField.name(),
- builderFields.get(contributionType).name());
- fieldSelect = MemberSelect.instanceSelect(name, Snippet.format("%s", componentField.name()));
- componentContributionFields.put(contributionType, fieldSelect);
- }
- return Optional.of(fieldSelect.getSnippetFor(name));
- }
-
- private Snippet getMemberSelectSnippet(BindingKey key) {
- return getMemberSelect(key).getSnippetFor(name);
- }
-
- protected MemberSelect getMemberSelect(BindingKey key) {
- return memberSelectSnippets.get(key);
- }
-
- protected Optional<MemberSelect> getMultibindingContributionSnippet(ContributionBinding binding) {
- return Optional.fromNullable(multibindingContributionSnippets.get(binding));
- }
-
- /**
- * Returns the initialization state of the factory field for a binding key in this component.
- */
- protected InitializationState getInitializationState(BindingKey bindingKey) {
- return initializationStates.containsKey(bindingKey)
- ? initializationStates.get(bindingKey)
- : UNINITIALIZED;
- }
-
- private void setInitializationState(BindingKey bindingKey, InitializationState state) {
- initializationStates.put(bindingKey, state);
- }
-
- private InitializationState getContributionInitializationState(Binding binding) {
- return contributionInitializationStates.containsKey(binding)
- ? contributionInitializationStates.get(binding)
- : UNINITIALIZED;
- }
-
- private void setContributionInitializationState(Binding binding, InitializationState state) {
- contributionInitializationStates.put(binding, state);
- }
-
- ImmutableSet<JavaWriter> write() {
- if (javaWriters.isEmpty()) {
- writeComponent();
- }
- return ImmutableSet.copyOf(javaWriters);
- }
-
- private void writeComponent() {
- componentWriter = createComponentClass();
- addConstructor();
- addBuilder();
- addFactoryMethods();
- addFields();
- initializeFrameworkTypes();
- implementInterfaceMethods();
- addSubcomponents();
- }
-
- /**
- * Creates the component implementation class.
- */
- protected abstract ClassWriter createComponentClass();
-
- private void addConstructor() {
- constructorWriter = componentWriter.addConstructor();
- constructorWriter.addModifiers(PRIVATE);
- }
-
- /**
- * Adds a builder type.
- */
- protected void addBuilder() {
- ClassWriter builderWriter = createBuilder();
- builderWriter.addModifiers(FINAL);
- builderWriter.addConstructor().addModifiers(PRIVATE);
- builderName = Optional.of(builderWriter.name());
-
- Optional<BuilderSpec> builderSpec = graph.componentDescriptor().builderSpec();
- if (builderSpec.isPresent()) {
- builderWriter.addModifiers(PRIVATE);
- builderWriter.setSupertype(builderSpec.get().builderDefinitionType());
- } else {
- builderWriter.addModifiers(PUBLIC);
- }
-
- builderFields = addBuilderFields(builderWriter);
- addBuildMethod(builderWriter, builderSpec);
- addBuilderMethods(builderWriter, builderSpec);
-
- constructorWriter.addParameter(builderWriter, "builder");
- constructorWriter.body().addSnippet("assert builder != null;");
- }
-
- /**
- * Adds fields for each of the {@linkplain BindingGraph#componentRequirements component
- * requirements}. Regardless of builder spec, there is always one field per requirement.
- */
- private ImmutableMap<TypeElement, FieldWriter> addBuilderFields(ClassWriter builderWriter) {
- ImmutableMap.Builder<TypeElement, FieldWriter> builderFieldsBuilder = ImmutableMap.builder();
- for (TypeElement contributionElement : graph.componentRequirements()) {
- String contributionName = simpleVariableName(contributionElement);
- FieldWriter builderField = builderWriter.addField(contributionElement, contributionName);
- builderField.addModifiers(PRIVATE);
- builderFieldsBuilder.put(contributionElement, builderField);
- }
- return builderFieldsBuilder.build();
- }
-
- /** Adds the build method to the builder. */
- private void addBuildMethod(ClassWriter builderWriter, Optional<BuilderSpec> builderSpec) {
- MethodWriter buildMethod;
- if (builderSpec.isPresent()) {
- ExecutableElement specBuildMethod = builderSpec.get().buildMethod();
- // Note: we don't use the specBuildMethod.getReturnType() as the return type
- // because it might be a type variable. We make use of covariant returns to allow
- // us to return the component type, which will always be valid.
- buildMethod =
- builderWriter.addMethod(
- componentDefinitionTypeName(), specBuildMethod.getSimpleName().toString());
- buildMethod.annotate(Override.class);
- } else {
- buildMethod = builderWriter.addMethod(componentDefinitionTypeName(), "build");
- }
- buildMethod.addModifiers(PUBLIC);
-
- for (Map.Entry<TypeElement, FieldWriter> builderFieldEntry : builderFields.entrySet()) {
- FieldWriter builderField = builderFieldEntry.getValue();
- if (componentCanMakeNewInstances(builderFieldEntry.getKey())) {
- buildMethod.body()
- .addSnippet("if (%1$s == null) { this.%1$s = new %2$s(); }",
- builderField.name(),
- builderField.type());
- } else {
- buildMethod.body()
- .addSnippet(
- "if (%s == null) { throw new %s(%s.class.getCanonicalName() + \" must be set\"); }",
- builderField.name(),
- ClassName.fromClass(IllegalStateException.class),
- builderField.type());
- }
- }
-
- buildMethod.body().addSnippet("return new %s(this);", name);
- }
-
- /**
- * Adds the methods that set each of parameters on the builder. If the {@link BuilderSpec} is
- * present, it will tailor the methods to match the spec.
- */
- private void addBuilderMethods(
- ClassWriter builderWriter,
- Optional<BuilderSpec> builderSpec) {
- if (builderSpec.isPresent()) {
- for (Map.Entry<TypeElement, ExecutableElement> builderMethodEntry :
- builderSpec.get().methodMap().entrySet()) {
- TypeElement builderMethodType = builderMethodEntry.getKey();
- ExecutableElement specMethod = builderMethodEntry.getValue();
- MethodWriter builderMethod = addBuilderMethodFromSpec(builderWriter, specMethod);
- String parameterName =
- Iterables.getOnlyElement(specMethod.getParameters()).getSimpleName().toString();
- builderMethod.addParameter(builderMethodType, parameterName);
- builderMethod.body().addSnippet(nullCheck(parameterName));
- if (graph.componentRequirements().contains(builderMethodType)) {
- // required type
- builderMethod.body().addSnippet("this.%s = %s;",
- builderFields.get(builderMethodType).name(),
- parameterName);
- addBuilderMethodReturnStatementForSpec(specMethod, builderMethod);
- } else if (graph.ownedModuleTypes().contains(builderMethodType)) {
- // owned, but not required
- builderMethod.body()
- .addSnippet("// This module is declared, but not used in the component. "
- + "This method is a no-op");
- addBuilderMethodReturnStatementForSpec(specMethod, builderMethod);
- } else {
- // neither owned nor required, so it must be an inherited module
- builderMethod
- .body()
- .addSnippet(
- "throw new %s(%s.format(%s, %s.class.getCanonicalName()));",
- ClassName.fromClass(UnsupportedOperationException.class),
- ClassName.fromClass(String.class),
- StringLiteral.forValue(
- "%s cannot be set because it is inherited from the enclosing component"),
- ClassName.fromTypeElement(builderMethodType));
- }
- }
- } else {
- for (TypeElement componentRequirement : graph.availableDependencies()) {
- String componentRequirementName = simpleVariableName(componentRequirement);
- MethodWriter builderMethod = builderWriter.addMethod(
- builderWriter.name(),
- componentRequirementName);
- builderMethod.addModifiers(PUBLIC);
- builderMethod.addParameter(componentRequirement, componentRequirementName);
- builderMethod.body().addSnippet(nullCheck(componentRequirementName));
- if (graph.componentRequirements().contains(componentRequirement)) {
- builderMethod.body()
- .addSnippet("this.%s = %s;",
- builderFields.get(componentRequirement).name(),
- componentRequirementName);
- } else {
- builderMethod.annotate(Deprecated.class);
- }
- builderMethod.body().addSnippet("return this;");
- }
- }
- }
-
- private void addBuilderMethodReturnStatementForSpec(
- ExecutableElement specMethod, MethodWriter builderMethod) {
- if (!specMethod.getReturnType().getKind().equals(VOID)) {
- builderMethod.body().addSnippet("return this;");
- }
- }
-
- private MethodWriter addBuilderMethodFromSpec(
- ClassWriter builderWriter, ExecutableElement method) {
- String methodName = method.getSimpleName().toString();
- TypeMirror returnType = method.getReturnType();
- // If the return type is void, we add a method with the void return type.
- // Otherwise we use the builderWriter and take advantage of covariant returns
- // (so that we don't have to worry about setter methods that return type variables).
- MethodWriter builderMethod =
- returnType.getKind().equals(TypeKind.VOID)
- ? builderWriter.addMethod(returnType, methodName)
- : builderWriter.addMethod(builderWriter, methodName);
- builderMethod.annotate(Override.class);
- builderMethod.addModifiers(Sets.difference(method.getModifiers(), ImmutableSet.of(ABSTRACT)));
- return builderMethod;
- }
-
- /**
- * Creates the builder class.
- */
- protected abstract ClassWriter createBuilder();
-
- /**
- * Adds component factory methods.
- */
- protected abstract void addFactoryMethods();
-
- private void addFields() {
- for (ResolvedBindings resolvedBindings : graph.resolvedBindings().values()) {
- addField(resolvedBindings);
- }
- }
-
- private void addField(ResolvedBindings resolvedBindings) {
- BindingKey bindingKey = resolvedBindings.bindingKey();
-
- // No field needed if there are no owned bindings.
- if (resolvedBindings.ownedBindings().isEmpty()) {
- return;
- }
-
- // No field needed for bindings with no dependencies or state.
- Optional<MemberSelect> staticMemberSelect = staticMemberSelect(resolvedBindings);
- if (staticMemberSelect.isPresent()) {
- memberSelectSnippets.put(bindingKey, staticMemberSelect.get());
- return;
- }
-
- Optional<String> bindingPackage = bindingPackageFor(resolvedBindings.bindings());
- boolean useRawType = bindingPackage.isPresent()
- && !bindingPackage.get().equals(name.packageName());
- if (bindingKey.kind().equals(BindingKey.Kind.CONTRIBUTION)) {
- ImmutableSet<ContributionBinding> contributionBindings =
- resolvedBindings.contributionBindings();
- if (ContributionBinding.contributionTypeFor(contributionBindings).isMultibinding()) {
- // note that here we rely on the order of the resolved bindings being from parent to child
- // otherwise, the numbering wouldn't work
- int contributionNumber = 0;
- for (ContributionBinding contributionBinding : contributionBindings) {
- if (!contributionBinding.isSyntheticBinding()) {
- contributionNumber++;
- if (resolvedBindings.ownedContributionBindings().contains(contributionBinding)) {
- FrameworkField contributionBindingField =
- FrameworkField.createForSyntheticContributionBinding(
- contributionNumber, contributionBinding);
- FieldWriter contributionField =
- addFrameworkField(useRawType, contributionBindingField);
-
- ImmutableList<String> contributionSelectTokens =
- new ImmutableList.Builder<String>()
- .add(contributionField.name())
- .build();
- multibindingContributionSnippets.put(
- contributionBinding,
- MemberSelect.instanceSelect(name, memberSelectSnippet(contributionSelectTokens)));
- }
- }
- }
- }
- }
-
- FrameworkField bindingField = FrameworkField.createForResolvedBindings(resolvedBindings);
- FieldWriter frameworkField = addFrameworkField(useRawType, bindingField);
-
- ImmutableList<String> memberSelectTokens =
- new ImmutableList.Builder<String>()
- .add(frameworkField.name())
- .build();
- memberSelectSnippets.put(
- bindingKey,
- MemberSelect.instanceSelect(name, Snippet.memberSelectSnippet(memberSelectTokens)));
- }
-
- private FieldWriter addFrameworkField(boolean useRawType,
- FrameworkField contributionBindingField) {
- FieldWriter contributionField =
- componentWriter.addField(
- useRawType
- ? contributionBindingField.frameworkType().type()
- : contributionBindingField.frameworkType(),
- contributionBindingField.name());
- contributionField.addModifiers(PRIVATE);
- if (useRawType) {
- contributionField.annotate(SuppressWarnings.class).setValue("rawtypes");
- }
- return contributionField;
- }
-
- /**
- * If {@code resolvedBindings} is an unscoped provision binding with no factory arguments or a
- * no-op members injection binding, then we don't need a field to hold its factory. In that case,
- * this method returns the static member select snippet that returns the factory or no-op members
- * injector.
- */
- private Optional<MemberSelect> staticMemberSelect(ResolvedBindings resolvedBindings) {
- switch (resolvedBindings.bindingKey().kind()) {
- case CONTRIBUTION:
- if (resolvedBindings.contributionBindings().size() != 1) {
- return Optional.absent();
- }
- ContributionBinding contributionBinding =
- getOnlyElement(resolvedBindings.contributionBindings());
- if (contributionBinding.contributionType().isMultibinding()
- || !(contributionBinding.bindingType().equals(Binding.Type.PROVISION))) {
- return Optional.absent();
- }
- if (contributionBinding.factoryCreationStrategy().equals(ENUM_INSTANCE)
- && !contributionBinding.scope().isPresent()) {
- return Optional.of(
- staticSelect(
- generatedClassNameForBinding(contributionBinding), Snippet.format("create()")));
- }
- break;
-
- case MEMBERS_INJECTION:
- Optional<MembersInjectionBinding> membersInjectionBinding =
- resolvedBindings.membersInjectionBinding();
- if (membersInjectionBinding.isPresent()
- && membersInjectionBinding.get().injectionStrategy().equals(NO_OP)) {
- return Optional.of(
- staticMethodInvocationWithCast(
- ClassName.fromClass(MembersInjectors.class),
- Snippet.format("noOp()"),
- ClassName.fromClass(MembersInjector.class)));
- }
- break;
-
- default:
- throw new AssertionError();
- }
- return Optional.absent();
- }
-
- private void implementInterfaceMethods() {
- Set<MethodSignature> interfaceMethods = Sets.newHashSet();
- for (ComponentMethodDescriptor componentMethod :
- graph.componentDescriptor().componentMethods()) {
- if (componentMethod.dependencyRequest().isPresent()) {
- DependencyRequest interfaceRequest = componentMethod.dependencyRequest().get();
- ExecutableElement requestElement =
- MoreElements.asExecutable(interfaceRequest.requestElement());
- ExecutableType requestType = MoreTypes.asExecutable(types.asMemberOf(
- MoreTypes.asDeclared(componentDefinitionType().asType()), requestElement));
- MethodSignature signature = MethodSignature.fromExecutableType(
- requestElement.getSimpleName().toString(), requestType);
- if (!interfaceMethods.contains(signature)) {
- interfaceMethods.add(signature);
- MethodWriter interfaceMethod =
- requestType.getReturnType().getKind().equals(VOID)
- ? componentWriter.addMethod(
- VoidName.VOID, requestElement.getSimpleName().toString())
- : componentWriter.addMethod(
- requestType.getReturnType(), requestElement.getSimpleName().toString());
- interfaceMethod.annotate(Override.class);
- interfaceMethod.addModifiers(PUBLIC);
- BindingKey bindingKey = interfaceRequest.bindingKey();
- MemberSelect memberSelect = getMemberSelect(bindingKey);
- Snippet memberSelectSnippet = memberSelect.getSnippetFor(name);
- switch (interfaceRequest.kind()) {
- case MEMBERS_INJECTOR:
- List<? extends VariableElement> parameters = requestElement.getParameters();
- if (parameters.isEmpty()) {
- // we're returning the framework type
- interfaceMethod.body().addSnippet("return %s;", memberSelectSnippet);
- } else {
- VariableElement parameter = Iterables.getOnlyElement(parameters);
- Name parameterName = parameter.getSimpleName();
- interfaceMethod.addParameter(
- TypeNames.forTypeMirror(
- Iterables.getOnlyElement(requestType.getParameterTypes())),
- parameterName.toString());
- interfaceMethod
- .body()
- .addSnippet(
- "%s.injectMembers(%s);",
- memberSelectSnippet,
- parameterName);
- if (!requestType.getReturnType().getKind().equals(VOID)) {
- interfaceMethod.body().addSnippet("return %s;", parameterName);
- }
- }
- break;
- case INSTANCE:
- if (memberSelect.staticMember()
- && bindingKey.key().type().getKind().equals(DECLARED)
- && !((DeclaredType) bindingKey.key().type()).getTypeArguments().isEmpty()) {
- // If using a parameterized enum type, then we need to store the factory
- // in a temporary variable, in order to help javac be able to infer
- // the generics of the Factory.create methods.
- TypeName factoryType =
- ParameterizedTypeName.create(
- Provider.class, TypeNames.forTypeMirror(requestType.getReturnType()));
- interfaceMethod
- .body()
- .addSnippet(
- "%s factory = %s;", factoryType, memberSelectSnippet);
- interfaceMethod.body().addSnippet("return factory.get();");
- break;
- }
- // fall through in the else case.
- case LAZY:
- case PRODUCED:
- case PRODUCER:
- case PROVIDER:
- case FUTURE:
- interfaceMethod
- .body()
- .addSnippet(
- "return %s;",
- frameworkTypeUsageStatement(
- memberSelectSnippet, interfaceRequest.kind()));
- break;
- default:
- throw new AssertionError();
- }
- }
- }
- }
- }
-
- private void addSubcomponents() {
- for (Map.Entry<ExecutableElement, BindingGraph> subgraphEntry : graph.subgraphs().entrySet()) {
- SubcomponentWriter subcomponent =
- new SubcomponentWriter(this, subgraphEntry.getKey(), subgraphEntry.getValue());
- javaWriters.addAll(subcomponent.write());
- }
- }
-
- private static final int SNIPPETS_PER_INITIALIZATION_METHOD = 100;
-
- private void initializeFrameworkTypes() {
- ImmutableList.Builder<Snippet> snippetsBuilder = ImmutableList.builder();
- for (BindingKey bindingKey : graph.resolvedBindings().keySet()) {
- snippetsBuilder.add(initializeFrameworkType(bindingKey));
- }
- ImmutableList<Snippet> snippets = snippetsBuilder.build();
-
- List<List<Snippet>> partitions = Lists.partition(snippets, SNIPPETS_PER_INITIALIZATION_METHOD);
- for (int i = 0; i < partitions.size(); i++) {
- MethodWriter initializeMethod =
- componentWriter.addMethod(VoidName.VOID, "initialize" + ((i == 0) ? "" : i));
- /* TODO(gak): Strictly speaking, we only need the suppression here if we are also initializing
- * a raw field in this method, but the structure of this code makes it awkward to pass that
- * bit through. This will be cleaned up when we no longer separate fields and initilization
- * as we do now. */
- initializeMethod.annotate(SuppressWarnings.class).setValue("unchecked");
- for (Snippet snippet : partitions.get(i)) {
- initializeMethod.body().addSnippet(snippet);
- }
- initializeMethod.addModifiers(PRIVATE);
- if (builderName.isPresent()) {
- initializeMethod.addParameter(builderName.get(), "builder").addModifiers(FINAL);
- constructorWriter.body().addSnippet("%s(builder);", initializeMethod.name());
- } else {
- constructorWriter.body().addSnippet("%s();", initializeMethod.name());
- }
- }
- }
-
- /**
- * Returns a single snippet representing the initialization of the framework type.
- *
- * <p>Note that this must be a single snippet because initialization snippets can be invoked from
- * any place in any order. By requiring a single snippet (often of concatenated snippets) we
- * ensure that things like local variables always behave as expected by the initialization logic.
- */
- private Snippet initializeFrameworkType(BindingKey bindingKey) {
- ResolvedBindings resolvedBindings = graph.resolvedBindings().get(bindingKey);
-
- // There's no field for inherited bindings.
- if (resolvedBindings.ownedBindings().isEmpty()) {
- return Snippet.format("");
- }
-
- switch (bindingKey.kind()) {
- case CONTRIBUTION:
- switch (contributionTypeFor(resolvedBindings.contributionBindings())) {
- case SET:
- return initializeSetMultibindings(resolvedBindings);
- case MAP:
- return initializeMapMultibindings(resolvedBindings);
- case UNIQUE:
- return initializeUniqueContributionBinding(resolvedBindings);
- default:
- throw new AssertionError();
- }
-
- case MEMBERS_INJECTION:
- return initializeMembersInjectionBinding(resolvedBindings);
-
- default:
- throw new AssertionError();
- }
- }
-
- private Snippet initializeSetMultibindings(ResolvedBindings resolvedBindings) {
- ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
-
- ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
- for (ContributionBinding binding : resolvedBindings.contributionBindings()) {
- Optional<MemberSelect> multibindingContributionSnippet =
- getMultibindingContributionSnippet(binding);
- checkState(multibindingContributionSnippet.isPresent(), "%s was not found", binding);
- Snippet snippet = multibindingContributionSnippet.get().getSnippetFor(name);
- if (multibindingContributionSnippet.get().owningClass().equals(name)
- // the binding might already be initialized by a different set binding that shares the
- // same contributions (e.g., Set<T> and Set<Produced<T>>)
- && getContributionInitializationState(binding)
- .equals(InitializationState.UNINITIALIZED)) {
- Snippet initializeSnippet = initializeFactoryForContributionBinding(binding);
- initializationSnippets.add(Snippet.format("this.%s = %s;", snippet, initializeSnippet));
- setContributionInitializationState(binding, InitializationState.INITIALIZED);
- }
- parameterSnippets.add(snippet);
- }
- Class<?> factoryClass =
- Iterables.all(resolvedBindings.contributionBindings(), Binding.Type.PROVISION)
- ? SetFactory.class
- : Util.isSetOfProduced(resolvedBindings.bindingKey().key().type())
- ? SetOfProducedProducer.class
- : SetProducer.class;
- Snippet initializeSetSnippet =
- Snippet.format(
- "%s.create(%s)",
- ClassName.fromClass(factoryClass),
- makeParametersSnippet(parameterSnippets.build()));
- initializationSnippets.add(
- initializeMember(resolvedBindings.bindingKey(), initializeSetSnippet));
-
- return Snippet.concat(initializationSnippets.build());
- }
-
- private Snippet initializeMapMultibindings(ResolvedBindings resolvedBindings) {
- ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
-
- if (any(resolvedBindings.contributionBindings(), Binding.Type.PRODUCTION)) {
- // TODO(beder): Implement producer map bindings.
- throw new IllegalStateException("producer map bindings not implemented yet");
- }
- for (ContributionBinding binding : resolvedBindings.contributionBindings()) {
- Optional<MemberSelect> multibindingContributionSnippet =
- getMultibindingContributionSnippet(binding);
- if (!isMapWithNonProvidedValues(binding.key().type())
- && multibindingContributionSnippet.isPresent()
- && multibindingContributionSnippet.get().owningClass().equals(name)) {
- initializationSnippets.add(
- Snippet.format(
- "this.%s = %s;",
- multibindingContributionSnippet.get().getSnippetFor(name),
- initializeFactoryForContributionBinding(binding)));
- }
- }
- initializationSnippets.add(
- initializeMember(
- resolvedBindings.bindingKey(),
- initializeMapBinding(resolvedBindings.contributionBindings())));
-
- return Snippet.concat(initializationSnippets.build());
- }
-
- private Snippet initializeUniqueContributionBinding(ResolvedBindings resolvedBindings) {
- ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
-
- ContributionBinding binding = getOnlyElement(resolvedBindings.ownedContributionBindings());
- if (!binding.factoryCreationStrategy().equals(ENUM_INSTANCE) || binding.scope().isPresent()) {
- initializationSnippets.add(initializeDelegateFactories(binding));
- initializationSnippets.add(
- initializeMember(
- resolvedBindings.bindingKey(), initializeFactoryForContributionBinding(binding)));
- }
-
- return Snippet.concat(initializationSnippets.build());
- }
-
- private Snippet initializeMembersInjectionBinding(ResolvedBindings resolvedBindings) {
- ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
-
- MembersInjectionBinding binding = resolvedBindings.membersInjectionBinding().get();
- if (!binding.injectionStrategy().equals(MembersInjectionBinding.Strategy.NO_OP)) {
- initializationSnippets.add(initializeDelegateFactories(binding));
- initializationSnippets.add(
- initializeMember(
- resolvedBindings.bindingKey(), initializeMembersInjectorForBinding(binding)));
- }
-
- return Snippet.concat(initializationSnippets.build());
- }
-
- private Snippet initializeDelegateFactories(Binding binding) {
- ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
-
- for (Collection<DependencyRequest> requestsForKey :
- indexDependenciesByUnresolvedKey(types, binding.dependencies()).asMap().values()) {
- BindingKey dependencyKey =
- Iterables.getOnlyElement(
- FluentIterable.from(requestsForKey)
- .transform(DependencyRequest.BINDING_KEY_FUNCTION)
- .toSet());
- if (!getMemberSelect(dependencyKey).staticMember()
- && getInitializationState(dependencyKey).equals(UNINITIALIZED)) {
- initializationSnippets.add(
- Snippet.format(
- "this.%s = new %s();",
- getMemberSelectSnippet(dependencyKey),
- ClassName.fromClass(DelegateFactory.class)));
- setInitializationState(dependencyKey, DELEGATED);
- }
- }
-
- return Snippet.concat(initializationSnippets.build());
- }
-
- private Snippet initializeMember(BindingKey bindingKey, Snippet initializationSnippet) {
- ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
-
- Snippet memberSelect = getMemberSelectSnippet(bindingKey);
- Snippet delegateFactoryVariable = delegateFactoryVariableSnippet(bindingKey);
- if (getInitializationState(bindingKey).equals(DELEGATED)) {
- initializationSnippets.add(
- Snippet.format(
- "%1$s %2$s = (%1$s) %3$s;",
- ClassName.fromClass(DelegateFactory.class),
- delegateFactoryVariable,
- memberSelect));
- }
- initializationSnippets.add(
- Snippet.format("this.%s = %s;", memberSelect, initializationSnippet));
- if (getInitializationState(bindingKey).equals(DELEGATED)) {
- initializationSnippets.add(
- Snippet.format("%s.setDelegatedProvider(%s);", delegateFactoryVariable, memberSelect));
- }
- setInitializationState(bindingKey, INITIALIZED);
-
- return Snippet.concat(initializationSnippets.build());
- }
-
- private Snippet delegateFactoryVariableSnippet(BindingKey key) {
- return Snippet.format("%sDelegate", getMemberSelectSnippet(key).toString().replace('.', '_'));
- }
-
- private Snippet initializeFactoryForContributionBinding(ContributionBinding binding) {
- TypeName bindingKeyTypeName = TypeNames.forTypeMirror(binding.key().type());
- switch (binding.bindingKind()) {
- case COMPONENT:
- return Snippet.format(
- "%s.<%s>create(%s)",
- ClassName.fromClass(InstanceFactory.class),
- bindingKeyTypeName,
- bindingKeyTypeName.equals(componentDefinitionTypeName())
- ? "this"
- : getComponentContributionSnippet(MoreTypes.asTypeElement(binding.key().type())));
-
- case COMPONENT_PROVISION:
- {
- TypeElement bindingTypeElement =
- graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement());
- String localFactoryVariable = simpleVariableName(bindingTypeElement);
- Snippet callFactoryMethodSnippet =
- Snippet.format(
- "%s.%s()",
- localFactoryVariable,
- binding.bindingElement().getSimpleName().toString());
- // TODO(sameb): This throws a very vague NPE right now. The stack trace doesn't
- // help to figure out what the method or return type is. If we include a string
- // of the return type or method name in the error message, that can defeat obfuscation.
- // We can easily include the raw type (no generics) + annotation type (no values),
- // using .class & String.format -- but that wouldn't be the whole story.
- // What should we do?
- StringLiteral failMsg =
- StringLiteral.forValue(CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD);
- Snippet getMethodBody =
- binding.nullableType().isPresent()
- || nullableValidationType.equals(Diagnostic.Kind.WARNING)
- ? Snippet.format("return %s;", callFactoryMethodSnippet)
- : Snippet.format(
- Joiner.on('\n')
- .join(
- "%s provided = %s;",
- "if (provided == null) {",
- " throw new NullPointerException(%s);",
- "}",
- "return provided;"),
- bindingKeyTypeName,
- callFactoryMethodSnippet,
- failMsg);
- return Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s>() {",
- " private final %5$s %6$s = %3$s;",
- " %4$s@Override public %2$s get() {",
- " %7$s",
- " }",
- "}"),
- /* 1 */ ClassName.fromClass(Factory.class),
- /* 2 */ bindingKeyTypeName,
- /* 3 */ getComponentContributionSnippet(bindingTypeElement),
- /* 4 */ nullableSnippet(binding.nullableType()),
- /* 5 */ TypeNames.forTypeMirror(bindingTypeElement.asType()),
- /* 6 */ localFactoryVariable,
- /* 7 */ getMethodBody);
- }
-
- case SUBCOMPONENT_BUILDER:
- return Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s>() {",
- " @Override public %2$s get() {",
- " return %3$s();",
- " }",
- "}"),
- /* 1 */ ClassName.fromClass(Factory.class),
- /* 2 */ bindingKeyTypeName,
- /* 3 */ binding.bindingElement().getSimpleName().toString());
-
- case INJECTION:
- case PROVISION:
- {
- List<Snippet> parameters =
- Lists.newArrayListWithCapacity(binding.dependencies().size() + 1);
- if (binding.bindingKind().equals(PROVISION)
- && !binding.bindingElement().getModifiers().contains(STATIC)) {
- parameters.add(getComponentContributionSnippet(binding.contributedBy().get()));
- }
- parameters.addAll(getDependencyParameters(binding));
-
- Snippet factorySnippet =
- Snippet.format(
- "%s.create(%s)",
- generatedClassNameForBinding(binding),
- Snippet.makeParametersSnippet(parameters));
- return binding.scope().isPresent()
- ? Snippet.format(
- "%s.create(%s)", ClassName.fromClass(ScopedProvider.class), factorySnippet)
- : factorySnippet;
- }
-
- case COMPONENT_PRODUCTION:
- {
- TypeElement bindingTypeElement =
- graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement());
- return Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s>() {",
- " private final %6$s %7$s = %4$s;",
- " @Override public %3$s<%2$s> get() {",
- " return %7$s.%5$s();",
- " }",
- "}"),
- /* 1 */ ClassName.fromClass(Producer.class),
- /* 2 */ TypeNames.forTypeMirror(binding.key().type()),
- /* 3 */ ClassName.fromClass(ListenableFuture.class),
- /* 4 */ getComponentContributionSnippet(bindingTypeElement),
- /* 5 */ binding.bindingElement().getSimpleName().toString(),
- /* 6 */ TypeNames.forTypeMirror(bindingTypeElement.asType()),
- /* 7 */ simpleVariableName(bindingTypeElement));
- }
-
- case IMMEDIATE:
- case FUTURE_PRODUCTION:
- {
- List<Snippet> parameters =
- Lists.newArrayListWithCapacity(binding.implicitDependencies().size() + 2);
- if (!binding.bindingElement().getModifiers().contains(STATIC)) {
- parameters.add(getComponentContributionSnippet(binding.bindingTypeElement()));
- }
- parameters.add(
- getComponentContributionSnippet(
- graph.componentDescriptor().executorDependency().get()));
- parameters.addAll(getProducerDependencyParameters(binding));
-
- return Snippet.format(
- "new %s(%s)",
- generatedClassNameForBinding(binding),
- Snippet.makeParametersSnippet(parameters));
- }
-
- default:
- throw new AssertionError();
- }
- }
-
- private Snippet nullableSnippet(Optional<DeclaredType> nullableType) {
- return nullableType.isPresent()
- ? Snippet.format("@%s ", TypeNames.forTypeMirror(nullableType.get()))
- : Snippet.format("");
- }
-
- private Snippet initializeMembersInjectorForBinding(MembersInjectionBinding binding) {
- switch (binding.injectionStrategy()) {
- case NO_OP:
- return Snippet.format("%s.noOp()", ClassName.fromClass(MembersInjectors.class));
- case INJECT_MEMBERS:
- List<Snippet> parameters = getDependencyParameters(binding);
- return Snippet.format(
- "%s.create(%s)",
- membersInjectorNameForType(binding.bindingElement()),
- Snippet.makeParametersSnippet(parameters));
- default:
- throw new AssertionError();
- }
- }
-
- private List<Snippet> getDependencyParameters(Binding binding) {
- ImmutableList.Builder<Snippet> parameters = ImmutableList.builder();
- Set<Key> keysSeen = new HashSet<>();
- for (Collection<DependencyRequest> requestsForKey :
- indexDependenciesByUnresolvedKey(types, binding.implicitDependencies()).asMap().values()) {
- Set<BindingKey> requestedBindingKeys = new HashSet<>();
- for (DependencyRequest dependencyRequest : requestsForKey) {
- Element requestElement = dependencyRequest.requestElement();
- TypeMirror typeMirror = typeMirrorAsMemberOf(binding.bindingTypeElement(), requestElement);
- Key key = keyFactory.forQualifiedType(dependencyRequest.key().qualifier(), typeMirror);
- if (keysSeen.add(key)) {
- requestedBindingKeys.add(dependencyRequest.bindingKey());
- }
- }
- if (!requestedBindingKeys.isEmpty()) {
- BindingKey key = Iterables.getOnlyElement(requestedBindingKeys);
- parameters.add(getMemberSelect(key).getSnippetWithRawTypeCastFor(name));
- }
- }
- return parameters.build();
- }
-
- // TODO(dpb): Investigate use of asMemberOf here. Why aren't the dependency requests already
- // resolved?
- private TypeMirror typeMirrorAsMemberOf(TypeElement bindingTypeElement, Element requestElement) {
- TypeMirror requestType = requestElement.asType();
- if (requestType.getKind() == TypeKind.TYPEVAR) {
- return types.asMemberOf(
- MoreTypes.asDeclared(bindingTypeElement.asType()),
- (requestElement.getKind() == ElementKind.PARAMETER)
- ? MoreTypes.asElement(requestType)
- : requestElement);
- } else {
- return requestType;
- }
- }
-
- private List<Snippet> getProducerDependencyParameters(Binding binding) {
- ImmutableList.Builder<Snippet> parameters = ImmutableList.builder();
- for (Collection<DependencyRequest> requestsForKey :
- SourceFiles.indexDependenciesByUnresolvedKey(types, binding.implicitDependencies())
- .asMap()
- .values()) {
- BindingKey key = Iterables.getOnlyElement(FluentIterable.from(requestsForKey)
- .transform(DependencyRequest.BINDING_KEY_FUNCTION));
- ResolvedBindings resolvedBindings = graph.resolvedBindings().get(key);
- Class<?> frameworkClass =
- DependencyRequestMapper.FOR_PRODUCER.getFrameworkClass(requestsForKey);
- if (FrameworkField.frameworkClassForResolvedBindings(resolvedBindings).equals(Provider.class)
- && frameworkClass.equals(Producer.class)) {
- parameters.add(
- Snippet.format(
- "%s.producerFromProvider(%s)",
- ClassName.fromClass(Producers.class),
- getMemberSelectSnippet(key)));
- } else {
- parameters.add(getMemberSelectSnippet(key));
- }
- }
- return parameters.build();
- }
-
- private Snippet initializeMapBinding(Set<ContributionBinding> bindings) {
- // Get type information from the first binding.
- ContributionBinding firstBinding = bindings.iterator().next();
- DeclaredType mapType = asDeclared(firstBinding.key().type());
-
- if (isMapWithNonProvidedValues(mapType)) {
- return Snippet.format(
- "%s.create(%s)",
- ClassName.fromClass(MapFactory.class),
- getMemberSelectSnippet(getOnlyElement(firstBinding.dependencies()).bindingKey()));
- }
-
- ImmutableList.Builder<dagger.internal.codegen.writer.Snippet> snippets =
- ImmutableList.builder();
- snippets.add(Snippet.format("%s.<%s, %s>builder(%d)",
- ClassName.fromClass(MapProviderFactory.class),
- TypeNames.forTypeMirror(getKeyTypeOfMap(mapType)),
- TypeNames.forTypeMirror(getProvidedValueTypeOfMap(mapType)), // V of Map<K, Provider<V>>
- bindings.size()));
-
- for (ContributionBinding binding : bindings) {
- snippets.add(
- Snippet.format(
- " .put(%s, %s)",
- getMapKeySnippet(binding.bindingElement()),
- getMultibindingContributionSnippet(binding).get().getSnippetFor(name)));
- }
-
- snippets.add(Snippet.format(" .build()"));
-
- return Snippet.concat(snippets.build());
- }
-
- private static String simpleVariableName(TypeElement typeElement) {
- return UPPER_CAMEL.to(LOWER_CAMEL, typeElement.getSimpleName().toString());
- }
-
- /**
- * Initialization state for a factory field.
- */
- enum InitializationState {
- /** The field is {@code null}. */
- UNINITIALIZED,
-
- /** The field is set to a {@link DelegateFactory}. */
- DELEGATED,
-
- /** The field is set to an undelegated factory. */
- INITIALIZED;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/Binding.java b/compiler/src/main/java/dagger/internal/codegen/Binding.java
deleted file mode 100644
index 0a6b840..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/Binding.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import dagger.MembersInjector;
-import dagger.producers.Producer;
-import java.util.List;
-import java.util.Set;
-import javax.inject.Provider;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementVisitor;
-import javax.lang.model.element.Name;
-import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.WildcardType;
-import javax.lang.model.util.SimpleElementVisitor6;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.Types;
-
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-/**
- * An abstract type for classes representing a Dagger binding. Particularly, contains the
- * {@link Element} that generated the binding and the {@link DependencyRequest} instances that are
- * required to satisfy the binding, but leaves the specifics of the <i>mechanism</i> of the binding
- * to the subtypes.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-abstract class Binding {
-
- /**
- * The subtype of this binding.
- */
- enum Type implements Predicate<Binding> {
- /** A binding with this type is a {@link ProvisionBinding}. */
- PROVISION(Provider.class),
- /** A binding with this type is a {@link MembersInjectionBinding}. */
- MEMBERS_INJECTION(MembersInjector.class),
- /** A binding with this type is a {@link ProductionBinding}. */
- PRODUCTION(Producer.class),
- ;
-
- private final Class<?> frameworkClass;
-
- private Type(Class<?> frameworkClass) {
- this.frameworkClass = frameworkClass;
- }
-
- /**
- * Returns the framework class associated with bindings of this type.
- */
- Class<?> frameworkClass() {
- return frameworkClass;
- }
-
- BindingKey.Kind bindingKeyKind() {
- switch (this) {
- case MEMBERS_INJECTION:
- return BindingKey.Kind.MEMBERS_INJECTION;
- case PROVISION:
- case PRODUCTION:
- return BindingKey.Kind.CONTRIBUTION;
- default:
- throw new AssertionError();
- }
- }
-
- @Override
- public boolean apply(Binding binding) {
- return this.equals(binding.bindingType());
- }
- }
-
- abstract Binding.Type bindingType();
-
- /**
- * Returns the framework class associated with this binding.
- */
- Class<?> frameworkClass() {
- return bindingType().frameworkClass();
- }
-
- static Optional<String> bindingPackageFor(Iterable<? extends Binding> bindings) {
- ImmutableSet.Builder<String> bindingPackagesBuilder = ImmutableSet.builder();
- for (Binding binding : bindings) {
- bindingPackagesBuilder.addAll(binding.bindingPackage().asSet());
- }
- ImmutableSet<String> bindingPackages = bindingPackagesBuilder.build();
- switch (bindingPackages.size()) {
- case 0:
- return Optional.absent();
- case 1:
- return Optional.of(bindingPackages.iterator().next());
- default:
- throw new IllegalArgumentException();
- }
- }
-
- /** The {@link Key} that is provided by this binding. */
- protected abstract Key key();
-
- BindingKey bindingKey() {
- return BindingKey.create(bindingType().bindingKeyKind(), key());
- }
-
- /** Returns the {@link Element} instance that is responsible for declaring the binding. */
- abstract Element bindingElement();
-
- /** The type enclosing the binding {@link #bindingElement()}. */
- TypeElement bindingTypeElement() {
- return BINDING_TYPE_ELEMENT.visit(bindingElement());
- }
-
- private static final ElementVisitor<TypeElement, Void> BINDING_TYPE_ELEMENT =
- new SimpleElementVisitor6<TypeElement, Void>() {
- @Override
- protected TypeElement defaultAction(Element e, Void p) {
- return visit(e.getEnclosingElement());
- }
-
- @Override
- public TypeElement visitType(TypeElement e, Void p) {
- return e;
- }
- };
-
- /**
- * The explicit set of {@link DependencyRequest dependencies} required to satisfy this binding.
- */
- abstract ImmutableSet<DependencyRequest> dependencies();
-
- /**
- * The set of {@link DependencyRequest dependencies} required to satisfy this binding. This is a
- * superset of {@link #dependencies()}. This returns an unmodifiable set.
- */
- abstract Set<DependencyRequest> implicitDependencies();
-
- /**
- * Returns the name of the package in which this binding must be managed. E.g.: a binding
- * may reference non-public types.
- */
- abstract Optional<String> bindingPackage();
-
- protected static Optional<String> findBindingPackage(Key bindingKey) {
- Set<String> packages = nonPublicPackageUse(bindingKey.type());
- switch (packages.size()) {
- case 0:
- return Optional.absent();
- case 1:
- return Optional.of(packages.iterator().next());
- default:
- throw new IllegalStateException();
- }
- }
-
- private static Set<String> nonPublicPackageUse(TypeMirror typeMirror) {
- ImmutableSet.Builder<String> packages = ImmutableSet.builder();
- typeMirror.accept(new SimpleTypeVisitor6<Void, ImmutableSet.Builder<String>>() {
- @Override
- public Void visitArray(ArrayType t, ImmutableSet.Builder<String> p) {
- return t.getComponentType().accept(this, p);
- }
-
- @Override
- public Void visitDeclared(DeclaredType t, ImmutableSet.Builder<String> p) {
- for (TypeMirror typeArgument : t.getTypeArguments()) {
- typeArgument.accept(this, p);
- }
- // TODO(gak): address public nested types in non-public types
- TypeElement typeElement = MoreElements.asType(t.asElement());
- if (!typeElement.getModifiers().contains(PUBLIC)) {
- PackageElement elementPackage = MoreElements.getPackage(typeElement);
- Name qualifiedName = elementPackage.getQualifiedName();
- p.add(qualifiedName.toString());
- }
- // Also make sure enclosing types are visible, otherwise we're fooled by
- // class Foo { public class Bar }
- // (Note: we can't use t.getEnclosingType() because it doesn't work!)
- typeElement.getEnclosingElement().asType().accept(this, p);
- return null;
- }
-
- @Override
- public Void visitWildcard(WildcardType t, ImmutableSet.Builder<String> p) {
- if (t.getExtendsBound() != null) {
- t.getExtendsBound().accept(this, p);
- }
- if (t.getSuperBound() != null) {
- t.getSuperBound().accept(this, p);
- }
- return null;
- }
- }, packages);
- return packages.build();
- }
-
- /**
- * Returns true if this is a binding for a key that has a different type parameter list than the
- * element it's providing.
- */
- abstract boolean hasNonDefaultTypeParameters();
-
- /**
- * The scope of this binding.
- */
- Scope scope() {
- return Scope.unscoped();
- }
-
- // TODO(sameb): Remove the TypeElement parameter and pull it from the TypeMirror.
- static boolean hasNonDefaultTypeParameters(TypeElement element, TypeMirror type, Types types) {
- // If the element has no type parameters, nothing can be wrong.
- if (element.getTypeParameters().isEmpty()) {
- return false;
- }
-
- List<TypeMirror> defaultTypes = Lists.newArrayList();
- for (TypeParameterElement parameter : element.getTypeParameters()) {
- defaultTypes.add(parameter.asType());
- }
-
- List<TypeMirror> actualTypes =
- type.accept(
- new SimpleTypeVisitor6<List<TypeMirror>, Void>() {
- @Override
- protected List<TypeMirror> defaultAction(TypeMirror e, Void p) {
- return ImmutableList.of();
- }
-
- @Override
- public List<TypeMirror> visitDeclared(DeclaredType t, Void p) {
- return ImmutableList.<TypeMirror>copyOf(t.getTypeArguments());
- }
- },
- null);
-
- // The actual type parameter size can be different if the user is using a raw type.
- if (defaultTypes.size() != actualTypes.size()) {
- return true;
- }
-
- for (int i = 0; i < defaultTypes.size(); i++) {
- if (!types.isSameType(defaultTypes.get(i), actualTypes.get(i))) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java b/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java
deleted file mode 100644
index b951438..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java
+++ /dev/null
@@ -1,627 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.collect.TreeTraverser;
-import dagger.Component;
-import dagger.Subcomponent;
-import dagger.internal.codegen.Binding.Type;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.producers.Producer;
-import dagger.producers.ProductionComponent;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.common.base.Predicates.in;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.any;
-import static com.google.common.collect.Sets.union;
-import static dagger.internal.codegen.BindingKey.Kind.CONTRIBUTION;
-import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
-import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod;
-import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor.isOfKind;
-import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind.SUBCOMPONENT_BUILDER;
-import static dagger.internal.codegen.ComponentDescriptor.Kind.PRODUCTION_COMPONENT;
-import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
-import static dagger.internal.codegen.MembersInjectionBinding.Strategy.INJECT_MEMBERS;
-import static dagger.internal.codegen.MembersInjectionBinding.Strategy.NO_OP;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * The canonical representation of a full-resolved graph.
- *
- * @author Gregory Kick
- */
-@AutoValue
-abstract class BindingGraph {
- abstract ComponentDescriptor componentDescriptor();
- abstract ImmutableMap<BindingKey, ResolvedBindings> resolvedBindings();
- abstract ImmutableMap<ExecutableElement, BindingGraph> subgraphs();
-
- /**
- * Returns the set of modules that are owned by this graph regardless of whether or not any of
- * their bindings are used in this graph. For graphs representing top-level {@link Component
- * components}, this set will be the same as
- * {@linkplain ComponentDescriptor#transitiveModules the component's transitive modules}. For
- * {@linkplain Subcomponent subcomponents}, this set will be the transitive modules that are not
- * owned by any of their ancestors.
- */
- abstract ImmutableSet<ModuleDescriptor> ownedModules();
-
- ImmutableSet<TypeElement> ownedModuleTypes() {
- return FluentIterable.from(ownedModules())
- .transform(ModuleDescriptor.getModuleElement())
- .toSet();
- }
-
- private static final TreeTraverser<BindingGraph> SUBGRAPH_TRAVERSER =
- new TreeTraverser<BindingGraph>() {
- @Override
- public Iterable<BindingGraph> children(BindingGraph node) {
- return node.subgraphs().values();
- }
- };
-
- /**
- * Returns the set of types necessary to implement the component, but are not part of the injected
- * graph. This includes modules, component dependencies and an {@link Executor} in the case of
- * {@link ProductionComponent}.
- */
- ImmutableSet<TypeElement> componentRequirements() {
- return SUBGRAPH_TRAVERSER
- .preOrderTraversal(this)
- .transformAndConcat(
- new Function<BindingGraph, Iterable<ResolvedBindings>>() {
- @Override
- public Iterable<ResolvedBindings> apply(BindingGraph input) {
- return input.resolvedBindings().values();
- }
- })
- .transformAndConcat(
- new Function<ResolvedBindings, Set<ContributionBinding>>() {
- @Override
- public Set<ContributionBinding> apply(ResolvedBindings input) {
- return (input.bindingKey().kind().equals(CONTRIBUTION))
- ? input.contributionBindings()
- : ImmutableSet.<ContributionBinding>of();
- }
- })
- .transformAndConcat(
- new Function<ContributionBinding, Set<TypeElement>>() {
- @Override
- public Set<TypeElement> apply(ContributionBinding input) {
- return input.bindingElement().getModifiers().contains(STATIC)
- ? ImmutableSet.<TypeElement>of()
- : input.contributedBy().asSet();
- }
- })
- .filter(in(ownedModuleTypes()))
- .append(componentDescriptor().dependencies())
- .append(componentDescriptor().executorDependency().asSet())
- .toSet();
- }
-
- ImmutableSet<TypeElement> availableDependencies() {
- return new ImmutableSet.Builder<TypeElement>()
- .addAll(componentDescriptor().transitiveModuleTypes())
- .addAll(componentDescriptor().dependencies())
- .addAll(componentDescriptor().executorDependency().asSet())
- .build();
- }
-
- static final class Factory {
- private final Elements elements;
- private final InjectBindingRegistry injectBindingRegistry;
- private final Key.Factory keyFactory;
- private final ProvisionBinding.Factory provisionBindingFactory;
- private final ProductionBinding.Factory productionBindingFactory;
-
- Factory(Elements elements,
- InjectBindingRegistry injectBindingRegistry,
- Key.Factory keyFactory,
- ProvisionBinding.Factory provisionBindingFactory,
- ProductionBinding.Factory productionBindingFactory) {
- this.elements = elements;
- this.injectBindingRegistry = injectBindingRegistry;
- this.keyFactory = keyFactory;
- this.provisionBindingFactory = provisionBindingFactory;
- this.productionBindingFactory = productionBindingFactory;
- }
-
- BindingGraph create(ComponentDescriptor componentDescriptor) {
- return create(Optional.<Resolver>absent(), componentDescriptor);
- }
-
- private BindingGraph create(
- Optional<Resolver> parentResolver, ComponentDescriptor componentDescriptor) {
- ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
-
- // binding for the component itself
- TypeElement componentDefinitionType = componentDescriptor.componentDefinitionType();
- explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDefinitionType));
-
- // Collect Component dependencies.
- Optional<AnnotationMirror> componentMirror =
- getAnnotationMirror(componentDefinitionType, Component.class)
- .or(getAnnotationMirror(componentDefinitionType, ProductionComponent.class));
- ImmutableSet<TypeElement> componentDependencyTypes = componentMirror.isPresent()
- ? MoreTypes.asTypeElements(getComponentDependencies(componentMirror.get()))
- : ImmutableSet.<TypeElement>of();
- for (TypeElement componentDependency : componentDependencyTypes) {
- explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDependency));
- List<ExecutableElement> dependencyMethods =
- ElementFilter.methodsIn(elements.getAllMembers(componentDependency));
- for (ExecutableElement method : dependencyMethods) {
- // MembersInjection methods aren't "provided" explicitly, so ignore them.
- if (isComponentContributionMethod(elements, method)) {
- explicitBindingsBuilder.add(
- componentDescriptor.kind().equals(PRODUCTION_COMPONENT)
- && isComponentProductionMethod(elements, method)
- ? productionBindingFactory.forComponentMethod(method)
- : provisionBindingFactory.forComponentMethod(method));
- }
- }
- }
-
- // Bindings for subcomponent builders.
- for (ComponentMethodDescriptor subcomponentMethodDescriptor :
- Iterables.filter(
- componentDescriptor.subcomponents().keySet(), isOfKind(SUBCOMPONENT_BUILDER))) {
- explicitBindingsBuilder.add(
- provisionBindingFactory.forSubcomponentBuilderMethod(
- subcomponentMethodDescriptor.methodElement(),
- componentDescriptor.componentDefinitionType()));
- }
-
- // Collect transitive module bindings.
- for (ModuleDescriptor moduleDescriptor : componentDescriptor.transitiveModules()) {
- for (ContributionBinding binding : moduleDescriptor.bindings()) {
- explicitBindingsBuilder.add(binding);
- }
- }
-
- Resolver requestResolver =
- new Resolver(
- parentResolver,
- componentDescriptor,
- explicitBindingsByKey(explicitBindingsBuilder.build()));
- for (ComponentMethodDescriptor componentMethod : componentDescriptor.componentMethods()) {
- Optional<DependencyRequest> componentMethodRequest = componentMethod.dependencyRequest();
- if (componentMethodRequest.isPresent()) {
- requestResolver.resolve(componentMethodRequest.get());
- }
- }
-
- ImmutableMap.Builder<ExecutableElement, BindingGraph> subgraphsBuilder =
- ImmutableMap.builder();
- for (Entry<ComponentMethodDescriptor, ComponentDescriptor> subcomponentEntry :
- componentDescriptor.subcomponents().entrySet()) {
- subgraphsBuilder.put(
- subcomponentEntry.getKey().methodElement(),
- create(Optional.of(requestResolver), subcomponentEntry.getValue()));
- }
-
- for (ResolvedBindings resolvedBindings : requestResolver.getResolvedBindings().values()) {
- verify(
- resolvedBindings.owningComponent().equals(componentDescriptor),
- "%s is not owned by %s",
- resolvedBindings,
- componentDescriptor);
- }
-
- return new AutoValue_BindingGraph(
- componentDescriptor,
- requestResolver.getResolvedBindings(),
- subgraphsBuilder.build(),
- requestResolver.getOwnedModules());
- }
-
- private <B extends ContributionBinding> ImmutableSetMultimap<Key, B> explicitBindingsByKey(
- Iterable<? extends B> bindings) {
- // Multimaps.index() doesn't do ImmutableSetMultimaps.
- ImmutableSetMultimap.Builder<Key, B> builder = ImmutableSetMultimap.builder();
- for (B binding : bindings) {
- builder.put(binding.key(), binding);
- }
- return builder.build();
- }
-
- private final class Resolver {
- final Optional<Resolver> parentResolver;
- final ComponentDescriptor componentDescriptor;
- final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
- final ImmutableSet<ContributionBinding> explicitBindingsSet;
- final Map<BindingKey, ResolvedBindings> resolvedBindings;
- final Deque<BindingKey> cycleStack = new ArrayDeque<>();
- final Cache<BindingKey, Boolean> dependsOnLocalMultibindingsCache =
- CacheBuilder.newBuilder().<BindingKey, Boolean>build();
-
- Resolver(
- Optional<Resolver> parentResolver,
- ComponentDescriptor componentDescriptor,
- ImmutableSetMultimap<Key, ContributionBinding> explicitBindings) {
- assert parentResolver != null;
- this.parentResolver = parentResolver;
- assert componentDescriptor != null;
- this.componentDescriptor = componentDescriptor;
- assert explicitBindings != null;
- this.explicitBindings = explicitBindings;
- this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
- this.resolvedBindings = Maps.newLinkedHashMap();
- }
-
- /**
- * Looks up the bindings associated with a given dependency request and returns them.
- *
- * <p>Requests for {@code Map<K, V>} for which there are only bindings for
- * {@code Map<K, Provider<V>>} will resolve to a single implicit binding for the latter map
- * (and similarly for {@link Producer}s).
- *
- * <p>If there are no explicit bindings for a contribution, looks for implicit
- * {@link Inject @Inject}-annotated constructor types.
- */
- ResolvedBindings lookUpBindings(DependencyRequest request) {
- BindingKey bindingKey = request.bindingKey();
- switch (bindingKey.kind()) {
- case CONTRIBUTION:
- // First, check for explicit keys (those from modules and components)
- ImmutableSet<ContributionBinding> explicitBindingsForKey =
- getExplicitBindings(bindingKey.key());
-
- // If the key is Map<K, V>, get its implicit binding keys, which are either
- // Map<K, Provider<V>> or Map<K, Producer<V>>, and grab their explicit bindings.
- Optional<Key> mapProviderKey = keyFactory.implicitMapProviderKeyFrom(bindingKey.key());
- ImmutableSet.Builder<ContributionBinding> explicitMapBindingsBuilder =
- ImmutableSet.builder();
- if (mapProviderKey.isPresent()) {
- explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProviderKey.get()));
- }
-
- Optional<Key> mapProducerKey = keyFactory.implicitMapProducerKeyFrom(bindingKey.key());
- if (mapProducerKey.isPresent()) {
- explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProducerKey.get()));
- }
- ImmutableSet<ContributionBinding> explicitMapBindings =
- explicitMapBindingsBuilder.build();
-
- // If the key is Set<Produced<T>>, then we look up bindings by the alternate key Set<T>.
- Optional<Key> setKeyFromProduced =
- keyFactory.implicitSetKeyFromProduced(bindingKey.key());
- ImmutableSet<ContributionBinding> explicitSetBindings =
- setKeyFromProduced.isPresent()
- ? getExplicitBindings(setKeyFromProduced.get())
- : ImmutableSet.<ContributionBinding>of();
-
- if (!explicitBindingsForKey.isEmpty() || !explicitSetBindings.isEmpty()) {
- /* If there are any explicit bindings for this key, then combine those with any
- * conflicting Map<K, Provider<V>> bindings and let the validator fail. */
- ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding> bindings =
- ImmutableSetMultimap.builder();
- for (ContributionBinding binding :
- union(explicitBindingsForKey, union(explicitSetBindings, explicitMapBindings))) {
- bindings.put(getOwningComponent(request, binding), binding);
- }
- return ResolvedBindings.forContributionBindings(
- bindingKey, componentDescriptor, bindings.build());
- } else if (any(explicitMapBindings, Binding.Type.PRODUCTION)) {
- /* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
- * some explicit Map<K, Producer<V>> bindings, then this binding must have only the
- * implicit dependency on Map<K, Producer<V>>. */
- return ResolvedBindings.forContributionBindings(
- bindingKey,
- componentDescriptor,
- productionBindingFactory.implicitMapOfProducerBinding(request));
- } else if (any(explicitMapBindings, Binding.Type.PROVISION)) {
- /* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
- * some explicit Map<K, Provider<V>> bindings, then this binding must have only the
- * implicit dependency on Map<K, Provider<V>>. */
- return ResolvedBindings.forContributionBindings(
- bindingKey,
- componentDescriptor,
- provisionBindingFactory.implicitMapOfProviderBinding(request));
- } else {
- /* If there are no explicit bindings at all, look for an implicit @Inject-constructed
- * binding. */
- Optional<ProvisionBinding> provisionBinding =
- injectBindingRegistry.getOrFindProvisionBinding(bindingKey.key());
- ComponentDescriptor owningComponent =
- provisionBinding.isPresent()
- && isResolvedInParent(request, provisionBinding.get())
- && !shouldOwnParentBinding(request, provisionBinding.get())
- ? getOwningResolver(provisionBinding.get()).get().componentDescriptor
- : componentDescriptor;
- return ResolvedBindings.forContributionBindings(
- bindingKey,
- componentDescriptor,
- ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>builder()
- .putAll(owningComponent, provisionBinding.asSet())
- .build());
- }
-
- case MEMBERS_INJECTION:
- // no explicit deps for members injection, so just look it up
- return ResolvedBindings.forMembersInjectionBinding(
- bindingKey, componentDescriptor, rollUpMembersInjectionBindings(bindingKey.key()));
- default:
- throw new AssertionError();
- }
- }
-
- /**
- * If {@code binding} should be owned by a parent component, resolves the binding in that
- * component's resolver and returns that component. Otherwise returns the component for this
- * resolver.
- */
- private ComponentDescriptor getOwningComponent(
- DependencyRequest request, ContributionBinding binding) {
- return isResolvedInParent(request, binding) && !shouldOwnParentBinding(request, binding)
- ? getOwningResolver(binding).get().componentDescriptor
- : componentDescriptor;
- }
-
- /**
- * Returns {@code true} if {@code binding} is owned by a parent resolver. If so, calls
- * {@link #resolve(DependencyRequest) resolve(request)} on that resolver.
- */
- private boolean isResolvedInParent(DependencyRequest request, ContributionBinding binding) {
- Optional<Resolver> owningResolver = getOwningResolver(binding);
- if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
- owningResolver.get().resolve(request);
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Returns {@code true} if {@code binding}, which was previously resolved by a parent
- * resolver, should be moved into this resolver's bindings for {@code request} because it is
- * unscoped and {@linkplain #dependsOnLocalMultibindings(ResolvedBindings) depends on local
- * multibindings}, or {@code false} if it can satisfy {@code request} as an inherited binding.
- */
- private boolean shouldOwnParentBinding(
- DependencyRequest request, ContributionBinding binding) {
- return !binding.scope().isPresent()
- && dependsOnLocalMultibindings(
- getPreviouslyResolvedBindings(request.bindingKey()).get());
- }
-
- private MembersInjectionBinding rollUpMembersInjectionBindings(Key key) {
- MembersInjectionBinding membersInjectionBinding =
- injectBindingRegistry.getOrFindMembersInjectionBinding(key);
-
- if (membersInjectionBinding.parentInjectorRequest().isPresent()
- && membersInjectionBinding.injectionStrategy().equals(INJECT_MEMBERS)) {
- MembersInjectionBinding parentBinding =
- rollUpMembersInjectionBindings(
- membersInjectionBinding.parentInjectorRequest().get().key());
- if (parentBinding.injectionStrategy().equals(NO_OP)) {
- return membersInjectionBinding.withoutParentInjectorRequest();
- }
- }
-
- return membersInjectionBinding;
- }
-
- private Optional<Resolver> getOwningResolver(ContributionBinding provisionBinding) {
- for (Resolver requestResolver : getResolverLineage().reverse()) {
- if (requestResolver.explicitBindingsSet.contains(provisionBinding)) {
- return Optional.of(requestResolver);
- }
- }
-
- // look for scope separately. we do this for the case where @Singleton can appear twice
- // in the † compatibility mode
- Scope bindingScope = provisionBinding.scope();
- if (bindingScope.isPresent()) {
- for (Resolver requestResolver : getResolverLineage().reverse()) {
- if (bindingScope.equals(requestResolver.componentDescriptor.scope())) {
- return Optional.of(requestResolver);
- }
- }
- }
- return Optional.absent();
- }
-
- /** Returns the resolver lineage from parent to child. */
- private ImmutableList<Resolver> getResolverLineage() {
- List<Resolver> resolverList = Lists.newArrayList();
- for (Optional<Resolver> currentResolver = Optional.of(this);
- currentResolver.isPresent();
- currentResolver = currentResolver.get().parentResolver) {
- resolverList.add(currentResolver.get());
- }
- return ImmutableList.copyOf(Lists.reverse(resolverList));
- }
-
- private ImmutableSet<ContributionBinding> getExplicitBindings(Key requestKey) {
- ImmutableSet.Builder<ContributionBinding> explicitBindingsForKey = ImmutableSet.builder();
- for (Resolver resolver : getResolverLineage()) {
- explicitBindingsForKey.addAll(resolver.explicitBindings.get(requestKey));
- }
- return explicitBindingsForKey.build();
- }
-
- private Optional<ResolvedBindings> getPreviouslyResolvedBindings(
- final BindingKey bindingKey) {
- Optional<ResolvedBindings> result = Optional.fromNullable(resolvedBindings.get(bindingKey));
- if (result.isPresent()) {
- return result;
- } else if (parentResolver.isPresent()) {
- return parentResolver.get().getPreviouslyResolvedBindings(bindingKey);
- } else {
- return Optional.absent();
- }
- }
-
- void resolve(DependencyRequest request) {
- BindingKey bindingKey = request.bindingKey();
-
- // If we find a cycle, stop resolving. The original request will add it with all of the
- // other resolved deps.
- if (cycleStack.contains(bindingKey)) {
- return;
- }
-
- // If the binding was previously resolved in this (sub)component, don't resolve it again.
- if (resolvedBindings.containsKey(bindingKey)) {
- return;
- }
-
- // If the binding was previously resolved in a supercomponent, then test to see if it
- // depends on multibindings with contributions from this subcomponent. If it does, then we
- // have to resolve it in this subcomponent so that it sees the local contributions. If it
- // does not, then we can stop resolving it in this subcomponent and rely on the
- // supercomponent resolution.
- Optional<ResolvedBindings> bindingsPreviouslyResolvedInParent =
- getPreviouslyResolvedBindings(bindingKey);
- if (bindingsPreviouslyResolvedInParent.isPresent()
- && !dependsOnLocalMultibindings(bindingsPreviouslyResolvedInParent.get())) {
- return;
- }
-
- cycleStack.push(bindingKey);
- try {
- ResolvedBindings bindings = lookUpBindings(request);
- for (Binding binding : bindings.ownedBindings()) {
- for (DependencyRequest dependency : binding.implicitDependencies()) {
- resolve(dependency);
- }
- }
- resolvedBindings.put(bindingKey, bindings);
- } finally {
- cycleStack.pop();
- }
- }
-
- /**
- * Returns {@code true} if {@code previouslyResolvedBindings} is multibindings with
- * contributions declared within this (sub)component's modules, or if any of its unscoped
- * provision-dependencies depend on such local multibindings.
- *
- * <p>We don't care about scoped dependencies or production bindings because they will never
- * depend on multibindings with contributions from subcomponents.
- */
- private boolean dependsOnLocalMultibindings(ResolvedBindings previouslyResolvedBindings) {
- return dependsOnLocalMultibindings(previouslyResolvedBindings, new HashSet<BindingKey>());
- }
-
- private boolean dependsOnLocalMultibindings(
- final ResolvedBindings previouslyResolvedBindings, final Set<BindingKey> cycleChecker) {
- // Don't recur infinitely if there are valid cycles in the dependency graph.
- if (!cycleChecker.add(previouslyResolvedBindings.bindingKey())) {
- return false;
- }
- try {
- return dependsOnLocalMultibindingsCache.get(
- previouslyResolvedBindings.bindingKey(),
- new Callable<Boolean>() {
- @Override
- public Boolean call() {
- if (previouslyResolvedBindings.isMultibindings()
- && hasLocalContributions(previouslyResolvedBindings)) {
- return true;
- }
-
- for (Binding binding : previouslyResolvedBindings.bindings()) {
- if (!binding.scope().isPresent()
- && !binding.bindingType().equals(Type.PRODUCTION)) {
- for (DependencyRequest dependency : binding.implicitDependencies()) {
- if (dependsOnLocalMultibindings(
- getPreviouslyResolvedBindings(dependency.bindingKey()).get(),
- cycleChecker)) {
- return true;
- }
- }
- }
- }
- return false;
- }
- });
- } catch (ExecutionException e) {
- throw new AssertionError(e);
- }
- }
-
- private boolean hasLocalContributions(ResolvedBindings resolvedBindings) {
- return !explicitBindings.get(resolvedBindings.bindingKey().key()).isEmpty();
- }
-
- ImmutableMap<BindingKey, ResolvedBindings> getResolvedBindings() {
- ImmutableMap.Builder<BindingKey, ResolvedBindings> resolvedBindingsBuilder =
- ImmutableMap.builder();
- resolvedBindingsBuilder.putAll(resolvedBindings);
- if (parentResolver.isPresent()) {
- Collection<ResolvedBindings> bindingsResolvedInParent =
- Maps.difference(parentResolver.get().getResolvedBindings(), resolvedBindings)
- .entriesOnlyOnLeft()
- .values();
- for (ResolvedBindings resolvedInParent : bindingsResolvedInParent) {
- resolvedBindingsBuilder.put(
- resolvedInParent.bindingKey(),
- resolvedInParent.asInheritedIn(componentDescriptor));
- }
- }
- return resolvedBindingsBuilder.build();
- }
-
- ImmutableSet<ModuleDescriptor> getInheritedModules() {
- return parentResolver.isPresent()
- ? Sets.union(
- parentResolver.get().getInheritedModules(),
- parentResolver.get().componentDescriptor.transitiveModules())
- .immutableCopy()
- : ImmutableSet.<ModuleDescriptor>of();
- }
-
- ImmutableSet<ModuleDescriptor> getOwnedModules() {
- return Sets.difference(componentDescriptor.transitiveModules(), getInheritedModules())
- .immutableCopy();
- }
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java b/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java
deleted file mode 100644
index f8010c3..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java
+++ /dev/null
@@ -1,1160 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
-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.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import dagger.Component;
-import dagger.Lazy;
-import dagger.MapKey;
-import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ContributionBinding.ContributionType;
-import dagger.internal.codegen.writer.TypeNames;
-import java.util.ArrayDeque;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.Formatter;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Provider;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic;
-
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.auto.common.MoreTypes.asExecutable;
-import static com.google.auto.common.MoreTypes.asTypeElements;
-import static com.google.common.base.Predicates.equalTo;
-import static com.google.common.base.Predicates.in;
-import static com.google.common.base.Predicates.not;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.all;
-import static com.google.common.collect.Iterables.any;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.google.common.collect.Iterables.indexOf;
-import static com.google.common.collect.Iterables.skip;
-import static com.google.common.collect.Maps.filterKeys;
-import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor.isOfKind;
-import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind.SUBCOMPONENT;
-import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
-import static dagger.internal.codegen.ContributionBinding.indexMapBindingsByAnnotationType;
-import static dagger.internal.codegen.ContributionBinding.indexMapBindingsByMapKey;
-import static dagger.internal.codegen.ErrorMessages.DUPLICATE_SIZE_LIMIT;
-import static dagger.internal.codegen.ErrorMessages.INDENT;
-import static dagger.internal.codegen.ErrorMessages.MEMBERS_INJECTION_WITH_UNBOUNDED_TYPE;
-import static dagger.internal.codegen.ErrorMessages.REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_FORMAT;
-import static dagger.internal.codegen.ErrorMessages.REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_OR_PRODUCER_FORMAT;
-import static dagger.internal.codegen.ErrorMessages.REQUIRES_PROVIDER_FORMAT;
-import static dagger.internal.codegen.ErrorMessages.REQUIRES_PROVIDER_OR_PRODUCER_FORMAT;
-import static dagger.internal.codegen.ErrorMessages.duplicateMapKeysError;
-import static dagger.internal.codegen.ErrorMessages.inconsistentMapKeyAnnotationsError;
-import static dagger.internal.codegen.ErrorMessages.nullableToNonNullable;
-import static dagger.internal.codegen.ErrorMessages.stripCommonTypePrefixes;
-import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
-import static dagger.internal.codegen.Util.getKeyTypeOfMap;
-import static dagger.internal.codegen.Util.getProvidedValueTypeOfMap;
-import static dagger.internal.codegen.Util.getValueTypeOfMap;
-import static dagger.internal.codegen.Util.isMapWithNonProvidedValues;
-import static dagger.internal.codegen.Util.isMapWithProvidedValues;
-import static javax.tools.Diagnostic.Kind.ERROR;
-import static javax.tools.Diagnostic.Kind.WARNING;
-
-public class BindingGraphValidator {
-
- private final Types types;
- private final InjectBindingRegistry injectBindingRegistry;
- private final ValidationType scopeCycleValidationType;
- private final Diagnostic.Kind nullableValidationType;
- private final ContributionBindingFormatter contributionBindingFormatter;
- private final MethodSignatureFormatter methodSignatureFormatter;
- private final DependencyRequestFormatter dependencyRequestFormatter;
- private final KeyFormatter keyFormatter;
-
- BindingGraphValidator(
- Types types,
- InjectBindingRegistry injectBindingRegistry,
- ValidationType scopeCycleValidationType,
- Diagnostic.Kind nullableValidationType,
- ContributionBindingFormatter contributionBindingFormatter,
- MethodSignatureFormatter methodSignatureFormatter,
- DependencyRequestFormatter dependencyRequestFormatter,
- KeyFormatter keyFormatter) {
- this.types = types;
- this.injectBindingRegistry = injectBindingRegistry;
- this.scopeCycleValidationType = scopeCycleValidationType;
- this.nullableValidationType = nullableValidationType;
- this.contributionBindingFormatter = contributionBindingFormatter;
- this.methodSignatureFormatter = methodSignatureFormatter;
- this.dependencyRequestFormatter = dependencyRequestFormatter;
- this.keyFormatter = keyFormatter;
- }
-
- private class Validation {
- final BindingGraph topLevelGraph;
- final BindingGraph subject;
- final ValidationReport.Builder<TypeElement> reportBuilder;
-
- Validation(BindingGraph topLevelGraph, BindingGraph subject) {
- this.topLevelGraph = topLevelGraph;
- this.subject = subject;
- this.reportBuilder =
- ValidationReport.about(subject.componentDescriptor().componentDefinitionType());
- }
-
- Validation(BindingGraph topLevelGraph) {
- this(topLevelGraph, topLevelGraph);
- }
-
- ValidationReport<TypeElement> buildReport() {
- return reportBuilder.build();
- }
-
- void validateSubgraph() {
- validateComponentScope();
- validateDependencyScopes();
- validateComponentHierarchy();
- validateBuilders();
-
- for (ComponentMethodDescriptor componentMethod :
- subject.componentDescriptor().componentMethods()) {
- Optional<DependencyRequest> entryPoint = componentMethod.dependencyRequest();
- if (entryPoint.isPresent()) {
- traverseRequest(
- entryPoint.get(),
- new ArrayDeque<ResolvedRequest>(),
- new LinkedHashSet<BindingKey>(),
- subject,
- new HashSet<DependencyRequest>());
- }
- }
-
- for (Map.Entry<ComponentMethodDescriptor, ComponentDescriptor> entry :
- filterKeys(subject.componentDescriptor().subcomponents(), isOfKind(SUBCOMPONENT))
- .entrySet()) {
- validateSubcomponentFactoryMethod(
- entry.getKey().methodElement(), entry.getValue().componentDefinitionType());
- }
-
- for (BindingGraph subgraph : subject.subgraphs().values()) {
- Validation subgraphValidation =
- new Validation(topLevelGraph, subgraph);
- subgraphValidation.validateSubgraph();
- reportBuilder.addSubreport(subgraphValidation.buildReport());
- }
- }
-
- private void validateSubcomponentFactoryMethod(
- ExecutableElement factoryMethod, TypeElement subcomponentType) {
- BindingGraph subgraph = subject.subgraphs().get(factoryMethod);
- FluentIterable<TypeElement> missingModules =
- FluentIterable.from(subgraph.componentRequirements())
- .filter(not(in(subgraphFactoryMethodParameters(factoryMethod))))
- .filter(
- new Predicate<TypeElement>() {
- @Override
- public boolean apply(TypeElement moduleType) {
- return !componentCanMakeNewInstances(moduleType);
- }
- });
- if (!missingModules.isEmpty()) {
- reportBuilder.addError(
- String.format(
- "%s requires modules which have no visible default constructors. "
- + "Add the following modules as parameters to this method: %s",
- subcomponentType.getQualifiedName(),
- Joiner.on(", ").join(missingModules.toSet())),
- factoryMethod);
- }
- }
-
- private ImmutableSet<TypeElement> subgraphFactoryMethodParameters(
- ExecutableElement factoryMethod) {
- DeclaredType componentType =
- asDeclared(subject.componentDescriptor().componentDefinitionType().asType());
- ExecutableType factoryMethodType =
- asExecutable(types.asMemberOf(componentType, factoryMethod));
- return asTypeElements(factoryMethodType.getParameterTypes());
- }
-
- /**
- * Traverse the resolved dependency requests, validating resolved bindings, and reporting any
- * cycles found.
- *
- * @param request the current dependency request
- * @param bindingPath the dependency request path from the parent of {@code request} at the head
- * up to the root dependency request from the component method at the tail
- * @param keysInPath the binding keys corresponding to the dependency requests in
- * {@code bindingPath}, but in reverse order: the first element is the binding key from the
- * component method
- * @param resolvedRequests the requests that have already been resolved, so we can avoid
- * traversing that part of the graph again
- */
- // TODO(dpb): It might be simpler to invert bindingPath's order.
- private void traverseRequest(
- DependencyRequest request,
- Deque<ResolvedRequest> bindingPath,
- LinkedHashSet<BindingKey> keysInPath,
- BindingGraph graph,
- Set<DependencyRequest> resolvedRequests) {
- verify(bindingPath.size() == keysInPath.size(),
- "mismatched path vs keys -- (%s vs %s)", bindingPath, keysInPath);
- BindingKey requestKey = request.bindingKey();
- if (keysInPath.contains(requestKey)) {
- reportCycle(
- // Invert bindingPath to match keysInPath's order
- ImmutableList.copyOf(bindingPath).reverse(),
- request,
- indexOf(keysInPath, equalTo(requestKey)));
- return;
- }
-
- // If request has already been resolved, avoid re-traversing the binding path.
- if (resolvedRequests.add(request)) {
- ResolvedRequest resolvedRequest = ResolvedRequest.create(request, graph);
- bindingPath.push(resolvedRequest);
- keysInPath.add(requestKey);
- validateResolvedBinding(bindingPath, resolvedRequest.binding());
-
- for (Binding binding : resolvedRequest.binding().bindings()) {
- for (DependencyRequest nextRequest : binding.implicitDependencies()) {
- traverseRequest(nextRequest, bindingPath, keysInPath, graph, resolvedRequests);
- }
- }
- bindingPath.poll();
- keysInPath.remove(requestKey);
- }
- }
-
- /**
- * Validates that the set of bindings resolved is consistent with the type of the binding, and
- * returns true if the bindings are valid.
- */
- private boolean validateResolvedBinding(
- Deque<ResolvedRequest> path, ResolvedBindings resolvedBinding) {
- if (resolvedBinding.bindings().isEmpty()) {
- reportMissingBinding(path);
- return false;
- }
-
- switch (resolvedBinding.bindingKey().kind()) {
- case CONTRIBUTION:
- ImmutableSet<ContributionBinding> contributionBindings =
- resolvedBinding.contributionBindings();
- if (any(contributionBindings, Binding.Type.MEMBERS_INJECTION)) {
- throw new IllegalArgumentException(
- "contribution binding keys should never have members injection bindings");
- }
- if (!validateNullability(path.peek().request(), contributionBindings)) {
- return false;
- }
- if (any(contributionBindings, Binding.Type.PRODUCTION)
- && doesPathRequireProvisionOnly(path)) {
- reportProviderMayNotDependOnProducer(path);
- return false;
- }
- if (contributionBindings.size() <= 1) {
- return true;
- }
- ImmutableListMultimap<ContributionType, ContributionBinding> contributionsByType =
- ContributionBinding.contributionTypesFor(contributionBindings);
- if (contributionsByType.keySet().size() > 1) {
- reportMultipleBindingTypes(path);
- return false;
- }
- switch (getOnlyElement(contributionsByType.keySet())) {
- case UNIQUE:
- reportDuplicateBindings(path);
- return false;
- case MAP:
- boolean duplicateMapKeys = hasDuplicateMapKeys(path, contributionBindings);
- boolean inconsistentMapKeyAnnotationTypes =
- hasInconsistentMapKeyAnnotationTypes(path, contributionBindings);
- return !duplicateMapKeys && !inconsistentMapKeyAnnotationTypes;
- case SET:
- break;
- default:
- throw new AssertionError();
- }
- break;
- case MEMBERS_INJECTION:
- if (!all(resolvedBinding.bindings(), Binding.Type.MEMBERS_INJECTION)) {
- throw new IllegalArgumentException(
- "members injection binding keys should never have contribution bindings");
- }
- if (resolvedBinding.bindings().size() > 1) {
- reportDuplicateBindings(path);
- return false;
- }
- return validateMembersInjectionBinding(getOnlyElement(resolvedBinding.bindings()), path);
- default:
- throw new AssertionError();
- }
- return true;
- }
-
- /** Ensures that if the request isn't nullable, then each contribution is also not nullable. */
- private boolean validateNullability(
- DependencyRequest request, Set<ContributionBinding> bindings) {
- if (request.isNullable()) {
- return true;
- }
-
- // Note: the method signature will include the @Nullable in it!
- /* TODO(sameb): Sometimes javac doesn't include the Element in its output.
- * (Maybe this happens if the code was already compiled before this point?)
- * ... we manually print out the request in that case, otherwise the error
- * message is kind of useless. */
- String typeName = TypeNames.forTypeMirror(request.key().type()).toString();
-
- boolean valid = true;
- for (ContributionBinding binding : bindings) {
- if (binding.nullableType().isPresent()) {
- reportBuilder.addItem(
- nullableToNonNullable(typeName, contributionBindingFormatter.format(binding))
- + "\n at: "
- + dependencyRequestFormatter.format(request),
- nullableValidationType,
- request.requestElement());
- valid = false;
- }
- }
- return valid;
- }
-
- /**
- * Returns {@code true} (and reports errors) if {@code mapBindings} has more than one binding
- * for the same map key.
- */
- private boolean hasDuplicateMapKeys(
- Deque<ResolvedRequest> path, Set<ContributionBinding> mapBindings) {
- boolean hasDuplicateMapKeys = false;
- for (Collection<ContributionBinding> mapBindingsForMapKey :
- indexMapBindingsByMapKey(mapBindings).asMap().values()) {
- if (mapBindingsForMapKey.size() > 1) {
- hasDuplicateMapKeys = true;
- reportDuplicateMapKeys(path, mapBindingsForMapKey);
- }
- }
- return hasDuplicateMapKeys;
- }
-
- /**
- * Returns {@code true} (and reports errors) if {@code mapBindings} uses more than one
- * {@link MapKey} annotation type.
- */
- private boolean hasInconsistentMapKeyAnnotationTypes(
- Deque<ResolvedRequest> path, Set<ContributionBinding> contributionBindings) {
- ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
- mapBindingsByAnnotationType = indexMapBindingsByAnnotationType(contributionBindings);
- if (mapBindingsByAnnotationType.keySet().size() > 1) {
- reportInconsistentMapKeyAnnotations(path, mapBindingsByAnnotationType);
- return true;
- }
- return false;
- }
-
- /**
- * Validates a members injection binding, returning false (and reporting the error) if it wasn't
- * valid.
- */
- private boolean validateMembersInjectionBinding(
- Binding binding, final Deque<ResolvedRequest> path) {
- return binding
- .key()
- .type()
- .accept(
- new SimpleTypeVisitor6<Boolean, Void>() {
- @Override
- protected Boolean defaultAction(TypeMirror e, Void p) {
- reportBuilder.addError(
- "Invalid members injection request.", path.peek().request().requestElement());
- return false;
- }
-
- @Override
- public Boolean visitDeclared(DeclaredType type, Void ignored) {
- // If the key has type arguments, validate that each type argument is declared.
- // Otherwise the type argument may be a wildcard (or other type), and we can't
- // resolve that to actual types. If the arg was an array, validate the type
- // of the array.
- for (TypeMirror arg : type.getTypeArguments()) {
- boolean declared;
- switch (arg.getKind()) {
- case ARRAY:
- declared =
- MoreTypes.asArray(arg)
- .getComponentType()
- .accept(
- new SimpleTypeVisitor6<Boolean, Void>() {
- @Override
- protected Boolean defaultAction(TypeMirror e, Void p) {
- return false;
- }
-
- @Override
- public Boolean visitDeclared(DeclaredType t, Void p) {
- for (TypeMirror arg : t.getTypeArguments()) {
- if (!arg.accept(this, null)) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public Boolean visitArray(ArrayType t, Void p) {
- return t.getComponentType().accept(this, null);
- }
-
- @Override
- public Boolean visitPrimitive(PrimitiveType t, Void p) {
- return true;
- }
- },
- null);
- break;
- case DECLARED:
- declared = true;
- break;
- default:
- declared = false;
- }
- if (!declared) {
- ImmutableList<String> printableDependencyPath =
- FluentIterable.from(path)
- .transform(REQUEST_FROM_RESOLVED_REQUEST)
- .transform(dependencyRequestFormatter)
- .filter(Predicates.not(Predicates.equalTo("")))
- .toList()
- .reverse();
- reportBuilder.addError(
- String.format(
- MEMBERS_INJECTION_WITH_UNBOUNDED_TYPE,
- arg.toString(),
- type.toString(),
- Joiner.on('\n').join(printableDependencyPath)),
- path.peek().request().requestElement());
- return false;
- }
- }
-
- TypeElement element = MoreElements.asType(type.asElement());
- // Also validate that the key is not the erasure of a generic type.
- // If it is, that means the user referred to Foo<T> as just 'Foo',
- // which we don't allow. (This is a judgement call -- we *could*
- // allow it and instantiate the type bounds... but we don't.)
- if (!MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty()
- && types.isSameType(types.erasure(element.asType()), type)) {
- ImmutableList<String> printableDependencyPath =
- FluentIterable.from(path)
- .transform(REQUEST_FROM_RESOLVED_REQUEST)
- .transform(dependencyRequestFormatter)
- .filter(Predicates.not(Predicates.equalTo("")))
- .toList()
- .reverse();
- reportBuilder.addError(
- String.format(
- ErrorMessages.MEMBERS_INJECTION_WITH_RAW_TYPE,
- type.toString(),
- Joiner.on('\n').join(printableDependencyPath)),
- path.peek().request().requestElement());
- return false;
- }
-
- return true; // valid
- }
- },
- null);
- }
-
- /**
- * Validates that component dependencies do not form a cycle.
- */
- private void validateComponentHierarchy() {
- ComponentDescriptor descriptor = subject.componentDescriptor();
- TypeElement componentType = descriptor.componentDefinitionType();
- validateComponentHierarchy(componentType, componentType, new ArrayDeque<TypeElement>());
- }
-
- /**
- * Recursive method to validate that component dependencies do not form a cycle.
- */
- private void validateComponentHierarchy(
- TypeElement rootComponent,
- TypeElement componentType,
- Deque<TypeElement> componentStack) {
-
- if (componentStack.contains(componentType)) {
- // Current component has already appeared in the component chain.
- StringBuilder message = new StringBuilder();
- message.append(rootComponent.getQualifiedName());
- message.append(" contains a cycle in its component dependencies:\n");
- componentStack.push(componentType);
- appendIndentedComponentsList(message, componentStack);
- componentStack.pop();
- reportBuilder.addItem(message.toString(),
- scopeCycleValidationType.diagnosticKind().get(),
- rootComponent, getAnnotationMirror(rootComponent, Component.class).get());
- } else {
- Optional<AnnotationMirror> componentAnnotation =
- getAnnotationMirror(componentType, Component.class);
- if (componentAnnotation.isPresent()) {
- componentStack.push(componentType);
-
- ImmutableSet<TypeElement> dependencies =
- MoreTypes.asTypeElements(getComponentDependencies(componentAnnotation.get()));
- for (TypeElement dependency : dependencies) {
- validateComponentHierarchy(rootComponent, dependency, componentStack);
- }
-
- componentStack.pop();
- }
- }
- }
-
- /**
- * Validates that among the dependencies are at most one scoped dependency,
- * that there are no cycles within the scoping chain, and that singleton
- * components have no scoped dependencies.
- */
- private void validateDependencyScopes() {
- ComponentDescriptor descriptor = subject.componentDescriptor();
- Scope scope = descriptor.scope();
- ImmutableSet<TypeElement> scopedDependencies = scopedTypesIn(descriptor.dependencies());
- if (scope.isPresent()) {
- // Dagger 1.x scope compatibility requires this be suppress-able.
- if (scopeCycleValidationType.diagnosticKind().isPresent()
- && scope.isSingleton()) {
- // Singleton is a special-case representing the longest lifetime, and therefore
- // @Singleton components may not depend on scoped components
- if (!scopedDependencies.isEmpty()) {
- StringBuilder message = new StringBuilder(
- "This @Singleton component cannot depend on scoped components:\n");
- appendIndentedComponentsList(message, scopedDependencies);
- reportBuilder.addItem(message.toString(),
- scopeCycleValidationType.diagnosticKind().get(),
- descriptor.componentDefinitionType(),
- descriptor.componentAnnotation());
- }
- } else if (scopedDependencies.size() > 1) {
- // Scoped components may depend on at most one scoped component.
- StringBuilder message = new StringBuilder(scope.getReadableSource())
- .append(' ')
- .append(descriptor.componentDefinitionType().getQualifiedName())
- .append(" depends on more than one scoped component:\n");
- appendIndentedComponentsList(message, scopedDependencies);
- reportBuilder.addError(
- message.toString(),
- descriptor.componentDefinitionType(),
- descriptor.componentAnnotation());
- } else {
- // Dagger 1.x scope compatibility requires this be suppress-able.
- if (!scopeCycleValidationType.equals(ValidationType.NONE)) {
- validateScopeHierarchy(descriptor.componentDefinitionType(),
- descriptor.componentDefinitionType(),
- new ArrayDeque<Scope>(),
- new ArrayDeque<TypeElement>());
- }
- }
- } else {
- // Scopeless components may not depend on scoped components.
- if (!scopedDependencies.isEmpty()) {
- StringBuilder message =
- new StringBuilder(descriptor.componentDefinitionType().getQualifiedName())
- .append(" (unscoped) cannot depend on scoped components:\n");
- appendIndentedComponentsList(message, scopedDependencies);
- reportBuilder.addError(
- message.toString(),
- descriptor.componentDefinitionType(),
- descriptor.componentAnnotation());
- }
- }
- }
-
- private void validateBuilders() {
- ComponentDescriptor componentDesc = subject.componentDescriptor();
- if (!componentDesc.builderSpec().isPresent()) {
- // If no builder, nothing to validate.
- return;
- }
-
- Set<TypeElement> availableDependencies = subject.availableDependencies();
- Set<TypeElement> requiredDependencies =
- Sets.filter(
- availableDependencies,
- new Predicate<TypeElement>() {
- @Override
- public boolean apply(TypeElement input) {
- return !Util.componentCanMakeNewInstances(input);
- }
- });
- final BuilderSpec spec = componentDesc.builderSpec().get();
- Map<TypeElement, ExecutableElement> allSetters = spec.methodMap();
-
- ErrorMessages.ComponentBuilderMessages msgs =
- ErrorMessages.builderMsgsFor(subject.componentDescriptor().kind());
- Set<TypeElement> extraSetters = Sets.difference(allSetters.keySet(), availableDependencies);
- if (!extraSetters.isEmpty()) {
- Collection<ExecutableElement> excessMethods =
- Maps.filterKeys(allSetters, Predicates.in(extraSetters)).values();
- Iterable<String> formatted = FluentIterable.from(excessMethods).transform(
- new Function<ExecutableElement, String>() {
- @Override public String apply(ExecutableElement input) {
- return methodSignatureFormatter.format(input,
- Optional.of(MoreTypes.asDeclared(spec.builderDefinitionType().asType())));
- }});
- reportBuilder.addError(
- String.format(msgs.extraSetters(), formatted), spec.builderDefinitionType());
- }
-
- Set<TypeElement> missingSetters = Sets.difference(requiredDependencies, allSetters.keySet());
- if (!missingSetters.isEmpty()) {
- reportBuilder.addError(
- String.format(msgs.missingSetters(), missingSetters), spec.builderDefinitionType());
- }
- }
-
- /**
- * Validates that scopes do not participate in a scoping cycle - that is to say, scoped
- * components are in a hierarchical relationship terminating with Singleton.
- *
- * <p>As a side-effect, this means scoped components cannot have a dependency cycle between
- * themselves, since a component's presence within its own dependency path implies a cyclical
- * relationship between scopes. However, cycles in component dependencies are explicitly
- * checked in {@link #validateComponentHierarchy()}.
- */
- private void validateScopeHierarchy(TypeElement rootComponent,
- TypeElement componentType,
- Deque<Scope> scopeStack,
- Deque<TypeElement> scopedDependencyStack) {
- Scope scope = Scope.scopeOf(componentType);
- if (scope.isPresent()) {
- if (scopeStack.contains(scope)) {
- scopedDependencyStack.push(componentType);
- // Current scope has already appeared in the component chain.
- StringBuilder message = new StringBuilder();
- message.append(rootComponent.getQualifiedName());
- message.append(" depends on scoped components in a non-hierarchical scope ordering:\n");
- appendIndentedComponentsList(message, scopedDependencyStack);
- if (scopeCycleValidationType.diagnosticKind().isPresent()) {
- reportBuilder.addItem(message.toString(),
- scopeCycleValidationType.diagnosticKind().get(),
- rootComponent, getAnnotationMirror(rootComponent, Component.class).get());
- }
- scopedDependencyStack.pop();
- } else {
- Optional<AnnotationMirror> componentAnnotation =
- getAnnotationMirror(componentType, Component.class);
- if (componentAnnotation.isPresent()) {
- ImmutableSet<TypeElement> scopedDependencies = scopedTypesIn(
- MoreTypes.asTypeElements(getComponentDependencies(componentAnnotation.get())));
- if (scopedDependencies.size() == 1) {
- // empty can be ignored (base-case), and > 1 is a different error reported separately.
- scopeStack.push(scope);
- scopedDependencyStack.push(componentType);
- validateScopeHierarchy(rootComponent, getOnlyElement(scopedDependencies),
- scopeStack, scopedDependencyStack);
- scopedDependencyStack.pop();
- scopeStack.pop();
- }
- } // else: we skip component dependencies which are not components
- }
- }
- }
-
- /**
- * Validates that the scope (if any) of this component are compatible with the scopes of the
- * bindings available in this component
- */
- void validateComponentScope() {
- ImmutableMap<BindingKey, ResolvedBindings> resolvedBindings = subject.resolvedBindings();
- Scope componentScope = subject.componentDescriptor().scope();
- ImmutableSet.Builder<String> incompatiblyScopedMethodsBuilder = ImmutableSet.builder();
- for (ResolvedBindings bindings : resolvedBindings.values()) {
- if (bindings.bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)) {
- for (ContributionBinding contributionBinding : bindings.ownedContributionBindings()) {
- Scope bindingScope = contributionBinding.scope();
- if (bindingScope.isPresent() && !bindingScope.equals(componentScope)) {
- // Scoped components cannot reference bindings to @Provides methods or @Inject
- // types decorated by a different scope annotation. Unscoped components cannot
- // reference to scoped @Provides methods or @Inject types decorated by any
- // scope annotation.
- switch (contributionBinding.bindingKind()) {
- case PROVISION:
- ExecutableElement provisionMethod =
- MoreElements.asExecutable(contributionBinding.bindingElement());
- incompatiblyScopedMethodsBuilder.add(
- methodSignatureFormatter.format(provisionMethod));
- break;
- case INJECTION:
- incompatiblyScopedMethodsBuilder.add(
- bindingScope.getReadableSource()
- + " class "
- + contributionBinding.bindingTypeElement().getQualifiedName());
- break;
- default:
- throw new IllegalStateException();
- }
- }
- }
- }
- }
- ImmutableSet<String> incompatiblyScopedMethods = incompatiblyScopedMethodsBuilder.build();
- if (!incompatiblyScopedMethods.isEmpty()) {
- TypeElement componentType = subject.componentDescriptor().componentDefinitionType();
- StringBuilder message = new StringBuilder(componentType.getQualifiedName());
- if (componentScope.isPresent()) {
- message.append(" scoped with ");
- message.append(componentScope.getReadableSource());
- message.append(" may not reference bindings with different scopes:\n");
- } else {
- message.append(" (unscoped) may not reference scoped bindings:\n");
- }
- for (String method : incompatiblyScopedMethods) {
- message.append(ErrorMessages.INDENT).append(method).append("\n");
- }
- reportBuilder.addError(
- message.toString(), componentType, subject.componentDescriptor().componentAnnotation());
- }
- }
-
- @SuppressWarnings("resource") // Appendable is a StringBuilder.
- private void reportProviderMayNotDependOnProducer(Deque<ResolvedRequest> path) {
- StringBuilder errorMessage = new StringBuilder();
- if (path.size() == 1) {
- new Formatter(errorMessage)
- .format(
- ErrorMessages.PROVIDER_ENTRY_POINT_MAY_NOT_DEPEND_ON_PRODUCER_FORMAT,
- formatRootRequestKey(path));
- } else {
- ImmutableSet<? extends Binding> dependentProvisions =
- provisionsDependingOnLatestRequest(path);
- // TODO(beder): Consider displaying all dependent provisions in the error message. If we do
- // that, should we display all productions that depend on them also?
- new Formatter(errorMessage).format(ErrorMessages.PROVIDER_MAY_NOT_DEPEND_ON_PRODUCER_FORMAT,
- keyFormatter.format(dependentProvisions.iterator().next().key()));
- }
- reportBuilder.addError(errorMessage.toString(), path.getLast().request().requestElement());
- }
-
- private void reportMissingBinding(Deque<ResolvedRequest> path) {
- Key key = path.peek().request().key();
- BindingKey bindingKey = path.peek().request().bindingKey();
- boolean requiresContributionMethod = !key.isValidImplicitProvisionKey(types);
- boolean requiresProvision = doesPathRequireProvisionOnly(path);
- StringBuilder errorMessage = new StringBuilder();
- String requiresErrorMessageFormat = requiresContributionMethod
- ? requiresProvision
- ? REQUIRES_PROVIDER_FORMAT
- : REQUIRES_PROVIDER_OR_PRODUCER_FORMAT
- : requiresProvision
- ? REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_FORMAT
- : REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_OR_PRODUCER_FORMAT;
- errorMessage.append(String.format(requiresErrorMessageFormat, keyFormatter.format(key)));
- if (key.isValidMembersInjectionKey()
- && !injectBindingRegistry.getOrFindMembersInjectionBinding(key).injectionSites()
- .isEmpty()) {
- errorMessage.append(" ").append(ErrorMessages.MEMBERS_INJECTION_DOES_NOT_IMPLY_PROVISION);
- }
- ImmutableList<String> printableDependencyPath =
- FluentIterable.from(path)
- .transform(REQUEST_FROM_RESOLVED_REQUEST)
- .transform(dependencyRequestFormatter)
- .filter(Predicates.not(Predicates.equalTo("")))
- .toList()
- .reverse();
- for (String dependency :
- printableDependencyPath.subList(1, printableDependencyPath.size())) {
- errorMessage.append('\n').append(dependency);
- }
- for (String suggestion : MissingBindingSuggestions.forKey(topLevelGraph, bindingKey)) {
- errorMessage.append('\n').append(suggestion);
- }
- reportBuilder.addError(errorMessage.toString(), path.getLast().request().requestElement());
- }
-
- @SuppressWarnings("resource") // Appendable is a StringBuilder.
- private void reportDuplicateBindings(Deque<ResolvedRequest> path) {
- ResolvedBindings resolvedBinding = path.peek().binding();
- StringBuilder builder = new StringBuilder();
- new Formatter(builder)
- .format(ErrorMessages.DUPLICATE_BINDINGS_FOR_KEY_FORMAT, formatRootRequestKey(path));
- for (ContributionBinding binding :
- Iterables.limit(resolvedBinding.contributionBindings(), DUPLICATE_SIZE_LIMIT)) {
- builder.append('\n').append(INDENT).append(contributionBindingFormatter.format(binding));
- }
- int numberOfOtherBindings =
- resolvedBinding.contributionBindings().size() - DUPLICATE_SIZE_LIMIT;
- if (numberOfOtherBindings > 0) {
- builder.append('\n').append(INDENT)
- .append("and ").append(numberOfOtherBindings).append(" other");
- }
- if (numberOfOtherBindings > 1) {
- builder.append('s');
- }
- reportBuilder.addError(builder.toString(), path.getLast().request().requestElement());
- }
-
- @SuppressWarnings("resource") // Appendable is a StringBuilder.
- private void reportMultipleBindingTypes(Deque<ResolvedRequest> path) {
- ResolvedBindings resolvedBinding = path.peek().binding();
- StringBuilder builder = new StringBuilder();
- new Formatter(builder)
- .format(ErrorMessages.MULTIPLE_BINDING_TYPES_FOR_KEY_FORMAT, formatRootRequestKey(path));
- ImmutableListMultimap<ContributionType, ContributionBinding> bindingsByType =
- ContributionBinding.contributionTypesFor(resolvedBinding.contributionBindings());
- for (ContributionType type :
- Ordering.natural().immutableSortedCopy(bindingsByType.keySet())) {
- builder.append(INDENT);
- builder.append(formatBindingType(type));
- builder.append(" bindings:\n");
- for (ContributionBinding binding : bindingsByType.get(type)) {
- builder
- .append(INDENT)
- .append(INDENT)
- .append(contributionBindingFormatter.format(binding))
- .append('\n');
- }
- }
- reportBuilder.addError(builder.toString(), path.getLast().request().requestElement());
- }
-
- private void reportDuplicateMapKeys(
- Deque<ResolvedRequest> path, Collection<ContributionBinding> mapBindings) {
- StringBuilder builder = new StringBuilder();
- builder.append(duplicateMapKeysError(formatRootRequestKey(path)));
- appendBindings(builder, mapBindings, 1);
- reportBuilder.addError(builder.toString(), path.getLast().request().requestElement());
- }
-
- private void reportInconsistentMapKeyAnnotations(
- Deque<ResolvedRequest> path,
- Multimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
- mapBindingsByAnnotationType) {
- StringBuilder builder =
- new StringBuilder(inconsistentMapKeyAnnotationsError(formatRootRequestKey(path)));
- for (Map.Entry<Equivalence.Wrapper<DeclaredType>, Collection<ContributionBinding>> entry :
- mapBindingsByAnnotationType.asMap().entrySet()) {
- DeclaredType annotationType = entry.getKey().get();
- Collection<ContributionBinding> bindings = entry.getValue();
-
- builder
- .append('\n')
- .append(INDENT)
- .append(annotationType)
- .append(':');
-
- appendBindings(builder, bindings, 2);
- }
- reportBuilder.addError(builder.toString(), path.getLast().request().requestElement());
- }
-
- /**
- * Reports a cycle in the binding path.
- *
- * @param bindingPath the binding path, starting with the component provision dependency, and
- * ending with the binding that depends on {@code request}
- * @param request the request that would have been added to the binding path if its
- * {@linkplain DependencyRequest#bindingKey() binding key} wasn't already in it
- * @param indexOfDuplicatedKey the index of the dependency request in {@code bindingPath} whose
- * {@linkplain DependencyRequest#bindingKey() binding key} matches {@code request}'s
- */
- private void reportCycle(
- Iterable<ResolvedRequest> bindingPath,
- DependencyRequest request,
- int indexOfDuplicatedKey) {
- ImmutableList<DependencyRequest> requestPath =
- FluentIterable.from(bindingPath)
- .transform(REQUEST_FROM_RESOLVED_REQUEST)
- .append(request)
- .toList();
- Element rootRequestElement = requestPath.get(0).requestElement();
- ImmutableList<DependencyRequest> cycle =
- requestPath.subList(indexOfDuplicatedKey, requestPath.size());
- Diagnostic.Kind kind = cycleHasProviderOrLazy(cycle) ? WARNING : ERROR;
- if (kind == WARNING
- && (suppressCycleWarnings(rootRequestElement)
- || suppressCycleWarnings(rootRequestElement.getEnclosingElement())
- || suppressCycleWarnings(cycle))) {
- return;
- }
- // TODO(cgruber): Provide a hint for the start and end of the cycle.
- TypeElement componentType = MoreElements.asType(rootRequestElement.getEnclosingElement());
- reportBuilder.addItem(
- String.format(
- ErrorMessages.CONTAINS_DEPENDENCY_CYCLE_FORMAT,
- componentType.getQualifiedName(),
- rootRequestElement.getSimpleName(),
- Joiner.on("\n")
- .join(
- FluentIterable.from(requestPath)
- .transform(dependencyRequestFormatter)
- .filter(not(equalTo("")))
- .skip(1))),
- kind,
- rootRequestElement);
- }
-
- /**
- * Returns {@code true} if any step of a dependency cycle after the first is a {@link Provider}
- * or {@link Lazy} or a {@code Map<K, Provider<V>>}.
- *
- * <p>If an implicit {@link Provider} dependency on {@code Map<K, Provider<V>>} is immediately
- * preceded by a dependency on {@code Map<K, V>}, which means that the map's {@link Provider}s'
- * {@link Provider#get() get()} methods are called during provision and so the cycle is not
- * really broken.
- */
- private boolean cycleHasProviderOrLazy(ImmutableList<DependencyRequest> cycle) {
- DependencyRequest lastDependencyRequest = cycle.get(0);
- for (DependencyRequest dependencyRequest : skip(cycle, 1)) {
- switch (dependencyRequest.kind()) {
- case PROVIDER:
- if (!isImplicitProviderMapForValueMap(dependencyRequest, lastDependencyRequest)) {
- return true;
- }
- break;
-
- case LAZY:
- return true;
-
- case INSTANCE:
- if (isMapWithProvidedValues(dependencyRequest.key().type())) {
- return true;
- } else {
- break;
- }
-
- default:
- break;
- }
- lastDependencyRequest = dependencyRequest;
- }
- return false;
- }
-
- /**
- * Returns {@code true} if {@code maybeValueMapRequest}'s key type is {@code Map<K, V>} and
- * {@code maybeProviderMapRequest}'s key type is {@code Map<K, Provider<V>>}, and both keys have
- * the same qualifier.
- */
- private boolean isImplicitProviderMapForValueMap(
- DependencyRequest maybeProviderMapRequest, DependencyRequest maybeValueMapRequest) {
- TypeMirror maybeProviderMapRequestType = maybeProviderMapRequest.key().type();
- TypeMirror maybeValueMapRequestType = maybeValueMapRequest.key().type();
- return maybeProviderMapRequest
- .key()
- .wrappedQualifier()
- .equals(maybeValueMapRequest.key().wrappedQualifier())
- && isMapWithProvidedValues(maybeProviderMapRequestType)
- && isMapWithNonProvidedValues(maybeValueMapRequestType)
- && types.isSameType(
- getKeyTypeOfMap(asDeclared(maybeProviderMapRequestType)),
- getKeyTypeOfMap(asDeclared(maybeValueMapRequestType)))
- && types.isSameType(
- getProvidedValueTypeOfMap(asDeclared(maybeProviderMapRequestType)),
- getValueTypeOfMap(asDeclared(maybeValueMapRequestType)));
- }
- }
-
- private boolean suppressCycleWarnings(Element requestElement) {
- SuppressWarnings suppressions = requestElement.getAnnotation(SuppressWarnings.class);
- return suppressions != null && Arrays.asList(suppressions.value()).contains("dependency-cycle");
- }
-
- private boolean suppressCycleWarnings(ImmutableList<DependencyRequest> pathElements) {
- for (DependencyRequest dependencyRequest : pathElements) {
- if (suppressCycleWarnings(dependencyRequest.requestElement())) {
- return true;
- }
- }
- return false;
- }
-
- ValidationReport<TypeElement> validate(BindingGraph subject) {
- Validation validation = new Validation(subject);
- validation.validateSubgraph();
- return validation.buildReport();
- }
-
- /**
- * Append and format a list of indented component types (with their scope annotations)
- */
- private void appendIndentedComponentsList(StringBuilder message, Iterable<TypeElement> types) {
- for (TypeElement scopedComponent : types) {
- message.append(INDENT);
- Scope scope = Scope.scopeOf(scopedComponent);
- if (scope.isPresent()) {
- message.append(scope.getReadableSource()).append(' ');
- }
- message.append(stripCommonTypePrefixes(scopedComponent.getQualifiedName().toString()))
- .append('\n');
- }
- }
-
- /**
- * Returns a set of type elements containing only those found in the input set that have
- * a scoping annotation.
- */
- private ImmutableSet<TypeElement> scopedTypesIn(Set<TypeElement> types) {
- return FluentIterable.from(types).filter(new Predicate<TypeElement>() {
- @Override public boolean apply(TypeElement input) {
- return Scope.scopeOf(input).isPresent();
- }
- }).toSet();
- }
-
- /**
- * Returns whether the given dependency path would require the most recent request to be resolved
- * by only provision bindings.
- */
- private boolean doesPathRequireProvisionOnly(Deque<ResolvedRequest> path) {
- if (path.size() == 1) {
- // if this is an entry-point, then we check the request
- switch (path.peek().request().kind()) {
- case INSTANCE:
- case PROVIDER:
- case LAZY:
- case MEMBERS_INJECTOR:
- return true;
- case PRODUCER:
- case PRODUCED:
- case FUTURE:
- return false;
- default:
- throw new AssertionError();
- }
- }
- // otherwise, the second-most-recent bindings determine whether the most recent one must be a
- // provision
- return !provisionsDependingOnLatestRequest(path).isEmpty();
- }
-
- /**
- * Returns any provision bindings resolved for the second-most-recent request in the given path;
- * that is, returns those provision bindings that depend on the latest request in the path.
- */
- private ImmutableSet<? extends Binding> provisionsDependingOnLatestRequest(
- Deque<ResolvedRequest> path) {
- Iterator<ResolvedRequest> iterator = path.iterator();
- final DependencyRequest request = iterator.next().request();
- ResolvedRequest previousResolvedRequest = iterator.next();
- return FluentIterable.from(previousResolvedRequest.binding().bindings())
- .filter(Binding.Type.PROVISION)
- .filter(
- new Predicate<Binding>() {
- @Override
- public boolean apply(Binding binding) {
- return binding.implicitDependencies().contains(request);
- }
- })
- .toSet();
- }
-
- private String formatBindingType(ContributionType type) {
- switch (type) {
- case MAP:
- return "Map";
- case SET:
- return "Set";
- case UNIQUE:
- return "Unique";
- default:
- throw new IllegalStateException("Unknown binding type: " + type);
- }
- }
-
- private String formatRootRequestKey(Deque<ResolvedRequest> path) {
- return keyFormatter.format(path.peek().request().key());
- }
-
- private void appendBindings(
- StringBuilder builder, Collection<ContributionBinding> bindings, int indentLevel) {
- for (ContributionBinding binding : Iterables.limit(bindings, DUPLICATE_SIZE_LIMIT)) {
- builder.append('\n');
- for (int i = 0; i < indentLevel; i++) {
- builder.append(INDENT);
- }
- builder.append(contributionBindingFormatter.format(binding));
- }
- int numberOfOtherBindings = bindings.size() - DUPLICATE_SIZE_LIMIT;
- if (numberOfOtherBindings > 0) {
- builder.append('\n');
- for (int i = 0; i < indentLevel; i++) {
- builder.append(INDENT);
- }
- builder.append("and ").append(numberOfOtherBindings).append(" other");
- }
- if (numberOfOtherBindings > 1) {
- builder.append('s');
- }
- }
-
- @AutoValue
- abstract static class ResolvedRequest {
- abstract DependencyRequest request();
- abstract ResolvedBindings binding();
-
- static ResolvedRequest create(DependencyRequest request, BindingGraph graph) {
- BindingKey bindingKey = request.bindingKey();
- ResolvedBindings resolvedBindings = graph.resolvedBindings().get(bindingKey);
- return new AutoValue_BindingGraphValidator_ResolvedRequest(
- request,
- resolvedBindings == null
- ? ResolvedBindings.noBindings(bindingKey, graph.componentDescriptor())
- : resolvedBindings);
- }
- }
-
- private static final Function<ResolvedRequest, DependencyRequest> REQUEST_FROM_RESOLVED_REQUEST =
- new Function<ResolvedRequest, DependencyRequest>() {
- @Override public DependencyRequest apply(ResolvedRequest resolvedRequest) {
- return resolvedRequest.request();
- }
- };
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/BindingKey.java b/compiler/src/main/java/dagger/internal/codegen/BindingKey.java
deleted file mode 100644
index cd29d8d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/BindingKey.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.value.AutoValue;
-
-/**
- * A value object that pairs a {@link Key} with the style of its binding (i.e., whether it's a
- * members injector or normal contribution).
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@AutoValue
-abstract class BindingKey {
- /** The style of binding that makes a {@link Key} available. */
- enum Kind {
- CONTRIBUTION, MEMBERS_INJECTION;
- }
-
- static BindingKey create(Kind kind, Key key) {
- return new AutoValue_BindingKey(kind, key);
- }
-
- abstract Kind kind();
- abstract Key key();
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/BuilderValidator.java b/compiler/src/main/java/dagger/internal/codegen/BuilderValidator.java
deleted file mode 100644
index ba96ebf..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/BuilderValidator.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * Validates {@link dagger.Component.Builder} annotations.
- *
- * @author sameb@google.com (Sam Berlin)
- */
-class BuilderValidator {
- private final Elements elements;
- private final Types types;
- private final ComponentDescriptor.Kind componentType;
-
- BuilderValidator(Elements elements, Types types, ComponentDescriptor.Kind componentType) {
- this.elements = elements;
- this.types = types;
- this.componentType = componentType;
- }
-
- public ValidationReport<TypeElement> validate(TypeElement subject) {
- ValidationReport.Builder<TypeElement> builder = ValidationReport.about(subject);
-
- Element componentElement = subject.getEnclosingElement();
- ErrorMessages.ComponentBuilderMessages msgs = ErrorMessages.builderMsgsFor(componentType);
- Class<? extends Annotation> componentAnnotation = componentType.annotationType();
- Class<? extends Annotation> builderAnnotation = componentType.builderAnnotationType();
- checkArgument(subject.getAnnotation(builderAnnotation) != null);
-
- if (!isAnnotationPresent(componentElement, componentAnnotation)) {
- builder.addError(msgs.mustBeInComponent(), subject);
- }
-
- switch (subject.getKind()) {
- case CLASS:
- List<? extends Element> allElements = subject.getEnclosedElements();
- List<ExecutableElement> cxtors = ElementFilter.constructorsIn(allElements);
- if (cxtors.size() != 1 || getOnlyElement(cxtors).getParameters().size() != 0) {
- builder.addError(msgs.cxtorOnlyOneAndNoArgs(), subject);
- }
- break;
- case INTERFACE:
- break;
- default:
- // If not the correct type, exit early since the rest of the messages will be bogus.
- builder.addError(msgs.mustBeClassOrInterface(), subject);
- return builder.build();
- }
-
- if (!subject.getTypeParameters().isEmpty()) {
- builder.addError(msgs.generics(), subject);
- }
-
- Set<Modifier> modifiers = subject.getModifiers();
- if (modifiers.contains(PRIVATE)) {
- builder.addError(msgs.isPrivate(), subject);
- }
- if (!modifiers.contains(STATIC)) {
- builder.addError(msgs.mustBeStatic(), subject);
- }
- // Note: Must be abstract, so no need to check for final.
- if (!modifiers.contains(ABSTRACT)) {
- builder.addError(msgs.mustBeAbstract(), subject);
- }
-
- ExecutableElement buildMethod = null;
- Multimap<Equivalence.Wrapper<TypeMirror>, ExecutableElement> methodsPerParam =
- LinkedHashMultimap.create();
- for (ExecutableElement method : Util.getUnimplementedMethods(elements, subject)) {
- ExecutableType resolvedMethodType =
- MoreTypes.asExecutable(types.asMemberOf(MoreTypes.asDeclared(subject.asType()), method));
- TypeMirror returnType = resolvedMethodType.getReturnType();
- if (method.getParameters().size() == 0) {
- // If this is potentially a build() method, validate it returns the correct type.
- if (types.isSameType(returnType, componentElement.asType())) {
- if (buildMethod != null) {
- // If we found more than one build-like method, fail.
- error(builder, method, msgs.twoBuildMethods(), msgs.inheritedTwoBuildMethods(),
- buildMethod);
- }
- } else {
- error(builder, method, msgs.buildMustReturnComponentType(),
- msgs.inheritedBuildMustReturnComponentType());
- }
- // We set the buildMethod regardless of the return type to reduce error spam.
- buildMethod = method;
- } else if (method.getParameters().size() > 1) {
- // If this is a setter, make sure it has one arg.
- error(builder, method, msgs.methodsMustTakeOneArg(), msgs.inheritedMethodsMustTakeOneArg());
- } else if (returnType.getKind() != TypeKind.VOID
- && !types.isSubtype(subject.asType(), returnType)) {
- // If this correctly had one arg, make sure the return types are valid.
- error(builder, method, msgs.methodsMustReturnVoidOrBuilder(),
- msgs.inheritedMethodsMustReturnVoidOrBuilder());
- } else {
- // If the return types are valid, record the method.
- methodsPerParam.put(
- MoreTypes.equivalence().<TypeMirror>wrap(
- Iterables.getOnlyElement(resolvedMethodType.getParameterTypes())),
- method);
- }
-
- if (!method.getTypeParameters().isEmpty()) {
- error(builder, method, msgs.methodsMayNotHaveTypeParameters(),
- msgs.inheritedMethodsMayNotHaveTypeParameters());
- }
- }
-
- if (buildMethod == null) {
- builder.addError(msgs.missingBuildMethod(), subject);
- }
-
- // Go back through each recorded method per param type. If we had more than one method
- // for a given param, fail.
- for (Map.Entry<Equivalence.Wrapper<TypeMirror>, Collection<ExecutableElement>> entry :
- methodsPerParam.asMap().entrySet()) {
- if (entry.getValue().size() > 1) {
- TypeMirror type = entry.getKey().get();
- builder.addError(String.format(msgs.manyMethodsForType(), type, entry.getValue()), subject);
- }
- }
-
- // Note: there's more validation in BindingGraphValidator,
- // specifically to make sure the setter methods mirror the deps.
-
- return builder.build();
- }
-
- /**
- * Generates one of two error messages. If the method is enclosed in the subject, we target the
- * error to the method itself. Otherwise we target the error to the subject and list the method as
- * an argumnent. (Otherwise we have no way of knowing if the method is being compiled in this pass
- * too, so javac might not be able to pinpoint it's line of code.)
- */
- /*
- * For Component.Builder, the prototypical example would be if someone had:
- * libfoo: interface SharedBuilder { void badSetter(A a, B b); }
- * libbar: BarComponent { BarBuilder extends SharedBuilder } }
- * ... the compiler only validates BarBuilder when compiling libbar, but it fails because
- * of libfoo's SharedBuilder (which could have been compiled in a previous pass).
- * So we can't point to SharedBuilder#badSetter as the subject of the BarBuilder validation
- * failure.
- *
- * This check is a little more strict than necessary -- ideally we'd check if method's enclosing
- * class was included in this compile run. But that's hard, and this is close enough.
- */
- private void error(
- ValidationReport.Builder<TypeElement> builder,
- ExecutableElement method,
- String enclosedError,
- String inheritedError,
- Object... extraArgs) {
- if (method.getEnclosingElement().equals(builder.getSubject())) {
- builder.addError(String.format(enclosedError, extraArgs), method);
- } else {
- Object[] newArgs = new Object[extraArgs.length + 1];
- newArgs[0] = method;
- System.arraycopy(extraArgs, 0, newArgs, 1, extraArgs.length);
- builder.addError(String.format(inheritedError, newArgs), builder.getSubject());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java b/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java
deleted file mode 100644
index 650fb9d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Component;
-import dagger.Lazy;
-import dagger.MembersInjector;
-import dagger.Module;
-import dagger.Subcomponent;
-import dagger.producers.ProductionComponent;
-import java.lang.annotation.Annotation;
-import java.util.EnumSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import javax.inject.Provider;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ConfigurationAnnotations.enclosedBuilders;
-import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
-import static dagger.internal.codegen.ConfigurationAnnotations.getComponentModules;
-import static dagger.internal.codegen.ConfigurationAnnotations.isComponent;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.VOID;
-
-/**
- * The logical representation of a {@link Component} or {@link ProductionComponent} definition.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@AutoValue
-abstract class ComponentDescriptor {
- ComponentDescriptor() {}
-
- enum Kind {
- COMPONENT(Component.class, Component.Builder.class, true),
- SUBCOMPONENT(Subcomponent.class, Subcomponent.Builder.class, false),
- PRODUCTION_COMPONENT(ProductionComponent.class, ProductionComponent.Builder.class, true);
-
- private final Class<? extends Annotation> annotationType;
- private final Class<? extends Annotation> builderType;
- private final boolean isTopLevel;
-
- /**
- * Returns the kind of an annotated element if it is annotated with one of the
- * {@linkplain #annotationType() annotation types}.
- *
- * @throws IllegalArgumentException if the element is annotated with more than one of the
- * annotation types
- */
- static Optional<Kind> forAnnotatedElement(TypeElement element) {
- Set<Kind> kinds = EnumSet.noneOf(Kind.class);
- for (Kind kind : values()) {
- if (MoreElements.isAnnotationPresent(element, kind.annotationType())) {
- kinds.add(kind);
- }
- }
- checkArgument(
- kinds.size() <= 1, "%s cannot be annotated with more than one of %s", element, kinds);
- return Optional.fromNullable(getOnlyElement(kinds, null));
- }
-
- Kind(
- Class<? extends Annotation> annotationType,
- Class<? extends Annotation> builderType,
- boolean isTopLevel) {
- this.annotationType = annotationType;
- this.builderType = builderType;
- this.isTopLevel = isTopLevel;
- }
-
- Class<? extends Annotation> annotationType() {
- return annotationType;
- }
-
- Class<? extends Annotation> builderAnnotationType() {
- return builderType;
- }
-
- boolean isTopLevel() {
- return isTopLevel;
- }
- }
-
- abstract Kind kind();
-
- abstract AnnotationMirror componentAnnotation();
-
- /**
- * The type (interface or abstract class) that defines the component. This is the element to which
- * the {@link Component} annotation was applied.
- */
- abstract TypeElement componentDefinitionType();
-
- /**
- * The set of component dependencies listed in {@link Component#dependencies}.
- */
- abstract ImmutableSet<TypeElement> dependencies();
-
- /**
- * The set of {@link ModuleDescriptor modules} declared directly in {@link Component#modules}.
- * Use {@link #transitiveModules} to get the full set of modules available upon traversing
- * {@link Module#includes}.
- */
- abstract ImmutableSet<ModuleDescriptor> modules();
-
- /**
- * Returns the set of {@link ModuleDescriptor modules} declared in {@link Component#modules} and
- * those reachable by traversing {@link Module#includes}.
- *
- * <p>Note that for subcomponents this <em>will not</em> include descriptors for any modules that
- * are declared in parent components.
- */
- ImmutableSet<ModuleDescriptor> transitiveModules() {
- Set<ModuleDescriptor> transitiveModules = new LinkedHashSet<>();
- for (ModuleDescriptor module : modules()) {
- addTransitiveModules(transitiveModules, module);
- }
- return ImmutableSet.copyOf(transitiveModules);
- }
-
- ImmutableSet<TypeElement> transitiveModuleTypes() {
- return FluentIterable.from(transitiveModules())
- .transform(ModuleDescriptor.getModuleElement())
- .toSet();
- }
-
- private static Set<ModuleDescriptor> addTransitiveModules(
- Set<ModuleDescriptor> transitiveModules, ModuleDescriptor module) {
- if (transitiveModules.add(module)) {
- for (ModuleDescriptor includedModule : module.includedModules()) {
- addTransitiveModules(transitiveModules, includedModule);
- }
- }
- return transitiveModules;
- }
-
- /**
- * An index of the type to which this component holds a reference (the type listed in
- * {@link Component#dependencies} or {@link ProductionComponent#dependencies} as opposed to the
- * enclosing type) for each method from a component dependency that can be used for binding.
- */
- abstract ImmutableMap<ExecutableElement, TypeElement> dependencyMethodIndex();
-
- /**
- * The element representing {@link Executor}, if it should be a dependency of this component.
- */
- abstract Optional<TypeElement> executorDependency();
-
- /**
- * The scope of the component.
- */
- abstract Scope scope();
-
- abstract ImmutableMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponents();
-
- abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
-
- // TODO(gak): Consider making this non-optional and revising the
- // interaction between the spec & generation
- abstract Optional<BuilderSpec> builderSpec();
-
- @AutoValue
- static abstract class ComponentMethodDescriptor {
- abstract ComponentMethodKind kind();
- abstract Optional<DependencyRequest> dependencyRequest();
- abstract ExecutableElement methodElement();
-
- /**
- * A predicate that passes for {@link ComponentMethodDescriptor}s of a given kind.
- */
- static Predicate<ComponentMethodDescriptor> isOfKind(final ComponentMethodKind kind) {
- return new Predicate<ComponentMethodDescriptor>() {
- @Override
- public boolean apply(ComponentMethodDescriptor descriptor) {
- return kind.equals(descriptor.kind());
- }
- };
- }
- }
-
- enum ComponentMethodKind {
- PROVISON,
- PRODUCTION,
- MEMBERS_INJECTION,
- SUBCOMPONENT,
- SUBCOMPONENT_BUILDER,
- }
-
- @AutoValue
- static abstract class BuilderSpec {
- abstract TypeElement builderDefinitionType();
- abstract Map<TypeElement, ExecutableElement> methodMap();
- abstract ExecutableElement buildMethod();
- abstract TypeMirror componentType();
- }
-
- static final class Factory {
- private final Elements elements;
- private final Types types;
- private final DependencyRequest.Factory dependencyRequestFactory;
- private final ModuleDescriptor.Factory moduleDescriptorFactory;
-
- Factory(
- Elements elements,
- Types types,
- DependencyRequest.Factory dependencyRequestFactory,
- ModuleDescriptor.Factory moduleDescriptorFactory) {
- this.elements = elements;
- this.types = types;
- this.dependencyRequestFactory = dependencyRequestFactory;
- this.moduleDescriptorFactory = moduleDescriptorFactory;
- }
-
- /**
- * Returns a component descriptor for a type annotated with either {@link Component @Component}
- * or {@link ProductionComponent @ProductionComponent}.
- */
- ComponentDescriptor forComponent(TypeElement componentDefinitionType) {
- Optional<Kind> kind = Kind.forAnnotatedElement(componentDefinitionType);
- checkArgument(
- kind.isPresent() && kind.get().isTopLevel(),
- "%s must be annotated with @Component or @ProductionComponent",
- componentDefinitionType);
- return create(componentDefinitionType, kind.get());
- }
-
- private ComponentDescriptor create(TypeElement componentDefinitionType, Kind kind) {
- DeclaredType declaredComponentType = MoreTypes.asDeclared(componentDefinitionType.asType());
- AnnotationMirror componentMirror =
- getAnnotationMirror(componentDefinitionType, kind.annotationType())
- .or(getAnnotationMirror(componentDefinitionType, Subcomponent.class))
- .get();
- ImmutableSet<TypeElement> componentDependencyTypes =
- isComponent(componentDefinitionType)
- ? MoreTypes.asTypeElements(getComponentDependencies(componentMirror))
- : ImmutableSet.<TypeElement>of();
-
- ImmutableMap.Builder<ExecutableElement, TypeElement> dependencyMethodIndex =
- ImmutableMap.builder();
-
- for (TypeElement componentDependency : componentDependencyTypes) {
- List<ExecutableElement> dependencyMethods =
- ElementFilter.methodsIn(elements.getAllMembers(componentDependency));
- for (ExecutableElement dependencyMethod : dependencyMethods) {
- if (isComponentContributionMethod(elements, dependencyMethod)) {
- dependencyMethodIndex.put(dependencyMethod, componentDependency);
- }
- }
- }
-
- Optional<TypeElement> executorDependency =
- kind.equals(Kind.PRODUCTION_COMPONENT)
- ? Optional.of(elements.getTypeElement(Executor.class.getCanonicalName()))
- : Optional.<TypeElement>absent();
-
- ImmutableSet.Builder<ModuleDescriptor> modules = ImmutableSet.builder();
- for (TypeMirror moduleIncludesType : getComponentModules(componentMirror)) {
- modules.add(moduleDescriptorFactory.create(MoreTypes.asTypeElement(moduleIncludesType)));
- }
- if (kind.equals(Kind.PRODUCTION_COMPONENT)) {
- modules.add(descriptorForMonitoringModule(componentDefinitionType));
- }
-
- ImmutableSet<ExecutableElement> unimplementedMethods =
- Util.getUnimplementedMethods(elements, componentDefinitionType);
-
- ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
- ImmutableSet.builder();
-
- ImmutableMap.Builder<ComponentMethodDescriptor, ComponentDescriptor> subcomponentDescriptors =
- ImmutableMap.builder();
- for (ExecutableElement componentMethod : unimplementedMethods) {
- ExecutableType resolvedMethod =
- MoreTypes.asExecutable(types.asMemberOf(declaredComponentType, componentMethod));
- ComponentMethodDescriptor componentMethodDescriptor =
- getDescriptorForComponentMethod(componentDefinitionType, kind, componentMethod);
- componentMethodsBuilder.add(componentMethodDescriptor);
- switch (componentMethodDescriptor.kind()) {
- case SUBCOMPONENT:
- subcomponentDescriptors.put(
- componentMethodDescriptor,
- create(
- MoreElements.asType(MoreTypes.asElement(resolvedMethod.getReturnType())),
- Kind.SUBCOMPONENT));
- break;
- case SUBCOMPONENT_BUILDER:
- subcomponentDescriptors.put(
- componentMethodDescriptor,
- create(
- MoreElements.asType(
- MoreTypes.asElement(resolvedMethod.getReturnType()).getEnclosingElement()),
- Kind.SUBCOMPONENT));
- break;
- default: // nothing special to do for other methods.
- }
-
- }
-
- ImmutableList<DeclaredType> enclosedBuilders = kind.builderAnnotationType() == null
- ? ImmutableList.<DeclaredType>of()
- : enclosedBuilders(componentDefinitionType, kind.builderAnnotationType());
- Optional<DeclaredType> builderType =
- Optional.fromNullable(getOnlyElement(enclosedBuilders, null));
-
- Scope scope = Scope.scopeOf(componentDefinitionType);
- return new AutoValue_ComponentDescriptor(
- kind,
- componentMirror,
- componentDefinitionType,
- componentDependencyTypes,
- modules.build(),
- dependencyMethodIndex.build(),
- executorDependency,
- scope,
- subcomponentDescriptors.build(),
- componentMethodsBuilder.build(),
- createBuilderSpec(builderType));
- }
-
- private ComponentMethodDescriptor getDescriptorForComponentMethod(TypeElement componentElement,
- Kind componentKind,
- ExecutableElement componentMethod) {
- ExecutableType resolvedComponentMethod = MoreTypes.asExecutable(types.asMemberOf(
- MoreTypes.asDeclared(componentElement.asType()), componentMethod));
- TypeMirror returnType = resolvedComponentMethod.getReturnType();
- if (returnType.getKind().equals(DECLARED)) {
- if (MoreTypes.isTypeOf(Provider.class, returnType)
- || MoreTypes.isTypeOf(Lazy.class, returnType)) {
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
- ComponentMethodKind.PROVISON,
- Optional.of(dependencyRequestFactory.forComponentProvisionMethod(componentMethod,
- resolvedComponentMethod)),
- componentMethod);
- } else if (MoreTypes.isTypeOf(MembersInjector.class, returnType)) {
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
- ComponentMethodKind.MEMBERS_INJECTION,
- Optional.of(dependencyRequestFactory.forComponentMembersInjectionMethod(
- componentMethod,
- resolvedComponentMethod)),
- componentMethod);
- } else if (isAnnotationPresent(MoreTypes.asElement(returnType), Subcomponent.class)) {
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
- ComponentMethodKind.SUBCOMPONENT,
- Optional.<DependencyRequest>absent(),
- componentMethod);
- } else if (isAnnotationPresent(MoreTypes.asElement(returnType),
- Subcomponent.Builder.class)) {
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
- ComponentMethodKind.SUBCOMPONENT_BUILDER,
- Optional.<DependencyRequest>absent(),
- componentMethod);
- }
- }
-
- // a typical provision method
- if (componentMethod.getParameters().isEmpty()
- && !componentMethod.getReturnType().getKind().equals(VOID)) {
- switch (componentKind) {
- case COMPONENT:
- case SUBCOMPONENT:
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
- ComponentMethodKind.PROVISON,
- Optional.of(dependencyRequestFactory.forComponentProvisionMethod(componentMethod,
- resolvedComponentMethod)),
- componentMethod);
- case PRODUCTION_COMPONENT:
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
- ComponentMethodKind.PRODUCTION,
- Optional.of(dependencyRequestFactory.forComponentProductionMethod(componentMethod,
- resolvedComponentMethod)),
- componentMethod);
- default:
- throw new AssertionError();
- }
- }
-
- List<? extends TypeMirror> parameterTypes = resolvedComponentMethod.getParameterTypes();
- if (parameterTypes.size() == 1
- && (returnType.getKind().equals(VOID)
- || MoreTypes.equivalence().equivalent(returnType, parameterTypes.get(0)))) {
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor(
- ComponentMethodKind.MEMBERS_INJECTION,
- Optional.of(dependencyRequestFactory.forComponentMembersInjectionMethod(
- componentMethod,
- resolvedComponentMethod)),
- componentMethod);
- }
-
- throw new IllegalArgumentException("not a valid component method: " + componentMethod);
- }
-
- private Optional<BuilderSpec> createBuilderSpec(Optional<DeclaredType> builderType) {
- if (!builderType.isPresent()) {
- return Optional.absent();
- }
- TypeElement element = MoreTypes.asTypeElement(builderType.get());
- ImmutableSet<ExecutableElement> methods = Util.getUnimplementedMethods(elements, element);
- ImmutableMap.Builder<TypeElement, ExecutableElement> map = ImmutableMap.builder();
- ExecutableElement buildMethod = null;
- for (ExecutableElement method : methods) {
- if (method.getParameters().isEmpty()) {
- buildMethod = method;
- } else {
- ExecutableType resolved =
- MoreTypes.asExecutable(types.asMemberOf(builderType.get(), method));
- map.put(MoreTypes.asTypeElement(getOnlyElement(resolved.getParameterTypes())), method);
- }
- }
- verify(buildMethod != null); // validation should have ensured this.
- return Optional.<BuilderSpec>of(new AutoValue_ComponentDescriptor_BuilderSpec(element,
- map.build(), buildMethod, element.getEnclosingElement().asType()));
- }
-
- /**
- * Returns a descriptor for a generated module that handles monitoring for production
- * components. This module is generated in the {@link MonitoringModuleProcessingStep}.
- *
- * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
- * processor to retry in a later processing round.
- */
- private ModuleDescriptor descriptorForMonitoringModule(TypeElement componentDefinitionType) {
- String generatedMonitorModuleName =
- SourceFiles.generatedMonitoringModuleName(componentDefinitionType).canonicalName();
- TypeElement monitoringModule = elements.getTypeElement(generatedMonitorModuleName);
- if (monitoringModule == null) {
- throw new TypeNotPresentException(generatedMonitorModuleName, null);
- }
- return moduleDescriptorFactory.create(monitoringModule);
- }
- }
-
- static boolean isComponentContributionMethod(Elements elements, ExecutableElement method) {
- return method.getParameters().isEmpty()
- && !method.getReturnType().getKind().equals(VOID)
- && !elements.getTypeElement(Object.class.getCanonicalName())
- .equals(method.getEnclosingElement());
- }
-
- static boolean isComponentProductionMethod(Elements elements, ExecutableElement method) {
- return isComponentContributionMethod(elements, method)
- && MoreTypes.isTypeOf(ListenableFuture.class, method.getReturnType());
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java b/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java
deleted file mode 100644
index 72e761c..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import dagger.Component;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.FieldWriter;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.TypeName;
-import javax.annotation.processing.Filer;
-import javax.lang.model.element.Element;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic;
-
-/**
- * Generates the implementation of the abstract types annotated with {@link Component}.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class ComponentGenerator extends SourceFileGenerator<BindingGraph> {
- private final Types types;
- private final Elements elements;
- private final Key.Factory keyFactory;
- private final Diagnostic.Kind nullableValidationType;
-
- ComponentGenerator(
- Filer filer,
- Elements elements,
- Types types,
- Key.Factory keyFactory,
- Diagnostic.Kind nullableValidationType) {
- super(filer);
- this.types = types;
- this.elements = elements;
- this.keyFactory = keyFactory;
- this.nullableValidationType = nullableValidationType;
- }
-
- @Override
- ClassName nameGeneratedType(BindingGraph input) {
- ClassName componentDefinitionClassName =
- ClassName.fromTypeElement(input.componentDescriptor().componentDefinitionType());
- String componentName = "Dagger" + componentDefinitionClassName.classFileName('_');
- return componentDefinitionClassName.topLevelClassName().peerNamed(componentName);
- }
-
- @Override
- Iterable<? extends Element> getOriginatingElements(BindingGraph input) {
- return ImmutableSet.of(input.componentDescriptor().componentDefinitionType());
- }
-
- @Override
- Optional<? extends Element> getElementForErrorReporting(BindingGraph input) {
- return Optional.of(input.componentDescriptor().componentDefinitionType());
- }
-
- @AutoValue static abstract class MemberSelect {
- static MemberSelect instanceSelect(ClassName owningClass, Snippet snippet) {
- return new AutoValue_ComponentGenerator_MemberSelect(
- Optional.<TypeName> absent(), owningClass, false, snippet);
- }
-
- static MemberSelect staticSelect(ClassName owningClass, Snippet snippet) {
- return new AutoValue_ComponentGenerator_MemberSelect(
- Optional.<TypeName> absent(), owningClass, true, snippet);
- }
-
- static MemberSelect staticMethodInvocationWithCast(
- ClassName owningClass, Snippet snippet, TypeName castType) {
- return new AutoValue_ComponentGenerator_MemberSelect(
- Optional.of(castType), owningClass, true, snippet);
- }
-
- /**
- * This exists only to facilitate edge cases in which we need to select a member, but that
- * member uses a type parameter that can't be inferred.
- */
- abstract Optional<TypeName> selectedCast();
- abstract ClassName owningClass();
- abstract boolean staticMember();
- abstract Snippet snippet();
-
- private Snippet qualifiedSelectSnippet() {
- return Snippet.format(
- "%s" + (staticMember() ? "" : ".this") + ".%s",
- owningClass(), snippet());
- }
-
- Snippet getSnippetWithRawTypeCastFor(ClassName usingClass) {
- Snippet snippet = getSnippetFor(usingClass);
- return selectedCast().isPresent()
- ? Snippet.format("(%s) %s", selectedCast().get(), snippet)
- : snippet;
- }
-
- Snippet getSnippetFor(ClassName usingClass) {
- return owningClass().equals(usingClass) ? snippet() : qualifiedSelectSnippet();
- }
- }
-
- @Override
- ImmutableSet<JavaWriter> write(ClassName componentName, BindingGraph input) {
- return new ComponentWriter(
- types, elements, keyFactory, nullableValidationType, componentName, input)
- .write();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentHierarchyValidator.java b/compiler/src/main/java/dagger/internal/codegen/ComponentHierarchyValidator.java
deleted file mode 100644
index 8fb4191..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentHierarchyValidator.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import java.util.Map;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-
-import static com.google.common.base.Functions.constant;
-
-/**
- * Validates the relationships between parent components and subcomponents.
- */
-final class ComponentHierarchyValidator {
- ValidationReport<TypeElement> validate(ComponentDescriptor componentDescriptor) {
- return validateSubcomponentMethods(
- componentDescriptor,
- Maps.toMap(
- componentDescriptor.transitiveModuleTypes(),
- constant(componentDescriptor.componentDefinitionType())));
- }
-
- private ValidationReport<TypeElement> validateSubcomponentMethods(
- ComponentDescriptor componentDescriptor,
- Map<TypeElement, TypeElement> existingModuleToOwners) {
- ValidationReport.Builder<TypeElement> reportBuilder =
- ValidationReport.about(componentDescriptor.componentDefinitionType());
- for (Map.Entry<ComponentMethodDescriptor, ComponentDescriptor> subcomponentEntry :
- componentDescriptor.subcomponents().entrySet()) {
- ComponentMethodDescriptor subcomponentMethodDescriptor = subcomponentEntry.getKey();
- ComponentDescriptor subcomponentDescriptor = subcomponentEntry.getValue();
- // validate the way that we create subcomponents
- switch (subcomponentMethodDescriptor.kind()) {
- case SUBCOMPONENT:
- for (VariableElement factoryMethodParameter :
- subcomponentMethodDescriptor.methodElement().getParameters()) {
- TypeElement origininatingComponent =
- existingModuleToOwners.get(
- MoreTypes.asTypeElement(factoryMethodParameter.asType()));
- if (origininatingComponent != null) {
- /* Factory method tries to pass a module that is already present in the parent.
- * This is an error. */
- reportBuilder.addError(
- String.format(
- "This module is present in %s. Subcomponents cannot use an instance of a "
- + "module that differs from its parent.",
- origininatingComponent.getQualifiedName()),
- factoryMethodParameter);
- }
- }
- break;
- case SUBCOMPONENT_BUILDER:
- BuilderSpec subcomponentBuilderSpec = subcomponentDescriptor.builderSpec().get();
- for (Map.Entry<TypeElement, ExecutableElement> builderMethodEntry :
- subcomponentBuilderSpec.methodMap().entrySet()) {
- TypeElement origininatingComponent =
- existingModuleToOwners.get(builderMethodEntry.getKey());
- if (origininatingComponent != null) {
- /* A subcomponent builder allows you to pass a module that is already present in the
- * parent. This can't be an error because it might be valid in _other_ components, so
- * we warn here. */
- ExecutableElement builderMethodElement = builderMethodEntry.getValue();
- /* TODO(gak): consider putting this on the builder method directly if it's in the
- * component being compiled */
- reportBuilder.addWarning(
- String.format(
- "This module is present in %s. Subcomponents cannot use an instance of a "
- + "module that differs from its parent. The implementation of %s "
- + "in this component will throw %s.",
- origininatingComponent.getQualifiedName(),
- builderMethodElement.getSimpleName(),
- UnsupportedOperationException.class.getSimpleName()),
- builderMethodElement);
- }
- }
- break;
- default:
- throw new AssertionError();
- }
- reportBuilder.addSubreport(
- validateSubcomponentMethods(
- subcomponentDescriptor,
- new ImmutableMap.Builder<TypeElement, TypeElement>()
- .putAll(existingModuleToOwners)
- .putAll(
- Maps.toMap(
- Sets.difference(
- subcomponentDescriptor.transitiveModuleTypes(),
- existingModuleToOwners.keySet()),
- constant(subcomponentDescriptor.componentDefinitionType())))
- .build()));
- }
- return reportBuilder.build();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/ComponentProcessingStep.java
deleted file mode 100644
index 39b21ca..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentProcessingStep.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.SetMultimap;
-import dagger.Component;
-import dagger.Subcomponent;
-import dagger.internal.codegen.ComponentDescriptor.Factory;
-import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
-import java.lang.annotation.Annotation;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/**
- * A {@link ProcessingStep} that is responsible for dealing with the {@link Component} annotation
- * as part of the {@link ComponentProcessor}.
- *
- * @author Gregory Kick
- */
-final class ComponentProcessingStep extends AbstractComponentProcessingStep {
- private final Messager messager;
- private final ComponentValidator componentValidator;
- private final ComponentValidator subcomponentValidator;
- private final BuilderValidator componentBuilderValidator;
- private final BuilderValidator subcomponentBuilderValidator;
-
- ComponentProcessingStep(
- Messager messager,
- ComponentValidator componentValidator,
- ComponentValidator subcomponentValidator,
- BuilderValidator componentBuilderValidator,
- BuilderValidator subcomponentBuilderValidator,
- ComponentHierarchyValidator componentHierarchyValidator,
- BindingGraphValidator bindingGraphValidator,
- Factory componentDescriptorFactory,
- BindingGraph.Factory bindingGraphFactory,
- ComponentGenerator componentGenerator) {
- super(
- Component.class,
- messager,
- componentHierarchyValidator,
- bindingGraphValidator,
- componentDescriptorFactory,
- bindingGraphFactory,
- componentGenerator);
- this.messager = messager;
- this.componentValidator = componentValidator;
- this.subcomponentValidator = subcomponentValidator;
- this.componentBuilderValidator = componentBuilderValidator;
- this.subcomponentBuilderValidator = subcomponentBuilderValidator;
- }
-
- @Override
- public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.<Class<? extends Annotation>>of(Component.class, Component.Builder.class,
- Subcomponent.class, Subcomponent.Builder.class);
- }
-
- @Override
- protected ComponentElementValidator componentElementValidator(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- final Map<Element, ValidationReport<TypeElement>> builderReportsByComponent =
- processComponentBuilders(elementsByAnnotation.get(Component.Builder.class));
- final Set<Element> subcomponentBuilderElements =
- elementsByAnnotation.get(Subcomponent.Builder.class);
- final Map<Element, ValidationReport<TypeElement>> builderReportsBySubcomponent =
- processSubcomponentBuilders(subcomponentBuilderElements);
- final Set<Element> subcomponentElements = elementsByAnnotation.get(Subcomponent.class);
- final Map<Element, ValidationReport<TypeElement>> reportsBySubcomponent =
- processSubcomponents(subcomponentElements, subcomponentBuilderElements);
- return new ComponentElementValidator() {
- @Override
- boolean validateComponent(TypeElement componentTypeElement, Messager messager) {
- ComponentValidationReport validationReport =
- componentValidator.validate(
- componentTypeElement, subcomponentElements, subcomponentBuilderElements);
- validationReport.report().printMessagesTo(messager);
- return isClean(
- validationReport,
- builderReportsByComponent,
- reportsBySubcomponent,
- builderReportsBySubcomponent);
- }
- };
- }
-
- private Map<Element, ValidationReport<TypeElement>> processComponentBuilders(
- Set<? extends Element> componentBuilderElements) {
- Map<Element, ValidationReport<TypeElement>> builderReportsByComponent = Maps.newHashMap();
- for (Element element : componentBuilderElements) {
- ValidationReport<TypeElement> report =
- componentBuilderValidator.validate(MoreElements.asType(element));
- report.printMessagesTo(messager);
- builderReportsByComponent.put(element.getEnclosingElement(), report);
- }
- return builderReportsByComponent;
- }
-
- private Map<Element, ValidationReport<TypeElement>> processSubcomponentBuilders(
- Set<? extends Element> subcomponentBuilderElements) {
- Map<Element, ValidationReport<TypeElement>> builderReportsBySubcomponent = Maps.newHashMap();
- for (Element element : subcomponentBuilderElements) {
- ValidationReport<TypeElement> report =
- subcomponentBuilderValidator.validate(MoreElements.asType(element));
- report.printMessagesTo(messager);
- builderReportsBySubcomponent.put(element, report);
- }
- return builderReportsBySubcomponent;
- }
-
- private Map<Element, ValidationReport<TypeElement>> processSubcomponents(
- Set<? extends Element> subcomponentElements,
- Set<? extends Element> subcomponentBuilderElements) {
- Map<Element, ValidationReport<TypeElement>> reportsBySubcomponent = Maps.newHashMap();
- for (Element element : subcomponentElements) {
- ComponentValidationReport report = subcomponentValidator.validate(
- MoreElements.asType(element), subcomponentElements, subcomponentBuilderElements);
- report.report().printMessagesTo(messager);
- reportsBySubcomponent.put(element, report.report());
- }
- return reportsBySubcomponent;
- }
-
- /**
- * Returns true if the component's report is clean, its builder report is clean, and all
- * referenced subcomponent reports & subcomponent builder reports are clean.
- */
- private boolean isClean(ComponentValidationReport report,
- Map<Element, ValidationReport<TypeElement>> builderReportsByComponent,
- Map<Element, ValidationReport<TypeElement>> reportsBySubcomponent,
- Map<Element, ValidationReport<TypeElement>> builderReportsBySubcomponent) {
- Element component = report.report().subject();
- ValidationReport<?> componentReport = report.report();
- if (!componentReport.isClean()) {
- return false;
- }
- ValidationReport<?> builderReport = builderReportsByComponent.get(component);
- if (builderReport != null && !builderReport.isClean()) {
- return false;
- }
- for (Element element : report.referencedSubcomponents()) {
- ValidationReport<?> subcomponentBuilderReport = builderReportsBySubcomponent.get(element);
- if (subcomponentBuilderReport != null && !subcomponentBuilderReport.isClean()) {
- return false;
- }
- ValidationReport<?> subcomponentReport = reportsBySubcomponent.get(element);
- if (subcomponentReport != null && !subcomponentReport.isClean()) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java b/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java
deleted file mode 100644
index c35058b..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor;
-import com.google.auto.service.AutoService;
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import dagger.Module;
-import dagger.Provides;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.lang.annotation.Annotation;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.Messager;
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.Processor;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic;
-
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-/**
- * The annotation processor responsible for generating the classes that drive the Dagger 2.0
- * implementation.
- *
- * TODO(gak): give this some better documentation
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@AutoService(Processor.class)
-public final class ComponentProcessor extends BasicAnnotationProcessor {
- private InjectBindingRegistry injectBindingRegistry;
- private FactoryGenerator factoryGenerator;
- private MembersInjectorGenerator membersInjectorGenerator;
-
- @Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
-
- @Override
- public Set<String> getSupportedOptions() {
- return ImmutableSet.of(
- DISABLE_INTER_COMPONENT_SCOPE_VALIDATION_KEY,
- NULLABLE_VALIDATION_KEY,
- PRIVATE_MEMBER_VALIDATION_TYPE_KEY,
- STATIC_MEMBER_VALIDATION_TYPE_KEY
- );
- }
-
- @Override
- protected Iterable<ProcessingStep> initSteps() {
- Messager messager = processingEnv.getMessager();
- Types types = processingEnv.getTypeUtils();
- Elements elements = processingEnv.getElementUtils();
- Filer filer = processingEnv.getFiler();
-
- Diagnostic.Kind nullableDiagnosticType =
- nullableValidationType(processingEnv).diagnosticKind().get();
-
- MethodSignatureFormatter methodSignatureFormatter = new MethodSignatureFormatter(types);
- ContributionBindingFormatter contributionBindingFormatter =
- new ContributionBindingFormatter(methodSignatureFormatter);
- DependencyRequestFormatter dependencyRequestFormatter = new DependencyRequestFormatter(types);
- KeyFormatter keyFormatter = new KeyFormatter();
-
- InjectConstructorValidator injectConstructorValidator = new InjectConstructorValidator();
- InjectFieldValidator injectFieldValidator = new InjectFieldValidator(
- privateMemberValidationType(processingEnv).diagnosticKind().get(),
- staticMemberValidationType(processingEnv).diagnosticKind().get());
- InjectMethodValidator injectMethodValidator = new InjectMethodValidator(
- privateMemberValidationType(processingEnv).diagnosticKind().get(),
- staticMemberValidationType(processingEnv).diagnosticKind().get());
- ModuleValidator moduleValidator =
- new ModuleValidator(
- types,
- elements,
- methodSignatureFormatter,
- Module.class,
- ImmutableList.<Class<? extends Annotation>>of(Module.class),
- Provides.class);
- ProvidesMethodValidator providesMethodValidator = new ProvidesMethodValidator(elements);
- BuilderValidator componentBuilderValidator =
- new BuilderValidator(elements, types, ComponentDescriptor.Kind.COMPONENT);
- BuilderValidator subcomponentBuilderValidator =
- new BuilderValidator(elements, types, ComponentDescriptor.Kind.SUBCOMPONENT);
- ComponentValidator subcomponentValidator = ComponentValidator.createForSubcomponent(elements,
- types, moduleValidator, subcomponentBuilderValidator);
- ComponentValidator componentValidator = ComponentValidator.createForComponent(elements, types,
- moduleValidator, subcomponentValidator, subcomponentBuilderValidator);
- MapKeyValidator mapKeyValidator = new MapKeyValidator();
- ModuleValidator producerModuleValidator =
- new ModuleValidator(
- types,
- elements,
- methodSignatureFormatter,
- ProducerModule.class,
- ImmutableList.of(Module.class, ProducerModule.class),
- Produces.class);
- ProducesMethodValidator producesMethodValidator = new ProducesMethodValidator(elements);
- ProductionComponentValidator productionComponentValidator = new ProductionComponentValidator();
- BuilderValidator productionComponentBuilderValidator =
- new BuilderValidator(elements, types, ComponentDescriptor.Kind.PRODUCTION_COMPONENT);
-
- Key.Factory keyFactory = new Key.Factory(types, elements);
-
- this.factoryGenerator =
- new FactoryGenerator(filer, DependencyRequestMapper.FOR_PROVIDER, nullableDiagnosticType);
- this.membersInjectorGenerator =
- new MembersInjectorGenerator(filer, DependencyRequestMapper.FOR_PROVIDER);
- ComponentGenerator componentGenerator =
- new ComponentGenerator(filer, elements, types, keyFactory, nullableDiagnosticType);
- ProducerFactoryGenerator producerFactoryGenerator =
- new ProducerFactoryGenerator(filer, DependencyRequestMapper.FOR_PRODUCER);
- MonitoringModuleGenerator monitoringModuleGenerator = new MonitoringModuleGenerator(filer);
-
- DependencyRequest.Factory dependencyRequestFactory =
- new DependencyRequest.Factory(elements, keyFactory);
- ProvisionBinding.Factory provisionBindingFactory =
- new ProvisionBinding.Factory(elements, types, keyFactory, dependencyRequestFactory);
- ProductionBinding.Factory productionBindingFactory =
- new ProductionBinding.Factory(types, keyFactory, dependencyRequestFactory);
-
- MembersInjectionBinding.Factory membersInjectionBindingFactory =
- new MembersInjectionBinding.Factory(elements, types, keyFactory, dependencyRequestFactory);
-
- this.injectBindingRegistry = new InjectBindingRegistry(
- elements, types, messager, provisionBindingFactory, membersInjectionBindingFactory);
-
- ModuleDescriptor.Factory moduleDescriptorFactory = new ModuleDescriptor.Factory(
- elements, provisionBindingFactory, productionBindingFactory);
-
- ComponentDescriptor.Factory componentDescriptorFactory = new ComponentDescriptor.Factory(
- elements, types, dependencyRequestFactory, moduleDescriptorFactory);
-
- BindingGraph.Factory bindingGraphFactory =
- new BindingGraph.Factory(
- elements,
- injectBindingRegistry,
- keyFactory,
- provisionBindingFactory,
- productionBindingFactory);
-
- MapKeyGenerator mapKeyGenerator = new MapKeyGenerator(filer);
- ComponentHierarchyValidator componentHierarchyValidator = new ComponentHierarchyValidator();
- BindingGraphValidator bindingGraphValidator =
- new BindingGraphValidator(
- types,
- injectBindingRegistry,
- scopeValidationType(processingEnv),
- nullableDiagnosticType,
- contributionBindingFormatter,
- methodSignatureFormatter,
- dependencyRequestFormatter,
- keyFormatter);
-
- return ImmutableList.<ProcessingStep>of(
- new MapKeyProcessingStep(messager, types, mapKeyValidator, mapKeyGenerator),
- new InjectProcessingStep(
- messager,
- injectConstructorValidator,
- injectFieldValidator,
- injectMethodValidator,
- provisionBindingFactory,
- membersInjectionBindingFactory,
- injectBindingRegistry),
- new MonitoringModuleProcessingStep(messager, monitoringModuleGenerator),
- new ModuleProcessingStep(
- messager,
- moduleValidator,
- providesMethodValidator,
- provisionBindingFactory,
- factoryGenerator),
- new ComponentProcessingStep(
- messager,
- componentValidator,
- subcomponentValidator,
- componentBuilderValidator,
- subcomponentBuilderValidator,
- componentHierarchyValidator,
- bindingGraphValidator,
- componentDescriptorFactory,
- bindingGraphFactory,
- componentGenerator),
- new ProducerModuleProcessingStep(
- messager,
- producerModuleValidator,
- producesMethodValidator,
- productionBindingFactory,
- producerFactoryGenerator),
- new ProductionComponentProcessingStep(
- messager,
- productionComponentValidator,
- productionComponentBuilderValidator,
- componentHierarchyValidator,
- bindingGraphValidator,
- componentDescriptorFactory,
- bindingGraphFactory,
- componentGenerator));
- }
-
- @Override
- protected void postProcess() {
- try {
- injectBindingRegistry.generateSourcesForRequiredBindings(
- factoryGenerator, membersInjectorGenerator);
- } catch (SourceFileGenerationException e) {
- e.printMessageTo(processingEnv.getMessager());
- }
- }
-
- private static final String DISABLE_INTER_COMPONENT_SCOPE_VALIDATION_KEY =
- "dagger.disableInterComponentScopeValidation";
-
- private static final String NULLABLE_VALIDATION_KEY = "dagger.nullableValidation";
-
- private static final String PRIVATE_MEMBER_VALIDATION_TYPE_KEY =
- "dagger.privateMemberValidation";
-
- private static final String STATIC_MEMBER_VALIDATION_TYPE_KEY =
- "dagger.staticMemberValidation";
-
- private static ValidationType scopeValidationType(ProcessingEnvironment processingEnv) {
- return valueOf(processingEnv,
- DISABLE_INTER_COMPONENT_SCOPE_VALIDATION_KEY,
- ValidationType.ERROR,
- EnumSet.allOf(ValidationType.class));
- }
-
- private static ValidationType nullableValidationType(ProcessingEnvironment processingEnv) {
- return valueOf(processingEnv,
- NULLABLE_VALIDATION_KEY,
- ValidationType.ERROR,
- EnumSet.of(ValidationType.ERROR, ValidationType.WARNING));
- }
-
- private static ValidationType privateMemberValidationType(ProcessingEnvironment processingEnv) {
- return valueOf(processingEnv,
- PRIVATE_MEMBER_VALIDATION_TYPE_KEY,
- ValidationType.ERROR,
- EnumSet.of(ValidationType.ERROR, ValidationType.WARNING));
- }
-
- private static ValidationType staticMemberValidationType(ProcessingEnvironment processingEnv) {
- return valueOf(processingEnv,
- STATIC_MEMBER_VALIDATION_TYPE_KEY,
- ValidationType.ERROR,
- EnumSet.of(ValidationType.ERROR, ValidationType.WARNING));
- }
-
- private static <T extends Enum<T>> T valueOf(ProcessingEnvironment processingEnv, String key,
- T defaultValue, Set<T> validValues) {
- Map<String, String> options = processingEnv.getOptions();
- if (options.containsKey(key)) {
- try {
- T type = Enum.valueOf(
- defaultValue.getDeclaringClass(),
- Ascii.toUpperCase(options.get(key)));
- if (!validValues.contains(type)) {
- throw new IllegalArgumentException(); // let handler below print out good msg.
- }
- return type;
- } catch (IllegalArgumentException e) {
- processingEnv.getMessager().printMessage(ERROR, "Processor option -A"
- + key + " may only have the values " + validValues
- + " (case insensitive), found: " + options.get(key));
- }
- }
- return defaultValue;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java b/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java
deleted file mode 100644
index a9e82a8..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import dagger.Component;
-import dagger.Module;
-import dagger.Subcomponent;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static dagger.internal.codegen.ConfigurationAnnotations.enclosedBuilders;
-import static dagger.internal.codegen.ConfigurationAnnotations.getComponentModules;
-import static dagger.internal.codegen.ConfigurationAnnotations.getTransitiveModules;
-import static javax.lang.model.element.ElementKind.CLASS;
-import static javax.lang.model.element.ElementKind.INTERFACE;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.type.TypeKind.VOID;
-
-/**
- * Performs superficial validation of the contract of the {@link Component} annotation.
- *
- * @author Gregory Kick
- */
-final class ComponentValidator {
- private final Elements elements;
- private final Types types;
- private final ComponentDescriptor.Kind componentType;
- private final ModuleValidator moduleValidator;
- private final ComponentValidator subcomponentValidator;
- private final BuilderValidator subcomponentBuilderValidator;
-
- private ComponentValidator(Elements elements,
- Types types,
- ModuleValidator moduleValidator,
- BuilderValidator subcomponentBuilderValidator) {
- this.elements = elements;
- this.types = types;
- this.componentType = ComponentDescriptor.Kind.SUBCOMPONENT;
- this.moduleValidator = moduleValidator;
- this.subcomponentValidator = this;
- this.subcomponentBuilderValidator = subcomponentBuilderValidator;
- }
-
- private ComponentValidator(Elements elements,
- Types types,
- ModuleValidator moduleValidator,
- ComponentValidator subcomponentValidator,
- BuilderValidator subcomponentBuilderValidator) {
- this.elements = elements;
- this.types = types;
- this.componentType = ComponentDescriptor.Kind.COMPONENT;
- this.moduleValidator = moduleValidator;
- this.subcomponentValidator = subcomponentValidator;
- this.subcomponentBuilderValidator = subcomponentBuilderValidator;
- }
-
- static ComponentValidator createForComponent(Elements elements,
- Types types,
- ModuleValidator moduleValidator,
- ComponentValidator subcomponentValidator,
- BuilderValidator subcomponentBuilderValidator) {
- return new ComponentValidator(elements,
- types,
- moduleValidator,
- subcomponentValidator,
- subcomponentBuilderValidator);
- }
-
- static ComponentValidator createForSubcomponent(Elements elements,
- Types types,
- ModuleValidator moduleValidator,
- BuilderValidator subcomponentBuilderValidator) {
- return new ComponentValidator(elements,
- types,
- moduleValidator,
- subcomponentBuilderValidator);
- }
-
- @AutoValue
- static abstract class ComponentValidationReport {
- abstract Set<Element> referencedSubcomponents();
- abstract ValidationReport<TypeElement> report();
- }
-
- /**
- * Validates the given component subject. Also validates any referenced subcomponents that aren't
- * already included in the {@code validatedSubcomponents} set.
- */
- public ComponentValidationReport validate(final TypeElement subject,
- Set<? extends Element> validatedSubcomponents,
- Set<? extends Element> validatedSubcomponentBuilders) {
- ValidationReport.Builder<TypeElement> builder = ValidationReport.about(subject);
-
- if (!subject.getKind().equals(INTERFACE)
- && !(subject.getKind().equals(CLASS) && subject.getModifiers().contains(ABSTRACT))) {
- builder.addError(
- String.format(
- "@%s may only be applied to an interface or abstract class",
- componentType.annotationType().getSimpleName()),
- subject);
- }
-
- ImmutableList<DeclaredType> builders =
- enclosedBuilders(subject, componentType.builderAnnotationType());
- if (builders.size() > 1) {
- builder.addError(
- String.format(ErrorMessages.builderMsgsFor(componentType).moreThanOne(), builders),
- subject);
- }
-
- DeclaredType subjectType = MoreTypes.asDeclared(subject.asType());
-
- // TODO(gak): This should use Util.findLocalAndInheritedMethods, otherwise
- // it can return a logical method multiple times (including overrides, etc.)
- List<? extends Element> members = elements.getAllMembers(subject);
- Multimap<Element, ExecutableElement> referencedSubcomponents = LinkedHashMultimap.create();
- for (ExecutableElement method : ElementFilter.methodsIn(members)) {
- if (method.getModifiers().contains(ABSTRACT)) {
- ExecutableType resolvedMethod =
- MoreTypes.asExecutable(types.asMemberOf(subjectType, method));
- List<? extends TypeMirror> parameterTypes = resolvedMethod.getParameterTypes();
- List<? extends VariableElement> parameters = method.getParameters();
- TypeMirror returnType = resolvedMethod.getReturnType();
-
- // abstract methods are ones we have to implement, so they each need to be validated
- // first, check the return type. if it's a subcomponent, validate that method as such.
- Optional<AnnotationMirror> subcomponentAnnotation =
- checkForAnnotation(returnType, Subcomponent.class);
- Optional<AnnotationMirror> subcomponentBuilderAnnotation =
- checkForAnnotation(returnType, Subcomponent.Builder.class);
- if (subcomponentAnnotation.isPresent()) {
- referencedSubcomponents.put(MoreTypes.asElement(returnType), method);
- validateSubcomponentMethod(builder,
- method,
- parameters,
- parameterTypes,
- returnType,
- subcomponentAnnotation);
- } else if (subcomponentBuilderAnnotation.isPresent()) {
- referencedSubcomponents.put(MoreTypes.asElement(returnType).getEnclosingElement(),
- method);
- validateSubcomponentBuilderMethod(builder,
- method,
- parameters,
- returnType,
- validatedSubcomponentBuilders);
- } else {
- // if it's not a subcomponent...
- switch (parameters.size()) {
- case 0:
- // no parameters means that it is a provision method
- // basically, there are no restrictions here. \o/
- break;
- case 1:
- // one parameter means that it's a members injection method
- TypeMirror onlyParameter = Iterables.getOnlyElement(parameterTypes);
- if (!(returnType.getKind().equals(VOID)
- || types.isSameType(returnType, onlyParameter))) {
- builder.addError(
- "Members injection methods may only return the injected type or void.", method);
- }
- break;
- default:
- // this isn't any method that we know how to implement...
- builder.addError(
- "This method isn't a valid provision method, members injection method or "
- + "subcomponent factory method. Dagger cannot implement this method",
- method);
- break;
- }
- }
- }
- }
-
- for (Map.Entry<Element, Collection<ExecutableElement>> entry :
- referencedSubcomponents.asMap().entrySet()) {
- if (entry.getValue().size() > 1) {
- builder.addError(
- String.format(
- ErrorMessages.SubcomponentBuilderMessages.INSTANCE.moreThanOneRefToSubcomponent(),
- entry.getKey(),
- entry.getValue()),
- subject);
- }
- }
-
- AnnotationMirror componentMirror =
- getAnnotationMirror(subject, componentType.annotationType()).get();
- ImmutableList<TypeMirror> moduleTypes = getComponentModules(componentMirror);
- moduleValidator.validateReferencedModules(subject, builder, moduleTypes);
-
- // Make sure we validate any subcomponents we're referencing, unless we know we validated
- // them already in this pass.
- // TODO(sameb): If subcomponents refer to each other and both aren't in
- // 'validatedSubcomponents' (e.g, both aren't compiled in this pass),
- // then this can loop forever.
- ImmutableSet.Builder<Element> allSubcomponents =
- ImmutableSet.<Element>builder().addAll(referencedSubcomponents.keySet());
- for (Element subcomponent :
- Sets.difference(referencedSubcomponents.keySet(), validatedSubcomponents)) {
- ComponentValidationReport subreport = subcomponentValidator.validate(
- MoreElements.asType(subcomponent), validatedSubcomponents, validatedSubcomponentBuilders);
- builder.addItems(subreport.report().items());
- allSubcomponents.addAll(subreport.referencedSubcomponents());
- }
-
- return new AutoValue_ComponentValidator_ComponentValidationReport(allSubcomponents.build(),
- builder.build());
- }
-
- private void validateSubcomponentMethod(final ValidationReport.Builder<TypeElement> builder,
- ExecutableElement method,
- List<? extends VariableElement> parameters,
- List<? extends TypeMirror> parameterTypes,
- TypeMirror returnType,
- Optional<AnnotationMirror> subcomponentAnnotation) {
- ImmutableSet<TypeElement> moduleTypes =
- MoreTypes.asTypeElements(getComponentModules(subcomponentAnnotation.get()));
-
- // TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse
- // subcomponents and their modules separately from how it is done in ComponentDescriptor and
- // ModuleDescriptor
- @SuppressWarnings("deprecation")
- ImmutableSet<TypeElement> transitiveModules =
- getTransitiveModules(types, elements, moduleTypes);
-
- Set<TypeElement> variableTypes = Sets.newHashSet();
-
- for (int i = 0; i < parameterTypes.size(); i++) {
- VariableElement parameter = parameters.get(i);
- TypeMirror parameterType = parameterTypes.get(i);
- Optional<TypeElement> moduleType = parameterType.accept(
- new SimpleTypeVisitor6<Optional<TypeElement>, Void>() {
- @Override protected Optional<TypeElement> defaultAction(TypeMirror e, Void p) {
- return Optional.absent();
- }
-
- @Override public Optional<TypeElement> visitDeclared(DeclaredType t, Void p) {
- return MoreElements.isAnnotationPresent(t.asElement(), Module.class)
- ? Optional.of(MoreTypes.asTypeElement(t))
- : Optional.<TypeElement>absent();
- }
- }, null);
- if (moduleType.isPresent()) {
- if (variableTypes.contains(moduleType.get())) {
- builder.addError(
- String.format(
- "A module may only occur once an an argument in a Subcomponent factory "
- + "method, but %s was already passed.",
- moduleType.get().getQualifiedName()),
- parameter);
- }
- if (!transitiveModules.contains(moduleType.get())) {
- builder.addError(
- String.format(
- "%s is present as an argument to the %s factory method, but is not one of the"
- + " modules used to implement the subcomponent.",
- moduleType.get().getQualifiedName(),
- MoreTypes.asTypeElement(returnType).getQualifiedName()),
- method);
- }
- variableTypes.add(moduleType.get());
- } else {
- builder.addError(
- String.format(
- "Subcomponent factory methods may only accept modules, but %s is not.",
- parameterType),
- parameter);
- }
- }
- }
-
- private void validateSubcomponentBuilderMethod(ValidationReport.Builder<TypeElement> builder,
- ExecutableElement method, List<? extends VariableElement> parameters, TypeMirror returnType,
- Set<? extends Element> validatedSubcomponentBuilders) {
-
- if (!parameters.isEmpty()) {
- builder.addError(
- ErrorMessages.SubcomponentBuilderMessages.INSTANCE.builderMethodRequiresNoArgs(), method);
- }
-
- // If we haven't already validated the subcomponent builder itself, validate it now.
- TypeElement builderElement = MoreTypes.asTypeElement(returnType);
- if (!validatedSubcomponentBuilders.contains(builderElement)) {
- // TODO(sameb): The builder validator right now assumes the element is being compiled
- // in this pass, which isn't true here. We should change error messages to spit out
- // this method as the subject and add the original subject to the message output.
- builder.addItems(subcomponentBuilderValidator.validate(builderElement).items());
- }
- }
-
- private Optional<AnnotationMirror> checkForAnnotation(TypeMirror type,
- final Class<? extends Annotation> annotation) {
- return type.accept(new SimpleTypeVisitor6<Optional<AnnotationMirror>, Void>() {
- @Override
- protected Optional<AnnotationMirror> defaultAction(TypeMirror e, Void p) {
- return Optional.absent();
- }
-
- @Override
- public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void p) {
- return MoreElements.getAnnotationMirror(t.asElement(), annotation);
- }
- }, null);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentWriter.java b/compiler/src/main/java/dagger/internal/codegen/ComponentWriter.java
deleted file mode 100644
index 5221cf0..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentWriter.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import javax.annotation.Generated;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic.Kind;
-
-import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * Creates the implementation class for a component.
- */
-class ComponentWriter extends AbstractComponentWriter {
-
- ComponentWriter(
- Types types,
- Elements elements,
- Key.Factory keyFactory,
- Kind nullableValidationType,
- ClassName name,
- BindingGraph graph) {
- super(types, elements, keyFactory, nullableValidationType, name, graph);
- }
-
- @Override
- protected ClassWriter createComponentClass() {
- JavaWriter javaWriter = JavaWriter.inPackage(name.packageName());
- javaWriters.add(javaWriter);
-
- ClassWriter componentWriter = javaWriter.addClass(name.simpleName());
- componentWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getCanonicalName());
- componentWriter.addModifiers(PUBLIC, FINAL);
- componentWriter.setSupertype(componentDefinitionType());
- return componentWriter;
- }
-
- @Override
- protected ClassWriter createBuilder() {
- ClassWriter builderWriter = componentWriter.addNestedClass("Builder");
- builderWriter.addModifiers(STATIC);
-
- // Only top-level components have the factory builder() method.
- // Mirror the user's builder API type if they had one.
- MethodWriter builderFactoryMethod =
- graph.componentDescriptor().builderSpec().isPresent()
- ? componentWriter.addMethod(
- graph
- .componentDescriptor()
- .builderSpec()
- .get()
- .builderDefinitionType()
- .asType(),
- "builder")
- : componentWriter.addMethod(builderWriter, "builder");
- builderFactoryMethod.addModifiers(PUBLIC, STATIC);
- builderFactoryMethod.body().addSnippet("return new %s();", builderWriter.name());
- return builderWriter;
- }
-
- @Override
- protected void addFactoryMethods() {
- if (canInstantiateAllRequirements()) {
- MethodWriter factoryMethod =
- componentWriter.addMethod(componentDefinitionTypeName(), "create");
- factoryMethod.addModifiers(PUBLIC, STATIC);
- // TODO(gak): replace this with something that doesn't allocate a builder
- factoryMethod
- .body()
- .addSnippet(
- "return builder().%s();",
- graph.componentDescriptor().builderSpec().isPresent()
- ? graph
- .componentDescriptor()
- .builderSpec()
- .get()
- .buildMethod()
- .getSimpleName()
- : "build");
- }
- }
-
- /** {@code true} if all of the graph's required dependencies can be automatically constructed. */
- private boolean canInstantiateAllRequirements() {
- return Iterables.all(
- graph.componentRequirements(),
- new Predicate<TypeElement>() {
- @Override
- public boolean apply(TypeElement dependency) {
- return componentCanMakeNewInstances(dependency);
- }
- });
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java b/compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java
deleted file mode 100644
index 50e3435..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import dagger.Component;
-import dagger.Module;
-import dagger.Subcomponent;
-import dagger.producers.ProducerModule;
-import dagger.producers.ProductionComponent;
-import java.lang.annotation.Annotation;
-import java.util.ArrayDeque;
-import java.util.List;
-import java.util.Queue;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.AnnotationValueVisitor;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.SimpleAnnotationValueVisitor6;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Utility methods related to dagger configuration annotations (e.g.: {@link Component}
- * and {@link Module}).
- *
- * @author Gregory Kick
- */
-final class ConfigurationAnnotations {
-
- static boolean isComponent(TypeElement componentDefinitionType) {
- return MoreElements.isAnnotationPresent(componentDefinitionType, Component.class)
- || MoreElements.isAnnotationPresent(componentDefinitionType, ProductionComponent.class);
- }
-
- private static final String MODULES_ATTRIBUTE = "modules";
-
- static ImmutableList<TypeMirror> getComponentModules(AnnotationMirror componentAnnotation) {
- checkNotNull(componentAnnotation);
- return convertClassArrayToListOfTypes(componentAnnotation, MODULES_ATTRIBUTE);
- }
-
- private static final String DEPENDENCIES_ATTRIBUTE = "dependencies";
-
- static ImmutableList<TypeMirror> getComponentDependencies(AnnotationMirror componentAnnotation) {
- checkNotNull(componentAnnotation);
- return convertClassArrayToListOfTypes(componentAnnotation, DEPENDENCIES_ATTRIBUTE);
- }
-
- private static final String INCLUDES_ATTRIBUTE = "includes";
-
- static ImmutableList<TypeMirror> getModuleIncludes(AnnotationMirror moduleAnnotation) {
- checkNotNull(moduleAnnotation);
- return convertClassArrayToListOfTypes(moduleAnnotation, INCLUDES_ATTRIBUTE);
- }
-
- private static final String INJECTS_ATTRIBUTE = "injects";
-
- static ImmutableList<TypeMirror> getModuleInjects(AnnotationMirror moduleAnnotation) {
- checkNotNull(moduleAnnotation);
- return convertClassArrayToListOfTypes(moduleAnnotation, INJECTS_ATTRIBUTE);
- }
-
- /** Returns the first type that specifies this' nullability, or absent if none. */
- static Optional<DeclaredType> getNullableType(Element element) {
- List<? extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
- for (AnnotationMirror mirror : mirrors) {
- if (mirror.getAnnotationType().asElement().getSimpleName().toString().equals("Nullable")) {
- return Optional.of(mirror.getAnnotationType());
- }
- }
- return Optional.absent();
- }
-
- /**
- * Extracts the list of types that is the value of the annotation member {@code elementName} of
- * {@code annotationMirror}.
- *
- * @throws IllegalArgumentException if no such member exists on {@code annotationMirror}, or it
- * exists but is not an array
- * @throws TypeNotPresentException if any of the values cannot be converted to a type
- */
- static ImmutableList<TypeMirror> convertClassArrayToListOfTypes(
- AnnotationMirror annotationMirror, String elementName) {
- return TO_LIST_OF_TYPES.visit(getAnnotationValue(annotationMirror, elementName), elementName);
- }
-
- private static final AnnotationValueVisitor<ImmutableList<TypeMirror>, String> TO_LIST_OF_TYPES =
- new SimpleAnnotationValueVisitor6<ImmutableList<TypeMirror>, String>() {
- @Override
- public ImmutableList<TypeMirror> visitArray(
- List<? extends AnnotationValue> vals, String elementName) {
- return FluentIterable.from(vals)
- .transform(
- new Function<AnnotationValue, TypeMirror>() {
- @Override
- public TypeMirror apply(AnnotationValue typeValue) {
- return TO_TYPE.visit(typeValue);
- }
- })
- .toList();
- }
-
- @Override
- protected ImmutableList<TypeMirror> defaultAction(Object o, String elementName) {
- throw new IllegalArgumentException(elementName + " is not an array: " + o);
- }
- };
-
- private static final AnnotationValueVisitor<TypeMirror, Void> TO_TYPE =
- new SimpleAnnotationValueVisitor6<TypeMirror, Void>() {
- @Override
- public TypeMirror visitType(TypeMirror t, Void p) {
- return t;
- }
-
- @Override
- protected TypeMirror defaultAction(Object o, Void p) {
- throw new TypeNotPresentException(o.toString(), null);
- }
- };
-
- /**
- * Returns the full set of modules transitively {@linkplain Module#includes included} from the
- * given seed modules. If a module is malformed and a type listed in {@link Module#includes}
- * is not annotated with {@link Module}, it is ignored.
- *
- * @deprecated Use {@link ComponentDescriptor#transitiveModules}.
- */
- @Deprecated
- static ImmutableSet<TypeElement> getTransitiveModules(
- Types types, Elements elements, Iterable<TypeElement> seedModules) {
- TypeMirror objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType();
- Queue<TypeElement> moduleQueue = new ArrayDeque<>();
- Iterables.addAll(moduleQueue, seedModules);
- Set<TypeElement> moduleElements = Sets.newLinkedHashSet();
- for (TypeElement moduleElement = moduleQueue.poll();
- moduleElement != null;
- moduleElement = moduleQueue.poll()) {
- Optional<AnnotationMirror> moduleMirror = getAnnotationMirror(moduleElement, Module.class)
- .or(getAnnotationMirror(moduleElement, ProducerModule.class));
- if (moduleMirror.isPresent()) {
- ImmutableSet.Builder<TypeElement> moduleDependenciesBuilder = ImmutableSet.builder();
- moduleDependenciesBuilder.addAll(
- MoreTypes.asTypeElements(getModuleIncludes(moduleMirror.get())));
- // (note: we don't recurse on the parent class because we don't want the parent class as a
- // root that the component depends on, and also because we want the dependencies rooted
- // against this element, not the parent.)
- addIncludesFromSuperclasses(types, moduleElement, moduleDependenciesBuilder, objectType);
- ImmutableSet<TypeElement> moduleDependencies = moduleDependenciesBuilder.build();
- moduleElements.add(moduleElement);
- for (TypeElement dependencyType : moduleDependencies) {
- if (!moduleElements.contains(dependencyType)) {
- moduleQueue.add(dependencyType);
- }
- }
- }
- }
- return ImmutableSet.copyOf(moduleElements);
- }
-
- /** Returns the enclosed elements annotated with the given annotation type. */
- static ImmutableList<DeclaredType> enclosedBuilders(TypeElement typeElement,
- final Class<? extends Annotation> annotation) {
- final ImmutableList.Builder<DeclaredType> builders = ImmutableList.builder();
- for (TypeElement element : ElementFilter.typesIn(typeElement.getEnclosedElements())) {
- if (MoreElements.isAnnotationPresent(element, annotation)) {
- builders.add(MoreTypes.asDeclared(element.asType()));
- }
- }
- return builders.build();
- }
-
- static boolean isSubcomponentType(TypeMirror type) {
- return type.accept(new SubcomponentDetector(), null).isPresent();
- }
-
- private static final class SubcomponentDetector
- extends SimpleTypeVisitor6<Optional<AnnotationMirror>, Void> {
- @Override
- protected Optional<AnnotationMirror> defaultAction(TypeMirror e, Void p) {
- return Optional.absent();
- }
-
- @Override
- public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void p) {
- return MoreElements.getAnnotationMirror(t.asElement(), Subcomponent.class);
- }
- }
-
- /** Traverses includes from superclasses and adds them into the builder. */
- private static void addIncludesFromSuperclasses(Types types, TypeElement element,
- ImmutableSet.Builder<TypeElement> builder, TypeMirror objectType) {
- // Also add the superclass to the queue, in case any @Module definitions were on that.
- TypeMirror superclass = element.getSuperclass();
- while (!types.isSameType(objectType, superclass)
- && superclass.getKind().equals(TypeKind.DECLARED)) {
- element = MoreElements.asType(types.asElement(superclass));
- Optional<AnnotationMirror> moduleMirror = getAnnotationMirror(element, Module.class)
- .or(getAnnotationMirror(element, ProducerModule.class));
- if (moduleMirror.isPresent()) {
- builder.addAll(MoreTypes.asTypeElements(getModuleIncludes(moduleMirror.get())));
- }
- superclass = element.getSuperclass();
- }
- }
-
- private ConfigurationAnnotations() {}
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java b/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java
deleted file mode 100644
index 831943f..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Equivalence;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Component;
-import dagger.MapKey;
-import dagger.Provides;
-import dagger.producers.Produces;
-import dagger.producers.ProductionComponent;
-import java.util.EnumSet;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.MapKeys.getMapKey;
-import static dagger.internal.codegen.MapKeys.unwrapValue;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * An abstract class for a value object representing the mechanism by which a {@link Key} can be
- * contributed to a dependency graph.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-abstract class ContributionBinding extends Binding {
-
- @Override
- Set<DependencyRequest> implicitDependencies() {
- // Optimization: If we don't need the memberInjectionRequest, don't create more objects.
- if (!membersInjectionRequest().isPresent()) {
- return dependencies();
- } else {
- // Optimization: Avoid creating an ImmutableSet+Builder just to union two things together.
- return Sets.union(membersInjectionRequest().asSet(), dependencies());
- }
- }
-
- static enum ContributionType {
- /** Represents map bindings. */
- MAP,
- /** Represents set bindings. */
- SET,
- /** Represents a valid non-collection binding. */
- UNIQUE;
-
- boolean isMultibinding() {
- return !this.equals(UNIQUE);
- }
- }
-
- ContributionType contributionType() {
- switch (provisionType()) {
- case SET:
- case SET_VALUES:
- return ContributionType.SET;
- case MAP:
- return ContributionType.MAP;
- case UNIQUE:
- return ContributionType.UNIQUE;
- default:
- throw new AssertionError("Unknown provision type: " + provisionType());
- }
- }
-
- /** Returns the type that specifies this' nullability, absent if not nullable. */
- abstract Optional<DeclaredType> nullableType();
-
- /**
- * If this is a provision request from an {@code @Provides} or {@code @Produces} method, this will
- * be the element that contributed it. In the case of subclassed modules, this may differ than the
- * binding's enclosed element, as this will return the subclass whereas the enclosed element will
- * be the superclass.
- */
- abstract Optional<TypeElement> contributedBy();
-
- /**
- * Returns whether this binding is synthetic, i.e., not explicitly tied to code, but generated
- * implicitly by the framework.
- */
- boolean isSyntheticBinding() {
- return bindingKind().equals(Kind.SYNTHETIC);
- }
-
- /** If this provision requires members injection, this will be the corresponding request. */
- abstract Optional<DependencyRequest> membersInjectionRequest();
-
- /**
- * The kind of contribution this binding represents. Defines which elements can specify this kind
- * of contribution.
- */
- enum Kind {
- /**
- * A binding that is not explicitly tied to an element, but generated implicitly by the
- * framework.
- */
- SYNTHETIC,
-
- // Provision kinds
-
- /** An {@link Inject}-annotated constructor. */
- INJECTION,
-
- /** A {@link Provides}-annotated method. */
- PROVISION,
-
- /** An implicit binding to a {@link Component @Component}-annotated type. */
- COMPONENT,
-
- /** A provision method on a component's {@linkplain Component#dependencies() dependency}. */
- COMPONENT_PROVISION,
-
- /**
- * A subcomponent builder method on a component or subcomponent.
- */
- SUBCOMPONENT_BUILDER,
-
- // Production kinds
-
- /** A {@link Produces}-annotated method that doesn't return a {@link ListenableFuture}. */
- IMMEDIATE,
-
- /** A {@link Produces}-annotated method that returns a {@link ListenableFuture}. */
- FUTURE_PRODUCTION,
-
- /**
- * A production method on a production component's
- * {@linkplain ProductionComponent#dependencies() dependency} that returns a
- * {@link ListenableFuture}. Methods on production component dependencies that don't return a
- * {@link ListenableFuture} are considered {@linkplain #PROVISION provision bindings}.
- */
- COMPONENT_PRODUCTION,
- }
-
- /**
- * The kind of this contribution binding.
- */
- protected abstract Kind bindingKind();
-
- /**
- * A predicate that passes for bindings of a given kind.
- */
- static Predicate<ContributionBinding> isOfKind(final Kind kind) {
- return new Predicate<ContributionBinding>() {
- @Override
- public boolean apply(ContributionBinding binding) {
- return binding.bindingKind().equals(kind);
- }};
- }
-
- /** The provision type that was used to bind the key. */
- abstract Provides.Type provisionType();
-
- /**
- * The strategy for getting an instance of a factory for a {@link ContributionBinding}.
- */
- enum FactoryCreationStrategy {
- /** The factory class is an enum with one value named {@code INSTANCE}. */
- ENUM_INSTANCE,
- /** The factory must be created by calling the constructor. */
- CLASS_CONSTRUCTOR,
- }
-
- /**
- * Returns {@link FactoryCreationStrategy#ENUM_INSTANCE} if the binding has no dependencies and
- * is a static provision binding or an {@link Inject @Inject} constructor binding. Otherwise
- * returns {@link FactoryCreationStrategy#CLASS_CONSTRUCTOR}.
- */
- FactoryCreationStrategy factoryCreationStrategy() {
- switch (bindingKind()) {
- case PROVISION:
- return implicitDependencies().isEmpty() && bindingElement().getModifiers().contains(STATIC)
- ? FactoryCreationStrategy.ENUM_INSTANCE
- : FactoryCreationStrategy.CLASS_CONSTRUCTOR;
-
- case INJECTION:
- return implicitDependencies().isEmpty()
- ? FactoryCreationStrategy.ENUM_INSTANCE
- : FactoryCreationStrategy.CLASS_CONSTRUCTOR;
-
- default:
- return FactoryCreationStrategy.CLASS_CONSTRUCTOR;
- }
- }
-
- /**
- * Returns the {@link ContributionType}s represented by a given {@link ContributionBinding}
- * collection.
- */
- static <B extends ContributionBinding>
- ImmutableListMultimap<ContributionType, B> contributionTypesFor(
- Iterable<? extends B> bindings) {
- ImmutableListMultimap.Builder<ContributionType, B> builder = ImmutableListMultimap.builder();
- builder.orderKeysBy(Ordering.<ContributionType>natural());
- for (B binding : bindings) {
- builder.put(binding.contributionType(), binding);
- }
- return builder.build();
- }
-
- /**
- * Returns a single {@link ContributionType} represented by a given collection of
- * {@link ContributionBinding}s.
- *
- * @throws IllegalArgumentException if the given bindings are not all of one type
- */
- static ContributionType contributionTypeFor(Iterable<ContributionBinding> bindings) {
- checkNotNull(bindings);
- checkArgument(!Iterables.isEmpty(bindings), "no bindings");
- Set<ContributionType> types = EnumSet.noneOf(ContributionType.class);
- for (ContributionBinding binding : bindings) {
- types.add(binding.contributionType());
- }
- if (types.size() > 1) {
- throw new IllegalArgumentException(
- String.format(ErrorMessages.MULTIPLE_CONTRIBUTION_TYPES_FORMAT, types));
- }
- return Iterables.getOnlyElement(types);
- }
-
- /**
- * Indexes map-multibindings by map key (the result of calling
- * {@link AnnotationValue#getValue()} on a single member or the whole {@link AnnotationMirror}
- * itself, depending on {@link MapKey#unwrapValue()}).
- */
- static ImmutableSetMultimap<Object, ContributionBinding> indexMapBindingsByMapKey(
- Set<ContributionBinding> mapBindings) {
- return ImmutableSetMultimap.copyOf(
- Multimaps.index(
- mapBindings,
- new Function<ContributionBinding, Object>() {
- @Override
- public Object apply(ContributionBinding mapBinding) {
- AnnotationMirror mapKey = getMapKey(mapBinding.bindingElement()).get();
- Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey);
- return unwrappedValue.isPresent() ? unwrappedValue.get().getValue() : mapKey;
- }
- }));
- }
-
- /**
- * Indexes map-multibindings by map key annotation type.
- */
- static ImmutableSetMultimap<Wrapper<DeclaredType>, ContributionBinding>
- indexMapBindingsByAnnotationType(Set<ContributionBinding> mapBindings) {
- return ImmutableSetMultimap.copyOf(
- Multimaps.index(
- mapBindings,
- new Function<ContributionBinding, Equivalence.Wrapper<DeclaredType>>() {
- @Override
- public Equivalence.Wrapper<DeclaredType> apply(ContributionBinding mapBinding) {
- return MoreTypes.equivalence()
- .wrap(getMapKey(mapBinding.bindingElement()).get().getAnnotationType());
- }
- }));
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ContributionBindingFormatter.java b/compiler/src/main/java/dagger/internal/codegen/ContributionBindingFormatter.java
deleted file mode 100644
index 0d26761..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ContributionBindingFormatter.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Optional;
-
-import static com.google.auto.common.MoreElements.asExecutable;
-import static com.google.auto.common.MoreTypes.asDeclared;
-
-/**
- * Formats a {@link ContributionBinding} into a {@link String} suitable for use in error messages.
- *
- * @author Christian Gruber
- * @since 2.0
- */
-final class ContributionBindingFormatter extends Formatter<ContributionBinding> {
- private final MethodSignatureFormatter methodSignatureFormatter;
-
- ContributionBindingFormatter(MethodSignatureFormatter methodSignatureFormatter) {
- this.methodSignatureFormatter = methodSignatureFormatter;
- }
-
- @Override public String format(ContributionBinding binding) {
- switch (binding.bindingKind()) {
- case COMPONENT_PROVISION:
- case COMPONENT_PRODUCTION:
- return methodSignatureFormatter.format(asExecutable(binding.bindingElement()));
-
- case PROVISION:
- case SUBCOMPONENT_BUILDER:
- case IMMEDIATE:
- case FUTURE_PRODUCTION:
- return methodSignatureFormatter.format(
- asExecutable(binding.bindingElement()),
- Optional.of(asDeclared(binding.contributedBy().get().asType())));
-
- default:
- throw new UnsupportedOperationException(
- "Not yet supporting " + binding.bindingKind() + " binding types.");
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java b/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java
deleted file mode 100644
index 49fcd9c..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Lazy;
-import dagger.MembersInjector;
-import dagger.Provides;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.internal.AbstractProducer;
-import java.util.List;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-
-import static com.google.auto.common.MoreTypes.isTypeOf;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.util.ElementFilter.constructorsIn;
-
-/**
- * Represents a request for a key at an injection point. Parameters to {@link Inject} constructors
- * or {@link Provides} methods are examples of key requests.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-// TODO(gak): Set bindings and the permutations thereof need to be addressed
-@AutoValue
-abstract class DependencyRequest {
- static final Function<DependencyRequest, BindingKey> BINDING_KEY_FUNCTION =
- new Function<DependencyRequest, BindingKey>() {
- @Override public BindingKey apply(DependencyRequest request) {
- return request.bindingKey();
- }
- };
-
- enum Kind {
- /** A default request for an instance. E.g.: {@code Blah} */
- INSTANCE,
- /** A request for a {@link Provider}. E.g.: {@code Provider<Blah>} */
- PROVIDER,
- /** A request for a {@link Lazy}. E.g.: {@code Lazy<Blah>} */
- LAZY,
- /** A request for a {@link MembersInjector}. E.g.: {@code MembersInjector<Blah>} */
- MEMBERS_INJECTOR,
- /** A request for a {@link Producer}. E.g.: {@code Producer<Blah>} */
- PRODUCER,
- /** A request for a {@link Produced}. E.g.: {@code Produced<Blah>} */
- PRODUCED,
- /**
- * A request for a {@link ListenableFuture}. E.g.: {@code ListenableFuture<Blah>}.
- * These can only be requested by component interfaces.
- */
- FUTURE,
- }
-
- abstract Kind kind();
- abstract Key key();
-
- BindingKey bindingKey() {
- switch (kind()) {
- case INSTANCE:
- case LAZY:
- case PROVIDER:
- case PRODUCER:
- case PRODUCED:
- case FUTURE:
- return BindingKey.create(BindingKey.Kind.CONTRIBUTION, key());
- case MEMBERS_INJECTOR:
- return BindingKey.create(BindingKey.Kind.MEMBERS_INJECTION, key());
- default:
- throw new AssertionError();
- }
- }
-
- abstract Element requestElement();
-
- /**
- * Returns the possibly resolved type that contained the requesting element. For members injection
- * requests, this is the type itself.
- */
- abstract DeclaredType enclosingType();
-
- /** Returns true if this request allows null objects. */
- abstract boolean isNullable();
-
- /**
- * An optional name for this request when it's referred to in generated code. If absent, it will
- * use a name derived from {@link #requestElement}.
- */
- abstract Optional<String> overriddenVariableName();
-
- /**
- * Factory for {@link DependencyRequest}s.
- *
- * <p>Any factory method may throw {@link TypeNotPresentException} if a type is not available,
- * which may mean that the type will be generated in a later round of processing.
- */
- static final class Factory {
- private final Elements elements;
- private final Key.Factory keyFactory;
-
- Factory(Elements elements, Key.Factory keyFactory) {
- this.elements = elements;
- this.keyFactory = keyFactory;
- }
-
- ImmutableSet<DependencyRequest> forRequiredResolvedVariables(DeclaredType container,
- List<? extends VariableElement> variables, List<? extends TypeMirror> resolvedTypes) {
- checkState(resolvedTypes.size() == variables.size());
- ImmutableSet.Builder<DependencyRequest> builder = ImmutableSet.builder();
- for (int i = 0; i < variables.size(); i++) {
- builder.add(forRequiredResolvedVariable(container, variables.get(i), resolvedTypes.get(i)));
- }
- return builder.build();
- }
-
- ImmutableSet<DependencyRequest> forRequiredVariables(
- List<? extends VariableElement> variables) {
- return FluentIterable.from(variables)
- .transform(
- new Function<VariableElement, DependencyRequest>() {
- @Override
- public DependencyRequest apply(VariableElement input) {
- return forRequiredVariable(input);
- }
- })
- .toSet();
- }
-
- /**
- * Creates a implicit {@link DependencyRequest} for {@code mapOfFactoryKey}, which will be used
- * to satisfy the {@code mapOfValueRequest}.
- *
- * @param mapOfValueRequest a request for {@code Map<K, V>}
- * @param mapOfFactoryKey a key equivalent to {@code mapOfValueRequest}'s key, whose type is
- * {@code Map<K, Provider<V>>} or {@code Map<K, Producer<V>>}
- */
- DependencyRequest forImplicitMapBinding(
- DependencyRequest mapOfValueRequest, Key mapOfFactoryKey) {
- checkNotNull(mapOfValueRequest);
- return new AutoValue_DependencyRequest(
- Kind.PROVIDER,
- mapOfFactoryKey,
- mapOfValueRequest.requestElement(),
- mapOfValueRequest.enclosingType(),
- false /* doesn't allow null */,
- Optional.<String>absent());
- }
-
- DependencyRequest forRequiredVariable(VariableElement variableElement) {
- return forRequiredVariable(variableElement, Optional.<String>absent());
- }
-
- DependencyRequest forRequiredVariable(VariableElement variableElement, Optional<String> name) {
- checkNotNull(variableElement);
- TypeMirror type = variableElement.asType();
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement);
- return newDependencyRequest(
- variableElement, type, qualifier, getEnclosingType(variableElement), name);
- }
-
- DependencyRequest forRequiredResolvedVariable(DeclaredType container,
- VariableElement variableElement,
- TypeMirror resolvedType) {
- checkNotNull(variableElement);
- checkNotNull(resolvedType);
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement);
- return newDependencyRequest(
- variableElement, resolvedType, qualifier, container, Optional.<String>absent());
- }
-
- DependencyRequest forComponentProvisionMethod(ExecutableElement provisionMethod,
- ExecutableType provisionMethodType) {
- checkNotNull(provisionMethod);
- checkNotNull(provisionMethodType);
- checkArgument(
- provisionMethod.getParameters().isEmpty(),
- "Component provision methods must be empty: %s",
- provisionMethod);
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(provisionMethod);
- return newDependencyRequest(
- provisionMethod,
- provisionMethodType.getReturnType(),
- qualifier,
- getEnclosingType(provisionMethod),
- Optional.<String>absent());
- }
-
- DependencyRequest forComponentProductionMethod(ExecutableElement productionMethod,
- ExecutableType productionMethodType) {
- checkNotNull(productionMethod);
- checkNotNull(productionMethodType);
- checkArgument(productionMethod.getParameters().isEmpty(),
- "Component production methods must be empty: %s", productionMethod);
- TypeMirror type = productionMethodType.getReturnType();
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(productionMethod);
- DeclaredType container = getEnclosingType(productionMethod);
- // Only a component production method can be a request for a ListenableFuture, so we
- // special-case it here.
- if (isTypeOf(ListenableFuture.class, type)) {
- return new AutoValue_DependencyRequest(
- Kind.FUTURE,
- keyFactory.forQualifiedType(
- qualifier, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())),
- productionMethod,
- container,
- false /* doesn't allow null */,
- Optional.<String>absent());
- } else {
- return newDependencyRequest(
- productionMethod, type, qualifier, container, Optional.<String>absent());
- }
- }
-
- DependencyRequest forComponentMembersInjectionMethod(ExecutableElement membersInjectionMethod,
- ExecutableType membersInjectionMethodType) {
- checkNotNull(membersInjectionMethod);
- checkNotNull(membersInjectionMethodType);
- Optional<AnnotationMirror> qualifier =
- InjectionAnnotations.getQualifier(membersInjectionMethod);
- checkArgument(!qualifier.isPresent());
- TypeMirror returnType = membersInjectionMethodType.getReturnType();
- if (returnType.getKind().equals(DECLARED)
- && MoreTypes.isTypeOf(MembersInjector.class, returnType)) {
- return new AutoValue_DependencyRequest(
- Kind.MEMBERS_INJECTOR,
- keyFactory.forMembersInjectedType(
- Iterables.getOnlyElement(((DeclaredType) returnType).getTypeArguments())),
- membersInjectionMethod,
- getEnclosingType(membersInjectionMethod),
- false /* doesn't allow null */,
- Optional.<String>absent());
- } else {
- return new AutoValue_DependencyRequest(
- Kind.MEMBERS_INJECTOR,
- keyFactory.forMembersInjectedType(
- Iterables.getOnlyElement(membersInjectionMethodType.getParameterTypes())),
- membersInjectionMethod,
- getEnclosingType(membersInjectionMethod),
- false /* doesn't allow null */,
- Optional.<String>absent());
- }
- }
-
- DependencyRequest forMembersInjectedType(DeclaredType type) {
- return new AutoValue_DependencyRequest(
- Kind.MEMBERS_INJECTOR,
- keyFactory.forMembersInjectedType(type),
- type.asElement(),
- type,
- false /* doesn't allow null */,
- Optional.<String>absent());
- }
-
- DependencyRequest forProductionComponentMonitorProvider() {
- TypeElement element = elements.getTypeElement(AbstractProducer.class.getCanonicalName());
- for (ExecutableElement constructor : constructorsIn(element.getEnclosedElements())) {
- if (constructor.getParameters().size() == 2) {
- // the 2-arg constructor has the appropriate dependency as its first arg
- return forRequiredVariable(constructor.getParameters().get(0), Optional.of("monitor"));
- }
- }
- throw new AssertionError("expected 2-arg constructor in AbstractProducer");
- }
-
- private DependencyRequest newDependencyRequest(
- Element requestElement,
- TypeMirror type,
- Optional<AnnotationMirror> qualifier,
- DeclaredType container,
- Optional<String> name) {
- KindAndType kindAndType = extractKindAndType(type);
- if (kindAndType.kind().equals(Kind.MEMBERS_INJECTOR)) {
- checkArgument(!qualifier.isPresent());
- }
- // Only instance types can be non-null -- all other requests are wrapped
- // inside something (e.g, Provider, Lazy, etc..).
- // TODO(sameb): should Produced/Producer always require non-nullable?
- boolean allowsNull = !kindAndType.kind().equals(Kind.INSTANCE)
- || ConfigurationAnnotations.getNullableType(requestElement).isPresent();
- return new AutoValue_DependencyRequest(
- kindAndType.kind(),
- keyFactory.forQualifiedType(qualifier, kindAndType.type()),
- requestElement,
- container,
- allowsNull,
- name);
- }
-
- @AutoValue
- static abstract class KindAndType {
- abstract Kind kind();
- abstract TypeMirror type();
- }
-
- /**
- * Extracts the correct requesting type & kind out a request type. For example, if a user
- * requests {@code Provider<Foo>}, this will return ({@link Kind#PROVIDER}, {@code Foo}).
- *
- * @throws TypeNotPresentException if {@code type}'s kind is {@link TypeKind#ERROR}, which may
- * mean that the type will be generated in a later round of processing
- */
- static KindAndType extractKindAndType(TypeMirror type) {
- if (type.getKind().equals(TypeKind.ERROR)) {
- throw new TypeNotPresentException(type.toString(), null);
- }
-
- // We must check TYPEVAR explicitly before the below checks because calling
- // isTypeOf(..) on a TYPEVAR throws an exception (because it can't be
- // represented as a Class).
- if (type.getKind().equals(TypeKind.TYPEVAR)) {
- return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.INSTANCE, type);
- } else if (isTypeOf(Provider.class, type)) {
- return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.PROVIDER,
- Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments()));
- } else if (isTypeOf(Lazy.class, type)) {
- return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.LAZY,
- Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments()));
- } else if (isTypeOf(MembersInjector.class, type)) {
- return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.MEMBERS_INJECTOR,
- Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments()));
- } else if (isTypeOf(Producer.class, type)) {
- return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.PRODUCER,
- Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments()));
- } else if (isTypeOf(Produced.class, type)) {
- return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.PRODUCED,
- Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments()));
- } else {
- return new AutoValue_DependencyRequest_Factory_KindAndType(Kind.INSTANCE, type);
- }
- }
-
- static DeclaredType getEnclosingType(Element element) {
- while (!MoreElements.isType(element)) {
- element = element.getEnclosingElement();
- }
- return MoreTypes.asDeclared(element.asType());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/DependencyRequestFormatter.java b/compiler/src/main/java/dagger/internal/codegen/DependencyRequestFormatter.java
deleted file mode 100644
index 0e5f1f2..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/DependencyRequestFormatter.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Optional;
-import java.util.List;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleElementVisitor6;
-import javax.lang.model.util.Types;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ErrorMessages.INDENT;
-
-/**
- * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing
- * a chain of dependencies.
- *
- * @author Christian Gruber
- * @since 2.0
- */
-final class DependencyRequestFormatter extends Formatter<DependencyRequest> {
- private final Types types;
-
- DependencyRequestFormatter(Types types) {
- this.types = types;
- }
-
- // TODO(cgruber): Sweep this class for TypeMirror.toString() usage and do some preventive format.
- // TODO(cgruber): consider returning a small structure containing strings to be indented later.
- @Override public String format(final DependencyRequest request) {
- Element requestElement = request.requestElement();
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(requestElement);
- return requestElement.accept(new SimpleElementVisitor6<String, Optional<AnnotationMirror>>(){
-
- /* Handle component methods */
- @Override public String visitExecutable(
- ExecutableElement method, Optional<AnnotationMirror> qualifier) {
- StringBuilder builder = new StringBuilder(INDENT);
- if (method.getParameters().isEmpty()) {
- // some.package.name.MyComponent.myMethod()
- // [component method with return type: @other.package.Qualifier some.package.name.Foo]
- appendEnclosingTypeAndMemberName(method, builder).append("()\n")
- .append(INDENT).append(INDENT).append("[component method with return type: ");
- if (qualifier.isPresent()) {
- // TODO(cgruber) use chenying's annotation mirror stringifier
- builder.append(qualifier.get()).append(' ');
- }
- builder.append(method.getReturnType()).append(']');
- } else {
- // some.package.name.MyComponent.myMethod(some.package.name.Foo foo)
- // [component injection method for type: some.package.name.Foo]
- VariableElement componentMethodParameter = getOnlyElement(method.getParameters());
- appendEnclosingTypeAndMemberName(method, builder).append("(");
- appendParameter(componentMethodParameter, componentMethodParameter.asType(), builder);
- builder.append(")\n");
- builder.append(INDENT).append(INDENT).append("[component injection method for type: ")
- .append(componentMethodParameter.asType())
- .append(']');
- }
- return builder.toString();
- }
-
- /* Handle injected fields or method/constructor parameter injection. */
- @Override public String visitVariable(
- VariableElement variable, Optional<AnnotationMirror> qualifier) {
- StringBuilder builder = new StringBuilder(INDENT);
- TypeMirror resolvedVariableType =
- MoreTypes.asMemberOf(types, request.enclosingType(), variable);
- if (variable.getKind().equals(ElementKind.PARAMETER)) {
- // some.package.name.MyClass.myMethod(some.package.name.Foo arg0, some.package.Bar arg1)
- // [parameter: @other.package.Qualifier some.package.name.Foo arg0]
- ExecutableElement methodOrConstructor =
- MoreElements.asExecutable(variable.getEnclosingElement());
- ExecutableType resolvedMethodOrConstructor = MoreTypes.asExecutable(
- types.asMemberOf(request.enclosingType(), methodOrConstructor));
- appendEnclosingTypeAndMemberName(methodOrConstructor, builder).append('(');
- List<? extends VariableElement> parameters = methodOrConstructor.getParameters();
- List<? extends TypeMirror> parameterTypes =
- resolvedMethodOrConstructor.getParameterTypes();
- checkState(parameters.size() == parameterTypes.size());
- for (int i = 0; i < parameters.size(); i++) {
- appendParameter(parameters.get(i), parameterTypes.get(i), builder);
- if (i != parameters.size() - 1) {
- builder.append(", ");
- }
- }
- builder.append(")\n").append(INDENT).append(INDENT).append("[parameter: ");
- } else {
- // some.package.name.MyClass.myField
- // [injected field of type: @other.package.Qualifier some.package.name.Foo myField]
- appendEnclosingTypeAndMemberName(variable, builder).append("\n")
- .append(INDENT).append(INDENT).append("[injected field of type: ");
- }
- if (qualifier.isPresent()) {
- // TODO(cgruber) use chenying's annotation mirror stringifier
- builder.append(qualifier.get()).append(' ');
- }
- builder.append(resolvedVariableType)
- .append(' ')
- .append(variable.getSimpleName())
- .append(']');
- return builder.toString();
- }
-
- @Override
- public String visitType(TypeElement e, Optional<AnnotationMirror> p) {
- return ""; // types by themselves provide no useful information.
- }
-
- @Override protected String defaultAction(Element element, Optional<AnnotationMirror> ignore) {
- throw new IllegalStateException(
- "Invalid request " + element.getKind() + " element " + element);
- }
- }, qualifier);
- }
-
- private StringBuilder appendParameter(VariableElement parameter, TypeMirror type,
- StringBuilder builder) {
- return builder.append(type).append(' ').append(parameter.getSimpleName());
- }
-
- private StringBuilder appendEnclosingTypeAndMemberName(Element member, StringBuilder builder) {
- TypeElement type = MoreElements.asType(member.getEnclosingElement());
- return builder.append(type.getQualifiedName())
- .append('.')
- .append(member.getSimpleName());
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/DependencyRequestMapper.java b/compiler/src/main/java/dagger/internal/codegen/DependencyRequestMapper.java
deleted file mode 100644
index 1dc48fc..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/DependencyRequestMapper.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import dagger.MembersInjector;
-import dagger.producers.Producer;
-import javax.inject.Provider;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-
-/**
- * A mapper for associating a {@link DependencyRequest} to a framework class, dependent on
- * the type of code to be generated (e.g., for {@link Provider} or {@link Producer}).
- *
- * @author Jesse Beder
- * @since 2.0
- */
-abstract class DependencyRequestMapper {
- abstract Class<?> getFrameworkClass(DependencyRequest request);
-
- /**
- * Returns the framework class to use for a collection of requests of the same {@link BindingKey}.
- * This allows factories to only take a single argument for multiple requests of the same key.
- */
- Class<?> getFrameworkClass(Iterable<DependencyRequest> requests) {
- ImmutableSet<Class<?>> classes = FluentIterable.from(requests)
- .transform(new Function<DependencyRequest, Class<?>>() {
- @Override public Class<?> apply(DependencyRequest request) {
- return getFrameworkClass(request);
- }
- })
- .toSet();
- if (classes.size() == 1) {
- return getOnlyElement(classes);
- } else if (classes.equals(ImmutableSet.of(Producer.class, Provider.class))) {
- return Provider.class;
- } else {
- throw new IllegalStateException("Bad set of framework classes: " + classes);
- }
- }
-
- private static final class MapperForProvider extends DependencyRequestMapper {
- @Override public Class<?> getFrameworkClass(DependencyRequest request) {
- switch (request.kind()) {
- case INSTANCE:
- case PROVIDER:
- case LAZY:
- return Provider.class;
- case MEMBERS_INJECTOR:
- return MembersInjector.class;
- case PRODUCED:
- case PRODUCER:
- throw new IllegalArgumentException();
- default:
- throw new AssertionError();
- }
- }
- }
-
- static final DependencyRequestMapper FOR_PROVIDER = new MapperForProvider();
-
- private static final class MapperForProducer extends DependencyRequestMapper {
- @Override public Class<?> getFrameworkClass(DependencyRequest request) {
- switch (request.kind()) {
- case INSTANCE:
- case PRODUCED:
- case PRODUCER:
- return Producer.class;
- case PROVIDER:
- case LAZY:
- return Provider.class;
- case MEMBERS_INJECTOR:
- return MembersInjector.class;
- default:
- throw new AssertionError();
- }
- }
- }
-
- static final DependencyRequestMapper FOR_PRODUCER = new MapperForProducer();
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java b/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java
deleted file mode 100644
index 5ba5635..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Ascii;
-import com.google.common.base.Function;
-import dagger.Lazy;
-import javax.inject.Provider;
-
-/**
- * Picks a reasonable name for what we think is being provided from the variable name associated
- * with the {@link DependencyRequest}. I.e. strips out words like "lazy" and "provider" if we
- * believe that those refer to {@link Lazy} and {@link Provider} rather than the type being
- * provided.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-//TODO(gak): develop the heuristics to get better names
-final class DependencyVariableNamer implements Function<DependencyRequest, String> {
- @Override
- public String apply(DependencyRequest dependency) {
- if (dependency.overriddenVariableName().isPresent()) {
- return dependency.overriddenVariableName().get();
- }
- String variableName = dependency.requestElement().getSimpleName().toString();
- switch (dependency.kind()) {
- case INSTANCE:
- return variableName;
- case LAZY:
- return variableName.startsWith("lazy") && !variableName.equals("lazy")
- ? Ascii.toLowerCase(variableName.charAt(4)) + variableName.substring(5)
- : variableName;
- case PROVIDER:
- return variableName.endsWith("Provider") && !variableName.equals("Provider")
- ? variableName.substring(0, variableName.length() - 8)
- : variableName;
- case MEMBERS_INJECTOR:
- return variableName.endsWith("MembersInjector") && !variableName.equals("MembersInjector")
- ? variableName.substring(0, variableName.length() - 15)
- : variableName;
- case PRODUCED:
- return variableName.startsWith("produced") && !variableName.equals("produced")
- ? Ascii.toLowerCase(variableName.charAt(8)) + variableName.substring(9)
- : variableName;
- case PRODUCER:
- return variableName.endsWith("Producer") && !variableName.equals("Producer")
- ? variableName.substring(0, variableName.length() - 8)
- : variableName;
- default:
- throw new AssertionError();
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java b/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java
deleted file mode 100644
index 1f52aca..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import dagger.Provides;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.lang.model.element.AnnotationMirror;
-
-/**
- * The collection of error messages to be reported back to users.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class ErrorMessages {
- /*
- * Common constants.
- */
- static final String INDENT = " ";
- static final int DUPLICATE_SIZE_LIMIT = 10;
-
- /*
- * JSR-330 errors
- *
- * These are errors that are explicitly outlined in the JSR-330 APIs
- */
-
- /* constructors */
- static final String MULTIPLE_INJECT_CONSTRUCTORS =
- "Types may only contain one @Inject constructor.";
-
- /* fields */
- static final String FINAL_INJECT_FIELD = "@Inject fields may not be final";
-
- /* methods */
- static final String ABSTRACT_INJECT_METHOD = "Methods with @Inject may not be abstract.";
- static final String GENERIC_INJECT_METHOD =
- "Methods with @Inject may not declare type parameters.";
-
- /* qualifiers */
- static final String MULTIPLE_QUALIFIERS =
- "A single injection site may not use more than one @Qualifier.";
-
- /* scope */
- static final String MULTIPLE_SCOPES = "A single binding may not declare more than one @Scope.";
-
- /*
- * Dagger errors
- *
- * These are errors that arise due to restrictions imposed by the dagger implementation.
- */
-
- /* constructors */
- static final String INJECT_ON_PRIVATE_CONSTRUCTOR =
- "Dagger does not support injection into private constructors";
- static final String INJECT_CONSTRUCTOR_ON_INNER_CLASS =
- "@Inject constructors are invalid on inner classes";
- static final String INJECT_CONSTRUCTOR_ON_ABSTRACT_CLASS =
- "@Inject is nonsense on the constructor of an abstract class";
- static final String QUALIFIER_ON_INJECT_CONSTRUCTOR =
- "@Qualifier annotations are not allowed on @Inject constructors.";
-
- /* fields */
- static final String PRIVATE_INJECT_FIELD =
- "Dagger does not support injection into private fields";
-
- static final String STATIC_INJECT_FIELD =
- "Dagger does not support injection into static fields";
-
- /* methods */
- static final String PRIVATE_INJECT_METHOD =
- "Dagger does not support injection into private methods";
-
- static final String STATIC_INJECT_METHOD =
- "Dagger does not support injection into static methods";
-
- /* all */
- static final String INJECT_INTO_PRIVATE_CLASS =
- "Dagger does not support injection into private classes";
-
- /*
- * Configuration errors
- *
- * These are errors that relate specifically to the Dagger configuration API (@Module, @Provides,
- * etc.)
- */
- static final String DUPLICATE_BINDINGS_FOR_KEY_FORMAT =
- "%s is bound multiple times:";
-
- static String duplicateMapKeysError(String key) {
- return "The same map key is bound more than once for " + key;
- }
-
- static String inconsistentMapKeyAnnotationsError(String key) {
- return key + " uses more than one @MapKey annotation type";
- }
-
- static final String PROVIDES_METHOD_RETURN_TYPE =
- "@Provides methods must either return a primitive, an array or a declared type.";
-
- static final String PRODUCES_METHOD_RETURN_TYPE =
- "@Produces methods must either return a primitive, an array or a declared type, or a"
- + " ListenableFuture of one of those types.";
-
- static final String PRODUCES_METHOD_RAW_FUTURE =
- "@Produces methods cannot return a raw ListenableFuture.";
-
- static final String BINDING_METHOD_SET_VALUES_RAW_SET =
- "@%s methods of type set values cannot return a raw Set";
-
- static final String PROVIDES_METHOD_SET_VALUES_RETURN_SET =
- "@Provides methods of type set values must return a Set";
-
- static final String PRODUCES_METHOD_SET_VALUES_RETURN_SET =
- "@Produces methods of type set values must return a Set or ListenableFuture of Set";
-
- static final String BINDING_METHOD_MUST_RETURN_A_VALUE =
- "@%s methods must return a value (not void).";
-
- static final String BINDING_METHOD_ABSTRACT = "@%s methods cannot be abstract";
-
- static final String BINDING_METHOD_PRIVATE = "@%s methods cannot be private";
-
- static final String BINDING_METHOD_TYPE_PARAMETER =
- "@%s methods may not have type parameters.";
-
- static final String BINDING_METHOD_NOT_IN_MODULE =
- "@%s methods can only be present within a @%s";
-
- static final String BINDING_METHOD_NOT_MAP_HAS_MAP_KEY =
- "@%s methods of non map type cannot declare a map key";
-
- static final String BINDING_METHOD_WITH_NO_MAP_KEY =
- "@%s methods of type map must declare a map key";
-
- static final String BINDING_METHOD_WITH_MULTIPLE_MAP_KEY =
- "@%s methods may not have more than one @MapKey-marked annotation";
-
- static final String BINDING_METHOD_WITH_SAME_NAME =
- "Cannot have more than one @%s method with the same name in a single module";
-
- static final String MODULES_WITH_TYPE_PARAMS_MUST_BE_ABSTRACT =
- "Modules with type parameters must be abstract";
-
- static final String REFERENCED_MODULES_MUST_NOT_BE_ABSTRACT =
- "%s is listed as a module, but is an abstract class or interface";
-
- static final String REFERENCED_MODULE_NOT_ANNOTATED =
- "%s is listed as a module, but is not annotated with %s";
-
- static final String REFERENCED_MODULE_MUST_NOT_HAVE_TYPE_PARAMS =
- "%s is listed as a module, but has type parameters";
-
- static final String PROVIDES_METHOD_OVERRIDES_ANOTHER =
- "@%s methods may not override another method. Overrides: %s";
-
- static final String METHOD_OVERRIDES_PROVIDES_METHOD =
- "@%s methods may not be overridden in modules. Overrides: %s";
-
- static final String PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS =
- "Cannot use more than one @Qualifier on a @Provides or @Produces method";
-
- /* mapKey errors*/
- static final String MAPKEY_WITHOUT_MEMBERS =
- "Map key annotations must have members";
-
- static final String UNWRAPPED_MAP_KEY_WITH_TOO_MANY_MEMBERS=
- "Map key annotations with unwrapped values must have exactly one member";
-
- static final String UNWRAPPED_MAP_KEY_WITH_ARRAY_MEMBER =
- "Map key annotations with unwrapped values cannot use arrays";
-
- /* collection binding errors */
- static final String MULTIPLE_CONTRIBUTION_TYPES_FORMAT =
- "More than one binding present of different types %s";
-
- static final String MULTIPLE_BINDING_TYPES_FOR_KEY_FORMAT =
- "%s has incompatible bindings:\n";
-
- static final String PROVIDER_ENTRY_POINT_MAY_NOT_DEPEND_ON_PRODUCER_FORMAT =
- "%s is a provision entry-point, which cannot depend on a production.";
-
- static final String PROVIDER_MAY_NOT_DEPEND_ON_PRODUCER_FORMAT =
- "%s is a provision, which cannot depend on a production.";
-
- static final String REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_FORMAT =
- "%s cannot be provided without an @Inject constructor or from an @Provides-annotated method.";
-
- static final String REQUIRES_PROVIDER_FORMAT =
- "%s cannot be provided without an @Provides-annotated method.";
-
- static final String REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_OR_PRODUCER_FORMAT =
- "%s cannot be provided without an @Inject constructor or from an @Provides- or "
- + "@Produces-annotated method.";
-
- static final String REQUIRES_PROVIDER_OR_PRODUCER_FORMAT =
- "%s cannot be provided without an @Provides- or @Produces-annotated method.";
-
- static final String MEMBERS_INJECTION_DOES_NOT_IMPLY_PROVISION =
- "This type supports members injection but cannot be implicitly provided.";
-
- static final String MEMBERS_INJECTION_WITH_RAW_TYPE =
- "%s has type parameters, cannot members inject the raw type. via:\n%s";
-
- static final String MEMBERS_INJECTION_WITH_UNBOUNDED_TYPE =
- "Type parameters must be bounded for members injection. %s required by %s, via:\n%s";
-
- static final String CONTAINS_DEPENDENCY_CYCLE_FORMAT = "%s.%s() contains a dependency cycle:\n%s";
-
- static final String MALFORMED_MODULE_METHOD_FORMAT =
- "Cannot generated a graph because method %s on module %s was malformed";
-
- static String nullableToNonNullable(String typeName, String bindingString) {
- return String.format(
- "%s is not nullable, but is being provided by %s",
- typeName,
- bindingString);
- }
-
- static final String CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD =
- "Cannot return null from a non-@Nullable component method";
-
- static final String CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD =
- "Cannot return null from a non-@Nullable @Provides method";
-
- static ComponentBuilderMessages builderMsgsFor(ComponentDescriptor.Kind kind) {
- switch(kind) {
- case COMPONENT:
- return ComponentBuilderMessages.INSTANCE;
- case SUBCOMPONENT:
- return SubcomponentBuilderMessages.INSTANCE;
- case PRODUCTION_COMPONENT:
- return ProductionComponentBuilderMessages.INSTANCE;
- default:
- throw new IllegalStateException(kind.toString());
- }
- }
-
- static class ComponentBuilderMessages {
- static final ComponentBuilderMessages INSTANCE = new ComponentBuilderMessages();
-
- protected String process(String s) { return s; }
-
- /** Errors for component builders. */
- final String moreThanOne() {
- return process("@Component has more than one @Component.Builder: %s");
- }
-
- final String cxtorOnlyOneAndNoArgs() {
- return process("@Component.Builder classes must have exactly one constructor,"
- + " and it must not have any parameters");
- }
-
- final String generics() {
- return process("@Component.Builder types must not have any generic types");
- }
-
- final String mustBeInComponent() {
- return process("@Component.Builder types must be nested within a @Component");
- }
-
- final String mustBeClassOrInterface() {
- return process("@Component.Builder types must be abstract classes or interfaces");
- }
-
- final String isPrivate() {
- return process("@Component.Builder types must not be private");
- }
-
- final String mustBeStatic() {
- return process("@Component.Builder types must be static");
- }
-
- final String mustBeAbstract() {
- return process("@Component.Builder types must be abstract");
- }
-
- final String missingBuildMethod() {
- return process("@Component.Builder types must have exactly one no-args method that "
- + " returns the @Component type");
- }
-
- final String manyMethodsForType() {
- return process("@Component.Builder types must not have more than one setter method per type,"
- + " but %s is set by %s");
- }
-
- final String extraSetters() {
- return process(
- "@Component.Builder has setters for modules or components that aren't required: %s");
- }
-
- final String missingSetters() {
- return process(
- "@Component.Builder is missing setters for required modules or components: %s");
- }
-
- final String twoBuildMethods() {
- return process("@Component.Builder types must have exactly one zero-arg method, and that"
- + " method must return the @Component type. Already found: %s");
- }
-
- final String inheritedTwoBuildMethods() {
- return process("@Component.Builder types must have exactly one zero-arg method, and that"
- + " method must return the @Component type. Found %s and %s");
- }
-
- final String buildMustReturnComponentType() {
- return process(
- "@Component.Builder methods that have no arguments must return the @Component type");
- }
-
- final String inheritedBuildMustReturnComponentType() {
- return process(
- "@Component.Builder methods that have no arguments must return the @Component type"
- + " Inherited method: %s");
- }
-
- final String methodsMustTakeOneArg() {
- return process("@Component.Builder methods must not have more than one argument");
- }
-
- final String inheritedMethodsMustTakeOneArg() {
- return process(
- "@Component.Builder methods must not have more than one argument. Inherited method: %s");
- }
-
- final String methodsMustReturnVoidOrBuilder() {
- return process("@Component.Builder setter methods must return void, the builder,"
- + " or a supertype of the builder");
- }
-
- final String inheritedMethodsMustReturnVoidOrBuilder() {
- return process("@Component.Builder setter methods must return void, the builder,"
- + "or a supertype of the builder. Inherited method: %s");
- }
-
- final String methodsMayNotHaveTypeParameters() {
- return process("@Component.Builder methods must not have type parameters");
- }
-
- final String inheritedMethodsMayNotHaveTypeParameters() {
- return process(
- "@Component.Builder methods must not have type parameters. Inherited method: %s");
- }
- }
-
- static final class SubcomponentBuilderMessages extends ComponentBuilderMessages {
- @SuppressWarnings("hiding")
- static final SubcomponentBuilderMessages INSTANCE = new SubcomponentBuilderMessages();
-
- @Override protected String process(String s) {
- return s.replaceAll("component", "subcomponent").replaceAll("Component", "Subcomponent");
- }
-
- String builderMethodRequiresNoArgs() {
- return "Methods returning a @Subcomponent.Builder must have no arguments";
- }
-
- String moreThanOneRefToSubcomponent() {
- return "Only one method can create a given subcomponent. %s is created by: %s";
- }
- }
-
- static final class ProductionComponentBuilderMessages extends ComponentBuilderMessages {
- @SuppressWarnings("hiding")
- static final ProductionComponentBuilderMessages INSTANCE =
- new ProductionComponentBuilderMessages();
-
- @Override protected String process(String s) {
- return s.replaceAll("component", "production component")
- .replaceAll("Component", "ProductionComponent");
- }
- }
-
- /**
- * A regular expression to match a small list of specific packages deemed to
- * be unhelpful to display in fully qualified types in error messages.
- *
- * Note: This should never be applied to messages themselves.
- */
- private static final Pattern COMMON_PACKAGE_PATTERN = Pattern.compile(
- "(?:^|[^.a-z_])" // What we want to match on but not capture.
- + "((?:" // Start a group with a non-capturing or part
- + "java[.]lang"
- + "|java[.]util"
- + "|javax[.]inject"
- + "|dagger"
- + "|com[.]google[.]common[.]base"
- + "|com[.]google[.]common[.]collect"
- + ")[.])" // Always end with a literal .
- + "[A-Z]"); // What we want to match on but not capture.
-
- /**
- * A method to strip out common packages and a few rare type prefixes
- * from types' string representation before being used in error messages.
- *
- * This type assumes a String value that is a valid fully qualified
- * (and possibly parameterized) type, and should NOT be used with
- * arbitrary text, especially prose error messages.
- *
- * TODO(cgruber): Tighten these to take type representations (mirrors
- * and elements) to avoid accidental mis-use by running errors
- * through this method.
- */
- static String stripCommonTypePrefixes(String type) {
- // Special case this enum's constants since they will be incredibly common.
- type = type.replace(Provides.Type.class.getCanonicalName() + ".", "");
-
- // Do regex magic to remove common packages we care to shorten.
- Matcher matcher = COMMON_PACKAGE_PATTERN.matcher(type);
- StringBuilder result = new StringBuilder();
- int index = 0;
- while (matcher.find()) {
- result.append(type.subSequence(index, matcher.start(1)));
- index = matcher.end(1); // Skip the matched pattern content.
- }
- result.append(type.subSequence(index, type.length()));
- return result.toString();
- }
-
- //TODO(cgruber): Extract Formatter and do something less stringy.
- static String format(AnnotationMirror annotation) {
- return stripCommonTypePrefixes(annotation.toString());
- }
-
- private ErrorMessages() {}
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java b/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java
deleted file mode 100644
index a0a48c8..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import dagger.MembersInjector;
-import dagger.Provides.Type;
-import dagger.internal.Factory;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.ConstructorWriter;
-import dagger.internal.codegen.writer.EnumWriter;
-import dagger.internal.codegen.writer.FieldWriter;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.StringLiteral;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import dagger.internal.codegen.writer.TypeVariableName;
-import dagger.internal.codegen.writer.TypeWriter;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Generated;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.type.TypeMirror;
-import javax.tools.Diagnostic;
-
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.Provides.Type.SET;
-import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
-import static dagger.internal.codegen.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD;
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for
- * {@link Inject} constructors.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
- private final DependencyRequestMapper dependencyRequestMapper;
- private final Diagnostic.Kind nullableValidationType;
-
- FactoryGenerator(Filer filer, DependencyRequestMapper dependencyRequestMapper,
- Diagnostic.Kind nullableValidationType) {
- super(filer);
- this.dependencyRequestMapper = dependencyRequestMapper;
- this.nullableValidationType = nullableValidationType;
- }
-
- @Override
- ClassName nameGeneratedType(ProvisionBinding binding) {
- return generatedClassNameForBinding(binding);
- }
-
- @Override
- Iterable<? extends Element> getOriginatingElements(ProvisionBinding binding) {
- return ImmutableSet.of(binding.bindingElement());
- }
-
- @Override
- Optional<? extends Element> getElementForErrorReporting(ProvisionBinding binding) {
- return Optional.of(binding.bindingElement());
- }
-
- @Override
- ImmutableSet<JavaWriter> write(ClassName generatedTypeName, ProvisionBinding binding) {
- // We don't want to write out resolved bindings -- we want to write out the generic version.
- checkState(!binding.hasNonDefaultTypeParameters());
-
- TypeMirror keyType = binding.provisionType().equals(Type.MAP)
- ? Util.getProvidedValueTypeOfMap(MoreTypes.asDeclared(binding.key().type()))
- : binding.key().type();
- TypeName providedTypeName = TypeNames.forTypeMirror(keyType);
- JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
-
- final TypeWriter factoryWriter;
- final Optional<ConstructorWriter> constructorWriter;
- List<TypeVariableName> typeParameters = Lists.newArrayList();
- for (TypeParameterElement typeParameter : binding.bindingTypeElement().getTypeParameters()) {
- typeParameters.add(TypeVariableName.fromTypeParameterElement(typeParameter));
- }
- switch (binding.factoryCreationStrategy()) {
- case ENUM_INSTANCE:
- EnumWriter enumWriter = writer.addEnum(generatedTypeName.simpleName());
- enumWriter.addConstant("INSTANCE");
- constructorWriter = Optional.absent();
- factoryWriter = enumWriter;
- // If we have type parameters, then remove the parameters from our providedTypeName,
- // since we'll be implementing an erased version of it.
- if (!typeParameters.isEmpty()) {
- factoryWriter.annotate(SuppressWarnings.class).setValue("rawtypes");
- providedTypeName = ((ParameterizedTypeName) providedTypeName).type();
- }
- break;
- case CLASS_CONSTRUCTOR:
- ClassWriter classWriter = writer.addClass(generatedTypeName.simpleName());
- classWriter.addTypeParameters(typeParameters);
- classWriter.addModifiers(FINAL);
- constructorWriter = Optional.of(classWriter.addConstructor());
- constructorWriter.get().addModifiers(PUBLIC);
- factoryWriter = classWriter;
- if (binding.bindingKind().equals(PROVISION)
- && !binding.bindingElement().getModifiers().contains(STATIC)) {
- TypeName enclosingType = TypeNames.forTypeMirror(binding.bindingTypeElement().asType());
- factoryWriter.addField(enclosingType, "module").addModifiers(PRIVATE, FINAL);
- constructorWriter.get().addParameter(enclosingType, "module");
- constructorWriter.get().body()
- .addSnippet("assert module != null;")
- .addSnippet("this.module = module;");
- }
- break;
- default:
- throw new AssertionError();
- }
-
- factoryWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName());
- factoryWriter.addModifiers(PUBLIC);
- factoryWriter.addImplementedType(
- ParameterizedTypeName.create(ClassName.fromClass(Factory.class), providedTypeName));
-
- MethodWriter getMethodWriter = factoryWriter.addMethod(providedTypeName, "get");
- getMethodWriter.annotate(Override.class);
- getMethodWriter.addModifiers(PUBLIC);
-
- if (binding.membersInjectionRequest().isPresent()) {
- ParameterizedTypeName membersInjectorType = ParameterizedTypeName.create(
- MembersInjector.class, providedTypeName);
- factoryWriter.addField(membersInjectorType, "membersInjector").addModifiers(PRIVATE, FINAL);
- constructorWriter.get().addParameter(membersInjectorType, "membersInjector");
- constructorWriter.get().body()
- .addSnippet("assert membersInjector != null;")
- .addSnippet("this.membersInjector = membersInjector;");
- }
-
- ImmutableMap<BindingKey, FrameworkField> fields =
- SourceFiles.generateBindingFieldsForDependencies(
- dependencyRequestMapper, binding.dependencies());
-
- for (FrameworkField bindingField : fields.values()) {
- TypeName fieldType = bindingField.frameworkType();
- FieldWriter field = factoryWriter.addField(fieldType, bindingField.name());
- field.addModifiers(PRIVATE, FINAL);
- constructorWriter.get().addParameter(field.type(), field.name());
- constructorWriter.get().body()
- .addSnippet("assert %s != null;", field.name())
- .addSnippet("this.%1$s = %1$s;", field.name());
- }
-
- // If constructing a factory for @Inject or @Provides bindings, we use a static create method
- // so that generated components can avoid having to refer to the generic types
- // of the factory. (Otherwise they may have visibility problems referring to the types.)
- switch(binding.bindingKind()) {
- case INJECTION:
- case PROVISION:
- // The return type is usually the same as the implementing type, except in the case
- // of enums with type variables (where we cast).
- TypeName returnType = ParameterizedTypeName.create(ClassName.fromClass(Factory.class),
- TypeNames.forTypeMirror(keyType));
- MethodWriter createMethodWriter = factoryWriter.addMethod(returnType, "create");
- createMethodWriter.addTypeParameters(typeParameters);
- createMethodWriter.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- Map<String, TypeName> params = constructorWriter.isPresent()
- ? constructorWriter.get().parameters() : ImmutableMap.<String, TypeName>of();
- for (Map.Entry<String, TypeName> param : params.entrySet()) {
- createMethodWriter.addParameter(param.getValue(), param.getKey());
- }
- switch (binding.factoryCreationStrategy()) {
- case ENUM_INSTANCE:
- if (typeParameters.isEmpty()) {
- createMethodWriter.body().addSnippet("return INSTANCE;");
- } else {
- // We use an unsafe cast here because the types are different.
- // It's safe because the type is never referenced anywhere.
- createMethodWriter.annotate(SuppressWarnings.class).setValue("unchecked");
- createMethodWriter.body().addSnippet("return (Factory) INSTANCE;");
- }
- break;
- case CLASS_CONSTRUCTOR:
- createMethodWriter.body().addSnippet("return new %s(%s);",
- parameterizedGeneratedTypeNameForBinding(binding),
- Joiner.on(", ").join(params.keySet()));
- break;
- default:
- throw new AssertionError();
- }
- break;
- default: // do nothing.
- }
-
- List<Snippet> parameters = Lists.newArrayList();
- for (DependencyRequest dependency : binding.dependencies()) {
- parameters.add(frameworkTypeUsageStatement(
- Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind()));
- }
- Snippet parametersSnippet = makeParametersSnippet(parameters);
-
- if (binding.bindingKind().equals(PROVISION)) {
- Snippet providesMethodInvocation = Snippet.format("%s.%s(%s)",
- binding.bindingElement().getModifiers().contains(STATIC)
- ? ClassName.fromTypeElement(binding.bindingTypeElement())
- : "module",
- binding.bindingElement().getSimpleName(),
- parametersSnippet);
-
- if (binding.provisionType().equals(SET)) {
- TypeName paramTypeName = TypeNames.forTypeMirror(
- MoreTypes.asDeclared(keyType).getTypeArguments().get(0));
- // TODO(cgruber): only be explicit with the parameter if paramType contains wildcards.
- getMethodWriter.body().addSnippet("return %s.<%s>singleton(%s);",
- ClassName.fromClass(Collections.class), paramTypeName, providesMethodInvocation);
- } else if (binding.nullableType().isPresent()
- || nullableValidationType.equals(Diagnostic.Kind.WARNING)) {
- if (binding.nullableType().isPresent()) {
- getMethodWriter.annotate(
- (ClassName) TypeNames.forTypeMirror(binding.nullableType().get()));
- }
- getMethodWriter.body().addSnippet("return %s;", providesMethodInvocation);
- } else {
- StringLiteral failMsg =
- StringLiteral.forValue(CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD);
- getMethodWriter.body().addSnippet(Snippet.format(Joiner.on('\n').join(
- "%s provided = %s;",
- "if (provided == null) {",
- " throw new NullPointerException(%s);",
- "}",
- "return provided;"),
- getMethodWriter.returnType(),
- providesMethodInvocation,
- failMsg));
- }
- } else if (binding.membersInjectionRequest().isPresent()) {
- getMethodWriter.body().addSnippet("%1$s instance = new %1$s(%2$s);",
- providedTypeName, parametersSnippet);
- getMethodWriter.body().addSnippet("membersInjector.injectMembers(instance);");
- getMethodWriter.body().addSnippet("return instance;");
- } else {
- getMethodWriter.body()
- .addSnippet("return new %s(%s);", providedTypeName, parametersSnippet);
- }
-
- // TODO(gak): write a sensible toString
- return ImmutableSet.of(writer);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/Formatter.java b/compiler/src/main/java/dagger/internal/codegen/Formatter.java
deleted file mode 100644
index 880b787..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/Formatter.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Function;
-
-/**
- * A formatter which transforms an instance of a particular type into a string
- * representation.
- *
- * @param <T> the type of the object to be transformed.
- * @author Christian Gruber
- * @since 2.0
- */
-abstract class Formatter<T> implements Function<T, String> {
-
- /**
- * Performs the transformation of an object into a string representation.
- */
- public abstract String format(T object);
-
- /**
- * Performs the transformation of an object into a string representation in
- * conformity with the {@link Function}{@code <T, String>} contract, delegating
- * to {@link #format(Object)}.
- *
- * @deprecated Call {@link #format(T)} instead. This method exists to make
- * formatters easy to use when functions are required, but shouldn't be called directly.
- */
- @SuppressWarnings("javadoc")
- @Deprecated
- @Override final public String apply(T object) {
- return format(object);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java b/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java
deleted file mode 100644
index 38e8f02..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.CaseFormat;
-import com.google.common.collect.ImmutableSet;
-import dagger.MembersInjector;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import javax.lang.model.element.ElementVisitor;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor6;
-
-import static com.google.common.collect.Iterables.any;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ContributionBinding.contributionTypeFor;
-
-/**
- * A value object that represents a field used by Dagger-generated code.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-@AutoValue
-abstract class FrameworkField {
- // TODO(gak): reexamine the this class and how consistently we're using it and its creation
- // methods
- static FrameworkField createWithTypeFromKey(
- Class<?> frameworkClass, BindingKey bindingKey, String name) {
- String suffix = frameworkClass.getSimpleName();
- ParameterizedTypeName frameworkType = ParameterizedTypeName.create(
- ClassName.fromClass(frameworkClass),
- TypeNames.forTypeMirror(bindingKey.key().type()));
- return new AutoValue_FrameworkField(frameworkClass, frameworkType, bindingKey,
- name.endsWith(suffix) ? name : name + suffix);
- }
-
- private static FrameworkField createForMapBindingContribution(
- Class<?> frameworkClass, BindingKey bindingKey, String name) {
- TypeMirror mapValueType =
- MoreTypes.asDeclared(bindingKey.key().type()).getTypeArguments().get(1);
- return new AutoValue_FrameworkField(frameworkClass,
- (ParameterizedTypeName) TypeNames.forTypeMirror(mapValueType),
- bindingKey,
- name);
- }
-
- static FrameworkField createForSyntheticContributionBinding(
- int contributionNumber, ContributionBinding contributionBinding) {
- switch (contributionBinding.contributionType()) {
- case MAP:
- return createForMapBindingContribution(
- contributionBinding.frameworkClass(),
- contributionBinding.bindingKey(),
- KeyVariableNamer.INSTANCE.apply(contributionBinding.key())
- + "Contribution"
- + contributionNumber);
-
- case SET:
- case UNIQUE:
- return createWithTypeFromKey(
- contributionBinding.frameworkClass(),
- contributionBinding.bindingKey(),
- KeyVariableNamer.INSTANCE.apply(contributionBinding.key())
- + "Contribution"
- + contributionNumber);
- default:
- throw new AssertionError();
- }
- }
-
- static FrameworkField createForResolvedBindings(ResolvedBindings resolvedBindings) {
- BindingKey bindingKey = resolvedBindings.bindingKey();
- switch (bindingKey.kind()) {
- case CONTRIBUTION:
- ImmutableSet<ContributionBinding> contributionBindings =
- resolvedBindings.contributionBindings();
- switch (contributionTypeFor(contributionBindings)) {
- case SET:
- case MAP:
- return createWithTypeFromKey(
- FrameworkField.frameworkClassForResolvedBindings(resolvedBindings),
- bindingKey,
- KeyVariableNamer.INSTANCE.apply(bindingKey.key()));
- case UNIQUE:
- ContributionBinding binding = getOnlyElement(contributionBindings);
- return createWithTypeFromKey(
- FrameworkField.frameworkClassForResolvedBindings(resolvedBindings),
- bindingKey,
- BINDING_ELEMENT_NAME.visit(binding.bindingElement()));
- default:
- throw new AssertionError();
- }
- case MEMBERS_INJECTION:
- return createWithTypeFromKey(
- MembersInjector.class,
- bindingKey,
- CaseFormat.UPPER_CAMEL.to(
- CaseFormat.LOWER_CAMEL,
- resolvedBindings
- .membersInjectionBinding()
- .get()
- .bindingElement()
- .getSimpleName()
- .toString()));
- default:
- throw new AssertionError();
- }
- }
-
- private static final ElementVisitor<String, Void> BINDING_ELEMENT_NAME =
- new ElementKindVisitor6<String, Void>() {
- @Override
- public String visitExecutableAsConstructor(ExecutableElement e, Void p) {
- return visit(e.getEnclosingElement());
- }
-
- @Override
- public String visitExecutableAsMethod(ExecutableElement e, Void p) {
- return e.getSimpleName().toString();
- }
-
- @Override
- public String visitType(TypeElement e, Void p) {
- return CaseFormat.UPPER_CAMEL.to(
- CaseFormat.LOWER_CAMEL, e.getSimpleName().toString());
- }
- };
-
- static Class<?> frameworkClassForResolvedBindings(ResolvedBindings resolvedBindings) {
- switch (resolvedBindings.bindingKey().kind()) {
- case CONTRIBUTION:
- return any(resolvedBindings.contributionBindings(), Binding.Type.PRODUCTION)
- ? Binding.Type.PRODUCTION.frameworkClass()
- : Binding.Type.PROVISION.frameworkClass();
- case MEMBERS_INJECTION:
- return MembersInjector.class;
- default:
- throw new AssertionError();
- }
- }
-
- abstract Class<?> frameworkClass();
- abstract ParameterizedTypeName frameworkType();
- abstract BindingKey bindingKey();
- abstract String name();
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java b/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java
deleted file mode 100644
index f7ca429..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import dagger.Component;
-import dagger.Provides;
-import dagger.internal.codegen.writer.ClassName;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic.Kind;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-
-/**
- * Maintains the collection of provision bindings from {@link Inject} constructors and members
- * injection bindings from {@link Inject} fields and methods known to the annotation processor.
- * Note that this registry <b>does not</b> handle any explicit bindings (those from {@link Provides}
- * methods, {@link Component} dependencies, etc.).
- *
- * @author Gregory Kick
- */
-final class InjectBindingRegistry {
- private final Elements elements;
- private final Types types;
- private final Messager messager;
- private final ProvisionBinding.Factory provisionBindingFactory;
- private final MembersInjectionBinding.Factory membersInjectionBindingFactory;
-
- final class BindingsCollection<B extends Binding> {
- private final Map<Key, B> bindingsByKey = Maps.newLinkedHashMap();
- private final Deque<B> bindingsRequiringGeneration = new ArrayDeque<>();
- private final Set<Key> materializedBindingKeys = Sets.newLinkedHashSet();
-
- void generateBindings(SourceFileGenerator<B> generator) throws SourceFileGenerationException {
- for (B binding = bindingsRequiringGeneration.poll();
- binding != null;
- binding = bindingsRequiringGeneration.poll()) {
- checkState(!binding.hasNonDefaultTypeParameters());
- generator.generate(binding);
- materializedBindingKeys.add(binding.key());
- }
- // Because Elements instantiated across processing rounds are not guaranteed to be equals() to
- // the logically same element, clear the cache after generating
- bindingsByKey.clear();
- }
-
- /** Returns a previously cached binding. */
- B getBinding(Key key) {
- return bindingsByKey.get(key);
- }
-
- /** Caches the binding and generates it if it needs generation. */
- void tryRegisterBinding(B binding, ClassName factoryName, boolean explicit) {
- tryToCacheBinding(binding);
- tryToGenerateBinding(binding, factoryName, explicit);
- }
-
- /**
- * Tries to generate a binding, not generating if it already is generated. For resolved
- * bindings, this will try to generate the unresolved version of the binding.
- */
- void tryToGenerateBinding(B binding, ClassName factoryName, boolean explicit) {
- if (shouldGenerateBinding(binding, factoryName)) {
- bindingsRequiringGeneration.offer(binding);
- if (!explicit) {
- messager.printMessage(Kind.NOTE, String.format(
- "Generating a MembersInjector or Factory for %s. "
- + "Prefer to run the dagger processor over that class instead.",
- types.erasure(binding.key().type()))); // erasure to strip <T> from msgs.
- }
- }
- }
-
- /** Returns true if the binding needs to be generated. */
- private boolean shouldGenerateBinding(B binding, ClassName factoryName) {
- return !binding.hasNonDefaultTypeParameters()
- && elements.getTypeElement(factoryName.canonicalName()) == null
- && !materializedBindingKeys.contains(binding.key())
- && !bindingsRequiringGeneration.contains(binding);
-
- }
-
- /** Caches the binding for future lookups by key. */
- private void tryToCacheBinding(B binding) {
- // We only cache resolved bindings or unresolved bindings w/o type arguments.
- // Unresolved bindings w/ type arguments aren't valid for the object graph.
- if (binding.hasNonDefaultTypeParameters()
- || binding.bindingTypeElement().getTypeParameters().isEmpty()) {
- Key key = binding.key();
- Binding previousValue = bindingsByKey.put(key, binding);
- checkState(previousValue == null || binding.equals(previousValue),
- "couldn't register %s. %s was already registered for %s",
- binding, previousValue, key);
- }
- }
- }
-
- private final BindingsCollection<ProvisionBinding> provisionBindings = new BindingsCollection<>();
- private final BindingsCollection<MembersInjectionBinding> membersInjectionBindings =
- new BindingsCollection<>();
-
- InjectBindingRegistry(Elements elements,
- Types types,
- Messager messager,
- ProvisionBinding.Factory provisionBindingFactory,
- MembersInjectionBinding.Factory membersInjectionBindingFactory) {
- this.elements = elements;
- this.types = types;
- this.messager = messager;
- this.provisionBindingFactory = provisionBindingFactory;
- this.membersInjectionBindingFactory = membersInjectionBindingFactory;
- }
-
- /**
- * This method ensures that sources for all registered {@link Binding bindings} (either
- * {@linkplain #registerBinding explicitly} or implicitly via
- * {@link #getOrFindMembersInjectionBinding} or {@link #getOrFindProvisionBinding}) are generated.
- */
- void generateSourcesForRequiredBindings(FactoryGenerator factoryGenerator,
- MembersInjectorGenerator membersInjectorGenerator) throws SourceFileGenerationException {
- provisionBindings.generateBindings(factoryGenerator);
- membersInjectionBindings.generateBindings(membersInjectorGenerator);
- }
-
- ProvisionBinding registerBinding(ProvisionBinding binding) {
- return registerBinding(binding, true);
- }
-
- MembersInjectionBinding registerBinding(MembersInjectionBinding binding) {
- return registerBinding(binding, true);
- }
-
- /**
- * Registers the binding for generation & later lookup. If the binding is resolved, we also
- * attempt to register an unresolved version of it.
- */
- private ProvisionBinding registerBinding(ProvisionBinding binding, boolean explicit) {
- ClassName factoryName = generatedClassNameForBinding(binding);
- provisionBindings.tryRegisterBinding(binding, factoryName, explicit);
- if (binding.hasNonDefaultTypeParameters()) {
- provisionBindings.tryToGenerateBinding(provisionBindingFactory.unresolve(binding),
- factoryName, explicit);
- }
- return binding;
- }
-
- /**
- * Registers the binding for generation & later lookup. If the binding is resolved, we also
- * attempt to register an unresolved version of it.
- */
- private MembersInjectionBinding registerBinding(
- MembersInjectionBinding binding, boolean explicit) {
- ClassName membersInjectorName = generatedClassNameForBinding(binding);
- membersInjectionBindings.tryRegisterBinding(binding, membersInjectorName, explicit);
- if (binding.hasNonDefaultTypeParameters()) {
- membersInjectionBindings.tryToGenerateBinding(
- membersInjectionBindingFactory.unresolve(binding), membersInjectorName, explicit);
- }
- return binding;
- }
-
- Optional<ProvisionBinding> getOrFindProvisionBinding(Key key) {
- checkNotNull(key);
- if (!key.isValidImplicitProvisionKey(types)) {
- return Optional.absent();
- }
- ProvisionBinding binding = provisionBindings.getBinding(key);
- if (binding != null) {
- return Optional.of(binding);
- }
-
- // ok, let's see if we can find an @Inject constructor
- TypeElement element = MoreElements.asType(types.asElement(key.type()));
- List<ExecutableElement> constructors =
- ElementFilter.constructorsIn(element.getEnclosedElements());
- ImmutableSet<ExecutableElement> injectConstructors = FluentIterable.from(constructors)
- .filter(new Predicate<ExecutableElement>() {
- @Override public boolean apply(ExecutableElement input) {
- return isAnnotationPresent(input, Inject.class);
- }
- }).toSet();
- switch (injectConstructors.size()) {
- case 0:
- // No constructor found.
- return Optional.absent();
- case 1:
- ProvisionBinding constructorBinding = provisionBindingFactory.forInjectConstructor(
- Iterables.getOnlyElement(injectConstructors), Optional.of(key.type()));
- return Optional.of(registerBinding(constructorBinding, false));
- default:
- throw new IllegalStateException("Found multiple @Inject constructors: "
- + injectConstructors);
- }
- }
-
- MembersInjectionBinding getOrFindMembersInjectionBinding(Key key) {
- checkNotNull(key);
- // TODO(gak): is checking the kind enough?
- checkArgument(key.isValidMembersInjectionKey());
- MembersInjectionBinding binding = membersInjectionBindings.getBinding(key);
- if (binding != null) {
- return binding;
- }
- return registerBinding(membersInjectionBindingFactory.forInjectedType(
- MoreTypes.asDeclared(key.type()), Optional.of(key.type())), false);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectConstructorValidator.java b/compiler/src/main/java/dagger/internal/codegen/InjectConstructorValidator.java
deleted file mode 100644
index 11cd066..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/InjectConstructorValidator.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.util.ElementFilter;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static dagger.internal.codegen.ErrorMessages.INJECT_CONSTRUCTOR_ON_ABSTRACT_CLASS;
-import static dagger.internal.codegen.ErrorMessages.INJECT_CONSTRUCTOR_ON_INNER_CLASS;
-import static dagger.internal.codegen.ErrorMessages.INJECT_INTO_PRIVATE_CLASS;
-import static dagger.internal.codegen.ErrorMessages.INJECT_ON_PRIVATE_CONSTRUCTOR;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_INJECT_CONSTRUCTORS;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_QUALIFIERS;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_SCOPES;
-import static dagger.internal.codegen.ErrorMessages.QUALIFIER_ON_INJECT_CONSTRUCTOR;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static dagger.internal.codegen.InjectionAnnotations.getScopes;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Inject} constructors.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class InjectConstructorValidator {
- ValidationReport<TypeElement> validate(ExecutableElement constructorElement) {
- ValidationReport.Builder<TypeElement> builder =
- ValidationReport.about(MoreElements.asType(constructorElement.getEnclosingElement()));
- if (constructorElement.getModifiers().contains(PRIVATE)) {
- builder.addError(INJECT_ON_PRIVATE_CONSTRUCTOR, constructorElement);
- }
-
- for (AnnotationMirror qualifier : getQualifiers(constructorElement)) {
- builder.addError(QUALIFIER_ON_INJECT_CONSTRUCTOR, constructorElement, qualifier);
- }
-
- for (VariableElement parameter : constructorElement.getParameters()) {
- ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(parameter);
- if (qualifiers.size() > 1) {
- for (AnnotationMirror qualifier : qualifiers) {
- builder.addError(MULTIPLE_QUALIFIERS, constructorElement, qualifier);
- }
- }
- }
-
- TypeElement enclosingElement =
- MoreElements.asType(constructorElement.getEnclosingElement());
- Set<Modifier> typeModifiers = enclosingElement.getModifiers();
-
- if (typeModifiers.contains(PRIVATE)) {
- builder.addError(INJECT_INTO_PRIVATE_CLASS, constructorElement);
- }
-
- if (typeModifiers.contains(ABSTRACT)) {
- builder.addError(INJECT_CONSTRUCTOR_ON_ABSTRACT_CLASS, constructorElement);
- }
-
- if (enclosingElement.getNestingKind().isNested()
- && !typeModifiers.contains(STATIC)) {
- builder.addError(INJECT_CONSTRUCTOR_ON_INNER_CLASS, constructorElement);
- }
-
- // This is computationally expensive, but probably preferable to a giant index
- FluentIterable<ExecutableElement> injectConstructors = FluentIterable.from(
- ElementFilter.constructorsIn(enclosingElement.getEnclosedElements()))
- .filter(new Predicate<ExecutableElement>() {
- @Override public boolean apply(ExecutableElement input) {
- return isAnnotationPresent(input, Inject.class);
- }
- });
-
- if (injectConstructors.size() > 1) {
- builder.addError(MULTIPLE_INJECT_CONSTRUCTORS, constructorElement);
- }
-
- ImmutableSet<? extends AnnotationMirror> scopes = getScopes(enclosingElement);
- if (scopes.size() > 1) {
- for (AnnotationMirror scope : scopes) {
- builder.addError(MULTIPLE_SCOPES, enclosingElement, scope);
- }
- }
-
- return builder.build();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java b/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java
deleted file mode 100644
index e30678a..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.VariableElement;
-import javax.tools.Diagnostic.Kind;
-
-import static dagger.internal.codegen.ErrorMessages.FINAL_INJECT_FIELD;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_QUALIFIERS;
-import static dagger.internal.codegen.ErrorMessages.PRIVATE_INJECT_FIELD;
-import static dagger.internal.codegen.ErrorMessages.STATIC_INJECT_FIELD;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Inject} fields.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class InjectFieldValidator {
- private Kind privateMemberValidationKind;
- private Kind staticMemberValidationKind;
-
- public InjectFieldValidator(
- Kind privateMemberValidationKind, Kind staticMemberValidationKind) {
- this.privateMemberValidationKind = privateMemberValidationKind;
- this.staticMemberValidationKind = staticMemberValidationKind;
- }
-
- ValidationReport<VariableElement> validate(VariableElement fieldElement) {
- ValidationReport.Builder<VariableElement> builder = ValidationReport.about(fieldElement);
- Set<Modifier> modifiers = fieldElement.getModifiers();
- if (modifiers.contains(FINAL)) {
- builder.addError(FINAL_INJECT_FIELD, fieldElement);
- }
-
- if (modifiers.contains(PRIVATE)) {
- builder.addItem(PRIVATE_INJECT_FIELD, privateMemberValidationKind, fieldElement);
- }
-
- if (modifiers.contains(STATIC)) {
- builder.addItem(STATIC_INJECT_FIELD, staticMemberValidationKind, fieldElement);
- }
-
- ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(fieldElement);
- if (qualifiers.size() > 1) {
- for (AnnotationMirror qualifier : qualifiers) {
- builder.addError(MULTIPLE_QUALIFIERS, fieldElement, qualifier);
- }
- }
-
- return builder.build();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectMethodValidator.java b/compiler/src/main/java/dagger/internal/codegen/InjectMethodValidator.java
deleted file mode 100644
index a716b7d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/InjectMethodValidator.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.VariableElement;
-import javax.tools.Diagnostic.Kind;
-
-import static dagger.internal.codegen.ErrorMessages.ABSTRACT_INJECT_METHOD;
-import static dagger.internal.codegen.ErrorMessages.GENERIC_INJECT_METHOD;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_QUALIFIERS;
-import static dagger.internal.codegen.ErrorMessages.PRIVATE_INJECT_METHOD;
-import static dagger.internal.codegen.ErrorMessages.STATIC_INJECT_METHOD;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Inject} methods.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class InjectMethodValidator {
- private Kind privateMemberValidationKind;
- private Kind staticMemberValidationKind;
-
- public InjectMethodValidator(
- Kind privateMemberValidationKind, Kind staticMemberValidationKind) {
- this.privateMemberValidationKind = privateMemberValidationKind;
- this.staticMemberValidationKind = staticMemberValidationKind;
- }
-
- ValidationReport<ExecutableElement> validate(ExecutableElement methodElement) {
- ValidationReport.Builder<ExecutableElement> builder = ValidationReport.about(methodElement);
- Set<Modifier> modifiers = methodElement.getModifiers();
- if (modifiers.contains(ABSTRACT)) {
- builder.addError(ABSTRACT_INJECT_METHOD, methodElement);
- }
-
- if (modifiers.contains(PRIVATE)) {
- builder.addItem(PRIVATE_INJECT_METHOD, privateMemberValidationKind, methodElement);
- }
-
- if (modifiers.contains(STATIC)) {
- builder.addItem(STATIC_INJECT_METHOD, staticMemberValidationKind, methodElement);
- }
-
- if (!methodElement.getTypeParameters().isEmpty()) {
- builder.addError(GENERIC_INJECT_METHOD, methodElement);
- }
-
- for (VariableElement parameter : methodElement.getParameters()) {
- ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(parameter);
- if (qualifiers.size() > 1) {
- for (AnnotationMirror qualifier : qualifiers) {
- builder.addError(MULTIPLE_QUALIFIERS, methodElement, qualifier);
- }
- }
- }
-
- return builder.build();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java
deleted file mode 100644
index dac904f..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
-
-import java.lang.annotation.Annotation;
-import java.util.Set;
-
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor6;
-
-/**
- * An annotation processor for generating Dagger implementation code based on the {@link Inject}
- * annotation.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class InjectProcessingStep implements BasicAnnotationProcessor.ProcessingStep {
- private final Messager messager;
- private final InjectConstructorValidator constructorValidator;
- private final InjectFieldValidator fieldValidator;
- private final InjectMethodValidator methodValidator;
- private final ProvisionBinding.Factory provisionBindingFactory;
- private final MembersInjectionBinding.Factory membersInjectionBindingFactory;
- private final InjectBindingRegistry injectBindingRegistry;
-
- InjectProcessingStep(
- Messager messager,
- InjectConstructorValidator constructorValidator,
- InjectFieldValidator fieldValidator,
- InjectMethodValidator methodValidator,
- ProvisionBinding.Factory provisionBindingFactory,
- MembersInjectionBinding.Factory membersInjectionBindingFactory,
- InjectBindingRegistry factoryRegistrar) {
- this.messager = messager;
- this.constructorValidator = constructorValidator;
- this.fieldValidator = fieldValidator;
- this.methodValidator = methodValidator;
- this.provisionBindingFactory = provisionBindingFactory;
- this.membersInjectionBindingFactory = membersInjectionBindingFactory;
- this.injectBindingRegistry = factoryRegistrar;
- }
-
- @Override
- public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.<Class<? extends Annotation>>of(Inject.class);
- }
-
- @Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
- // TODO(gak): add some error handling for bad source files
- final ImmutableSet.Builder<ProvisionBinding> provisions = ImmutableSet.builder();
- // TODO(gak): instead, we should collect reports by type and check later
- final ImmutableSet.Builder<DeclaredType> membersInjectedTypes = ImmutableSet.builder();
-
- for (Element injectElement : elementsByAnnotation.get(Inject.class)) {
- try {
- injectElement.accept(
- new ElementKindVisitor6<Void, Void>() {
- @Override
- public Void visitExecutableAsConstructor(
- ExecutableElement constructorElement, Void v) {
- ValidationReport<TypeElement> report =
- constructorValidator.validate(constructorElement);
-
- report.printMessagesTo(messager);
-
- if (report.isClean()) {
- provisions.add(
- provisionBindingFactory.forInjectConstructor(
- constructorElement, Optional.<TypeMirror>absent()));
- DeclaredType type =
- MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
- if (membersInjectionBindingFactory.hasInjectedMembers(type)) {
- membersInjectedTypes.add(type);
- }
- }
-
- return null;
- }
-
- @Override
- public Void visitVariableAsField(VariableElement fieldElement, Void p) {
- ValidationReport<VariableElement> report = fieldValidator.validate(fieldElement);
-
- report.printMessagesTo(messager);
-
- if (report.isClean()) {
- membersInjectedTypes.add(
- MoreTypes.asDeclared(fieldElement.getEnclosingElement().asType()));
- }
-
- return null;
- }
-
- @Override
- public Void visitExecutableAsMethod(ExecutableElement methodElement, Void p) {
- ValidationReport<ExecutableElement> report =
- methodValidator.validate(methodElement);
-
- report.printMessagesTo(messager);
-
- if (report.isClean()) {
- membersInjectedTypes.add(
- MoreTypes.asDeclared(methodElement.getEnclosingElement().asType()));
- }
-
- return null;
- }
- },
- null);
- } catch (TypeNotPresentException e) {
- rejectedElements.add(injectElement);
- }
- }
-
- for (DeclaredType injectedType : membersInjectedTypes.build()) {
- injectBindingRegistry.registerBinding(membersInjectionBindingFactory.forInjectedType(
- injectedType, Optional.<TypeMirror>absent()));
- }
-
- for (ProvisionBinding binding : provisions.build()) {
- injectBindingRegistry.registerBinding(binding);
- }
- return rejectedElements.build();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectionAnnotations.java b/compiler/src/main/java/dagger/internal/codegen/InjectionAnnotations.java
deleted file mode 100644
index b3b245d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/InjectionAnnotations.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.AnnotationMirrors;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import javax.inject.Qualifier;
-import javax.inject.Scope;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Utilities relating to annotations defined in the {@code javax.inject} package.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class InjectionAnnotations {
- static Optional<AnnotationMirror> getScopeAnnotation(Element e) {
- checkNotNull(e);
- ImmutableSet<? extends AnnotationMirror> scopeAnnotations = getScopes(e);
- switch (scopeAnnotations.size()) {
- case 0:
- return Optional.absent();
- case 1:
- return Optional.<AnnotationMirror>of(scopeAnnotations.iterator().next());
- default:
- throw new IllegalArgumentException(
- e + " was annotated with more than one @Scope annotation");
- }
- }
-
- static Optional<AnnotationMirror> getQualifier(Element e) {
- checkNotNull(e);
- ImmutableSet<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
- switch (qualifierAnnotations.size()) {
- case 0:
- return Optional.absent();
- case 1:
- return Optional.<AnnotationMirror>of(qualifierAnnotations.iterator().next());
- default:
- throw new IllegalArgumentException(
- e + " was annotated with more than one @Qualifier annotation");
- }
- }
-
- static ImmutableSet<? extends AnnotationMirror> getQualifiers(Element element) {
- return AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
- }
-
- static ImmutableSet<? extends AnnotationMirror> getScopes(Element element) {
- return AnnotationMirrors.getAnnotatedAnnotations(element, Scope.class);
- }
-
- private InjectionAnnotations() {}
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/Key.java b/compiler/src/main/java/dagger/internal/codegen/Key.java
deleted file mode 100644
index f0bd3a0..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/Key.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.AnnotationMirrors;
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Provides;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.Produces;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Provider;
-import javax.inject.Qualifier;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.MoreTypes.asExecutable;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
-import static dagger.internal.codegen.MapKeys.getMapKey;
-import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType;
-import static dagger.internal.codegen.Util.unwrapOptionalEquivalence;
-import static dagger.internal.codegen.Util.wrapOptionalInEquivalence;
-import static javax.lang.model.element.ElementKind.METHOD;
-
-/**
- * Represents a unique combination of {@linkplain TypeMirror type} and
- * {@linkplain Qualifier qualifier} to which binding can occur.
- *
- * @author Gregory Kick
- */
-@AutoValue
-abstract class Key {
- /**
- * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix
- * for the type of this key.
- *
- * Despite documentation in {@link AnnotationMirror}, equals and hashCode aren't implemented
- * to represent logical equality, so {@link AnnotationMirrors#equivalence()}
- * provides this facility.
- */
- abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier();
-
- /**
- * The type represented by this key.
- *
- * As documented in {@link TypeMirror}, equals and hashCode aren't implemented to represent
- * logical equality, so {@link MoreTypes#equivalence()} wraps this type.
- */
- abstract Equivalence.Wrapper<TypeMirror> wrappedType();
-
- Optional<AnnotationMirror> qualifier() {
- return unwrapOptionalEquivalence(wrappedQualifier());
- }
-
- TypeMirror type() {
- return wrappedType().get();
- }
-
- private static TypeMirror normalize(Types types, TypeMirror type) {
- TypeKind kind = type.getKind();
- return kind.isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type;
- }
-
- Key withType(Types types, TypeMirror newType) {
- return new AutoValue_Key(wrappedQualifier(),
- MoreTypes.equivalence().wrap(normalize(types, newType)));
- }
-
- boolean isValidMembersInjectionKey() {
- return !qualifier().isPresent();
- }
-
- /**
- * Returns true if the key is valid as an implicit key (that is, if it's valid for a just-in-time
- * binding by discovering an {@code @Inject} constructor).
- */
- boolean isValidImplicitProvisionKey(final Types types) {
- // Qualifiers disqualify implicit provisioning.
- if (qualifier().isPresent()) {
- return false;
- }
-
- return type().accept(new SimpleTypeVisitor6<Boolean, Void>() {
- @Override protected Boolean defaultAction(TypeMirror e, Void p) {
- return false; // Only declared types are allowed.
- }
-
- @Override public Boolean visitDeclared(DeclaredType type, Void ignored) {
- // Non-classes or abstract classes aren't allowed.
- TypeElement element = MoreElements.asType(type.asElement());
- if (!element.getKind().equals(ElementKind.CLASS)
- || element.getModifiers().contains(Modifier.ABSTRACT)) {
- return false;
- }
-
- // If the key has type arguments, validate that each type argument is declared.
- // Otherwise the type argument may be a wildcard (or other type), and we can't
- // resolve that to actual types.
- for (TypeMirror arg : type.getTypeArguments()) {
- if (arg.getKind() != TypeKind.DECLARED) {
- return false;
- }
- }
-
- // Also validate that the key is not the erasure of a generic type.
- // If it is, that means the user referred to Foo<T> as just 'Foo',
- // which we don't allow. (This is a judgement call -- we *could*
- // allow it and instantiate the type bounds... but we don't.)
- return MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty()
- || !types.isSameType(types.erasure(element.asType()), type());
- }
- }, null);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(Key.class)
- .omitNullValues()
- .add("qualifier", qualifier().orNull())
- .add("type", type())
- .toString();
- }
-
- static final class Factory {
- private final Types types;
- private final Elements elements;
-
- Factory(Types types, Elements elements) {
- this.types = checkNotNull(types);
- this.elements = checkNotNull(elements);
- }
-
- private TypeElement getSetElement() {
- return elements.getTypeElement(Set.class.getCanonicalName());
- }
-
- private TypeElement getMapElement() {
- return elements.getTypeElement(Map.class.getCanonicalName());
- }
-
- private TypeElement getProviderElement() {
- return elements.getTypeElement(Provider.class.getCanonicalName());
- }
-
- private TypeElement getProducerElement() {
- return elements.getTypeElement(Producer.class.getCanonicalName());
- }
-
- private TypeElement getClassElement(Class<?> cls) {
- return elements.getTypeElement(cls.getCanonicalName());
- }
-
- Key forComponentMethod(ExecutableElement componentMethod) {
- checkNotNull(componentMethod);
- checkArgument(componentMethod.getKind().equals(METHOD));
- TypeMirror returnType = normalize(types, componentMethod.getReturnType());
- return forMethod(componentMethod, returnType);
- }
-
- Key forProductionComponentMethod(ExecutableElement componentMethod) {
- checkNotNull(componentMethod);
- checkArgument(componentMethod.getKind().equals(METHOD));
- TypeMirror returnType = normalize(types, componentMethod.getReturnType());
- TypeMirror keyType = returnType;
- if (MoreTypes.isTypeOf(ListenableFuture.class, returnType)) {
- keyType = Iterables.getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
- }
- return forMethod(componentMethod, keyType);
- }
-
- Key forSubcomponentBuilderMethod(
- ExecutableElement subcomponentBuilderMethod, DeclaredType declaredContainer) {
- checkNotNull(subcomponentBuilderMethod);
- checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
- ExecutableType resolvedMethod =
- asExecutable(types.asMemberOf(declaredContainer, subcomponentBuilderMethod));
- TypeMirror returnType = normalize(types, resolvedMethod.getReturnType());
- return forMethod(subcomponentBuilderMethod, returnType);
- }
-
- Key forProvidesMethod(ExecutableType executableType, ExecutableElement method) {
- checkNotNull(method);
- checkArgument(method.getKind().equals(METHOD));
- Provides providesAnnotation = method.getAnnotation(Provides.class);
- checkArgument(providesAnnotation != null);
- TypeMirror returnType = normalize(types, executableType.getReturnType());
- TypeMirror keyType =
- providesOrProducesKeyType(
- returnType,
- method,
- Optional.of(providesAnnotation.type()),
- Optional.<Produces.Type>absent());
- return forMethod(method, keyType);
- }
-
- // TODO(user): Reconcile this method with forProvidesMethod when Provides.Type and
- // Produces.Type are no longer different.
- Key forProducesMethod(ExecutableType executableType, ExecutableElement method) {
- checkNotNull(method);
- checkArgument(method.getKind().equals(METHOD));
- Produces producesAnnotation = method.getAnnotation(Produces.class);
- checkArgument(producesAnnotation != null);
- TypeMirror returnType = normalize(types, executableType.getReturnType());
- TypeMirror unfuturedType = returnType;
- if (MoreTypes.isTypeOf(ListenableFuture.class, returnType)) {
- unfuturedType =
- Iterables.getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
- }
- TypeMirror keyType =
- providesOrProducesKeyType(
- unfuturedType,
- method,
- Optional.<Provides.Type>absent(),
- Optional.of(producesAnnotation.type()));
- return forMethod(method, keyType);
- }
-
- private TypeMirror providesOrProducesKeyType(
- TypeMirror returnType,
- ExecutableElement method,
- Optional<Provides.Type> providesType,
- Optional<Produces.Type> producesType) {
- switch (providesType.isPresent()
- ? providesType.get()
- : Provides.Type.valueOf(producesType.get().name())) {
- case UNIQUE:
- return returnType;
- case SET:
- return types.getDeclaredType(getSetElement(), returnType);
- case MAP:
- return mapOfFactoryType(
- method,
- returnType,
- providesType.isPresent() ? getProviderElement() : getProducerElement());
- case SET_VALUES:
- // TODO(gak): do we want to allow people to use "covariant return" here?
- checkArgument(MoreTypes.isType(returnType) && MoreTypes.isTypeOf(Set.class, returnType));
- return returnType;
- default:
- throw new AssertionError();
- }
- }
-
- private TypeMirror mapOfFactoryType(
- ExecutableElement method, TypeMirror valueType, TypeElement factoryType) {
- TypeMirror mapKeyType = mapKeyType(method);
- TypeMirror mapValueFactoryType = types.getDeclaredType(factoryType, valueType);
- return types.getDeclaredType(getMapElement(), mapKeyType, mapValueFactoryType);
- }
-
- private TypeMirror mapKeyType(ExecutableElement method) {
- AnnotationMirror mapKeyAnnotation = getMapKey(method).get();
- return MapKeys.unwrapValue(mapKeyAnnotation).isPresent()
- ? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types)
- : mapKeyAnnotation.getAnnotationType();
- }
-
- private Key forMethod(ExecutableElement method, TypeMirror keyType) {
- return new AutoValue_Key(
- wrapOptionalInEquivalence(AnnotationMirrors.equivalence(), getQualifier(method)),
- MoreTypes.equivalence().wrap(keyType));
- }
-
- Key forInjectConstructorWithResolvedType(TypeMirror type) {
- return new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(type));
- }
-
- Key forComponent(TypeMirror type) {
- return new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(normalize(types, type)));
- }
-
- Key forMembersInjectedType(TypeMirror type) {
- return new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(normalize(types, type)));
- }
-
- Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) {
- return new AutoValue_Key(
- wrapOptionalInEquivalence(AnnotationMirrors.equivalence(), qualifier),
- MoreTypes.equivalence().wrap(normalize(types, type)));
- }
-
- /**
- * Optionally extract a {@link Key} for the underlying provision binding(s) if such a
- * valid key can be inferred from the given key. Specifically, if the key represents a
- * {@link Map}{@code <K, V>}, a key of {@code Map<K, Provider<V>>} will be returned.
- */
- Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
- return maybeWrapMapValue(possibleMapKey, Provider.class);
- }
-
- /**
- * Optionally extract a {@link Key} for the underlying production binding(s) if such a
- * valid key can be inferred from the given key. Specifically, if the key represents a
- * {@link Map}{@code <K, V>}, a key of {@code Map<K, Producer<V>>} will be returned.
- */
- Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) {
- return maybeWrapMapValue(possibleMapKey, Producer.class);
- }
-
- /**
- * Returns a key of {@link Map}{@code <K, WrappingClass<V>>} if the input key represents a
- * {@code Map<K, V>}.
- */
- private Optional<Key> maybeWrapMapValue(Key possibleMapKey, Class<?> wrappingClass) {
- if (MoreTypes.isTypeOf(Map.class, possibleMapKey.type())) {
- DeclaredType declaredMapType = MoreTypes.asDeclared(possibleMapKey.type());
- TypeMirror mapValueType = Util.getValueTypeOfMap(declaredMapType);
- if (!MoreTypes.isTypeOf(wrappingClass, mapValueType)) {
- TypeMirror keyType = Util.getKeyTypeOfMap(declaredMapType);
- TypeElement wrappingElement = getClassElement(wrappingClass);
- if (wrappingElement == null) {
- // This target might not be compiled with Producers, so wrappingClass might not have an
- // associated element.
- return Optional.absent();
- }
- DeclaredType wrappedType = types.getDeclaredType(wrappingElement, mapValueType);
- TypeMirror mapType = types.getDeclaredType(getMapElement(), keyType, wrappedType);
- return Optional.<Key>of(new AutoValue_Key(
- possibleMapKey.wrappedQualifier(),
- MoreTypes.equivalence().wrap(mapType)));
- }
- }
- return Optional.absent();
- }
-
- /**
- * Optionally extract a {@link Key} for a {@code Set<T>} if the given key is for
- * {@code Set<Produced<T>>}.
- */
- Optional<Key> implicitSetKeyFromProduced(Key possibleSetOfProducedKey) {
- if (MoreTypes.isTypeOf(Set.class, possibleSetOfProducedKey.type())) {
- TypeMirror argType =
- MoreTypes.asDeclared(possibleSetOfProducedKey.type()).getTypeArguments().get(0);
- if (MoreTypes.isTypeOf(Produced.class, argType)) {
- TypeMirror producedArgType = MoreTypes.asDeclared(argType).getTypeArguments().get(0);
- TypeMirror setType = types.getDeclaredType(getSetElement(), producedArgType);
- return Optional.of(possibleSetOfProducedKey.withType(types, setType));
- }
- }
- return Optional.absent();
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java b/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java
deleted file mode 100644
index 6e695f3..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-/**
- * Formats a {@link Key} into a {@link String} suitable for use in error messages
- *
- * @author Christian Gruber
- * @since 2.0
- */
-final class KeyFormatter extends Formatter<Key> {
-
- @Override public String format(Key request) {
- StringBuilder builder = new StringBuilder();
- if (request.qualifier().isPresent()) {
- builder.append(request.qualifier().get());
- builder.append(' ');
- }
- builder.append(request.type()); // TODO(cgruber): Use TypeMirrorFormatter.
- return builder.toString();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/KeyVariableNamer.java b/compiler/src/main/java/dagger/internal/codegen/KeyVariableNamer.java
deleted file mode 100644
index 5fe12b1..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/KeyVariableNamer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Function;
-import java.util.Iterator;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-
-/**
- * Suggests a variable name for a type based on a {@link Key}. Prefer
- * {@link DependencyVariableNamer} for cases where a specific {@link DependencyRequest} is present.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-enum KeyVariableNamer implements Function<Key, String> {
- INSTANCE;
-
- @Override
- public String apply(Key key) {
- StringBuilder builder = new StringBuilder();
-
- if (key.qualifier().isPresent()) {
- // TODO(gak): Use a better name for fields with qualifiers with members.
- builder.append(key.qualifier().get().getAnnotationType().asElement().getSimpleName());
- }
-
- key.type().accept(new SimpleTypeVisitor6<Void, StringBuilder>() {
- @Override
- public Void visitDeclared(DeclaredType t, StringBuilder builder) {
- builder.append(t.asElement().getSimpleName());
- Iterator<? extends TypeMirror> argumentIterator = t.getTypeArguments().iterator();
- if (argumentIterator.hasNext()) {
- builder.append("Of");
- TypeMirror first = argumentIterator.next();
- first.accept(this, builder);
- while (argumentIterator.hasNext()) {
- builder.append("And");
- argumentIterator.next().accept(this, builder);
- }
- }
- return null;
- }
- }, builder);
-
- return UPPER_CAMEL.to(LOWER_CAMEL, builder.toString());
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MapKeyGenerator.java b/compiler/src/main/java/dagger/internal/codegen/MapKeyGenerator.java
deleted file mode 100644
index 8d72e5e..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MapKeyGenerator.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoAnnotation;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import dagger.MapKey;
-import dagger.internal.codegen.MapKeyGenerator.MapKeyCreatorSpecification;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import dagger.internal.codegen.writer.TypeWriter;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import javax.annotation.Generated;
-import javax.annotation.processing.Filer;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-import static dagger.internal.codegen.MapKeys.getMapKeyCreatorClassName;
-import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-/**
- * Generates classes that create annotations required to instantiate {@link MapKey}s.
- *
- * @since 2.0
- */
-final class MapKeyGenerator extends SourceFileGenerator<MapKeyCreatorSpecification> {
-
- /**
- * Specification of the {@link MapKey} annotation and the annotation type to generate.
- */
- @AutoValue
- abstract static class MapKeyCreatorSpecification {
- /**
- * The {@link MapKey}-annotated annotation.
- */
- abstract TypeElement mapKeyElement();
-
- /**
- * The annotation type to write create methods for. For wrapped {@link MapKey}s, this is
- * {@link #mapKeyElement()}. For unwrapped {@code MapKey}s whose single element is an
- * annotation, this is that annotation element.
- */
- abstract TypeElement annotationElement();
-
- /**
- * Returns a specification for a wrapped {@link MapKey}-annotated annotation.
- */
- static MapKeyCreatorSpecification wrappedMapKey(TypeElement mapKeyElement) {
- return new AutoValue_MapKeyGenerator_MapKeyCreatorSpecification(mapKeyElement, mapKeyElement);
- }
-
- /**
- * Returns a specification for an unwrapped {@link MapKey}-annotated annotation whose single
- * element is a nested annotation.
- */
- static MapKeyCreatorSpecification unwrappedMapKeyWithAnnotationValue(
- TypeElement mapKeyElement, TypeElement annotationElement) {
- return new AutoValue_MapKeyGenerator_MapKeyCreatorSpecification(
- mapKeyElement, annotationElement);
- }
- }
-
- MapKeyGenerator(Filer filer) {
- super(filer);
- }
-
- @Override
- ClassName nameGeneratedType(MapKeyCreatorSpecification mapKeyCreatorType) {
- return getMapKeyCreatorClassName(mapKeyCreatorType.mapKeyElement());
- }
-
- @Override
- Iterable<? extends Element> getOriginatingElements(MapKeyCreatorSpecification mapKeyCreatorType) {
- return ImmutableSet.of(mapKeyCreatorType.mapKeyElement());
- }
-
- @Override
- Optional<? extends Element> getElementForErrorReporting(
- MapKeyCreatorSpecification mapKeyCreatorType) {
- return Optional.of(mapKeyCreatorType.mapKeyElement());
- }
-
- @Override
- ImmutableSet<JavaWriter> write(
- ClassName generatedTypeName, MapKeyCreatorSpecification mapKeyCreatorType) {
- JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
- TypeWriter mapKeyCreatorWriter = writer.addClass(generatedTypeName.simpleName());
- mapKeyCreatorWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName());
- mapKeyCreatorWriter.addModifiers(PUBLIC, FINAL);
-
- for (TypeElement annotationElement :
- nestedAnnotationElements(mapKeyCreatorType.annotationElement())) {
- writeCreateMethod(mapKeyCreatorWriter, annotationElement);
- }
-
- return ImmutableSet.of(writer);
- }
-
- private void writeCreateMethod(TypeWriter mapKeyCreatorWriter, TypeElement annotationElement) {
- MethodWriter createMethod =
- mapKeyCreatorWriter.addMethod(
- annotationElement.asType(), "create" + annotationElement.getSimpleName());
-
- createMethod.annotate(AutoAnnotation.class);
- createMethod.addModifiers(PUBLIC, STATIC);
-
- ImmutableList.Builder<Snippet> parameters = ImmutableList.builder();
- for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
- String parameterName = annotationMember.getSimpleName().toString();
- TypeName parameterType = TypeNames.forTypeMirror(annotationMember.getReturnType());
- createMethod.addParameter(parameterType, parameterName);
- parameters.add(Snippet.format("%s", parameterName));
- }
-
- ClassName autoAnnotationClass = mapKeyCreatorWriter.name().peerNamed(
- "AutoAnnotation_" + mapKeyCreatorWriter.name().simpleName() + "_" + createMethod.name());
- createMethod.body().addSnippet(
- "return new %s(%s);", autoAnnotationClass, makeParametersSnippet(parameters.build()));
- }
-
- private static Set<TypeElement> nestedAnnotationElements(TypeElement annotationElement) {
- return nestedAnnotationElements(annotationElement, new LinkedHashSet<TypeElement>());
- }
-
- private static Set<TypeElement> nestedAnnotationElements(
- TypeElement annotationElement, Set<TypeElement> annotationElements) {
- if (annotationElements.add(annotationElement)) {
- for (ExecutableElement method : methodsIn(annotationElement.getEnclosedElements())) {
- TRAVERSE_NESTED_ANNOTATIONS.visit(method.getReturnType(), annotationElements);
- }
- }
- return annotationElements;
- }
-
- private static final SimpleTypeVisitor6<Void, Set<TypeElement>> TRAVERSE_NESTED_ANNOTATIONS =
- new SimpleTypeVisitor6<Void, Set<TypeElement>>() {
- @Override
- public Void visitDeclared(DeclaredType t, Set<TypeElement> p) {
- TypeElement typeElement = MoreTypes.asTypeElement(t);
- if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
- nestedAnnotationElements(typeElement, p);
- }
- return null;
- }
- };
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MapKeyProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/MapKeyProcessingStep.java
deleted file mode 100644
index c4a264a..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MapKeyProcessingStep.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor;
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
-import dagger.MapKey;
-import dagger.internal.codegen.MapKeyGenerator.MapKeyCreatorSpecification;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.util.Types;
-
-import static dagger.internal.codegen.MapKeyGenerator.MapKeyCreatorSpecification.unwrappedMapKeyWithAnnotationValue;
-import static dagger.internal.codegen.MapKeyGenerator.MapKeyCreatorSpecification.wrappedMapKey;
-import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType;
-
-/**
- * The annotation processor responsible for validating the mapKey annotation and auto-generate
- * implementation of annotations marked with @MapKey where necessary.
- *
- * @author Chenying Hou
- * @since 2.0
- */
-public class MapKeyProcessingStep implements BasicAnnotationProcessor.ProcessingStep {
- private final Messager messager;
- private final Types types;
- private final MapKeyValidator mapKeyValidator;
- private final MapKeyGenerator mapKeyGenerator;
-
- MapKeyProcessingStep(
- Messager messager,
- Types types,
- MapKeyValidator mapKeyValidator,
- MapKeyGenerator mapKeyGenerator) {
- this.messager = messager;
- this.types = types;
- this.mapKeyValidator = mapKeyValidator;
- this.mapKeyGenerator = mapKeyGenerator;
- }
-
- @Override
- public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.<Class<? extends Annotation>>of(MapKey.class);
- }
-
- @Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- for (Element element : elementsByAnnotation.get(MapKey.class)) {
- ValidationReport<Element> mapKeyReport = mapKeyValidator.validate(element);
- mapKeyReport.printMessagesTo(messager);
-
- if (mapKeyReport.isClean()) {
- MapKey mapkey = element.getAnnotation(MapKey.class);
- if (mapkey.unwrapValue()) {
- DeclaredType keyType =
- getUnwrappedMapKeyType(MoreTypes.asDeclared(element.asType()), types);
- if (keyType.asElement().getKind() == ElementKind.ANNOTATION_TYPE) {
- writeCreatorClass(
- unwrappedMapKeyWithAnnotationValue(
- MoreElements.asType(element), MoreTypes.asTypeElement(keyType)));
- }
- } else {
- writeCreatorClass(wrappedMapKey(MoreElements.asType(element)));
- }
- }
- }
- return ImmutableSet.of();
- }
-
- private void writeCreatorClass(MapKeyCreatorSpecification mapKeyCreatorType) {
- try {
- mapKeyGenerator.generate(mapKeyCreatorType);
- } catch (SourceFileGenerationException e) {
- e.printMessageTo(messager);
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java b/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java
deleted file mode 100644
index 586a1e9..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import dagger.MapKey;
-import java.util.List;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeKind;
-
-import static dagger.internal.codegen.ErrorMessages.MAPKEY_WITHOUT_MEMBERS;
-import static dagger.internal.codegen.ErrorMessages.UNWRAPPED_MAP_KEY_WITH_ARRAY_MEMBER;
-import static dagger.internal.codegen.ErrorMessages.UNWRAPPED_MAP_KEY_WITH_TOO_MANY_MEMBERS;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-/**
- * A validator for {@link MapKey} annotations.
- *
- * @author Chenying Hou
- * @since 2.0
- */
-// TODO(dpb,gak): Should unwrapped MapKeys be required to have their single member be named "value"?
-final class MapKeyValidator {
- ValidationReport<Element> validate(Element element) {
- ValidationReport.Builder<Element> builder = ValidationReport.about(element);
- List<ExecutableElement> members = methodsIn(((TypeElement) element).getEnclosedElements());
- if (members.isEmpty()) {
- builder.addError(MAPKEY_WITHOUT_MEMBERS, element);
- } else if (element.getAnnotation(MapKey.class).unwrapValue()) {
- if (members.size() > 1) {
- builder.addError(UNWRAPPED_MAP_KEY_WITH_TOO_MANY_MEMBERS, element);
- } else if (members.get(0).getReturnType().getKind() == TypeKind.ARRAY) {
- builder.addError(UNWRAPPED_MAP_KEY_WITH_ARRAY_MEMBER, element);
- }
- }
- return builder.build();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MapKeys.java b/compiler/src/main/java/dagger/internal/codegen/MapKeys.java
deleted file mode 100644
index fbbd8cf..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MapKeys.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import dagger.MapKey;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor6;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.google.common.collect.Iterables.transform;
-import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-/**
- * Methods for extracting {@link MapKey} annotations and key snippets from binding elements.
- */
-final class MapKeys {
-
- /**
- * If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it.
- *
- * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
- * annotation
- */
- static Optional<? extends AnnotationMirror> getMapKey(Element bindingElement) {
- ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(bindingElement);
- return mapKeys.isEmpty()
- ? Optional.<AnnotationMirror>absent()
- : Optional.of(getOnlyElement(mapKeys));
- }
-
- /**
- * Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}.
- */
- static ImmutableSet<? extends AnnotationMirror> getMapKeys(Element bindingElement) {
- return getAnnotatedAnnotations(bindingElement, MapKey.class);
- }
-
- /**
- * Returns the annotation value if {@code mapKey}'s type is annotated with
- * {@link MapKey @MapKey(unwrapValue = true)}.
- *
- * @throws IllegalArgumentException if {@code mapKey}'s type is not annotated with
- * {@link MapKey @MapKey} at all.
- */
- static Optional<? extends AnnotationValue> unwrapValue(AnnotationMirror mapKey) {
- MapKey mapKeyAnnotation = mapKey.getAnnotationType().asElement().getAnnotation(MapKey.class);
- checkArgument(
- mapKeyAnnotation != null, "%s is not annotated with @MapKey", mapKey.getAnnotationType());
- return mapKeyAnnotation.unwrapValue()
- ? Optional.of(getOnlyElement(mapKey.getElementValues().values()))
- : Optional.<AnnotationValue>absent();
- }
-
- /**
- * Returns the map key type for an unwrapped {@link MapKey} annotation type. If the single member
- * type is primitive, returns the boxed type.
- *
- * @throws IllegalArgumentException if {@code mapKeyAnnotationType} is not an annotation type or
- * has more than one member, or if its single member is an array
- * @throws NoSuchElementException if the annotation has no members
- */
- public static DeclaredType getUnwrappedMapKeyType(
- final DeclaredType mapKeyAnnotationType, final Types types) {
- checkArgument(
- MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE,
- "%s is not an annotation type",
- mapKeyAnnotationType);
-
- final ExecutableElement onlyElement =
- getOnlyElement(methodsIn(mapKeyAnnotationType.asElement().getEnclosedElements()));
-
- SimpleTypeVisitor6<DeclaredType, Void> keyTypeElementVisitor =
- new SimpleTypeVisitor6<DeclaredType, Void>() {
-
- @Override
- public DeclaredType visitArray(ArrayType t, Void p) {
- throw new IllegalArgumentException(
- mapKeyAnnotationType + "." + onlyElement.getSimpleName() + " cannot be an array");
- }
-
- @Override
- public DeclaredType visitPrimitive(PrimitiveType t, Void p) {
- return MoreTypes.asDeclared(types.boxedClass(t).asType());
- }
-
- @Override
- public DeclaredType visitDeclared(DeclaredType t, Void p) {
- return t;
- }
- };
- return keyTypeElementVisitor.visit(onlyElement.getReturnType());
- }
-
- /**
- * Returns the name of the generated class that contains the static {@code create} methods for a
- * {@link MapKey} annotation type.
- */
- public static ClassName getMapKeyCreatorClassName(TypeElement mapKeyType) {
- ClassName mapKeyTypeName = ClassName.fromTypeElement(mapKeyType);
- return mapKeyTypeName.topLevelClassName().peerNamed(mapKeyTypeName.classFileName() + "Creator");
- }
-
- /**
- * Returns a snippet for the map key specified by the {@link MapKey} annotation on
- * {@code bindingElement}.
- *
- * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
- * annotation
- * @throws IllegalStateException if {@code bindingElement} is not annotated with a {@code MapKey}
- * annotation
- */
- static Snippet getMapKeySnippet(Element bindingElement) {
- AnnotationMirror mapKey = getMapKey(bindingElement).get();
- ClassName mapKeyCreator =
- getMapKeyCreatorClassName(MoreTypes.asTypeElement(mapKey.getAnnotationType()));
- Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey);
- if (unwrappedValue.isPresent()) {
- return new MapKeySnippetExceptArrays(mapKeyCreator)
- .visit(unwrappedValue.get(), unwrappedValue.get());
- } else {
- return annotationSnippet(mapKey, new MapKeySnippet(mapKeyCreator));
- }
- }
-
- /**
- * Returns a snippet to create the visited value in code. Expects its parameter to be a class with
- * static creation methods for all nested annotation types.
- *
- * <p>Note that {@link AnnotationValue#toString()} is the source-code representation of the value
- * <em>when used in an annotation</em>, which is not always the same as the representation needed
- * when creating the value in a method body.
- *
- * <p>For example, inside an annotation, a nested array of {@code int}s is simply
- * <code>{1, 2, 3}</code>, but in code it would have to be <code> new int[] {1, 2, 3}</code>.
- */
- private static class MapKeySnippet
- extends SimpleAnnotationValueVisitor6<Snippet, AnnotationValue> {
-
- final ClassName mapKeyCreator;
-
- MapKeySnippet(ClassName mapKeyCreator) {
- this.mapKeyCreator = mapKeyCreator;
- }
-
- @Override
- public Snippet visitEnumConstant(VariableElement c, AnnotationValue p) {
- return Snippet.format(
- "%s.%s", TypeNames.forTypeMirror(c.getEnclosingElement().asType()), c.getSimpleName());
- }
-
- @Override
- public Snippet visitAnnotation(AnnotationMirror a, AnnotationValue p) {
- return annotationSnippet(a, this);
- }
-
- @Override
- public Snippet visitType(TypeMirror t, AnnotationValue p) {
- return Snippet.format("%s.class", TypeNames.forTypeMirror(t));
- }
-
- @Override
- public Snippet visitString(String s, AnnotationValue p) {
- return Snippet.format("%s", p);
- }
-
- @Override
- public Snippet visitByte(byte b, AnnotationValue p) {
- return Snippet.format("(byte) %s", b);
- }
-
- @Override
- public Snippet visitChar(char c, AnnotationValue p) {
- return Snippet.format("%s", p);
- }
-
- @Override
- public Snippet visitDouble(double d, AnnotationValue p) {
- return Snippet.format("%sD", d);
- }
-
- @Override
- public Snippet visitFloat(float f, AnnotationValue p) {
- return Snippet.format("%sF", f);
- }
-
- @Override
- public Snippet visitInt(int i, AnnotationValue p) {
- return Snippet.format("(int) %s", i);
- }
-
- @Override
- public Snippet visitLong(long i, AnnotationValue p) {
- return Snippet.format("%sL", i);
- }
-
- @Override
- public Snippet visitShort(short s, AnnotationValue p) {
- return Snippet.format("(short) %s", s);
- }
-
- @Override
- protected Snippet defaultAction(Object o, AnnotationValue p) {
- return Snippet.format("%s", o);
- }
-
- @Override
- public Snippet visitArray(List<? extends AnnotationValue> values, AnnotationValue p) {
- ImmutableList.Builder<Snippet> snippets = ImmutableList.builder();
- for (int i = 0; i < values.size(); i++) {
- snippets.add(this.visit(values.get(i), p));
- }
- return Snippet.format("{%s}", makeParametersSnippet(snippets.build()));
- }
- }
-
- /**
- * Returns a snippet for the visited value. Expects its parameter to be a class with static
- * creation methods for all nested annotation types.
- *
- * <p>Throws {@link IllegalArgumentException} if the visited value is an array.
- */
- private static class MapKeySnippetExceptArrays extends MapKeySnippet {
-
- MapKeySnippetExceptArrays(ClassName mapKeyCreator) {
- super(mapKeyCreator);
- }
-
- @Override
- public Snippet visitArray(List<? extends AnnotationValue> values, AnnotationValue p) {
- throw new IllegalArgumentException("Cannot unwrap arrays");
- }
- }
-
- /**
- * Returns a snippet that calls a static method on {@code mapKeySnippet.mapKeyCreator} to create
- * an annotation from {@code mapKeyAnnotation}.
- */
- private static Snippet annotationSnippet(
- AnnotationMirror mapKeyAnnotation, final MapKeySnippet mapKeySnippet) {
- return Snippet.format(
- "%s.create%s(%s)",
- mapKeySnippet.mapKeyCreator,
- mapKeyAnnotation.getAnnotationType().asElement().getSimpleName(),
- makeParametersSnippet(
- transform(
- getAnnotationValuesWithDefaults(mapKeyAnnotation).entrySet(),
- new Function<Map.Entry<ExecutableElement, AnnotationValue>, Snippet>() {
- @Override
- public Snippet apply(Map.Entry<ExecutableElement, AnnotationValue> entry) {
- return ARRAY_LITERAL_PREFIX.visit(
- entry.getKey().getReturnType(),
- mapKeySnippet.visit(entry.getValue(), entry.getValue()));
- }
- })));
- }
-
- /**
- * If the visited type is an array, prefixes the parameter snippet with {@code new T[]}, where
- * {@code T} is the raw array component type.
- */
- private static final SimpleTypeVisitor6<Snippet, Snippet> ARRAY_LITERAL_PREFIX =
- new SimpleTypeVisitor6<Snippet, Snippet>() {
-
- @Override
- public Snippet visitArray(ArrayType t, Snippet p) {
- return Snippet.format("new %s[] %s", RAW_TYPE_NAME.visit(t.getComponentType()), p);
- }
-
- @Override
- protected Snippet defaultAction(TypeMirror e, Snippet p) {
- return p;
- }
- };
-
- /**
- * If the visited type is an array, returns the name of its raw component type; otherwise returns
- * the name of the type itself.
- */
- private static final SimpleTypeVisitor6<TypeName, Void> RAW_TYPE_NAME =
- new SimpleTypeVisitor6<TypeName, Void>() {
- @Override
- public TypeName visitDeclared(DeclaredType t, Void p) {
- return ClassName.fromTypeElement(MoreTypes.asTypeElement(t));
- }
-
- @Override
- protected TypeName defaultAction(TypeMirror e, Void p) {
- return TypeNames.forTypeMirror(e);
- }
- };
-
- private MapKeys() {}
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java b/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java
deleted file mode 100644
index 7fbcf11..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.SetMultimap;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ElementVisitor;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor6;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * Represents the full members injection of a particular type. This does not pay attention to
- * injected members on supertypes.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@AutoValue
-abstract class MembersInjectionBinding extends Binding {
- @Override abstract TypeElement bindingElement();
-
- /** The set of individual sites where {@link Inject} is applied. */
- abstract ImmutableSortedSet<InjectionSite> injectionSites();
-
- abstract Optional<DependencyRequest> parentInjectorRequest();
-
- enum Strategy {
- NO_OP,
- INJECT_MEMBERS,
- }
-
- Strategy injectionStrategy() {
- return injectionSites().isEmpty() ? Strategy.NO_OP : Strategy.INJECT_MEMBERS;
- }
-
- MembersInjectionBinding withoutParentInjectorRequest() {
- return new AutoValue_MembersInjectionBinding(
- key(),
- dependencies(),
- implicitDependencies(),
- bindingPackage(),
- hasNonDefaultTypeParameters(),
- bindingElement(),
- injectionSites(),
- Optional.<DependencyRequest>absent());
- }
-
- @Override
- protected Binding.Type bindingType() {
- return Binding.Type.MEMBERS_INJECTION;
- }
-
- @AutoValue
- abstract static class InjectionSite {
- enum Kind {
- FIELD,
- METHOD,
- }
-
- abstract Kind kind();
-
- abstract Element element();
-
- abstract ImmutableSet<DependencyRequest> dependencies();
-
- protected int indexAmongSiblingMembers(InjectionSite injectionSite) {
- return injectionSite
- .element()
- .getEnclosingElement()
- .getEnclosedElements()
- .indexOf(injectionSite.element());
- }
- }
-
- static final class Factory {
- private final Elements elements;
- private final Types types;
- private final Key.Factory keyFactory;
- private final DependencyRequest.Factory dependencyRequestFactory;
-
- Factory(Elements elements, Types types, Key.Factory keyFactory,
- DependencyRequest.Factory dependencyRequestFactory) {
- this.elements = checkNotNull(elements);
- this.types = checkNotNull(types);
- this.keyFactory = checkNotNull(keyFactory);
- this.dependencyRequestFactory = checkNotNull(dependencyRequestFactory);
- }
-
- private InjectionSite injectionSiteForInjectMethod(
- ExecutableElement methodElement, DeclaredType containingType) {
- checkNotNull(methodElement);
- checkArgument(methodElement.getKind().equals(ElementKind.METHOD));
- ExecutableType resolved =
- MoreTypes.asExecutable(types.asMemberOf(containingType, methodElement));
- return new AutoValue_MembersInjectionBinding_InjectionSite(
- InjectionSite.Kind.METHOD,
- methodElement,
- dependencyRequestFactory.forRequiredResolvedVariables(
- containingType, methodElement.getParameters(), resolved.getParameterTypes()));
- }
-
- private InjectionSite injectionSiteForInjectField(
- VariableElement fieldElement, DeclaredType containingType) {
- checkNotNull(fieldElement);
- checkArgument(fieldElement.getKind().equals(ElementKind.FIELD));
- checkArgument(isAnnotationPresent(fieldElement, Inject.class));
- TypeMirror resolved = types.asMemberOf(containingType, fieldElement);
- return new AutoValue_MembersInjectionBinding_InjectionSite(
- InjectionSite.Kind.FIELD,
- fieldElement,
- ImmutableSet.of(
- dependencyRequestFactory.forRequiredResolvedVariable(
- containingType, fieldElement, resolved)));
- }
-
- /** Returns an unresolved version of this binding. */
- MembersInjectionBinding unresolve(MembersInjectionBinding binding) {
- checkState(binding.hasNonDefaultTypeParameters());
- DeclaredType unresolved = MoreTypes.asDeclared(binding.bindingElement().asType());
- return forInjectedType(unresolved, Optional.<TypeMirror>absent());
- }
-
- /** Returns true if the type has some injected members in itself or any of its super classes. */
- boolean hasInjectedMembers(DeclaredType declaredType) {
- return !getInjectionSites(declaredType).isEmpty();
- }
-
- /**
- * Returns a MembersInjectionBinding for the given type. If {@code resolvedType} is present,
- * this will return a resolved binding, with the key & type resolved to the given type (using
- * {@link Types#asMemberOf(DeclaredType, Element)}).
- */
- MembersInjectionBinding forInjectedType(
- DeclaredType declaredType, Optional<TypeMirror> resolvedType) {
- // If the class this is injecting has some type arguments, resolve everything.
- if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
- DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
- // Validate that we're resolving from the correct type.
- checkState(
- types.isSameType(types.erasure(resolved), types.erasure(declaredType)),
- "erased expected type: %s, erased actual type: %s",
- types.erasure(resolved),
- types.erasure(declaredType));
- declaredType = resolved;
- }
- ImmutableSortedSet<InjectionSite> injectionSites = getInjectionSites(declaredType);
- ImmutableSet<DependencyRequest> dependencies =
- FluentIterable.from(injectionSites)
- .transformAndConcat(
- new Function<InjectionSite, Set<DependencyRequest>>() {
- @Override
- public Set<DependencyRequest> apply(InjectionSite input) {
- return input.dependencies();
- }
- })
- .toSet();
-
- Optional<DependencyRequest> parentInjectorRequest =
- MoreTypes.nonObjectSuperclass(types, elements, declaredType)
- .transform(
- new Function<DeclaredType, DependencyRequest>() {
- @Override
- public DependencyRequest apply(DeclaredType input) {
- return dependencyRequestFactory.forMembersInjectedType(input);
- }
- });
-
- Key key = keyFactory.forMembersInjectedType(declaredType);
- TypeElement typeElement = MoreElements.asType(declaredType.asElement());
- return new AutoValue_MembersInjectionBinding(
- key,
- dependencies,
- dependencies,
- findBindingPackage(key),
- hasNonDefaultTypeParameters(typeElement, key.type(), types),
- typeElement,
- injectionSites,
- parentInjectorRequest);
- }
-
- private ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
- Set<InjectionSite> injectionSites = new HashSet<>();
- final List<TypeElement> ancestors = new ArrayList<>();
- SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create();
- for (Optional<DeclaredType> currentType = Optional.of(declaredType);
- currentType.isPresent();
- currentType = MoreTypes.nonObjectSuperclass(types, elements, currentType.get())) {
- final DeclaredType type = currentType.get();
- ancestors.add(MoreElements.asType(type.asElement()));
- for (Element enclosedElement : type.asElement().getEnclosedElements()) {
- Optional<InjectionSite> maybeInjectionSite =
- injectionSiteVisitor.visit(enclosedElement, type);
- if (maybeInjectionSite.isPresent()) {
- InjectionSite injectionSite = maybeInjectionSite.get();
- if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) {
- injectionSites.add(injectionSite);
- }
- if (injectionSite.kind() == InjectionSite.Kind.METHOD) {
- ExecutableElement injectionSiteMethod =
- MoreElements.asExecutable(injectionSite.element());
- overriddenMethodMap.put(
- injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod);
- }
- }
- }
- }
- return ImmutableSortedSet.copyOf(
- new Comparator<InjectionSite>() {
- @Override
- public int compare(InjectionSite left, InjectionSite right) {
- return ComparisonChain.start()
- // supertypes before subtypes
- .compare(
- ancestors.indexOf(right.element().getEnclosingElement()),
- ancestors.indexOf(left.element().getEnclosingElement()))
- // fields before methods
- .compare(left.element().getKind(), right.element().getKind())
- // then sort by whichever element comes first in the parent
- // this isn't necessary, but makes the processor nice and predictable
- .compare(
- left.indexAmongSiblingMembers(left), right.indexAmongSiblingMembers(right))
- .result();
- }
- },
- injectionSites);
- }
-
- private boolean shouldBeInjected(
- Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) {
- if (!isAnnotationPresent(injectionSite, Inject.class)
- || injectionSite.getModifiers().contains(PRIVATE)
- || injectionSite.getModifiers().contains(STATIC)) {
- return false;
- }
-
- if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors)
- return true;
- }
-
- // For each method with the same name belonging to any descendant class, return false if any
- // method has already overridden the injectionSite method. To decrease the number of methods
- // that are checked, we store the already injected methods in a SetMultimap and only
- // check the methods with the same name.
- ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite);
- TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement());
- for (ExecutableElement method :
- overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) {
- if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) {
- return false;
- }
- }
- return true;
- }
-
- private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor =
- new ElementKindVisitor6<Optional<InjectionSite>, DeclaredType>(
- Optional.<InjectionSite>absent()) {
- @Override
- public Optional<InjectionSite> visitExecutableAsMethod(
- ExecutableElement e, DeclaredType type) {
- return Optional.of(injectionSiteForInjectMethod(e, type));
- }
-
- @Override
- public Optional<InjectionSite> visitVariableAsField(
- VariableElement e, DeclaredType type) {
- return (isAnnotationPresent(e, Inject.class)
- && !e.getModifiers().contains(PRIVATE)
- && !e.getModifiers().contains(STATIC))
- ? Optional.of(injectionSiteForInjectField(e, type))
- : Optional.<InjectionSite>absent();
- }
- };
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java b/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java
deleted file mode 100644
index 3694d2e..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import dagger.MembersInjector;
-import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.ConstructorWriter;
-import dagger.internal.codegen.writer.FieldWriter;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import dagger.internal.codegen.writer.Modifiable;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import dagger.internal.codegen.writer.TypeVariableName;
-import dagger.internal.codegen.writer.VariableWriter;
-import dagger.internal.codegen.writer.VoidName;
-import dagger.producers.Producer;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-import javax.annotation.Generated;
-import javax.annotation.processing.Filer;
-import javax.inject.Provider;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeVisitor;
-import javax.lang.model.util.SimpleTypeVisitor7;
-
-import static com.google.auto.common.MoreElements.getPackage;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
-import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * Generates {@link MembersInjector} implementations from {@link MembersInjectionBinding} instances.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class MembersInjectorGenerator extends SourceFileGenerator<MembersInjectionBinding> {
- private final DependencyRequestMapper dependencyRequestMapper;
-
- MembersInjectorGenerator(
- Filer filer,
- DependencyRequestMapper dependencyRequestMapper) {
- super(filer);
- this.dependencyRequestMapper = dependencyRequestMapper;
- }
-
- @Override
- ClassName nameGeneratedType(MembersInjectionBinding binding) {
- return membersInjectorNameForType(binding.bindingElement());
- }
-
- @Override
- Iterable<? extends Element> getOriginatingElements(
- MembersInjectionBinding binding) {
- return FluentIterable.from(binding.injectionSites())
- .transform(new Function<InjectionSite, Element>() {
- @Override public Element apply(InjectionSite injectionSite) {
- return injectionSite.element();
- }
- })
- .toSet();
- }
-
- @Override
- Optional<? extends Element> getElementForErrorReporting(MembersInjectionBinding binding) {
- return Optional.of(binding.bindingElement());
- }
-
- @Override
- ImmutableSet<JavaWriter> write(ClassName generatedTypeName, MembersInjectionBinding binding) {
- // Empty members injection bindings are special and don't need source files.
- if (binding.injectionSites().isEmpty()) {
- return ImmutableSet.of();
- }
- Set<String> delegateMethods = new HashSet<>();
-
- // We don't want to write out resolved bindings -- we want to write out the generic version.
- checkState(!binding.hasNonDefaultTypeParameters());
-
- TypeName injectedTypeName = TypeNames.forTypeMirror(binding.key().type());
- JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
-
- ClassWriter injectorWriter = writer.addClass(generatedTypeName.simpleName());
- List<TypeVariableName> typeParameters = Lists.newArrayList();
- for (TypeParameterElement typeParameter : binding.bindingTypeElement().getTypeParameters()) {
- typeParameters.add(TypeVariableName.fromTypeParameterElement(typeParameter));
- }
- injectorWriter.addTypeParameters(typeParameters);
- injectorWriter.annotate(Generated.class)
- .setValue(ComponentProcessor.class.getCanonicalName());
- injectorWriter.addModifiers(PUBLIC, FINAL);
- TypeName implementedType =
- ParameterizedTypeName.create(MembersInjector.class, injectedTypeName);
- injectorWriter.addImplementedType(implementedType);
-
- ConstructorWriter constructorWriter = injectorWriter.addConstructor();
- constructorWriter.addModifiers(PUBLIC);
- MethodWriter injectMembersWriter = injectorWriter.addMethod(VoidName.VOID, "injectMembers");
- injectMembersWriter.addModifiers(PUBLIC);
- injectMembersWriter.annotate(Override.class);
- injectMembersWriter.addParameter(injectedTypeName, "instance");
- injectMembersWriter.body().addSnippet(Joiner.on('\n').join(
- "if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- "}"));
-
- ImmutableMap<BindingKey, FrameworkField> fields =
- SourceFiles.generateBindingFieldsForDependencies(
- dependencyRequestMapper, ImmutableSet.copyOf(binding.dependencies()));
-
- ImmutableMap.Builder<BindingKey, FieldWriter> dependencyFieldsBuilder =
- ImmutableMap.builder();
-
- // We use a static create method so that generated components can avoid having
- // to refer to the generic types of the factory.
- // (Otherwise they may have visibility problems referring to the types.)
- MethodWriter createMethodWriter = injectorWriter.addMethod(implementedType, "create");
- createMethodWriter.addTypeParameters(typeParameters);
- createMethodWriter.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
-
- boolean usesRawFrameworkTypes = false;
- for (Entry<BindingKey, FrameworkField> fieldEntry : fields.entrySet()) {
- BindingKey bindingKey = fieldEntry.getKey();
- FrameworkField bindingField = fieldEntry.getValue();
-
- // If the dependency type is not visible to this members injector, then use the raw framework
- // type for the field.
- boolean useRawFrameworkType =
- !VISIBLE_TO_MEMBERS_INJECTOR.visit(bindingKey.key().type(), binding);
-
- FieldWriter field =
- injectorWriter.addField(
- useRawFrameworkType
- ? bindingField.frameworkType().type()
- : bindingField.frameworkType(),
- bindingField.name());
-
- field.addModifiers(PRIVATE, FINAL);
- VariableWriter constructorParameter =
- constructorWriter.addParameter(field.type(), field.name());
- VariableWriter createMethodParameter =
- createMethodWriter.addParameter(constructorParameter.type(), constructorParameter.name());
-
- // If we're using the raw type for the field, then suppress the injectMembers method's
- // unchecked-type warning and the field's and the constructor and create-method's
- // parameters' raw-type warnings.
- if (useRawFrameworkType) {
- usesRawFrameworkTypes = true;
- suppressRawTypesWarning(field);
- suppressRawTypesWarning(constructorParameter);
- suppressRawTypesWarning(createMethodParameter);
- }
-
- constructorWriter.body().addSnippet("assert %s != null;", field.name());
- constructorWriter.body().addSnippet("this.%1$s = %1$s;", field.name());
- dependencyFieldsBuilder.put(bindingKey, field);
- }
-
- createMethodWriter
- .body()
- .addSnippet(
- " return new %s(%s);",
- parameterizedGeneratedTypeNameForBinding(binding),
- Joiner.on(", ").join(constructorWriter.parameters().keySet()));
-
- ImmutableMap<BindingKey, FieldWriter> dependencyFields = dependencyFieldsBuilder.build();
- for (InjectionSite injectionSite : binding.injectionSites()) {
- injectMembersWriter
- .body()
- .addSnippet(
- visibleToMembersInjector(binding, injectionSite.element())
- ? directInjectMemberSnippet(binding, dependencyFields, injectionSite)
- : delegateInjectMemberSnippet(dependencyFields, injectionSite));
- if (!injectionSite.element().getModifiers().contains(PUBLIC)
- && injectionSite.element().getEnclosingElement().equals(binding.bindingElement())
- && delegateMethods.add(injectionSiteDelegateMethodName(injectionSite.element()))) {
- writeInjectorMethodForSubclasses(
- injectorWriter,
- dependencyFields,
- typeParameters,
- injectedTypeName,
- injectionSite.element(),
- injectionSite.dependencies());
- }
- }
-
- if (usesRawFrameworkTypes) {
- injectMembersWriter.annotate(SuppressWarnings.class).setValue("unchecked");
- }
-
- return ImmutableSet.of(writer);
- }
-
- /**
- * Returns {@code true} if {@code element} is visible to the members injector for {@code binding}.
- */
- // TODO(dpb,gak): Make sure that all cases are covered here. E.g., what if element is public but
- // enclosed in a package-private element?
- private static boolean visibleToMembersInjector(
- MembersInjectionBinding binding, Element element) {
- return getPackage(element).equals(getPackage(binding.bindingElement()))
- || element.getModifiers().contains(PUBLIC);
- }
-
- /**
- * Returns a snippet that directly injects the instance's field or method.
- */
- private Snippet directInjectMemberSnippet(
- MembersInjectionBinding binding,
- ImmutableMap<BindingKey, FieldWriter> dependencyFields,
- InjectionSite injectionSite) {
- return Snippet.format(
- injectionSite.element().getKind().isField() ? "%s.%s = %s;" : "%s.%s(%s);",
- getInstanceSnippetWithPotentialCast(
- injectionSite.element().getEnclosingElement(), binding.bindingElement()),
- injectionSite.element().getSimpleName(),
- makeParametersSnippet(
- parameterSnippets(dependencyFields, injectionSite.dependencies(), true)));
- }
-
- /**
- * Returns a snippet that injects the instance's field or method by calling a static method on the
- * parent members injector class.
- */
- private Snippet delegateInjectMemberSnippet(
- ImmutableMap<BindingKey, FieldWriter> dependencyFields, InjectionSite injectionSite) {
- return Snippet.format(
- "%s.%s(%s);",
- membersInjectorNameForType(
- MoreElements.asType(injectionSite.element().getEnclosingElement())),
- injectionSiteDelegateMethodName(injectionSite.element()),
- makeParametersSnippet(
- new ImmutableList.Builder<Snippet>()
- .add(Snippet.format("instance"))
- .addAll(parameterSnippets(dependencyFields, injectionSite.dependencies(), false))
- .build()));
- }
-
- /**
- * Returns the parameters for injecting a member.
- *
- * @param passValue if {@code true}, each parameter snippet will be the result of converting the
- * field from the framework type ({@link Provider}, {@link Producer}, etc.) to the real value;
- * if {@code false}, each parameter snippet will be just the field
- */
- private ImmutableList<Snippet> parameterSnippets(
- ImmutableMap<BindingKey, FieldWriter> dependencyFields,
- ImmutableSet<DependencyRequest> dependencies,
- boolean passValue) {
- ImmutableList.Builder<Snippet> parameters = ImmutableList.builder();
- for (DependencyRequest dependency : dependencies) {
- Snippet fieldSnippet =
- Snippet.format("%s", dependencyFields.get(dependency.bindingKey()).name());
- parameters.add(
- passValue ? frameworkTypeUsageStatement(fieldSnippet, dependency.kind()) : fieldSnippet);
- }
- return parameters.build();
- }
-
- private Snippet getInstanceSnippetWithPotentialCast(
- Element injectionSiteElement, Element bindingElement) {
- return (injectionSiteElement.equals(bindingElement))
- ? Snippet.format("instance")
- : Snippet.format("((%s)instance)", injectionSiteElement);
- }
-
- private String injectionSiteDelegateMethodName(Element injectionSiteElement) {
- return "inject"
- + CaseFormat.LOWER_CAMEL.to(
- CaseFormat.UPPER_CAMEL, injectionSiteElement.getSimpleName().toString());
- }
-
- private void writeInjectorMethodForSubclasses(
- ClassWriter injectorWriter,
- ImmutableMap<BindingKey, FieldWriter> dependencyFields,
- List<TypeVariableName> typeParameters,
- TypeName injectedTypeName,
- Element injectionElement,
- ImmutableSet<DependencyRequest> dependencies) {
- MethodWriter methodWriter =
- injectorWriter.addMethod(VoidName.VOID, injectionSiteDelegateMethodName(injectionElement));
- methodWriter.addModifiers(PUBLIC, STATIC);
- methodWriter.addParameter(injectedTypeName, "instance");
- methodWriter.addTypeParameters(typeParameters);
- ImmutableList.Builder<Snippet> providedParameters = ImmutableList.builder();
- Set<String> parameterNames = new HashSet<>();
- for (DependencyRequest dependency : dependencies) {
- FieldWriter field = dependencyFields.get(dependency.bindingKey());
- VariableWriter parameter =
- methodWriter.addParameter(
- field.type(),
- staticInjectMethodDependencyParameterName(parameterNames, dependency, field));
- providedParameters.add(
- frameworkTypeUsageStatement(Snippet.format("%s", parameter.name()), dependency.kind()));
- }
- if (injectionElement.getKind().isField()) {
- methodWriter
- .body()
- .addSnippet(
- "instance.%s = %s;",
- injectionElement.getSimpleName(),
- getOnlyElement(providedParameters.build()));
- } else {
- methodWriter
- .body()
- .addSnippet(
- "instance.%s(%s);",
- injectionElement.getSimpleName(),
- makeParametersSnippet(providedParameters.build()));
- }
- }
-
- /**
- * Returns the static inject method parameter name for a dependency.
- *
- * @param parameterNames the parameter names used so far
- * @param dependency the dependency
- * @param field the field used to hold the framework type for the dependency
- */
- private String staticInjectMethodDependencyParameterName(
- Set<String> parameterNames, DependencyRequest dependency, FieldWriter field) {
- StringBuilder parameterName =
- new StringBuilder(dependency.requestElement().getSimpleName().toString());
- switch (dependency.kind()) {
- case LAZY:
- case INSTANCE:
- case FUTURE:
- String suffix = ((ParameterizedTypeName) field.type()).type().simpleName();
- if (parameterName.length() <= suffix.length()
- || !parameterName.substring(parameterName.length() - suffix.length()).equals(suffix)) {
- parameterName.append(suffix);
- }
- break;
-
- default:
- break;
- }
- int baseLength = parameterName.length();
- for (int i = 2; !parameterNames.add(parameterName.toString()); i++) {
- parameterName.replace(baseLength, parameterName.length(), String.valueOf(i));
- }
- return parameterName.toString();
- }
-
- private void suppressRawTypesWarning(Modifiable modifiable) {
- modifiable.annotate(SuppressWarnings.class).setValue("rawtypes");
- }
-
- private static final TypeVisitor<Boolean, MembersInjectionBinding> VISIBLE_TO_MEMBERS_INJECTOR =
- new SimpleTypeVisitor7<Boolean, MembersInjectionBinding>(true) {
- @Override
- public Boolean visitArray(ArrayType t, MembersInjectionBinding p) {
- return visit(t.getComponentType(), p);
- }
-
- @Override
- public Boolean visitDeclared(DeclaredType t, MembersInjectionBinding p) {
- return visibleToMembersInjector(p, t.asElement());
- }
- };
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MethodSignature.java b/compiler/src/main/java/dagger/internal/codegen/MethodSignature.java
deleted file mode 100644
index 447ed24..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MethodSignature.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.ImmutableList;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-@AutoValue
-abstract class MethodSignature {
- abstract String name();
- abstract ImmutableList<Equivalence.Wrapper<TypeMirror>> parameterTypes();
- abstract ImmutableList<Equivalence.Wrapper<TypeMirror>> thrownTypes();
-
- static MethodSignature fromExecutableType(String methodName, ExecutableType methodType) {
- checkNotNull(methodType);
- ImmutableList.Builder<Equivalence.Wrapper<TypeMirror>> parameters = ImmutableList.builder();
- ImmutableList.Builder<Equivalence.Wrapper<TypeMirror>> thrownTypes = ImmutableList.builder();
- for (TypeMirror parameter : methodType.getParameterTypes()) {
- parameters.add(MoreTypes.equivalence().wrap(parameter));
- }
- for (TypeMirror thrownType : methodType.getThrownTypes()) {
- thrownTypes.add(MoreTypes.equivalence().wrap(thrownType));
- }
- return new AutoValue_MethodSignature(
- methodName,
- parameters.build(),
- thrownTypes.build());
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MethodSignatureFormatter.java b/compiler/src/main/java/dagger/internal/codegen/MethodSignatureFormatter.java
deleted file mode 100644
index 078977e..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MethodSignatureFormatter.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Optional;
-import java.util.Iterator;
-import java.util.List;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Types;
-
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.ErrorMessages.stripCommonTypePrefixes;
-
-/**
- * Formats the signature of an {@link ExecutableElement} suitable for use in error messages.
- *
- * @author Christian Gruber
- * @since 2.0
- */
-final class MethodSignatureFormatter extends Formatter<ExecutableElement> {
- private final Types types;
-
- MethodSignatureFormatter(Types types) {
- this.types = types;
- }
-
- @Override public String format(ExecutableElement method) {
- return format(method, Optional.<DeclaredType>absent());
- }
-
- /**
- * Formats an ExecutableElement as if it were contained within the container, if the container is
- * present.
- */
- public String format(ExecutableElement method, Optional<DeclaredType> container) {
- StringBuilder builder = new StringBuilder();
- TypeElement type = MoreElements.asType(method.getEnclosingElement());
- ExecutableType executableType = MoreTypes.asExecutable(method.asType());
- if (container.isPresent()) {
- executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method));
- type = MoreElements.asType(container.get().asElement());
- }
-
- // TODO(cgruber): AnnotationMirror formatter.
- List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors();
- if (!annotations.isEmpty()) {
- Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator();
- for (int i = 0; annotationIterator.hasNext(); i++) {
- if (i > 0) {
- builder.append(' ');
- }
- builder.append(ErrorMessages.format(annotationIterator.next()));
- }
- builder.append(' ');
- }
- builder.append(nameOfType(executableType.getReturnType()));
- builder.append(' ');
- builder.append(type.getQualifiedName());
- builder.append('.');
- builder.append(method.getSimpleName());
- builder.append('(');
- checkState(method.getParameters().size() == executableType.getParameterTypes().size());
- Iterator<? extends VariableElement> parameters = method.getParameters().iterator();
- Iterator<? extends TypeMirror> parameterTypes = executableType.getParameterTypes().iterator();
- for (int i = 0; parameters.hasNext(); i++) {
- if (i > 0) {
- builder.append(", ");
- }
- appendParameter(builder, parameters.next(), parameterTypes.next());
- }
- builder.append(')');
- return builder.toString();
- }
-
- private static void appendParameter(StringBuilder builder, VariableElement parameter,
- TypeMirror type) {
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(parameter);
- if (qualifier.isPresent()) {
- builder.append(ErrorMessages.format(qualifier.get())).append(' ');
- }
- builder.append(nameOfType(type));
- }
-
- private static String nameOfType(TypeMirror type) {
- if (type.getKind().isPrimitive()) {
- return MoreTypes.asPrimitiveType(type).toString();
- } else if (type.getKind() == TypeKind.VOID) {
- return "void";
- } else {
- return stripCommonTypePrefixes(MoreTypes.asDeclared(type).toString());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java b/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java
deleted file mode 100644
index 4b6f0c3..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayDeque;
-import java.util.Deque;
-
-/**
- * Utility code that looks for bindings matching a key in all subcomponents in a binding graph so
- * that a user is advised that a binding exists elsewhere when it is not found in the current
- * subgraph. If a binding matching a key exists in a sub- or sibling component, that is often what
- * the user actually wants to use.
- */
-class MissingBindingSuggestions {
- /**
- * Searches the entire binding graph from the top-level graph for a binding matching
- * {@code key}.
- */
- static ImmutableList<String> forKey(BindingGraph topLevelGraph, BindingKey key) {
- ImmutableList.Builder<String> resolutions = new ImmutableList.Builder<>();
- Deque<BindingGraph> graphsToTry = new ArrayDeque<>();
-
- graphsToTry.add(topLevelGraph);
- do {
- BindingGraph graph = graphsToTry.removeLast();
- ResolvedBindings bindings = graph.resolvedBindings().get(key);
- if ((bindings == null) || bindings.bindings().isEmpty()) {
- graphsToTry.addAll(graph.subgraphs().values());
- } else {
- resolutions.add("A binding with matching key exists in component: "
- + graph.componentDescriptor().componentDefinitionType().getQualifiedName());
- }
- } while (!graphsToTry.isEmpty());
-
- return resolutions.build();
- }
-
- private MissingBindingSuggestions() {}
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java b/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java
deleted file mode 100644
index c938af2..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import dagger.Module;
-import dagger.Provides;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.ConfigurationAnnotations.getModuleIncludes;
-import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.NONE;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-@AutoValue
-abstract class ModuleDescriptor {
- static final Function<ModuleDescriptor, TypeElement> getModuleElement() {
- return new Function<ModuleDescriptor, TypeElement>() {
- @Override public TypeElement apply(ModuleDescriptor input) {
- return input.moduleElement();
- }
- };
- }
-
- abstract AnnotationMirror moduleAnnotation();
-
- abstract TypeElement moduleElement();
-
- abstract ImmutableSet<ModuleDescriptor> includedModules();
-
- abstract ImmutableSet<ContributionBinding> bindings();
-
- enum DefaultCreationStrategy {
- PASSED,
- CONSTRUCTED,
- }
-
- abstract DefaultCreationStrategy defaultCreationStrategy();
-
- static final class Factory {
- private final Elements elements;
- private final ProvisionBinding.Factory provisionBindingFactory;
- private final ProductionBinding.Factory productionBindingFactory;
-
- Factory(
- Elements elements,
- ProvisionBinding.Factory provisionBindingFactory,
- ProductionBinding.Factory productionBindingFactory) {
- this.elements = elements;
- this.provisionBindingFactory = provisionBindingFactory;
- this.productionBindingFactory = productionBindingFactory;
- }
-
- ModuleDescriptor create(TypeElement moduleElement) {
- AnnotationMirror moduleAnnotation = getModuleAnnotation(moduleElement).get();
-
- ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
- for (ExecutableElement moduleMethod : methodsIn(elements.getAllMembers(moduleElement))) {
- if (isAnnotationPresent(moduleMethod, Provides.class)) {
- bindings.add(
- provisionBindingFactory.forProvidesMethod(moduleMethod, moduleElement.asType()));
- }
- if (isAnnotationPresent(moduleMethod, Produces.class)) {
- bindings.add(
- productionBindingFactory.forProducesMethod(moduleMethod, moduleElement.asType()));
- }
- }
-
- DefaultCreationStrategy defaultCreationStrategy =
- (componentCanMakeNewInstances(moduleElement)
- && moduleElement.getTypeParameters().isEmpty())
- ? ModuleDescriptor.DefaultCreationStrategy.CONSTRUCTED
- : ModuleDescriptor.DefaultCreationStrategy.PASSED;
-
- return new AutoValue_ModuleDescriptor(
- moduleAnnotation,
- moduleElement,
- ImmutableSet.copyOf(
- collectIncludedModules(new LinkedHashSet<ModuleDescriptor>(), moduleElement)),
- bindings.build(),
- defaultCreationStrategy);
- }
-
- private static Optional<AnnotationMirror> getModuleAnnotation(TypeElement moduleElement) {
- return getAnnotationMirror(moduleElement, Module.class)
- .or(getAnnotationMirror(moduleElement, ProducerModule.class));
- }
-
- private Set<ModuleDescriptor> collectIncludedModules(
- Set<ModuleDescriptor> includedModules, TypeElement moduleElement) {
- TypeMirror superclass = moduleElement.getSuperclass();
- if (!superclass.getKind().equals(NONE)) {
- verify(superclass.getKind().equals(DECLARED));
- TypeElement superclassElement = MoreTypes.asTypeElement(superclass);
- if (!superclassElement.getQualifiedName().contentEquals(Object.class.getCanonicalName())) {
- collectIncludedModules(includedModules, superclassElement);
- }
- }
- Optional<AnnotationMirror> moduleAnnotation = getModuleAnnotation(moduleElement);
- if (moduleAnnotation.isPresent()) {
- for (TypeMirror moduleIncludesType : getModuleIncludes(moduleAnnotation.get())) {
- includedModules.add(create(MoreTypes.asTypeElement(moduleIncludesType)));
- }
- }
- return includedModules;
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ModuleProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/ModuleProcessingStep.java
deleted file mode 100644
index 1afda7d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ModuleProcessingStep.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor;
-import com.google.auto.common.MoreElements;
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import dagger.Module;
-import dagger.Provides;
-import java.lang.annotation.Annotation;
-import java.util.List;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.ElementFilter;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static javax.lang.model.element.ElementKind.METHOD;
-
-/**
- * An annotation processor for generating Dagger implementation code based on the {@link Module}
- * (and {@link Provides}) annotation.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class ModuleProcessingStep implements BasicAnnotationProcessor.ProcessingStep {
- private final Messager messager;
- private final ModuleValidator moduleValidator;
- private final ProvidesMethodValidator providesMethodValidator;
- private final ProvisionBinding.Factory provisionBindingFactory;
- private final FactoryGenerator factoryGenerator;
- private final Set<Element> processedModuleElements = Sets.newLinkedHashSet();
-
- ModuleProcessingStep(
- Messager messager,
- ModuleValidator moduleValidator,
- ProvidesMethodValidator providesMethodValidator,
- ProvisionBinding.Factory provisionBindingFactory,
- FactoryGenerator factoryGenerator) {
- this.messager = messager;
- this.moduleValidator = moduleValidator;
- this.providesMethodValidator = providesMethodValidator;
- this.provisionBindingFactory = provisionBindingFactory;
- this.factoryGenerator = factoryGenerator;
- }
-
- @Override
- public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(Module.class, Provides.class);
- }
-
- @Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- // first, check and collect all provides methods
- ImmutableSet.Builder<ExecutableElement> validProvidesMethodsBuilder = ImmutableSet.builder();
- for (Element providesElement : elementsByAnnotation.get(Provides.class)) {
- if (providesElement.getKind().equals(METHOD)) {
- ExecutableElement providesMethodElement = (ExecutableElement) providesElement;
- ValidationReport<ExecutableElement> methodReport =
- providesMethodValidator.validate(providesMethodElement);
- methodReport.printMessagesTo(messager);
- if (methodReport.isClean()) {
- validProvidesMethodsBuilder.add(providesMethodElement);
- }
- }
- }
- ImmutableSet<ExecutableElement> validProvidesMethods = validProvidesMethodsBuilder.build();
-
- // process each module
- for (Element moduleElement :
- Sets.difference(elementsByAnnotation.get(Module.class), processedModuleElements)) {
- ValidationReport<TypeElement> report =
- moduleValidator.validate(MoreElements.asType(moduleElement));
- report.printMessagesTo(messager);
-
- if (report.isClean()) {
- ImmutableSet.Builder<ExecutableElement> moduleProvidesMethodsBuilder =
- ImmutableSet.builder();
- List<ExecutableElement> moduleMethods =
- ElementFilter.methodsIn(moduleElement.getEnclosedElements());
- for (ExecutableElement methodElement : moduleMethods) {
- if (isAnnotationPresent(methodElement, Provides.class)) {
- moduleProvidesMethodsBuilder.add(methodElement);
- }
- }
- ImmutableSet<ExecutableElement> moduleProvidesMethods =
- moduleProvidesMethodsBuilder.build();
-
- if (Sets.difference(moduleProvidesMethods, validProvidesMethods).isEmpty()) {
- // all of the provides methods in this module are valid!
- // time to generate some factories!
- ImmutableSet<ProvisionBinding> bindings = FluentIterable.from(moduleProvidesMethods)
- .transform(new Function<ExecutableElement, ProvisionBinding>() {
- @Override
- public ProvisionBinding apply(ExecutableElement providesMethod) {
- return provisionBindingFactory.forProvidesMethod(providesMethod,
- providesMethod.getEnclosingElement().asType());
- }
- })
- .toSet();
-
- try {
- for (ProvisionBinding binding : bindings) {
- factoryGenerator.generate(binding);
- }
- } catch (SourceFileGenerationException e) {
- e.printMessageTo(messager);
- }
- }
- }
- processedModuleElements.add(moduleElement);
- }
- return ImmutableSet.of();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ModuleValidator.java b/compiler/src/main/java/dagger/internal/codegen/ModuleValidator.java
deleted file mode 100644
index 8b0c9a2..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ModuleValidator.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.Visibility;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Sets;
-import dagger.Module;
-import dagger.producers.ProducerModule;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.Visibility.PRIVATE;
-import static com.google.auto.common.Visibility.PUBLIC;
-import static com.google.auto.common.Visibility.effectiveVisibilityOfElement;
-import static com.google.common.collect.Iterables.any;
-import static dagger.internal.codegen.ConfigurationAnnotations.getModuleIncludes;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_SAME_NAME;
-import static dagger.internal.codegen.ErrorMessages.METHOD_OVERRIDES_PROVIDES_METHOD;
-import static dagger.internal.codegen.ErrorMessages.MODULES_WITH_TYPE_PARAMS_MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_METHOD_OVERRIDES_ANOTHER;
-import static dagger.internal.codegen.ErrorMessages.REFERENCED_MODULES_MUST_NOT_BE_ABSTRACT;
-import static dagger.internal.codegen.ErrorMessages.REFERENCED_MODULE_MUST_NOT_HAVE_TYPE_PARAMS;
-import static dagger.internal.codegen.ErrorMessages.REFERENCED_MODULE_NOT_ANNOTATED;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Module}s or {@link ProducerModule}s.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class ModuleValidator {
- private final Types types;
- private final Elements elements;
- private final Class<? extends Annotation> moduleClass;
- private final ImmutableList<Class<? extends Annotation>> includedModuleClasses;
- private final Class<? extends Annotation> methodClass;
- private final MethodSignatureFormatter methodSignatureFormatter;
-
- ModuleValidator(
- Types types,
- Elements elements,
- MethodSignatureFormatter methodSignatureFormatter,
- Class<? extends Annotation> moduleClass,
- ImmutableList<Class<? extends Annotation>> includedModuleClasses,
- Class<? extends Annotation> methodClass) {
- this.types = types;
- this.elements = elements;
- this.moduleClass = moduleClass;
- this.includedModuleClasses = includedModuleClasses;
- this.methodClass = methodClass;
- this.methodSignatureFormatter = methodSignatureFormatter;
- }
-
- ValidationReport<TypeElement> validate(final TypeElement subject) {
- final ValidationReport.Builder<TypeElement> builder = ValidationReport.about(subject);
-
- List<ExecutableElement> moduleMethods = ElementFilter.methodsIn(subject.getEnclosedElements());
- ListMultimap<String, ExecutableElement> allMethodsByName = ArrayListMultimap.create();
- ListMultimap<String, ExecutableElement> bindingMethodsByName = ArrayListMultimap.create();
- for (ExecutableElement moduleMethod : moduleMethods) {
- if (isAnnotationPresent(moduleMethod, methodClass)) {
- bindingMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod);
- }
- allMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod);
- }
-
- validateModuleVisibility(subject, builder);
- validateMethodsWithSameName(builder, bindingMethodsByName);
- if (subject.getKind() != ElementKind.INTERFACE) {
- validateProvidesOverrides(subject, builder, allMethodsByName, bindingMethodsByName);
- }
- validateModifiers(subject, builder);
- validateReferencedModules(subject, builder);
-
- // TODO(gak): port the dagger 1 module validation?
- return builder.build();
- }
-
- private void validateModifiers(
- TypeElement subject, ValidationReport.Builder<TypeElement> builder) {
- // This coupled with the check for abstract modules in ComponentValidator guarantees that
- // only modules without type parameters are referenced from @Component(modules={...}).
- if (!subject.getTypeParameters().isEmpty() && !subject.getModifiers().contains(ABSTRACT)) {
- builder.addError(MODULES_WITH_TYPE_PARAMS_MUST_BE_ABSTRACT, subject);
- }
- }
-
- private void validateMethodsWithSameName(
- ValidationReport.Builder<TypeElement> builder,
- ListMultimap<String, ExecutableElement> bindingMethodsByName) {
- for (Entry<String, Collection<ExecutableElement>> entry :
- bindingMethodsByName.asMap().entrySet()) {
- if (entry.getValue().size() > 1) {
- for (ExecutableElement offendingMethod : entry.getValue()) {
- builder.addError(
- String.format(BINDING_METHOD_WITH_SAME_NAME, methodClass.getSimpleName()),
- offendingMethod);
- }
- }
- }
- }
-
- private void validateReferencedModules(
- TypeElement subject, ValidationReport.Builder<TypeElement> builder) {
- // Validate that all the modules we include are valid for inclusion.
- AnnotationMirror mirror = getAnnotationMirror(subject, moduleClass).get();
- ImmutableList<TypeMirror> includedTypes = getModuleIncludes(mirror);
- validateReferencedModules(subject, builder, includedTypes);
- }
-
- /**
- * Used by {@link ModuleValidator} & {@link ComponentValidator} to validate referenced modules.
- */
- void validateReferencedModules(
- final TypeElement subject,
- final ValidationReport.Builder<TypeElement> builder,
- ImmutableList<TypeMirror> includedTypes) {
- for (TypeMirror includedType : includedTypes) {
- includedType.accept(
- new SimpleTypeVisitor6<Void, Void>() {
- @Override
- protected Void defaultAction(TypeMirror mirror, Void p) {
- builder.addError(mirror + " is not a valid module type.", subject);
- return null;
- }
-
- @Override
- public Void visitDeclared(DeclaredType t, Void p) {
- final TypeElement element = MoreElements.asType(t.asElement());
- if (!t.getTypeArguments().isEmpty()) {
- builder.addError(
- String.format(
- REFERENCED_MODULE_MUST_NOT_HAVE_TYPE_PARAMS, element.getQualifiedName()),
- subject);
- }
- boolean isIncludedModule =
- any(
- includedModuleClasses,
- new Predicate<Class<? extends Annotation>>() {
- @Override
- public boolean apply(Class<? extends Annotation> otherClass) {
- return MoreElements.isAnnotationPresent(element, otherClass);
- }
- });
- if (!isIncludedModule) {
- builder.addError(
- String.format(
- REFERENCED_MODULE_NOT_ANNOTATED,
- element.getQualifiedName(),
- (includedModuleClasses.size() > 1 ? "one of " : "")
- + Joiner.on(", ")
- .join(
- FluentIterable.from(includedModuleClasses)
- .transform(
- new Function<Class<? extends Annotation>, String>() {
- @Override
- public String apply(
- Class<? extends Annotation> otherClass) {
- return "@" + otherClass.getSimpleName();
- }
- }))),
- subject);
- }
- if (element.getModifiers().contains(ABSTRACT)) {
- builder.addError(
- String.format(
- REFERENCED_MODULES_MUST_NOT_BE_ABSTRACT, element.getQualifiedName()),
- subject);
- }
- return null;
- }
- },
- null);
- }
- }
-
- private void validateProvidesOverrides(
- TypeElement subject,
- ValidationReport.Builder<TypeElement> builder,
- ListMultimap<String, ExecutableElement> allMethodsByName,
- ListMultimap<String, ExecutableElement> bindingMethodsByName) {
- // For every @Provides method, confirm it overrides nothing *and* nothing overrides it.
- // Consider the following hierarchy:
- // class Parent {
- // @Provides Foo a() {}
- // @Provides Foo b() {}
- // Foo c() {}
- // }
- // class Child extends Parent {
- // @Provides Foo a() {}
- // Foo b() {}
- // @Provides Foo c() {}
- // }
- // In each of those cases, we want to fail. "a" is clear, "b" because Child is overriding
- // a method marked @Provides in Parent, and "c" because Child is defining an @Provides
- // method that overrides Parent.
- TypeElement currentClass = subject;
- TypeMirror objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType();
- // We keep track of methods that failed so we don't spam with multiple failures.
- Set<ExecutableElement> failedMethods = Sets.newHashSet();
- while (!types.isSameType(currentClass.getSuperclass(), objectType)) {
- currentClass = MoreElements.asType(types.asElement(currentClass.getSuperclass()));
- List<ExecutableElement> superclassMethods =
- ElementFilter.methodsIn(currentClass.getEnclosedElements());
- for (ExecutableElement superclassMethod : superclassMethods) {
- String name = superclassMethod.getSimpleName().toString();
- // For each method in the superclass, confirm our @Provides methods don't override it
- for (ExecutableElement providesMethod : bindingMethodsByName.get(name)) {
- if (!failedMethods.contains(providesMethod)
- && elements.overrides(providesMethod, superclassMethod, subject)) {
- failedMethods.add(providesMethod);
- builder.addError(
- String.format(
- PROVIDES_METHOD_OVERRIDES_ANOTHER,
- methodClass.getSimpleName(),
- methodSignatureFormatter.format(superclassMethod)),
- providesMethod);
- }
- }
- // For each @Provides method in superclass, confirm our methods don't override it.
- if (isAnnotationPresent(superclassMethod, methodClass)) {
- for (ExecutableElement method : allMethodsByName.get(name)) {
- if (!failedMethods.contains(method)
- && elements.overrides(method, superclassMethod, subject)) {
- failedMethods.add(method);
- builder.addError(
- String.format(
- METHOD_OVERRIDES_PROVIDES_METHOD,
- methodClass.getSimpleName(),
- methodSignatureFormatter.format(superclassMethod)),
- method);
- }
- }
- }
- allMethodsByName.put(superclassMethod.getSimpleName().toString(), superclassMethod);
- }
- }
- }
-
- private void validateModuleVisibility(final TypeElement moduleElement,
- final ValidationReport.Builder<?> reportBuilder) {
- Visibility moduleVisibility = Visibility.ofElement(moduleElement);
- if (moduleVisibility.equals(PRIVATE)) {
- reportBuilder.addError("Modules cannot be private.", moduleElement);
- } else if (effectiveVisibilityOfElement(moduleElement).equals(PRIVATE)) {
- reportBuilder.addError("Modules cannot be enclosed in private types.", moduleElement);
- }
-
- switch (moduleElement.getNestingKind()) {
- case ANONYMOUS:
- throw new IllegalStateException("Can't apply @Module to an anonymous class");
- case LOCAL:
- throw new IllegalStateException("Local classes shouldn't show up in the processor");
- case MEMBER:
- case TOP_LEVEL:
- if (moduleVisibility.equals(PUBLIC)) {
- ImmutableSet<Element> nonPublicModules = FluentIterable.from(getModuleIncludes(
- getAnnotationMirror(moduleElement, moduleClass).get()))
- .transform(new Function<TypeMirror, Element>() {
- @Override public Element apply(TypeMirror input) {
- return types.asElement(input);
- }
- })
- .filter(new Predicate<Element>() {
- @Override public boolean apply(Element input) {
- return effectiveVisibilityOfElement(input).compareTo(PUBLIC) < 0;
- }
- })
- .toSet();
- if (!nonPublicModules.isEmpty()) {
- reportBuilder.addError(
- String.format(
- "This module is public, but it includes non-public "
- + "(or effectively non-public) modules. "
- + "Either reduce the visibility of this module or make %s public.",
- formatListForErrorMessage(nonPublicModules.asList())),
- moduleElement);
- }
- }
- break;
- default:
- throw new AssertionError();
- }
- }
-
- private static String formatListForErrorMessage(List<?> things) {
- switch (things.size()) {
- case 0:
- return "";
- case 1:
- return things.get(0).toString();
- default:
- StringBuilder output = new StringBuilder();
- Joiner.on(", ").appendTo(output, things.subList(0, things.size() - 1));
- output.append(" and ").append(things.get(things.size() - 1));
- return output.toString();
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleGenerator.java b/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleGenerator.java
deleted file mode 100644
index a4e020b..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleGenerator.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.FieldWriter;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import dagger.producers.monitoring.internal.MonitorCache;
-
-import java.util.Set;
-import javax.annotation.Generated;
-import javax.annotation.processing.Filer;
-import javax.inject.Provider;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.element.Modifier.FINAL;
-
-/** Generates a monitoring module for use with production components. */
-final class MonitoringModuleGenerator extends SourceFileGenerator<TypeElement> {
- private static final TypeName SET_OF_FACTORIES =
- ParameterizedTypeName.create(
- Set.class, ClassName.fromClass(ProductionComponentMonitor.Factory.class));
-
- MonitoringModuleGenerator(Filer filer) {
- super(filer);
- }
-
- @Override
- ClassName nameGeneratedType(TypeElement componentElement) {
- return SourceFiles.generatedMonitoringModuleName(componentElement);
- }
-
- @Override
- Iterable<? extends Element> getOriginatingElements(TypeElement componentElement) {
- return ImmutableSet.of(componentElement);
- }
-
- @Override
- Optional<? extends Element> getElementForErrorReporting(TypeElement componentElement) {
- return Optional.of(componentElement);
- }
-
- @Override
- ImmutableSet<JavaWriter> write(ClassName generatedTypeName, TypeElement componentElement) {
- JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
- ClassWriter classWriter = writer.addClass(generatedTypeName.simpleName());
- classWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName());
- classWriter.annotate(Module.class);
- classWriter.addModifiers(FINAL);
-
- // TODO(beder): Replace this default set binding with EmptyCollections when it exists.
- MethodWriter emptySetBindingMethod =
- classWriter.addMethod(SET_OF_FACTORIES, "defaultSetOfFactories");
- emptySetBindingMethod.addModifiers(STATIC);
- emptySetBindingMethod.annotate(Provides.class).setMember("type", Provides.Type.SET_VALUES);
- emptySetBindingMethod
- .body()
- .addSnippet("return %s.of();", ClassName.fromClass(ImmutableSet.class));
-
- FieldWriter providerField = classWriter.addField(MonitorCache.class, "monitorCache");
- providerField.addModifiers(PRIVATE, FINAL);
- providerField.setInitializer("new %s()", ClassName.fromClass(MonitorCache.class));
- MethodWriter monitorMethod = classWriter.addMethod(ProductionComponentMonitor.class, "monitor");
- monitorMethod.annotate(Provides.class);
- monitorMethod.addParameter(
- ParameterizedTypeName.create(Provider.class, ClassName.fromTypeElement(componentElement)),
- "component");
- monitorMethod.addParameter(
- ParameterizedTypeName.create(Provider.class, SET_OF_FACTORIES), "factories");
- monitorMethod.body().addSnippet("return monitorCache.monitor(component, factories);");
-
- return ImmutableSet.of(writer);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
deleted file mode 100644
index 91b10e7..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
-import dagger.producers.ProductionComponent;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-import static javax.lang.model.util.ElementFilter.typesIn;
-
-/**
- * A processing step that is responsible for generating a special module for a
- * {@link ProductionComponent}.
- */
-final class MonitoringModuleProcessingStep implements ProcessingStep {
- private final Messager messager;
- private final MonitoringModuleGenerator monitoringModuleGenerator;
-
- MonitoringModuleProcessingStep(
- Messager messager, MonitoringModuleGenerator monitoringModuleGenerator) {
- this.messager = messager;
- this.monitoringModuleGenerator = monitoringModuleGenerator;
- }
-
- @Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(ProductionComponent.class);
- }
-
- @Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- for (TypeElement element : typesIn(elementsByAnnotation.get(ProductionComponent.class))) {
- try {
- monitoringModuleGenerator.generate(element);
- } catch (SourceFileGenerationException e) {
- e.printMessageTo(messager);
- }
- }
- return ImmutableSet.of();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java b/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java
deleted file mode 100644
index 90a0337..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Provides.Type;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.ConstructorWriter;
-import dagger.internal.codegen.writer.FieldWriter;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.Produces;
-import dagger.producers.internal.AbstractProducer;
-import dagger.producers.internal.Producers;
-import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import java.util.List;
-import java.util.concurrent.Executor;
-import javax.annotation.Generated;
-import javax.annotation.processing.Filer;
-import javax.lang.model.element.Element;
-import javax.lang.model.type.TypeMirror;
-
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * Generates {@link Producer} implementations from {@link ProductionBinding} instances.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBinding> {
- private final DependencyRequestMapper dependencyRequestMapper;
-
- ProducerFactoryGenerator(Filer filer, DependencyRequestMapper dependencyRequestMapper) {
- super(filer);
- this.dependencyRequestMapper = dependencyRequestMapper;
- }
-
- @Override
- ClassName nameGeneratedType(ProductionBinding binding) {
- return generatedClassNameForBinding(binding);
- }
-
- @Override
- Iterable<? extends Element> getOriginatingElements(ProductionBinding binding) {
- return ImmutableSet.of(binding.bindingElement());
- }
-
- @Override
- Optional<? extends Element> getElementForErrorReporting(ProductionBinding binding) {
- return Optional.of(binding.bindingElement());
- }
-
- @Override
- ImmutableSet<JavaWriter> write(ClassName generatedTypeName, ProductionBinding binding) {
- TypeMirror keyType = binding.productionType().equals(Type.MAP)
- ? Util.getProvidedValueTypeOfMap(MoreTypes.asDeclared(binding.key().type()))
- : binding.key().type();
- TypeName providedTypeName = TypeNames.forTypeMirror(keyType);
- TypeName futureTypeName = ParameterizedTypeName.create(
- ClassName.fromClass(ListenableFuture.class), providedTypeName);
- JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
-
- ClassWriter factoryWriter = writer.addClass(generatedTypeName.simpleName());
- ConstructorWriter constructorWriter = factoryWriter.addConstructor();
- constructorWriter.addModifiers(PUBLIC);
-
- ImmutableMap<BindingKey, FrameworkField> fields =
- SourceFiles.generateBindingFieldsForDependencies(
- dependencyRequestMapper, binding.implicitDependencies());
-
- constructorWriter
- .body()
- .addSnippet(
- "super(%s, %s.create(%s.class));",
- fields.get(binding.monitorRequest().get().bindingKey()).name(),
- ClassName.fromClass(ProducerToken.class),
- factoryWriter.name());
-
- if (!binding.bindingElement().getModifiers().contains(STATIC)) {
- factoryWriter.addField(binding.bindingTypeElement(), "module")
- .addModifiers(PRIVATE, FINAL);
- constructorWriter.addParameter(binding.bindingTypeElement(), "module");
- constructorWriter.body()
- .addSnippet("assert module != null;")
- .addSnippet("this.module = module;");
- }
-
- factoryWriter.addField(Executor.class, "executor")
- .addModifiers(PRIVATE, FINAL);
- constructorWriter.addParameter(Executor.class, "executor");
- constructorWriter.body()
- .addSnippet("assert executor != null;")
- .addSnippet("this.executor = executor;");
-
- factoryWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName());
- factoryWriter.addModifiers(PUBLIC);
- factoryWriter.addModifiers(FINAL);
- factoryWriter.setSuperclass(
- ParameterizedTypeName.create(AbstractProducer.class, providedTypeName));
-
- MethodWriter computeMethodWriter = factoryWriter.addMethod(futureTypeName, "compute");
- computeMethodWriter.annotate(Override.class);
- computeMethodWriter.addModifiers(PROTECTED);
- computeMethodWriter.addParameter(ProducerMonitor.class, "monitor").addModifiers(FINAL);
-
- for (FrameworkField bindingField : fields.values()) {
- TypeName fieldType = bindingField.frameworkType();
- FieldWriter field = factoryWriter.addField(fieldType, bindingField.name());
- field.addModifiers(PRIVATE, FINAL);
- constructorWriter.addParameter(field.type(), field.name());
- constructorWriter.body()
- .addSnippet("assert %s != null;", field.name())
- .addSnippet("this.%1$s = %1$s;", field.name());
- }
-
- boolean returnsFuture =
- binding.bindingKind().equals(ContributionBinding.Kind.FUTURE_PRODUCTION);
- ImmutableList<DependencyRequest> asyncDependencies =
- FluentIterable.from(binding.implicitDependencies())
- .filter(
- new Predicate<DependencyRequest>() {
- @Override
- public boolean apply(DependencyRequest dependency) {
- return isAsyncDependency(dependency);
- }
- })
- .toList();
-
- for (DependencyRequest dependency : asyncDependencies) {
- ParameterizedTypeName futureType = ParameterizedTypeName.create(
- ClassName.fromClass(ListenableFuture.class),
- asyncDependencyType(dependency));
- String name = fields.get(dependency.bindingKey()).name();
- Snippet futureAccess = Snippet.format("%s.get()", name);
- computeMethodWriter
- .body()
- .addSnippet(
- "%s %sFuture = %s;",
- futureType,
- name,
- dependency.kind().equals(DependencyRequest.Kind.PRODUCED)
- ? Snippet.format(
- "%s.createFutureProduced(%s)",
- ClassName.fromClass(Producers.class),
- futureAccess)
- : futureAccess);
- }
-
- FutureTransform futureTransform = FutureTransform.create(fields, binding, asyncDependencies);
- Snippet transformSnippet =
- Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s, %3$s>() {",
- " %4$s",
- " @Override public %5$s apply(%2$s %6$s) %7$s {",
- " %8$s",
- " }",
- "}"),
- ClassName.fromClass(AsyncFunction.class),
- futureTransform.applyArgType(),
- providedTypeName,
- futureTransform.hasUncheckedCast()
- ? "@SuppressWarnings(\"unchecked\") // safe by specification"
- : "",
- futureTypeName,
- futureTransform.applyArgName(),
- getThrowsClause(binding.thrownTypes()),
- getInvocationSnippet(!returnsFuture, binding, futureTransform.parameterSnippets()));
- computeMethodWriter
- .body()
- .addSnippet(
- "return %s.transform(%s, %s, executor);",
- ClassName.fromClass(Futures.class),
- futureTransform.futureSnippet(),
- transformSnippet);
-
- // TODO(gak): write a sensible toString
- return ImmutableSet.of(writer);
- }
-
- /** Represents the transformation of an input future by a producer method. */
- abstract static class FutureTransform {
- protected final ImmutableMap<BindingKey, FrameworkField> fields;
- protected final ProductionBinding binding;
-
- FutureTransform(ImmutableMap<BindingKey, FrameworkField> fields, ProductionBinding binding) {
- this.fields = fields;
- this.binding = binding;
- }
-
- /** The snippet representing the future that should be transformed. */
- abstract Snippet futureSnippet();
-
- /** The type of the argument to the apply method. */
- abstract TypeName applyArgType();
-
- /** The name of the argument to the apply method */
- abstract String applyArgName();
-
- /** The snippets to be passed to the produces method itself. */
- abstract ImmutableList<Snippet> parameterSnippets();
-
- /** Whether the transform method has an unchecked cast. */
- boolean hasUncheckedCast() {
- return false;
- }
-
- static FutureTransform create(
- ImmutableMap<BindingKey, FrameworkField> fields,
- ProductionBinding binding,
- ImmutableList<DependencyRequest> asyncDependencies) {
- if (asyncDependencies.isEmpty()) {
- return new NoArgFutureTransform(fields, binding);
- } else if (asyncDependencies.size() == 1) {
- return new SingleArgFutureTransform(
- fields, binding, Iterables.getOnlyElement(asyncDependencies));
- } else {
- return new MultiArgFutureTransform(fields, binding, asyncDependencies);
- }
- }
- }
-
- static final class NoArgFutureTransform extends FutureTransform {
- NoArgFutureTransform(
- ImmutableMap<BindingKey, FrameworkField> fields, ProductionBinding binding) {
- super(fields, binding);
- }
-
- @Override
- Snippet futureSnippet() {
- return Snippet.format(
- "%s.<%s>immediateFuture(null)",
- ClassName.fromClass(Futures.class),
- ClassName.fromClass(Void.class));
- }
-
- @Override
- TypeName applyArgType() {
- return ClassName.fromClass(Void.class);
- }
-
- @Override
- String applyArgName() {
- return "ignoredVoidArg";
- }
-
- @Override
- ImmutableList<Snippet> parameterSnippets() {
- ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
- for (DependencyRequest dependency : binding.dependencies()) {
- parameterSnippets.add(
- frameworkTypeUsageStatement(
- Snippet.format(
- "%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
- }
- return parameterSnippets.build();
- }
- }
-
- static final class SingleArgFutureTransform extends FutureTransform {
- private final DependencyRequest asyncDependency;
-
- SingleArgFutureTransform(
- ImmutableMap<BindingKey, FrameworkField> fields,
- ProductionBinding binding,
- DependencyRequest asyncDependency) {
- super(fields, binding);
- this.asyncDependency = asyncDependency;
- }
-
- @Override
- Snippet futureSnippet() {
- return Snippet.format("%s", fields.get(asyncDependency.bindingKey()).name() + "Future");
- }
-
- @Override
- TypeName applyArgType() {
- return asyncDependencyType(asyncDependency);
- }
-
- @Override
- String applyArgName() {
- return asyncDependency.requestElement().getSimpleName().toString();
- }
-
- @Override
- ImmutableList<Snippet> parameterSnippets() {
- ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
- for (DependencyRequest dependency : binding.dependencies()) {
- // We really want to compare instances here, because asyncDependency is an element in the
- // set binding.dependencies().
- if (dependency == asyncDependency) {
- parameterSnippets.add(Snippet.format("%s", applyArgName()));
- } else {
- parameterSnippets.add(
- frameworkTypeUsageStatement(
- Snippet.format(
- "%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
- }
- }
- return parameterSnippets.build();
- }
- }
-
- static final class MultiArgFutureTransform extends FutureTransform {
- private final ImmutableList<DependencyRequest> asyncDependencies;
-
- MultiArgFutureTransform(
- ImmutableMap<BindingKey, FrameworkField> fields,
- ProductionBinding binding,
- ImmutableList<DependencyRequest> asyncDependencies) {
- super(fields, binding);
- this.asyncDependencies = asyncDependencies;
- }
-
- @Override
- Snippet futureSnippet() {
- return Snippet.format(
- "%s.<%s>allAsList(%s)",
- ClassName.fromClass(Futures.class),
- ClassName.fromClass(Object.class),
- makeParametersSnippet(
- FluentIterable.from(asyncDependencies)
- .transform(DependencyRequest.BINDING_KEY_FUNCTION)
- .transform(
- new Function<BindingKey, Snippet>() {
- @Override
- public Snippet apply(BindingKey bindingKey) {
- return Snippet.format("%s", fields.get(bindingKey).name() + "Future");
- }
- })));
- }
-
- @Override
- TypeName applyArgType() {
- return ParameterizedTypeName.create(
- ClassName.fromClass(List.class), ClassName.fromClass(Object.class));
- }
-
- @Override
- String applyArgName() {
- return "args";
- }
-
- @Override
- ImmutableList<Snippet> parameterSnippets() {
- return getParameterSnippets(binding, fields, applyArgName());
- }
-
- @Override
- boolean hasUncheckedCast() {
- return true;
- }
- }
-
- private static boolean isAsyncDependency(DependencyRequest dependency) {
- switch (dependency.kind()) {
- case INSTANCE:
- case PRODUCED:
- return true;
- default:
- return false;
- }
- }
-
- private static TypeName asyncDependencyType(DependencyRequest dependency) {
- TypeName keyName = TypeNames.forTypeMirror(dependency.key().type());
- switch (dependency.kind()) {
- case INSTANCE:
- return keyName;
- case PRODUCED:
- return ParameterizedTypeName.create(ClassName.fromClass(Produced.class), keyName);
- default:
- throw new AssertionError();
- }
- }
-
- private static ImmutableList<Snippet> getParameterSnippets(
- ProductionBinding binding,
- ImmutableMap<BindingKey, FrameworkField> fields,
- String listArgName) {
- int argIndex = 0;
- ImmutableList.Builder<Snippet> snippets = ImmutableList.builder();
- for (DependencyRequest dependency : binding.dependencies()) {
- if (isAsyncDependency(dependency)) {
- snippets.add(Snippet.format(
- "(%s) %s.get(%s)",
- asyncDependencyType(dependency),
- listArgName,
- argIndex));
- argIndex++;
- } else {
- snippets.add(frameworkTypeUsageStatement(
- Snippet.format("%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
- }
- }
- return snippets.build();
- }
-
- /**
- * Creates a snippet for the invocation of the producer method from the module, which should be
- * used entirely within a method body.
- *
- * @param wrapWithFuture If true, wraps the result of the call to the producer method
- * in an immediate future.
- * @param binding The binding to generate the invocation snippet for.
- * @param parameterSnippets The snippets for all the parameters to the producer method.
- */
- private Snippet getInvocationSnippet(
- boolean wrapWithFuture, ProductionBinding binding, ImmutableList<Snippet> parameterSnippets) {
- Snippet moduleSnippet = Snippet.format("%s.%s(%s)",
- binding.bindingElement().getModifiers().contains(STATIC)
- ? ClassName.fromTypeElement(binding.bindingTypeElement())
- : "module",
- binding.bindingElement().getSimpleName(),
- makeParametersSnippet(parameterSnippets));
-
- // NOTE(beder): We don't worry about catching exeptions from the monitor methods themselves
- // because we'll wrap all monitoring in non-throwing monitors before we pass them to the
- // factories.
- ImmutableList.Builder<Snippet> snippets = ImmutableList.builder();
- snippets.add(Snippet.format("monitor.methodStarting();"));
-
- final Snippet valueSnippet;
- if (binding.productionType().equals(Produces.Type.SET)) {
- if (binding.bindingKind().equals(ContributionBinding.Kind.FUTURE_PRODUCTION)) {
- valueSnippet =
- Snippet.format(
- "%s.createFutureSingletonSet(%s)",
- ClassName.fromClass(Producers.class),
- moduleSnippet);
- } else {
- valueSnippet =
- Snippet.format("%s.of(%s)", ClassName.fromClass(ImmutableSet.class), moduleSnippet);
- }
- } else {
- valueSnippet = moduleSnippet;
- }
- Snippet returnSnippet =
- wrapWithFuture
- ? Snippet.format(
- "%s.<%s>immediateFuture(%s)",
- ClassName.fromClass(Futures.class),
- TypeNames.forTypeMirror(binding.key().type()),
- valueSnippet)
- : valueSnippet;
- return Snippet.format(
- Joiner.on('\n')
- .join(
- "monitor.methodStarting();",
- "try {",
- " return %s;",
- "} finally {",
- " monitor.methodFinished();",
- "}"),
- returnSnippet);
- }
-
- /**
- * Creates a Snippet for the throws clause.
- *
- * @param thrownTypes the list of thrown types.
- */
- private Snippet getThrowsClause(List<? extends TypeMirror> thrownTypes) {
- if (thrownTypes.isEmpty()) {
- return Snippet.format("");
- }
- return Snippet.format("throws %s ",
- Snippet.makeParametersSnippet(FluentIterable
- .from(thrownTypes)
- .transform(new Function<TypeMirror, Snippet>() {
- @Override public Snippet apply(TypeMirror thrownType) {
- return Snippet.format("%s", TypeNames.forTypeMirror(thrownType));
- }
- })
- .toList()));
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProducerModuleProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/ProducerModuleProcessingStep.java
deleted file mode 100644
index cc167e5..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProducerModuleProcessingStep.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.SuperficialValidation;
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.lang.annotation.Annotation;
-import java.util.List;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.ElementFilter;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static javax.lang.model.element.ElementKind.METHOD;
-
-/**
- * An annotation processor for generating Dagger implementation code based on the
- * {@link ProducerModule} (and {@link Produces}) annotation.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-final class ProducerModuleProcessingStep implements ProcessingStep {
- private final Messager messager;
- private final ModuleValidator moduleValidator;
- private final ProducesMethodValidator producesMethodValidator;
- private final ProductionBinding.Factory productionBindingFactory;
- private final ProducerFactoryGenerator factoryGenerator;
- private final Set<Element> processedModuleElements = Sets.newLinkedHashSet();
-
- ProducerModuleProcessingStep(
- Messager messager,
- ModuleValidator moduleValidator,
- ProducesMethodValidator producesMethodValidator,
- ProductionBinding.Factory productionBindingFactory,
- ProducerFactoryGenerator factoryGenerator) {
- this.messager = messager;
- this.moduleValidator = moduleValidator;
- this.producesMethodValidator = producesMethodValidator;
- this.productionBindingFactory = productionBindingFactory;
- this.factoryGenerator = factoryGenerator;
- }
-
- @Override
- public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(Produces.class, ProducerModule.class);
- }
-
- @Override
- public Set<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- // first, check and collect all produces methods
- ImmutableSet.Builder<ExecutableElement> validProducesMethodsBuilder = ImmutableSet.builder();
- for (Element producesElement : elementsByAnnotation.get(Produces.class)) {
- if (producesElement.getKind().equals(METHOD)) {
- ExecutableElement producesMethodElement = (ExecutableElement) producesElement;
- ValidationReport<ExecutableElement> methodReport =
- producesMethodValidator.validate(producesMethodElement);
- methodReport.printMessagesTo(messager);
- if (methodReport.isClean()) {
- validProducesMethodsBuilder.add(producesMethodElement);
- }
- }
- }
- ImmutableSet<ExecutableElement> validProducesMethods = validProducesMethodsBuilder.build();
-
- // process each module
- for (Element moduleElement :
- Sets.difference(elementsByAnnotation.get(ProducerModule.class),
- processedModuleElements)) {
- if (SuperficialValidation.validateElement(moduleElement)) {
- ValidationReport<TypeElement> report =
- moduleValidator.validate(MoreElements.asType(moduleElement));
- report.printMessagesTo(messager);
-
- if (report.isClean()) {
- ImmutableSet.Builder<ExecutableElement> moduleProducesMethodsBuilder =
- ImmutableSet.builder();
- List<ExecutableElement> moduleMethods =
- ElementFilter.methodsIn(moduleElement.getEnclosedElements());
- for (ExecutableElement methodElement : moduleMethods) {
- if (isAnnotationPresent(methodElement, Produces.class)) {
- moduleProducesMethodsBuilder.add(methodElement);
- }
- }
- ImmutableSet<ExecutableElement> moduleProducesMethods =
- moduleProducesMethodsBuilder.build();
-
- if (Sets.difference(moduleProducesMethods, validProducesMethods).isEmpty()) {
- // all of the produces methods in this module are valid!
- // time to generate some factories!
- ImmutableSet<ProductionBinding> bindings = FluentIterable.from(moduleProducesMethods)
- .transform(new Function<ExecutableElement, ProductionBinding>() {
- @Override
- public ProductionBinding apply(ExecutableElement producesMethod) {
- return productionBindingFactory.forProducesMethod(producesMethod,
- producesMethod.getEnclosingElement().asType());
- }
- })
- .toSet();
-
- try {
- for (ProductionBinding binding : bindings) {
- factoryGenerator.generate(binding);
- }
- } catch (SourceFileGenerationException e) {
- e.printMessageTo(messager);
- }
- }
- }
-
- processedModuleElements.add(moduleElement);
- }
- }
- return ImmutableSet.of();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProducesMethodValidator.java b/compiler/src/main/java/dagger/internal/codegen/ProducesMethodValidator.java
deleted file mode 100644
index b0b4df1..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProducesMethodValidator.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_ABSTRACT;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_MUST_RETURN_A_VALUE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_IN_MODULE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_MAP_HAS_MAP_KEY;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_PRIVATE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_SET_VALUES_RAW_SET;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_TYPE_PARAMETER;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_MULTIPLE_MAP_KEY;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_NO_MAP_KEY;
-import static dagger.internal.codegen.ErrorMessages.PRODUCES_METHOD_RAW_FUTURE;
-import static dagger.internal.codegen.ErrorMessages.PRODUCES_METHOD_RETURN_TYPE;
-import static dagger.internal.codegen.ErrorMessages.PRODUCES_METHOD_SET_VALUES_RETURN_SET;
-import static dagger.internal.codegen.MapKeys.getMapKeys;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.type.TypeKind.ARRAY;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.VOID;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Produces} methods.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-// TODO(user): Consider unifying this with the ProvidesMethodValidator after Provides.Type and
-// Produces.Type are reconciled.
-final class ProducesMethodValidator {
- private final Elements elements;
-
- ProducesMethodValidator(Elements elements) {
- this.elements = checkNotNull(elements);
- }
-
- private TypeElement getSetElement() {
- return elements.getTypeElement(Set.class.getCanonicalName());
- }
-
- ValidationReport<ExecutableElement> validate(ExecutableElement producesMethodElement) {
- ValidationReport.Builder<ExecutableElement> builder =
- ValidationReport.about(producesMethodElement);
-
- Produces producesAnnotation = producesMethodElement.getAnnotation(Produces.class);
- checkArgument(producesAnnotation != null);
-
- Element enclosingElement = producesMethodElement.getEnclosingElement();
- if (!isAnnotationPresent(enclosingElement, ProducerModule.class)) {
- builder.addError(
- formatModuleErrorMessage(BINDING_METHOD_NOT_IN_MODULE), producesMethodElement);
- }
-
- if (!producesMethodElement.getTypeParameters().isEmpty()) {
- builder.addError(formatErrorMessage(BINDING_METHOD_TYPE_PARAMETER), producesMethodElement);
- }
-
- Set<Modifier> modifiers = producesMethodElement.getModifiers();
- if (modifiers.contains(PRIVATE)) {
- builder.addError(formatErrorMessage(BINDING_METHOD_PRIVATE), producesMethodElement);
- }
- if (modifiers.contains(ABSTRACT)) {
- builder.addError(formatErrorMessage(BINDING_METHOD_ABSTRACT), producesMethodElement);
- }
-
- TypeMirror returnType = producesMethodElement.getReturnType();
- TypeKind returnTypeKind = returnType.getKind();
- if (returnTypeKind.equals(VOID)) {
- builder.addError(
- formatErrorMessage(BINDING_METHOD_MUST_RETURN_A_VALUE), producesMethodElement);
- }
-
- // check mapkey is right
- if (!producesAnnotation.type().equals(Produces.Type.MAP)
- && !getMapKeys(producesMethodElement).isEmpty()) {
- builder.addError(
- formatErrorMessage(BINDING_METHOD_NOT_MAP_HAS_MAP_KEY), producesMethodElement);
- }
-
- ProvidesMethodValidator.validateMethodQualifiers(builder, producesMethodElement);
-
- switch (producesAnnotation.type()) {
- case UNIQUE: // fall through
- case SET:
- validateSingleReturnType(builder, returnType);
- break;
- case MAP:
- validateSingleReturnType(builder, returnType);
- ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(producesMethodElement);
- switch (mapKeys.size()) {
- case 0:
- builder.addError(
- formatErrorMessage(BINDING_METHOD_WITH_NO_MAP_KEY), producesMethodElement);
- break;
- case 1:
- break;
- default:
- builder.addError(
- formatErrorMessage(BINDING_METHOD_WITH_MULTIPLE_MAP_KEY), producesMethodElement);
- break;
- }
- break;
- case SET_VALUES:
- if (returnTypeKind.equals(DECLARED)
- && MoreTypes.isTypeOf(ListenableFuture.class, returnType)) {
- DeclaredType declaredReturnType = MoreTypes.asDeclared(returnType);
- if (!declaredReturnType.getTypeArguments().isEmpty()) {
- validateSetType(builder, Iterables.getOnlyElement(
- declaredReturnType.getTypeArguments()));
- }
- } else {
- validateSetType(builder, returnType);
- }
- break;
- default:
- throw new AssertionError();
- }
-
- return builder.build();
- }
-
- private String formatErrorMessage(String msg) {
- return String.format(msg, Produces.class.getSimpleName());
- }
-
- private String formatModuleErrorMessage(String msg) {
- return String.format(msg, Produces.class.getSimpleName(), ProducerModule.class.getSimpleName());
- }
-
- private void validateKeyType(ValidationReport.Builder<? extends Element> reportBuilder,
- TypeMirror type) {
- TypeKind kind = type.getKind();
- if (!(kind.isPrimitive() || kind.equals(DECLARED) || kind.equals(ARRAY))) {
- reportBuilder.addError(PRODUCES_METHOD_RETURN_TYPE, reportBuilder.getSubject());
- }
- }
-
- private void validateSingleReturnType(ValidationReport.Builder<? extends Element> reportBuilder,
- TypeMirror type) {
- if (type.getKind().equals(DECLARED) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
- DeclaredType declaredType = MoreTypes.asDeclared(type);
- if (declaredType.getTypeArguments().isEmpty()) {
- reportBuilder.addError(PRODUCES_METHOD_RAW_FUTURE, reportBuilder.getSubject());
- } else {
- validateKeyType(reportBuilder, Iterables.getOnlyElement(declaredType.getTypeArguments()));
- }
- } else {
- validateKeyType(reportBuilder, type);
- }
- }
-
- private void validateSetType(ValidationReport.Builder<? extends Element> reportBuilder,
- TypeMirror type) {
- if (!type.getKind().equals(DECLARED)) {
- reportBuilder.addError(PRODUCES_METHOD_SET_VALUES_RETURN_SET, reportBuilder.getSubject());
- return;
- }
-
- // TODO(gak): should we allow "covariant return" for set values?
- DeclaredType declaredType = MoreTypes.asDeclared(type);
- if (!declaredType.asElement().equals(getSetElement())) {
- reportBuilder.addError(PRODUCES_METHOD_SET_VALUES_RETURN_SET, reportBuilder.getSubject());
- } else if (declaredType.getTypeArguments().isEmpty()) {
- reportBuilder.addError(
- formatErrorMessage(BINDING_METHOD_SET_VALUES_RAW_SET), reportBuilder.getSubject());
- } else {
- validateSingleReturnType(reportBuilder,
- Iterables.getOnlyElement(declaredType.getTypeArguments()));
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java b/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java
deleted file mode 100644
index 1666fbf..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Provides;
-import dagger.producers.Produces;
-import java.util.Set;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Types;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static javax.lang.model.element.ElementKind.METHOD;
-
-/**
- * A value object representing the mechanism by which a {@link Key} can be produced. New instances
- * should be created using an instance of the {@link Factory}.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-@AutoValue
-abstract class ProductionBinding extends ContributionBinding {
-
- @Override
- Binding.Type bindingType() {
- return Binding.Type.PRODUCTION;
- }
-
- @Override
- Provides.Type provisionType() {
- return Provides.Type.valueOf(productionType().name());
- }
-
- @Override
- Set<DependencyRequest> implicitDependencies() {
- // Similar optimizations to ContributionBinding.implicitDependencies().
- if (!monitorRequest().isPresent()) {
- return super.implicitDependencies();
- } else {
- return Sets.union(monitorRequest().asSet(), super.implicitDependencies());
- }
- }
-
- /** Returns provision type that was used to bind the key. */
- abstract Produces.Type productionType();
-
- /** Returns the list of types in the throws clause of the method. */
- abstract ImmutableList<? extends TypeMirror> thrownTypes();
-
- /** If this production requires a monitor, this will be the corresponding request. */
- abstract Optional<DependencyRequest> monitorRequest();
-
- @Override
- ContributionType contributionType() {
- switch (productionType()) {
- case SET:
- case SET_VALUES:
- return ContributionType.SET;
- case MAP:
- return ContributionType.MAP;
- case UNIQUE:
- return ContributionType.UNIQUE;
- default:
- throw new AssertionError("Unknown production type: " + productionType());
- }
- }
-
- static final class Factory {
- private final Types types;
- private final Key.Factory keyFactory;
- private final DependencyRequest.Factory dependencyRequestFactory;
-
- Factory(
- Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory) {
- this.types = types;
- this.keyFactory = keyFactory;
- this.dependencyRequestFactory = dependencyRequestFactory;
- }
-
- ProductionBinding forProducesMethod(
- ExecutableElement producesMethod, TypeMirror contributedBy) {
- checkNotNull(producesMethod);
- checkArgument(producesMethod.getKind().equals(METHOD));
- checkArgument(contributedBy.getKind().equals(TypeKind.DECLARED));
- Produces producesAnnotation = producesMethod.getAnnotation(Produces.class);
- checkArgument(producesAnnotation != null);
- DeclaredType declaredContainer = MoreTypes.asDeclared(contributedBy);
- ExecutableType resolvedMethod =
- MoreTypes.asExecutable(types.asMemberOf(declaredContainer, producesMethod));
- Key key = keyFactory.forProducesMethod(resolvedMethod, producesMethod);
- ImmutableSet<DependencyRequest> dependencies =
- dependencyRequestFactory.forRequiredResolvedVariables(
- declaredContainer,
- producesMethod.getParameters(),
- resolvedMethod.getParameterTypes());
- DependencyRequest monitorRequest =
- dependencyRequestFactory.forProductionComponentMonitorProvider();
- Kind kind = MoreTypes.isTypeOf(ListenableFuture.class, producesMethod.getReturnType())
- ? Kind.FUTURE_PRODUCTION
- : Kind.IMMEDIATE;
- return new AutoValue_ProductionBinding(
- key,
- producesMethod,
- dependencies,
- findBindingPackage(key),
- false,
- ConfigurationAnnotations.getNullableType(producesMethod),
- Optional.of(MoreTypes.asTypeElement(declaredContainer)),
- Optional.<DependencyRequest>absent(),
- kind,
- producesAnnotation.type(),
- ImmutableList.copyOf(producesMethod.getThrownTypes()),
- Optional.of(monitorRequest));
- }
-
- ProductionBinding implicitMapOfProducerBinding(DependencyRequest mapOfValueRequest) {
- checkNotNull(mapOfValueRequest);
- Optional<Key> implicitMapOfProducerKey =
- keyFactory.implicitMapProducerKeyFrom(mapOfValueRequest.key());
- checkArgument(
- implicitMapOfProducerKey.isPresent(), "%s is not for a Map<K, V>", mapOfValueRequest);
- DependencyRequest implicitMapOfProducerRequest =
- dependencyRequestFactory.forImplicitMapBinding(
- mapOfValueRequest, implicitMapOfProducerKey.get());
- return new AutoValue_ProductionBinding(
- mapOfValueRequest.key(),
- implicitMapOfProducerRequest.requestElement(),
- ImmutableSet.of(implicitMapOfProducerRequest),
- findBindingPackage(mapOfValueRequest.key()),
- false,
- Optional.<DeclaredType>absent(),
- Optional.<TypeElement>absent(),
- Optional.<DependencyRequest>absent(),
- Kind.SYNTHETIC,
- Produces.Type.MAP,
- ImmutableList.<TypeMirror>of(),
- Optional.<DependencyRequest>absent());
- }
-
- ProductionBinding forComponentMethod(ExecutableElement componentMethod) {
- checkNotNull(componentMethod);
- checkArgument(componentMethod.getKind().equals(METHOD));
- checkArgument(componentMethod.getParameters().isEmpty());
- checkArgument(MoreTypes.isTypeOf(ListenableFuture.class, componentMethod.getReturnType()));
- return new AutoValue_ProductionBinding(
- keyFactory.forProductionComponentMethod(componentMethod),
- componentMethod,
- ImmutableSet.<DependencyRequest>of(),
- Optional.<String>absent(),
- false,
- Optional.<DeclaredType>absent(),
- Optional.<TypeElement>absent(),
- Optional.<DependencyRequest>absent(),
- Kind.COMPONENT_PRODUCTION,
- Produces.Type.UNIQUE,
- ImmutableList.copyOf(componentMethod.getThrownTypes()),
- Optional.<DependencyRequest>absent());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java
deleted file mode 100644
index 0581b1b..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.SetMultimap;
-import dagger.producers.ProductionComponent;
-import java.lang.annotation.Annotation;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/**
- * A {@link ProcessingStep} that is responsible for dealing with the {@link ProductionComponent}
- * annotation as part of the {@link ComponentProcessor}.
- *
- * @author Jesse Beder
- */
-final class ProductionComponentProcessingStep extends AbstractComponentProcessingStep {
- private final Messager messager;
- private final ProductionComponentValidator componentValidator;
- private final BuilderValidator componentBuilderValidator;
-
- ProductionComponentProcessingStep(
- Messager messager,
- ProductionComponentValidator componentValidator,
- BuilderValidator componentBuilderValidator,
- ComponentHierarchyValidator componentHierarchyValidator,
- BindingGraphValidator bindingGraphValidator,
- ComponentDescriptor.Factory componentDescriptorFactory,
- BindingGraph.Factory bindingGraphFactory,
- ComponentGenerator componentGenerator) {
- super(
- ProductionComponent.class,
- messager,
- componentHierarchyValidator,
- bindingGraphValidator,
- componentDescriptorFactory,
- bindingGraphFactory,
- componentGenerator);
- this.messager = messager;
- this.componentValidator = componentValidator;
- this.componentBuilderValidator = componentBuilderValidator;
- }
-
- @Override
- public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.<Class<? extends Annotation>>of(
- ProductionComponent.class, ProductionComponent.Builder.class);
- }
-
- // TODO(beder): Move common logic into the AbstractComponentProcessingStep when implementing
- // production subcomponents.
- @Override
- protected ComponentElementValidator componentElementValidator(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- final Map<Element, ValidationReport<TypeElement>> builderReportsByComponent =
- processComponentBuilders(elementsByAnnotation.get(ProductionComponent.Builder.class));
- return new ComponentElementValidator() {
- @Override
- boolean validateComponent(TypeElement componentTypeElement, Messager messager) {
- ValidationReport<TypeElement> validationReport =
- componentValidator.validate(componentTypeElement);
- validationReport.printMessagesTo(messager);
- if (!validationReport.isClean()) {
- return false;
- }
- ValidationReport<?> builderReport = builderReportsByComponent.get(componentTypeElement);
- return builderReport == null || builderReport.isClean();
- }
- };
- }
-
- private Map<Element, ValidationReport<TypeElement>> processComponentBuilders(
- Set<? extends Element> componentBuilderElements) {
- Map<Element, ValidationReport<TypeElement>> builderReportsByComponent = Maps.newHashMap();
- for (Element element : componentBuilderElements) {
- ValidationReport<TypeElement> report =
- componentBuilderValidator.validate(MoreElements.asType(element));
- report.printMessagesTo(messager);
- builderReportsByComponent.put(element.getEnclosingElement(), report);
- }
- return builderReportsByComponent;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentValidator.java b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentValidator.java
deleted file mode 100644
index 2e2291d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentValidator.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableList;
-import dagger.Module;
-import dagger.producers.ProducerModule;
-import dagger.producers.ProductionComponent;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.ConfigurationAnnotations.getComponentModules;
-import static javax.lang.model.element.ElementKind.CLASS;
-import static javax.lang.model.element.ElementKind.INTERFACE;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-
-/**
- * Performs superficial validation of the contract of the {@link ProductionComponent} annotation.
- *
- * @author Jesse Beder
- */
-final class ProductionComponentValidator {
- ValidationReport<TypeElement> validate(final TypeElement subject) {
- final ValidationReport.Builder<TypeElement> builder = ValidationReport.about(subject);
-
- if (!subject.getKind().equals(INTERFACE)
- && !(subject.getKind().equals(CLASS) && subject.getModifiers().contains(ABSTRACT))) {
- builder.addError(
- "@ProductionComponent may only be applied to an interface or abstract class", subject);
- }
-
- AnnotationMirror componentMirror =
- getAnnotationMirror(subject, ProductionComponent.class).get();
- ImmutableList<TypeMirror> moduleTypes = getComponentModules(componentMirror);
-
- // TODO(gak): make unused modules an error
- for (TypeMirror moduleType : moduleTypes) {
- moduleType.accept(
- new SimpleTypeVisitor6<Void, Void>() {
- @Override
- protected Void defaultAction(TypeMirror mirror, Void p) {
- builder.addError(mirror + " is not a valid module type.", subject);
- return null;
- }
-
- @Override
- public Void visitDeclared(DeclaredType t, Void p) {
- checkState(t.getTypeArguments().isEmpty());
- TypeElement moduleElement = MoreElements.asType(t.asElement());
- if (!getAnnotationMirror(moduleElement, Module.class).isPresent()
- && !getAnnotationMirror(moduleElement, ProducerModule.class).isPresent()) {
- builder.addError(
- moduleElement.getQualifiedName()
- + " is listed as a module, but is not annotated with @Module or"
- + " @ProducerModule",
- subject);
- }
- return null;
- }
- },
- null);
- }
-
- return builder.build();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProvidesMethodValidator.java b/compiler/src/main/java/dagger/internal/codegen/ProvidesMethodValidator.java
deleted file mode 100644
index e9c8b16..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProvidesMethodValidator.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import dagger.Module;
-import dagger.Provides;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_ABSTRACT;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_MUST_RETURN_A_VALUE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_IN_MODULE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_MAP_HAS_MAP_KEY;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_PRIVATE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_SET_VALUES_RAW_SET;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_TYPE_PARAMETER;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_MULTIPLE_MAP_KEY;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_NO_MAP_KEY;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_METHOD_RETURN_TYPE;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_METHOD_SET_VALUES_RETURN_SET;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static dagger.internal.codegen.MapKeys.getMapKeys;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.type.TypeKind.ARRAY;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.VOID;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Provides} methods.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class ProvidesMethodValidator {
- private final Elements elements;
-
- ProvidesMethodValidator(Elements elements) {
- this.elements = checkNotNull(elements);
- }
-
- private TypeElement getSetElement() {
- return elements.getTypeElement(Set.class.getCanonicalName());
- }
-
- ValidationReport<ExecutableElement> validate(ExecutableElement providesMethodElement) {
- ValidationReport.Builder<ExecutableElement> builder =
- ValidationReport.about(providesMethodElement);
-
- Provides providesAnnotation = providesMethodElement.getAnnotation(Provides.class);
- checkArgument(providesAnnotation != null);
-
- Element enclosingElement = providesMethodElement.getEnclosingElement();
- if (!isAnnotationPresent(enclosingElement, Module.class)) {
- builder.addError(
- formatModuleErrorMessage(BINDING_METHOD_NOT_IN_MODULE), providesMethodElement);
- }
-
- if (!providesMethodElement.getTypeParameters().isEmpty()) {
- builder.addError(formatErrorMessage(BINDING_METHOD_TYPE_PARAMETER), providesMethodElement);
- }
-
- Set<Modifier> modifiers = providesMethodElement.getModifiers();
- if (modifiers.contains(PRIVATE)) {
- builder.addError(formatErrorMessage(BINDING_METHOD_PRIVATE), providesMethodElement);
- }
- if (modifiers.contains(ABSTRACT)) {
- builder.addError(formatErrorMessage(BINDING_METHOD_ABSTRACT), providesMethodElement);
- }
-
- TypeMirror returnType = providesMethodElement.getReturnType();
- TypeKind returnTypeKind = returnType.getKind();
- if (returnTypeKind.equals(VOID)) {
- builder.addError(
- formatErrorMessage(BINDING_METHOD_MUST_RETURN_A_VALUE), providesMethodElement);
- }
-
- // check mapkey is right
- if (!providesAnnotation.type().equals(Provides.Type.MAP)
- && !getMapKeys(providesMethodElement).isEmpty()) {
- builder.addError(
- formatErrorMessage(BINDING_METHOD_NOT_MAP_HAS_MAP_KEY), providesMethodElement);
- }
-
- validateMethodQualifiers(builder, providesMethodElement);
-
- switch (providesAnnotation.type()) {
- case UNIQUE: // fall through
- case SET:
- validateKeyType(builder, returnType);
- break;
- case MAP:
- validateKeyType(builder, returnType);
- ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(providesMethodElement);
- switch (mapKeys.size()) {
- case 0:
- builder.addError(
- formatErrorMessage(BINDING_METHOD_WITH_NO_MAP_KEY), providesMethodElement);
- break;
- case 1:
- break;
- default:
- builder.addError(
- formatErrorMessage(BINDING_METHOD_WITH_MULTIPLE_MAP_KEY), providesMethodElement);
- break;
- }
- break;
- case SET_VALUES:
- if (!returnTypeKind.equals(DECLARED)) {
- builder.addError(PROVIDES_METHOD_SET_VALUES_RETURN_SET, providesMethodElement);
- } else {
- DeclaredType declaredReturnType = (DeclaredType) returnType;
- // TODO(gak): should we allow "covariant return" for set values?
- if (!declaredReturnType.asElement().equals(getSetElement())) {
- builder.addError(PROVIDES_METHOD_SET_VALUES_RETURN_SET, providesMethodElement);
- } else if (declaredReturnType.getTypeArguments().isEmpty()) {
- builder.addError(
- formatErrorMessage(BINDING_METHOD_SET_VALUES_RAW_SET), providesMethodElement);
- } else {
- validateKeyType(builder,
- Iterables.getOnlyElement(declaredReturnType.getTypeArguments()));
- }
- }
- break;
- default:
- throw new AssertionError();
- }
-
- return builder.build();
- }
-
- /** Validates that a Provides or Produces method doesn't have multiple qualifiers. */
- static void validateMethodQualifiers(ValidationReport.Builder<ExecutableElement> builder,
- ExecutableElement methodElement) {
- ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(methodElement);
- if (qualifiers.size() > 1) {
- for (AnnotationMirror qualifier : qualifiers) {
- builder.addError(PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS, methodElement, qualifier);
- }
- }
- }
-
- private String formatErrorMessage(String msg) {
- return String.format(msg, Provides.class.getSimpleName());
- }
-
- private String formatModuleErrorMessage(String msg) {
- return String.format(msg, Provides.class.getSimpleName(), Module.class.getSimpleName());
- }
-
- private void validateKeyType(ValidationReport.Builder<? extends Element> reportBuilder,
- TypeMirror type) {
- TypeKind kind = type.getKind();
- if (!(kind.isPrimitive() || kind.equals(DECLARED) || kind.equals(ARRAY))) {
- reportBuilder.addError(PROVIDES_METHOD_RETURN_TYPE, reportBuilder.getSubject());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java b/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java
deleted file mode 100644
index b2ac74f..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import dagger.Provides;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
-import static dagger.internal.codegen.Scope.scopeOf;
-import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
-import static javax.lang.model.element.ElementKind.FIELD;
-import static javax.lang.model.element.ElementKind.METHOD;
-
-/**
- * A value object representing the mechanism by which a {@link Key} can be provided. New instances
- * should be created using an instance of the {@link Factory}.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@AutoValue
-abstract class ProvisionBinding extends ContributionBinding {
-
- @Override
- Binding.Type bindingType() {
- return Binding.Type.PROVISION;
- }
-
- @Override
- abstract Scope scope();
-
- static final class Factory {
- private final Elements elements;
- private final Types types;
- private final Key.Factory keyFactory;
- private final DependencyRequest.Factory dependencyRequestFactory;
-
- Factory(Elements elements, Types types, Key.Factory keyFactory,
- DependencyRequest.Factory dependencyRequestFactory) {
- this.elements = elements;
- this.types = types;
- this.keyFactory = keyFactory;
- this.dependencyRequestFactory = dependencyRequestFactory;
- }
-
- /** Returns an unresolved version of this binding. */
- ProvisionBinding unresolve(ProvisionBinding binding) {
- checkState(binding.hasNonDefaultTypeParameters());
- return forInjectConstructor((ExecutableElement) binding.bindingElement(),
- Optional.<TypeMirror>absent());
- }
-
- /**
- * Returns a ProvisionBinding for the given element. If {@code resolvedType} is present, this
- * will return a resolved binding, with the key & type resolved to the given type (using
- * {@link Types#asMemberOf(DeclaredType, Element)}).
- */
- ProvisionBinding forInjectConstructor(ExecutableElement constructorElement,
- Optional<TypeMirror> resolvedType) {
- checkNotNull(constructorElement);
- checkArgument(constructorElement.getKind().equals(CONSTRUCTOR));
- checkArgument(isAnnotationPresent(constructorElement, Inject.class));
- checkArgument(!getQualifier(constructorElement).isPresent());
-
- ExecutableType cxtorType = MoreTypes.asExecutable(constructorElement.asType());
- DeclaredType enclosingCxtorType =
- MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
- // If the class this is constructing has some type arguments, resolve everything.
- if (!enclosingCxtorType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
- DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
- // Validate that we're resolving from the correct type.
- checkState(types.isSameType(types.erasure(resolved), types.erasure(enclosingCxtorType)),
- "erased expected type: %s, erased actual type: %s",
- types.erasure(resolved), types.erasure(enclosingCxtorType));
- cxtorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement));
- enclosingCxtorType = resolved;
- }
-
- Key key = keyFactory.forInjectConstructorWithResolvedType(enclosingCxtorType);
- checkArgument(!key.qualifier().isPresent());
- ImmutableSet<DependencyRequest> dependencies =
- dependencyRequestFactory.forRequiredResolvedVariables(enclosingCxtorType,
- constructorElement.getParameters(),
- cxtorType.getParameterTypes());
- Optional<DependencyRequest> membersInjectionRequest =
- membersInjectionRequest(enclosingCxtorType);
- Scope scope = Scope.scopeOf(constructorElement.getEnclosingElement());
-
- TypeElement bindingTypeElement =
- MoreElements.asType(constructorElement.getEnclosingElement());
-
- return new AutoValue_ProvisionBinding(
- key,
- constructorElement,
- dependencies,
- findBindingPackage(key),
- hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types),
- Optional.<DeclaredType>absent(),
- Optional.<TypeElement>absent(),
- membersInjectionRequest,
- Kind.INJECTION,
- Provides.Type.UNIQUE,
- scope);
- }
-
- private static final ImmutableSet<ElementKind> MEMBER_KINDS =
- Sets.immutableEnumSet(METHOD, FIELD);
-
- private Optional<DependencyRequest> membersInjectionRequest(DeclaredType type) {
- TypeElement typeElement = MoreElements.asType(type.asElement());
- if (!types.isSameType(elements.getTypeElement(Object.class.getCanonicalName()).asType(),
- typeElement.getSuperclass())) {
- return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
- }
- for (Element enclosedElement : typeElement.getEnclosedElements()) {
- if (MEMBER_KINDS.contains(enclosedElement.getKind())
- && (isAnnotationPresent(enclosedElement, Inject.class))) {
- return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
- }
- }
- return Optional.absent();
- }
-
- ProvisionBinding forProvidesMethod(ExecutableElement providesMethod, TypeMirror contributedBy) {
- checkNotNull(providesMethod);
- checkArgument(providesMethod.getKind().equals(METHOD));
- checkArgument(contributedBy.getKind().equals(TypeKind.DECLARED));
- Provides providesAnnotation = providesMethod.getAnnotation(Provides.class);
- checkArgument(providesAnnotation != null);
- DeclaredType declaredContainer = MoreTypes.asDeclared(contributedBy);
- ExecutableType resolvedMethod =
- MoreTypes.asExecutable(types.asMemberOf(declaredContainer, providesMethod));
- Key key = keyFactory.forProvidesMethod(resolvedMethod, providesMethod);
- ImmutableSet<DependencyRequest> dependencies =
- dependencyRequestFactory.forRequiredResolvedVariables(
- declaredContainer,
- providesMethod.getParameters(),
- resolvedMethod.getParameterTypes());
- Scope scope = Scope.scopeOf(providesMethod);
- return new AutoValue_ProvisionBinding(
- key,
- providesMethod,
- dependencies,
- findBindingPackage(key),
- false /* no non-default parameter types */,
- ConfigurationAnnotations.getNullableType(providesMethod),
- Optional.of(MoreTypes.asTypeElement(declaredContainer)),
- Optional.<DependencyRequest>absent(),
- Kind.PROVISION,
- providesAnnotation.type(),
- scope);
- }
-
- ProvisionBinding implicitMapOfProviderBinding(DependencyRequest mapOfValueRequest) {
- checkNotNull(mapOfValueRequest);
- Optional<Key> implicitMapOfProviderKey =
- keyFactory.implicitMapProviderKeyFrom(mapOfValueRequest.key());
- checkArgument(
- implicitMapOfProviderKey.isPresent(),
- "%s is not a request for Map<K, V>",
- mapOfValueRequest);
- DependencyRequest implicitMapOfProviderRequest =
- dependencyRequestFactory.forImplicitMapBinding(
- mapOfValueRequest, implicitMapOfProviderKey.get());
- return new AutoValue_ProvisionBinding(
- mapOfValueRequest.key(),
- implicitMapOfProviderRequest.requestElement(),
- ImmutableSet.of(implicitMapOfProviderRequest),
- findBindingPackage(mapOfValueRequest.key()),
- false /* no non-default parameter types */,
- Optional.<DeclaredType>absent(),
- Optional.<TypeElement>absent(),
- Optional.<DependencyRequest>absent(),
- Kind.SYNTHETIC,
- Provides.Type.MAP,
- scopeOf(implicitMapOfProviderRequest.requestElement()));
- }
-
- ProvisionBinding forComponent(TypeElement componentDefinitionType) {
- checkNotNull(componentDefinitionType);
- return new AutoValue_ProvisionBinding(
- keyFactory.forComponent(componentDefinitionType.asType()),
- componentDefinitionType,
- ImmutableSet.<DependencyRequest>of(),
- Optional.<String>absent(),
- false /* no non-default parameter types */,
- Optional.<DeclaredType>absent(),
- Optional.<TypeElement>absent(),
- Optional.<DependencyRequest>absent(),
- Kind.COMPONENT,
- Provides.Type.UNIQUE,
- Scope.unscoped());
- }
-
- ProvisionBinding forComponentMethod(ExecutableElement componentMethod) {
- checkNotNull(componentMethod);
- checkArgument(componentMethod.getKind().equals(METHOD));
- checkArgument(componentMethod.getParameters().isEmpty());
- Scope scope = Scope.scopeOf(componentMethod);
- return new AutoValue_ProvisionBinding(
- keyFactory.forComponentMethod(componentMethod),
- componentMethod,
- ImmutableSet.<DependencyRequest>of(),
- Optional.<String>absent(),
- false /* no non-default parameter types */,
- ConfigurationAnnotations.getNullableType(componentMethod),
- Optional.<TypeElement>absent(),
- Optional.<DependencyRequest>absent(),
- Kind.COMPONENT_PROVISION,
- Provides.Type.UNIQUE,
- scope);
- }
-
- ProvisionBinding forSubcomponentBuilderMethod(
- ExecutableElement subcomponentBuilderMethod, TypeElement contributedBy) {
- checkNotNull(subcomponentBuilderMethod);
- checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
- checkArgument(subcomponentBuilderMethod.getParameters().isEmpty());
- DeclaredType declaredContainer = asDeclared(contributedBy.asType());
- return new AutoValue_ProvisionBinding(
- keyFactory.forSubcomponentBuilderMethod(subcomponentBuilderMethod, declaredContainer),
- subcomponentBuilderMethod,
- ImmutableSet.<DependencyRequest>of(),
- Optional.<String>absent(),
- false /* no non-default parameter types */,
- Optional.<DeclaredType>absent(),
- Optional.of(contributedBy),
- Optional.<DependencyRequest>absent(),
- Kind.SUBCOMPONENT_BUILDER,
- Provides.Type.UNIQUE,
- Scope.unscoped());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java b/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java
deleted file mode 100644
index 024097e..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.ContributionBinding.contributionTypeFor;
-
-/**
- * The collection of bindings that have been resolved for a binding key.
- *
- * @author Gregory Kick
- */
-@AutoValue
-abstract class ResolvedBindings {
- /**
- * The binding key for which the {@link #bindings()} have been resolved.
- */
- abstract BindingKey bindingKey();
-
- /**
- * The component in which the bindings in {@link #ownedBindings()},
- * {@link #ownedContributionBindings()}, and {@link #ownedMembersInjectionBinding()} were
- * resolved.
- */
- abstract ComponentDescriptor owningComponent();
-
- /**
- * The contribution bindings for {@link #bindingKey()} that were resolved in
- * {@link #owningComponent()} or its ancestor components, keyed by the component in which the
- * binding was resolved. If {@link #bindingKey()}'s kind is not
- * {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
- */
- abstract ImmutableSetMultimap<ComponentDescriptor, ContributionBinding> allContributionBindings();
-
- /**
- * The members-injection bindings for {@link #bindingKey()} that were resolved in
- * {@link #owningComponent()} or its ancestor components, keyed by the component in which the
- * binding was resolved. If {@link #bindingKey()}'s kind is not
- * {@link BindingKey.Kind#MEMBERS_INJECTION}, this is empty.
- */
- abstract ImmutableMap<ComponentDescriptor, MembersInjectionBinding> allMembersInjectionBindings();
-
- /**
- * All bindings for {@link #bindingKey()}, regardless of in which component they were resolved.
- */
- ImmutableSet<? extends Binding> bindings() {
- switch (bindingKey().kind()) {
- case CONTRIBUTION:
- return contributionBindings();
-
- case MEMBERS_INJECTION:
- return ImmutableSet.copyOf(membersInjectionBinding().asSet());
-
- default:
- throw new AssertionError(bindingKey());
- }
- }
-
- /**
- * All bindings for {@link #bindingKey()} that were resolved in {@link #owningComponent()}.
- */
- ImmutableSet<? extends Binding> ownedBindings() {
- switch (bindingKey().kind()) {
- case CONTRIBUTION:
- return ownedContributionBindings();
-
- case MEMBERS_INJECTION:
- return ImmutableSet.copyOf(ownedMembersInjectionBinding().asSet());
-
- default:
- throw new AssertionError(bindingKey());
- }
- }
-
- /**
- * All contribution bindings, regardless of owning component.
- *
- * @throws IllegalStateException if {@link #bindingKey()} is not a
- * {@link BindingKey.Kind#CONTRIBUTION}.
- */
- ImmutableSet<ContributionBinding> contributionBindings() {
- checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION));
- return ImmutableSet.copyOf(allContributionBindings().values());
- }
-
- /**
- * The contribution bindings that were resolved in {@link #owningComponent()}.
- *
- * @throws IllegalStateException if {@link #bindingKey()} is not a
- * {@link BindingKey.Kind#CONTRIBUTION}.
- */
- ImmutableSet<ContributionBinding> ownedContributionBindings() {
- checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION));
- return allContributionBindings().get(owningComponent());
- }
-
- /**
- * The members-injection binding, regardless of owning component.
- *
- * @throws IllegalStateException if {@link #bindingKey()} is not a
- * {@link BindingKey.Kind#MEMBERS_INJECTION}.
- */
- Optional<MembersInjectionBinding> membersInjectionBinding() {
- checkState(bindingKey().kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
- ImmutableSet<MembersInjectionBinding> membersInjectionBindings =
- FluentIterable.from(allMembersInjectionBindings().values()).toSet();
- return membersInjectionBindings.isEmpty()
- ? Optional.<MembersInjectionBinding>absent()
- : Optional.of(Iterables.getOnlyElement(membersInjectionBindings));
- }
-
- /**
- * The members-injection binding that was resolved in {@link #owningComponent()}.
- *
- * @throws IllegalStateException if {@link #bindingKey()} is not a
- * {@link BindingKey.Kind#MEMBERS_INJECTION}.
- */
- Optional<MembersInjectionBinding> ownedMembersInjectionBinding() {
- checkState(bindingKey().kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
- return Optional.fromNullable(allMembersInjectionBindings().get(owningComponent()));
- }
-
- /**
- * Creates a {@link ResolvedBindings} for contribution bindings.
- */
- static ResolvedBindings forContributionBindings(
- BindingKey bindingKey,
- ComponentDescriptor owningComponent,
- Multimap<ComponentDescriptor, ? extends ContributionBinding> contributionBindings) {
- checkArgument(bindingKey.kind().equals(BindingKey.Kind.CONTRIBUTION));
- return new AutoValue_ResolvedBindings(
- bindingKey,
- owningComponent,
- ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>copyOf(contributionBindings),
- ImmutableMap.<ComponentDescriptor, MembersInjectionBinding>of());
- }
-
- /**
- * Creates a {@link ResolvedBindings} for contribution bindings.
- */
- static ResolvedBindings forContributionBindings(
- BindingKey bindingKey,
- ComponentDescriptor owningComponent,
- ContributionBinding... ownedContributionBindings) {
- return forContributionBindings(
- bindingKey,
- owningComponent,
- ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>builder()
- .putAll(owningComponent, ownedContributionBindings)
- .build());
- }
-
- /**
- * Creates a {@link ResolvedBindings} for members injection bindings.
- */
- static ResolvedBindings forMembersInjectionBinding(
- BindingKey bindingKey,
- ComponentDescriptor owningComponent,
- MembersInjectionBinding ownedMembersInjectionBinding) {
- checkArgument(bindingKey.kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
- return new AutoValue_ResolvedBindings(
- bindingKey,
- owningComponent,
- ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>of(),
- ImmutableMap.of(owningComponent, ownedMembersInjectionBinding));
- }
-
- /**
- * Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
- */
- static ResolvedBindings noBindings(BindingKey bindingKey, ComponentDescriptor owningComponent) {
- return new AutoValue_ResolvedBindings(
- bindingKey,
- owningComponent,
- ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>of(),
- ImmutableMap.<ComponentDescriptor, MembersInjectionBinding>of());
- }
-
- /**
- * Returns a {@code ResolvedBindings} with the same {@link #bindingKey()} and {@link #bindings()}
- * as this one, but no {@link #ownedBindings()}.
- */
- ResolvedBindings asInheritedIn(ComponentDescriptor owningComponent) {
- return new AutoValue_ResolvedBindings(
- bindingKey(), owningComponent, allContributionBindings(), allMembersInjectionBindings());
- }
-
- /**
- * {@code true} if this is a multibindings contribution.
- */
- boolean isMultibindings() {
- return bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)
- && !contributionBindings().isEmpty()
- && contributionTypeFor(contributionBindings()).isMultibinding();
- }
-
- /**
- * {@code true} if this is a unique contribution binding.
- */
- boolean isUniqueContribution() {
- return bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)
- && !contributionBindings().isEmpty()
- && !contributionTypeFor(contributionBindings()).isMultibinding();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/Scope.java b/compiler/src/main/java/dagger/internal/codegen/Scope.java
deleted file mode 100644
index bcb009d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/Scope.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.AnnotationMirrors;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import javax.annotation.Nullable;
-import javax.inject.Singleton;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-import static com.google.auto.common.MoreTypes.isTypeOf;
-import static dagger.internal.codegen.ErrorMessages.stripCommonTypePrefixes;
-import static dagger.internal.codegen.InjectionAnnotations.getScopeAnnotation;
-
-/**
- * A representation of the scope (or lack of it) associated with a component, providing method
- * or injection location.
- */
-final class Scope {
-
- /**
- * An internal representation for an unscoped binding.
- */
- private static final Scope UNSCOPED = new Scope();
-
- /**
- * The underlying {@link AnnotationMirror} that represents the scope annotation.
- */
- @Nullable
- private final AnnotationMirror annotationMirror;
-
- private Scope(@Nullable AnnotationMirror annotationMirror) {
- this.annotationMirror = annotationMirror;
- }
-
- private Scope() {
- this(null);
- }
-
- /**
- * Returns representation for an unscoped binding.
- */
- static Scope unscoped() {
- return UNSCOPED;
- }
-
- /**
- * If the source code element has an associated scoped annotation then returns a representation
- * of that scope, otherwise returns a representation for an unscoped binding.
- */
- static Scope scopeOf(Element element) {
- Optional<AnnotationMirror> scopeAnnotation = getScopeAnnotation(element);
- return scopeAnnotation.isPresent() ? new Scope(scopeAnnotation.get()) : UNSCOPED;
- }
-
- /**
- * Returns true if the scope is present, i.e. it's not unscoped binding.
- */
- public boolean isPresent() {
- return annotationMirror != null;
- }
-
- /**
- * Returns true if the scope represents the {@link Singleton @Singleton} annotation.
- */
- public boolean isSingleton() {
- return annotationMirror != null
- && isTypeOf(Singleton.class, annotationMirror.getAnnotationType());
- }
-
- /**
- * Returns the readable source representation (name with @ prefix) of the annotation type.
- *
- * <p>It's readable source because it has had common package prefixes removed, e.g.
- * {@code @javax.inject.Singleton} is returned as {@code @Singleton}.
- *
- * <p>Make sure that the scope is actually {@link #isPresent() present} before calling as it will
- * throw an {@link IllegalStateException} otherwise. This does not return any annotation values
- * as according to {@link javax.inject.Scope} scope annotations are not supposed to use them.
- */
- public String getReadableSource() {
- return stripCommonTypePrefixes("@" + getQualifiedName());
- }
-
- /**
- * Returns the fully qualified name of the annotation type.
- *
- * <p>Make sure that the scope is actually {@link #isPresent() present} before calling as it will
- * throw an {@link IllegalStateException} otherwise. This does not return any annotation values
- * as according to {@link javax.inject.Scope} scope annotations are not supposed to use them.
- */
- public String getQualifiedName() {
- Preconditions.checkState(annotationMirror != null,
- "Cannot create a stripped source representation of no annotation");
- TypeElement typeElement = MoreTypes.asTypeElement(annotationMirror.getAnnotationType());
- return typeElement.getQualifiedName().toString();
- }
-
- /**
- * Scopes are equal if the underlying {@link AnnotationMirror} are equivalent according to
- * {@link AnnotationMirrors#equivalence()}.
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- } else if (obj instanceof Scope) {
- Scope that = (Scope) obj;
- return AnnotationMirrors.equivalence()
- .equivalent(this.annotationMirror, that.annotationMirror);
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return AnnotationMirrors.equivalence().hash(annotationMirror);
- }
-
- /**
- * Returns a debug representation of the scope.
- */
- @Override
- public String toString() {
- return annotationMirror == null ? "UNSCOPED" : annotationMirror.toString();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerationException.java b/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerationException.java
deleted file mode 100644
index c262098..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerationException.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import dagger.internal.codegen.writer.ClassName;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-/**
- * An exception thrown to indicate that a source file could not be generated.
- *
- * <p>This exception <b>should not</b> be used to report detectable, logical errors as it may mask
- * other errors that might have been caught upon further processing. Use a {@link ValidationReport}
- * for that.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-final class SourceFileGenerationException extends Exception {
- private final ImmutableSet<ClassName> generatedClassNames;
- private final Optional<? extends Element> associatedElement;
-
- SourceFileGenerationException(Iterable<ClassName> generatedClassNames, Throwable cause,
- Optional<? extends Element> associatedElement) {
- super(createMessage(generatedClassNames, cause.getMessage()), cause);
- this.generatedClassNames = ImmutableSet.copyOf(generatedClassNames);
- this.associatedElement = checkNotNull(associatedElement);
- }
-
- SourceFileGenerationException(Iterable<ClassName> generatedClassNames, Throwable cause) {
- this(generatedClassNames, cause, Optional.<Element>absent());
- }
-
- SourceFileGenerationException(Iterable<ClassName> generatedClassNames, Throwable cause,
- Element associatedElement) {
- this(generatedClassNames, cause, Optional.of(associatedElement));
- }
-
- public ImmutableSet<ClassName> generatedClassNames() {
- return generatedClassNames;
- }
-
- public Optional<? extends Element> associatedElement() {
- return associatedElement;
- }
-
- private static String createMessage(Iterable<ClassName> generatedClassNames, String message) {
- return String.format("Could not generate %s: %s.",
- Iterables.isEmpty(generatedClassNames)
- ? "unknown files"
- : Iterables.toString(generatedClassNames),
- message);
- }
-
- void printMessageTo(Messager messager) {
- if (associatedElement.isPresent()) {
- messager.printMessage(ERROR, getMessage(), associatedElement.get());
- } else {
- messager.printMessage(ERROR, getMessage());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerator.java b/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerator.java
deleted file mode 100644
index 4b6efc0..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerator.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.JavaWriter;
-import dagger.internal.codegen.writer.TypeWriter;
-import java.io.IOException;
-import javax.annotation.processing.Filer;
-import javax.lang.model.element.Element;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * A template class that provides a framework for properly handling IO while generating source files
- * from an annotation processor. Particularly, it makes a best effort to ensure that files that
- * fail to write successfully are deleted.
- *
- * @param <T> The input type from which source is to be generated.
- * @author Gregory Kick
- * @since 2.0
- */
-abstract class SourceFileGenerator<T> {
- private final Filer filer;
-
- SourceFileGenerator(Filer filer) {
- this.filer = checkNotNull(filer);
- }
-
- final void generate(T input) throws SourceFileGenerationException {
- ClassName generatedTypeName = nameGeneratedType(input);
- ImmutableSet<Element> originatingElements =
- ImmutableSet.<Element>copyOf(getOriginatingElements(input));
- try {
- ImmutableSet<JavaWriter> writers = write(generatedTypeName, input);
- for (JavaWriter javaWriter : writers) {
- try {
- javaWriter.file(filer, originatingElements);
- } catch (IOException e) {
- throw new SourceFileGenerationException(getNamesForWriters(javaWriter.getTypeWriters()),
- e, getElementForErrorReporting(input));
- }
- }
- } catch (Exception e) {
- // if the code above threw a SFGE, use that
- Throwables.propagateIfPossible(e, SourceFileGenerationException.class);
- // otherwise, throw a new one
- throw new SourceFileGenerationException(ImmutableList.<ClassName>of(), e,
- getElementForErrorReporting(input));
- }
- }
-
- private static Iterable<ClassName> getNamesForWriters(Iterable<TypeWriter> typeWriters) {
- return Iterables.transform(typeWriters, new Function<TypeWriter, ClassName>() {
- @Override public ClassName apply(TypeWriter input) {
- return input.name();
- }
- });
- }
-
- /**
- * Implementations should return the {@link ClassName} for the top-level type to be generated.
- */
- abstract ClassName nameGeneratedType(T input);
-
- /**
- * Implementations should return {@link Element} instances from which the source is to be
- * generated.
- */
- abstract Iterable<? extends Element> getOriginatingElements(T input);
-
- /**
- * Returns an optional element to be used for reporting errors. This returns a single element
- * rather than a collection to reduce output noise.
- */
- abstract Optional<? extends Element> getElementForErrorReporting(T input);
-
- /**
- */
- abstract ImmutableSet<JavaWriter> write(ClassName generatedTypeName, T input);
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java b/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java
deleted file mode 100644
index 7ad0acb..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License
- * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing permissions and limitations under
- * the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.CaseFormat;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Ordering;
-import dagger.internal.DoubleCheckLazy;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Types;
-
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Utilities for generating files.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-class SourceFiles {
- /**
- * Sorts {@link DependencyRequest} instances in an order likely to reflect their logical
- * importance.
- */
- static final Ordering<DependencyRequest> DEPENDENCY_ORDERING = new Ordering<DependencyRequest>() {
- @Override
- public int compare(DependencyRequest left, DependencyRequest right) {
- return ComparisonChain.start()
- // put fields before parameters
- .compare(left.requestElement().getKind(), right.requestElement().getKind())
- // order by dependency kind
- .compare(left.kind(), right.kind())
- // then sort by name
- .compare(left.requestElement().getSimpleName().toString(),
- right.requestElement().getSimpleName().toString()).result();
- }
- };
-
- /**
- * A variant of {@link #indexDependenciesByKey} that maps from unresolved keys
- * to requests. This is used when generating component's initialize()
- * methods (and in members injectors) in order to instantiate dependent
- * providers. Consider a generic type of {@code Foo<T>} with a constructor
- * of {@code Foo(T t, T t1, A a, A a1)}. That will be collapsed to a factory
- * taking a {@code Provider<T> tProvider, Provider<A> aProvider}. However,
- * if it was referenced as {@code Foo<A>}, we need to make sure we still
- * pass two providers. Naively (if we just referenced by resolved BindingKey),
- * we would have passed a single {@code aProvider}.
- */
- // TODO(user): Refactor these indexing methods so that the binding itself knows what sort of
- // binding keys and framework classes that it needs.
- static ImmutableSetMultimap<BindingKey, DependencyRequest> indexDependenciesByUnresolvedKey(
- Types types, Iterable<? extends DependencyRequest> dependencies) {
- ImmutableSetMultimap.Builder<BindingKey, DependencyRequest> dependenciesByKeyBuilder =
- new ImmutableSetMultimap.Builder<BindingKey, DependencyRequest>()
- .orderValuesBy(DEPENDENCY_ORDERING);
- for (DependencyRequest dependency : dependencies) {
- BindingKey resolved = dependency.bindingKey();
- // To get the proper unresolved type, we have to extract the proper type from the
- // request type again (because we're looking at the actual element's type).
- TypeMirror unresolvedType =
- DependencyRequest.Factory.extractKindAndType(dependency.requestElement().asType()).type();
- BindingKey unresolved =
- BindingKey.create(resolved.kind(), resolved.key().withType(types, unresolvedType));
- dependenciesByKeyBuilder.put(unresolved, dependency);
- }
- return dependenciesByKeyBuilder.build();
- }
-
- /**
- * Allows dependency requests to be grouped by the key they're requesting.
- * This is used by factory generation in order to minimize the number of parameters
- * required in the case where a given key is requested more than once. This expects
- * unresolved dependency requests, otherwise we may generate factories based on
- * a particular usage of a class as opposed to the generic types of the class.
- */
- static ImmutableSetMultimap<BindingKey, DependencyRequest> indexDependenciesByKey(
- Iterable<? extends DependencyRequest> dependencies) {
- ImmutableSetMultimap.Builder<BindingKey, DependencyRequest> dependenciesByKeyBuilder =
- new ImmutableSetMultimap.Builder<BindingKey, DependencyRequest>()
- .orderValuesBy(DEPENDENCY_ORDERING);
- for (DependencyRequest dependency : dependencies) {
- dependenciesByKeyBuilder.put(dependency.bindingKey(), dependency);
- }
- return dependenciesByKeyBuilder.build();
- }
-
- /**
- * This method generates names and keys for the framework classes necessary for all of the
- * bindings. It is responsible for the following:
- * <ul>
- * <li>Choosing a name that associates the binding with all of the dependency requests for this
- * type.
- * <li>Choosing a name that is <i>probably</i> associated with the type being bound.
- * <li>Ensuring that no two bindings end up with the same name.
- * </ul>
- *
- * @return Returns the mapping from {@link BindingKey} to field, sorted by the name of the field.
- */
- static ImmutableMap<BindingKey, FrameworkField> generateBindingFieldsForDependencies(
- DependencyRequestMapper dependencyRequestMapper,
- Iterable<? extends DependencyRequest> dependencies) {
- ImmutableSetMultimap<BindingKey, DependencyRequest> dependenciesByKey =
- indexDependenciesByKey(dependencies);
- Map<BindingKey, Collection<DependencyRequest>> dependenciesByKeyMap =
- dependenciesByKey.asMap();
- ImmutableMap.Builder<BindingKey, FrameworkField> bindingFields = ImmutableMap.builder();
- for (Entry<BindingKey, Collection<DependencyRequest>> entry
- : dependenciesByKeyMap.entrySet()) {
- BindingKey bindingKey = entry.getKey();
- Collection<DependencyRequest> requests = entry.getValue();
- Class<?> frameworkClass =
- dependencyRequestMapper.getFrameworkClass(requests.iterator().next());
- // collect together all of the names that we would want to call the provider
- ImmutableSet<String> dependencyNames =
- FluentIterable.from(requests).transform(new DependencyVariableNamer()).toSet();
-
- if (dependencyNames.size() == 1) {
- // if there's only one name, great! use it!
- String name = Iterables.getOnlyElement(dependencyNames);
- bindingFields.put(bindingKey,
- FrameworkField.createWithTypeFromKey(frameworkClass, bindingKey, name));
- } else {
- // in the event that a field is being used for a bunch of deps with different names,
- // add all the names together with "And"s in the middle. E.g.: stringAndS
- Iterator<String> namesIterator = dependencyNames.iterator();
- String first = namesIterator.next();
- StringBuilder compositeNameBuilder = new StringBuilder(first);
- while (namesIterator.hasNext()) {
- compositeNameBuilder.append("And").append(
- CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, namesIterator.next()));
- }
- bindingFields.put(bindingKey, FrameworkField.createWithTypeFromKey(
- frameworkClass, bindingKey, compositeNameBuilder.toString()));
- }
- }
- return bindingFields.build();
- }
-
- static Snippet frameworkTypeUsageStatement(Snippet frameworkTypeMemberSelect,
- DependencyRequest.Kind dependencyKind) {
- switch (dependencyKind) {
- case LAZY:
- return Snippet.format("%s.create(%s)", ClassName.fromClass(DoubleCheckLazy.class),
- frameworkTypeMemberSelect);
- case INSTANCE:
- case FUTURE:
- return Snippet.format("%s.get()", frameworkTypeMemberSelect);
- case PROVIDER:
- case PRODUCER:
- case MEMBERS_INJECTOR:
- return Snippet.format("%s", frameworkTypeMemberSelect);
- default:
- throw new AssertionError();
- }
- }
-
- /**
- * Returns the generated factory or members injector name for a binding.
- */
- static ClassName generatedClassNameForBinding(Binding binding) {
- switch (binding.bindingType()) {
- case PROVISION:
- case PRODUCTION:
- ContributionBinding contribution = (ContributionBinding) binding;
- checkArgument(!contribution.isSyntheticBinding());
- ClassName enclosingClassName = ClassName.fromTypeElement(contribution.bindingTypeElement());
- switch (contribution.bindingKind()) {
- case INJECTION:
- case PROVISION:
- case IMMEDIATE:
- case FUTURE_PRODUCTION:
- return enclosingClassName
- .topLevelClassName()
- .peerNamed(
- enclosingClassName.classFileName()
- + "_"
- + factoryPrefix(contribution)
- + "Factory");
-
- default:
- throw new AssertionError();
- }
-
- case MEMBERS_INJECTION:
- return membersInjectorNameForType(binding.bindingTypeElement());
-
- default:
- throw new AssertionError();
- }
- }
-
- /**
- * Returns the generated factory or members injector name parameterized with the proper type
- * parameters if necessary.
- */
- static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
- return generatedClassNameForBinding(binding).withTypeParameters(bindingTypeParameters(binding));
- }
-
- private static ImmutableList<TypeName> bindingTypeParameters(Binding binding)
- throws AssertionError {
- TypeMirror bindingType;
- switch (binding.bindingType()) {
- case PROVISION:
- case PRODUCTION:
- ContributionBinding contributionBinding = (ContributionBinding) binding;
- if (contributionBinding.contributionType().isMultibinding()) {
- return ImmutableList.of();
- }
- switch (contributionBinding.bindingKind()) {
- case INJECTION:
- bindingType = contributionBinding.key().type();
- break;
-
- case PROVISION:
- // For provision bindings, we parameterize creation on the types of
- // the module, not the types of the binding.
- // Consider: Module<A, B, C> { @Provides List<B> provideB(B b) { .. }}
- // The binding is just parameterized on <B>, but we need all of <A, B, C>.
- bindingType = contributionBinding.bindingTypeElement().asType();
- break;
-
- case IMMEDIATE:
- case FUTURE_PRODUCTION:
- // TODO(beder): Can these be treated just like PROVISION?
- throw new UnsupportedOperationException();
-
- default:
- return ImmutableList.of();
- }
- break;
-
- case MEMBERS_INJECTION:
- bindingType = binding.key().type();
- break;
-
- default:
- throw new AssertionError();
- }
- TypeName bindingTypeName = TypeNames.forTypeMirror(bindingType);
- return bindingTypeName instanceof ParameterizedTypeName
- ? ((ParameterizedTypeName) bindingTypeName).parameters()
- : ImmutableList.<TypeName>of();
- }
-
- static ClassName membersInjectorNameForType(TypeElement typeElement) {
- ClassName injectedClassName = ClassName.fromTypeElement(typeElement);
- return injectedClassName
- .topLevelClassName()
- .peerNamed(injectedClassName.classFileName() + "_MembersInjector");
- }
-
- static ClassName generatedMonitoringModuleName(TypeElement componentElement) {
- ClassName componentName = ClassName.fromTypeElement(componentElement);
- return componentName
- .topLevelClassName()
- .peerNamed(componentName.classFileName() + "_MonitoringModule");
- }
-
- private static String factoryPrefix(ContributionBinding binding) {
- switch (binding.bindingKind()) {
- case INJECTION:
- return "";
-
- case PROVISION:
- case IMMEDIATE:
- case FUTURE_PRODUCTION:
- return CaseFormat.LOWER_CAMEL.to(
- UPPER_CAMEL, ((ExecutableElement) binding.bindingElement()).getSimpleName().toString());
-
- default:
- throw new IllegalArgumentException();
- }
- }
-
- private SourceFiles() {}
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java b/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java
deleted file mode 100644
index 1287668..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
-import dagger.internal.codegen.ComponentGenerator.MemberSelect;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ClassWriter;
-import dagger.internal.codegen.writer.FieldWriter;
-import dagger.internal.codegen.writer.MethodWriter;
-import dagger.internal.codegen.writer.Snippet;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import java.util.List;
-import java.util.Set;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Sets.difference;
-import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.UNINITIALIZED;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-/**
- * Creates the nested implementation class for a subcomponent.
- */
-class SubcomponentWriter extends AbstractComponentWriter {
-
- private AbstractComponentWriter parent;
- private ExecutableElement subcomponentFactoryMethod;
-
- public SubcomponentWriter(
- AbstractComponentWriter parent,
- ExecutableElement subcomponentFactoryMethod,
- BindingGraph subgraph) {
- super(
- parent.types,
- parent.elements,
- parent.keyFactory,
- parent.nullableValidationType,
- parent.name.nestedClassNamed(subcomponentSimpleName(subgraph)),
- subgraph);
- this.parent = parent;
- this.subcomponentFactoryMethod = subcomponentFactoryMethod;
- }
-
- private static String subcomponentSimpleName(BindingGraph subgraph) {
- return subgraph.componentDescriptor().componentDefinitionType().getSimpleName() + "Impl";
- }
-
- @Override
- protected InitializationState getInitializationState(BindingKey bindingKey) {
- InitializationState initializationState = super.getInitializationState(bindingKey);
- return initializationState.equals(UNINITIALIZED)
- ? parent.getInitializationState(bindingKey)
- : initializationState;
- }
-
- @Override
- protected Optional<Snippet> getOrCreateComponentContributionFieldSnippet(
- TypeElement contributionType) {
- return super.getOrCreateComponentContributionFieldSnippet(contributionType)
- .or(parent.getOrCreateComponentContributionFieldSnippet(contributionType));
- }
-
- @Override
- protected MemberSelect getMemberSelect(BindingKey key) {
- MemberSelect memberSelect = super.getMemberSelect(key);
- return memberSelect == null ? parent.getMemberSelect(key) : memberSelect;
- }
-
- @Override
- protected Optional<MemberSelect> getMultibindingContributionSnippet(ContributionBinding binding) {
- return super.getMultibindingContributionSnippet(binding)
- .or(parent.getMultibindingContributionSnippet(binding));
- }
-
- private ExecutableType resolvedSubcomponentFactoryMethod() {
- return MoreTypes.asExecutable(
- types.asMemberOf(
- MoreTypes.asDeclared(parent.componentDefinitionType().asType()),
- subcomponentFactoryMethod));
- }
-
- @Override
- protected ClassWriter createComponentClass() {
- ClassWriter componentWriter = parent.componentWriter.addNestedClass(name.simpleName());
- componentWriter.addModifiers(PRIVATE, FINAL);
- componentWriter.setSupertype(
- MoreTypes.asTypeElement(
- graph.componentDescriptor().builderSpec().isPresent()
- ? graph
- .componentDescriptor()
- .builderSpec()
- .get()
- .componentType()
- : resolvedSubcomponentFactoryMethod().getReturnType()));
- return componentWriter;
- }
-
- @Override
- protected void addBuilder() {
- // Only write subcomponent builders if there is a spec.
- if (graph.componentDescriptor().builderSpec().isPresent()) {
- super.addBuilder();
- }
- }
-
- @Override
- protected ClassWriter createBuilder() {
- // Only write subcomponent builders if there is a spec.
- verify(graph.componentDescriptor().builderSpec().isPresent());
- return parent.componentWriter.addNestedClass(
- componentDefinitionTypeName().simpleName() + "Builder");
- }
-
- @Override
- protected void addFactoryMethods() {
- MethodWriter componentMethod;
- if (graph.componentDescriptor().builderSpec().isPresent()) {
- BuilderSpec spec = graph.componentDescriptor().builderSpec().get();
- componentMethod =
- parent.componentWriter.addMethod(
- spec.builderDefinitionType().asType(),
- subcomponentFactoryMethod.getSimpleName().toString());
- componentMethod.body().addSnippet("return new %s();", builderName.get());
- } else {
- ExecutableType resolvedMethod = resolvedSubcomponentFactoryMethod();
- componentMethod =
- parent.componentWriter.addMethod(
- resolvedMethod.getReturnType(), subcomponentFactoryMethod.getSimpleName().toString());
- writeSubcomponentWithoutBuilder(componentMethod, resolvedMethod);
- }
- componentMethod.addModifiers(PUBLIC);
- componentMethod.annotate(Override.class);
- }
-
- private void writeSubcomponentWithoutBuilder(
- MethodWriter componentMethod, ExecutableType resolvedMethod) {
- ImmutableList.Builder<Snippet> subcomponentConstructorParameters = ImmutableList.builder();
- List<? extends VariableElement> params = subcomponentFactoryMethod.getParameters();
- List<? extends TypeMirror> paramTypes = resolvedMethod.getParameterTypes();
- for (int i = 0; i < params.size(); i++) {
- VariableElement moduleVariable = params.get(i);
- TypeElement moduleTypeElement = MoreTypes.asTypeElement(paramTypes.get(i));
- TypeName moduleType = TypeNames.forTypeMirror(paramTypes.get(i));
- componentMethod.addParameter(moduleType, moduleVariable.getSimpleName().toString());
- if (!componentContributionFields.containsKey(moduleTypeElement)) {
- String preferredModuleName =
- CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleTypeElement.getSimpleName().toString());
- FieldWriter contributionField =
- componentWriter.addField(moduleTypeElement, preferredModuleName);
- contributionField.addModifiers(PRIVATE, FINAL);
- String actualModuleName = contributionField.name();
- constructorWriter.addParameter(moduleType, actualModuleName);
- constructorWriter.body()
- .addSnippet("if (%s == null) {", actualModuleName)
- .addSnippet(" throw new NullPointerException();")
- .addSnippet("}");
- constructorWriter.body().addSnippet("this.%1$s = %1$s;", actualModuleName);
- MemberSelect moduleSelect =
- MemberSelect.instanceSelect(name, Snippet.format(actualModuleName));
- componentContributionFields.put(moduleTypeElement, moduleSelect);
- subcomponentConstructorParameters.add(Snippet.format("%s", moduleVariable.getSimpleName()));
- }
- }
-
- Set<TypeElement> uninitializedModules =
- difference(graph.componentRequirements(), componentContributionFields.keySet());
-
- for (TypeElement moduleType : uninitializedModules) {
- String preferredModuleName =
- CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleType.getSimpleName().toString());
- FieldWriter contributionField = componentWriter.addField(moduleType, preferredModuleName);
- contributionField.addModifiers(PRIVATE, FINAL);
- String actualModuleName = contributionField.name();
- constructorWriter.body().addSnippet("this.%s = new %s();",
- actualModuleName, ClassName.fromTypeElement(moduleType));
- MemberSelect moduleSelect =
- MemberSelect.instanceSelect(name, Snippet.format(actualModuleName));
- componentContributionFields.put(moduleType, moduleSelect);
- }
-
- componentMethod.body().addSnippet("return new %s(%s);",
- name, Snippet.makeParametersSnippet(subcomponentConstructorParameters.build()));
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/Util.java b/compiler/src/main/java/dagger/internal/codegen/Util.java
deleted file mode 100644
index 8c1aba3..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/Util.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2013 Google, Inc.
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Equivalence;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import dagger.producers.Produced;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Provider;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-
-import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.common.base.Preconditions.checkState;
-import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-/**
- * Utilities for handling types in annotation processors
- */
-final class Util {
- /**
- * Returns the {@code V} type for a {@link Map} type like {@code Map<K, Provider<V>>} if the map
- * includes such a construction
- */
- public static TypeMirror getProvidedValueTypeOfMap(DeclaredType mapType) {
- checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
- return asDeclared(mapType.getTypeArguments().get(1)).getTypeArguments().get(0);
- }
-
- // TODO(cgruber): Consider an object that holds and exposes the various parts of a Map type.
- /**
- * returns the value type for a {@link Map} type like Map<K, V>}.
- */
- public static TypeMirror getValueTypeOfMap(DeclaredType mapType) {
- checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
- return mapType.getTypeArguments().get(1);
- }
-
- /**
- * Returns the key type for a {@link Map} type like Map<K, Provider<V>>}
- */
- public static TypeMirror getKeyTypeOfMap(DeclaredType mapType) {
- checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
- return mapType.getTypeArguments().get(0);
- }
-
- /**
- * Returns true if {@code type} is a {@link Map} whose value type is not a {@link Provider}.
- */
- public static boolean isMapWithNonProvidedValues(TypeMirror type) {
- return MoreTypes.isType(type)
- && MoreTypes.isTypeOf(Map.class, type)
- && !MoreTypes.isTypeOf(Provider.class, asDeclared(type).getTypeArguments().get(1));
- }
-
- /**
- * Returns true if {@code type} is a {@link Map} whose value type is a {@link Provider}.
- */
- public static boolean isMapWithProvidedValues(TypeMirror type) {
- return MoreTypes.isType(type)
- && MoreTypes.isTypeOf(Map.class, type)
- && MoreTypes.isTypeOf(Provider.class, asDeclared(type).getTypeArguments().get(1));
- }
-
- /** Returns true if {@code type} is a {@code Set<Produced<T>>}. */
- static boolean isSetOfProduced(TypeMirror type) {
- return MoreTypes.isType(type)
- && MoreTypes.isTypeOf(Set.class, type)
- && MoreTypes.isTypeOf(Produced.class, MoreTypes.asDeclared(type).getTypeArguments().get(0));
- }
-
- /**
- * Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Wrapper} for that type.
- */
- static <T> Optional<Equivalence.Wrapper<T>> wrapOptionalInEquivalence(
- Equivalence<T> equivalence, Optional<T> optional) {
- return optional.isPresent()
- ? Optional.of(equivalence.wrap(optional.get()))
- : Optional.<Equivalence.Wrapper<T>>absent();
- }
-
- /**
- * Unwraps an {@link Optional} of a {@link Wrapper} into an {@code Optional} of the underlying
- * type.
- */
- static <T> Optional<T> unwrapOptionalEquivalence(
- Optional<Equivalence.Wrapper<T>> wrappedOptional) {
- return wrappedOptional.isPresent()
- ? Optional.of(wrappedOptional.get().get())
- : Optional.<T>absent();
- }
-
- private static boolean requiresEnclosingInstance(TypeElement typeElement) {
- switch (typeElement.getNestingKind()) {
- case TOP_LEVEL:
- return false;
- case MEMBER:
- return !typeElement.getModifiers().contains(STATIC);
- case ANONYMOUS:
- case LOCAL:
- return true;
- default:
- throw new AssertionError("TypeElement cannot have nesting kind: "
- + typeElement.getNestingKind());
- }
- }
-
- /**
- * Returns true if and only if a component can instantiate new instances (typically of a module)
- * rather than requiring that they be passed.
- */
- static boolean componentCanMakeNewInstances(TypeElement typeElement) {
- switch (typeElement.getKind()) {
- case CLASS:
- break;
- case ENUM:
- case ANNOTATION_TYPE:
- case INTERFACE:
- return false;
- default:
- throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind());
- }
-
- if (typeElement.getModifiers().contains(ABSTRACT)) {
- return false;
- }
-
- if (requiresEnclosingInstance(typeElement)) {
- return false;
- }
-
- for (Element enclosed : typeElement.getEnclosedElements()) {
- if (enclosed.getKind().equals(CONSTRUCTOR)
- && ((ExecutableElement) enclosed).getParameters().isEmpty()
- && !enclosed.getModifiers().contains(PRIVATE)) {
- return true;
- }
- }
-
- // TODO(gak): still need checks for visibility
-
- return false;
- }
-
- static ImmutableSet<ExecutableElement> getUnimplementedMethods(
- Elements elements, TypeElement type) {
- ImmutableSet.Builder<ExecutableElement> unimplementedMethods = ImmutableSet.builder();
- Set<ExecutableElement> methods = getLocalAndInheritedMethods(type, elements);
- for (ExecutableElement method : methods) {
- if (method.getModifiers().contains(Modifier.ABSTRACT)) {
- unimplementedMethods.add(method);
- }
- }
- return unimplementedMethods.build();
- }
-
- private Util() {}
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ValidationReport.java b/compiler/src/main/java/dagger/internal/codegen/ValidationReport.java
deleted file mode 100644
index e174067..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ValidationReport.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.util.SimpleElementVisitor6;
-import javax.tools.Diagnostic;
-import javax.tools.Diagnostic.Kind;
-
-import static javax.tools.Diagnostic.Kind.ERROR;
-import static javax.tools.Diagnostic.Kind.NOTE;
-import static javax.tools.Diagnostic.Kind.WARNING;
-
-/**
- * A collection of items describing contractual issues with the code as presented to an annotation
- * processor. A "clean" report (i.e. with no issues) is a report with no {@linkplain Item items}
- * and clean subreports. Callers will typically print the results of the report to a
- * {@link Messager} instance using {@link #printMessagesTo}.
- *
- * <p>A report describes a subject {@link Element}. Callers may choose to add report items about
- * other elements that are contained within or related to the subject. Since {@link Diagnostic}
- * reporting is expected to be associated with elements that are currently being compiled,
- * {@link #printMessagesTo(Messager)} will only associate messages with non-subject elements if they
- * are contained within the subject. Otherwise, they will be associated with the subject and contain
- * a reference to the item's element in the message string. It is the responsibility of the caller
- * to choose subjects that are part of the compilation.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@AutoValue
-abstract class ValidationReport<T extends Element> {
- abstract T subject();
- abstract ImmutableSet<Item> items();
- abstract ImmutableSet<ValidationReport<?>> subreports();
-
- boolean isClean() {
- for (Item item : items()) {
- switch (item.kind()) {
- case ERROR:
- return false;
- default:
- break;
- }
- }
- for (ValidationReport<?> subreport : subreports()) {
- if (!subreport.isClean()) {
- return false;
- }
- }
- return true;
- }
-
- void printMessagesTo(Messager messager) {
- for (Item item : items()) {
- if (isEnclosedIn(subject(), item.element())) {
- if (item.annotation().isPresent()) {
- messager.printMessage(
- item.kind(), item.message(), item.element(), item.annotation().get());
- } else {
- messager.printMessage(item.kind(), item.message(), item.element());
- }
- } else {
- String message = String.format("[%s] %s", elementString(item.element()), item.message());
- if (item.annotation().isPresent()) {
- messager.printMessage(item.kind(), message, subject(), item.annotation().get());
- } else {
- messager.printMessage(item.kind(), message, subject());
- }
- }
- }
- for (ValidationReport<?> subreport : subreports()) {
- subreport.printMessagesTo(messager);
- }
- }
-
- private static String elementString(Element element) {
- return element.accept(
- new SimpleElementVisitor6<String, Void>() {
- @Override
- protected String defaultAction(Element e, Void p) {
- return e.toString();
- }
-
- @Override
- public String visitExecutable(ExecutableElement e, Void p) {
- return e.getEnclosingElement().accept(this, null) + '.' + e.toString();
- }
- },
- null);
- }
-
- private static boolean isEnclosedIn(Element parent, Element child) {
- Element current = child;
- while (current != null) {
- if (current.equals(parent)) {
- return true;
- }
- current = current.getEnclosingElement();
- }
- return false;
- }
-
- @AutoValue
- static abstract class Item {
- abstract String message();
- abstract Kind kind();
- abstract Element element();
- abstract Optional<AnnotationMirror> annotation();
- }
-
- static <T extends Element> Builder<T> about(T subject) {
- return new Builder<T>(subject);
- }
-
- static final class Builder<T extends Element> {
- private final T subject;
- private final ImmutableSet.Builder<Item> items = ImmutableSet.builder();
- private final ImmutableSet.Builder<ValidationReport<?>> subreports = ImmutableSet.builder();
-
- private Builder(T subject) {
- this.subject = subject;
- }
-
- T getSubject() {
- return subject;
- }
-
- Builder<T> addItems(Iterable<Item> newItems) {
- items.addAll(newItems);
- return this;
- }
-
- Builder<T> addError(String message) {
- addItem(message, ERROR, subject, Optional.<AnnotationMirror>absent());
- return this;
- }
-
- Builder<T> addError(String message, Element element) {
- addItem(message, ERROR, element, Optional.<AnnotationMirror>absent());
- return this;
- }
-
- Builder<T> addError(String message, Element element, AnnotationMirror annotation) {
- addItem(message, ERROR, element, Optional.of(annotation));
- return this;
- }
-
- Builder<T> addWarning(String message) {
- addItem(message, WARNING, subject, Optional.<AnnotationMirror>absent());
- return this;
- }
-
- Builder<T> addWarning(String message, Element element) {
- addItem(message, WARNING, element, Optional.<AnnotationMirror>absent());
- return this;
- }
-
- Builder<T> addWarning(String message, Element element, AnnotationMirror annotation) {
- addItem(message, WARNING, element, Optional.of(annotation));
- return this;
- }
-
- Builder<T> addNote(String message) {
- addItem(message, NOTE, subject, Optional.<AnnotationMirror>absent());
- return this;
- }
-
- Builder<T> addNote(String message, Element element) {
- addItem(message, NOTE, element, Optional.<AnnotationMirror>absent());
- return this;
- }
-
- Builder<T> addNote(String message, Element element, AnnotationMirror annotation) {
- addItem(message, NOTE, element, Optional.of(annotation));
- return this;
- }
-
- Builder<T> addItem(String message, Kind kind, Element element) {
- addItem(message, kind, element, Optional.<AnnotationMirror>absent());
- return this;
- }
-
- Builder<T> addItem(String message, Kind kind, Element element, AnnotationMirror annotation) {
- addItem(message, kind, element, Optional.of(annotation));
- return this;
- }
-
- private Builder<T> addItem(String message, Kind kind, Element element,
- Optional<AnnotationMirror> annotation) {
- items.add(new AutoValue_ValidationReport_Item(message, kind, element, annotation));
- return this;
- }
-
- Builder<T> addSubreport(ValidationReport<?> subreport) {
- subreports.add(subreport);
- return this;
- }
-
- ValidationReport<T> build() {
- return new AutoValue_ValidationReport<T>(subject, items.build(), subreports.build());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ValidationType.java b/compiler/src/main/java/dagger/internal/codegen/ValidationType.java
deleted file mode 100644
index d602072..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/ValidationType.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package dagger.internal.codegen;
-
-import com.google.common.base.Optional;
-import javax.tools.Diagnostic;
-
-/**
- * Allows options to control how component process validates things such as scope cycles
- * or nullability.
- */
-enum ValidationType {
- ERROR,
- WARNING,
- NONE;
-
- Optional<Diagnostic.Kind> diagnosticKind() {
- switch (this) {
- case ERROR:
- return Optional.of(Diagnostic.Kind.ERROR);
- case WARNING:
- return Optional.of(Diagnostic.Kind.WARNING);
- default:
- return Optional.absent();
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java
deleted file mode 100644
index 8dbf27b..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import java.io.IOException;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.SortedMap;
-
-import static dagger.internal.codegen.writer.Writables.toStringWritable;
-
-public final class AnnotationWriter implements Writable, HasClassReferences {
- private final ClassName annotationName;
- private final Set<HasClassReferences> memberReferences = Sets.newLinkedHashSet();
- private final SortedMap<String, Writable> memberMap = Maps.newTreeMap();
-
- AnnotationWriter(ClassName annotationName) {
- this.annotationName = annotationName;
- }
-
- public void setValue(String value) {
- setMember("value", value);
- }
-
- public void setMember(String name, int value) {
- memberMap.put(name, toStringWritable(value));
- }
-
- public void setMember(String name, String value) {
- memberMap.put(name, toStringWritable(StringLiteral.forValue(value)));
- }
-
- public <T extends Enum<T>> void setMember(String name, T value) {
- Snippet snippet = Snippet.format("%s.%s", ClassName.fromClass(value.getClass()), value);
- memberMap.put(name, snippet);
- memberReferences.add(snippet);
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- appendable.append('@');
- annotationName.write(appendable, context);
- if (!memberMap.isEmpty()) {
- appendable.append('(');
- if (memberMap.size() == 1) {
- Entry<String, Writable> onlyEntry = Iterables.getOnlyElement(memberMap.entrySet());
- if (!onlyEntry.getKey().equals("value")) {
- appendable.append(onlyEntry.getKey()).append(" = ");
- }
- onlyEntry.getValue().write(appendable, context);
- }
- appendable.append(')');
- }
- return appendable;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(memberReferences)
- .append(annotationName)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/ArrayTypeName.java b/compiler/src/main/java/dagger/internal/codegen/writer/ArrayTypeName.java
deleted file mode 100644
index e796062..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/ArrayTypeName.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import java.io.IOException;
-import java.util.Set;
-
-final class ArrayTypeName implements TypeName {
- private final TypeName componentType;
-
- ArrayTypeName(TypeName componentType) {
- this.componentType = componentType;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return componentType.referencedClasses();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- return componentType.write(appendable, context).append("[]");
- }
-
- @Override
- public boolean equals(Object obj) {
- return (obj instanceof ArrayTypeName)
- && this.componentType.equals(((ArrayTypeName) obj).componentType);
- }
-
- @Override
- public int hashCode() {
- return componentType.hashCode();
- }
-
- @Override
- public String toString() {
- return Writables.writeToString(this);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/BlockWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/BlockWriter.java
deleted file mode 100644
index c00dd5f..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/BlockWriter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Lists;
-import java.io.IOException;
-import java.util.List;
-import java.util.Set;
-
-public final class BlockWriter implements Writable, HasClassReferences {
- private final List<Snippet> snippets;
-
- BlockWriter() {
- this.snippets = Lists.newArrayList();
- }
-
- public BlockWriter addSnippet(String snippet, Object... args) {
- snippets.add(Snippet.format(snippet, args));
- return this;
- }
-
- public BlockWriter addSnippet(Snippet snippet) {
- snippets.add(snippet);
- return this;
- }
-
- boolean isEmpty() {
- return snippets.isEmpty();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- for (Snippet snippet : snippets) {
- appendable.append('\n');
- snippet.write(appendable, context);
- }
- return appendable.append('\n');
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(snippets)
- .transformAndConcat(new Function<HasClassReferences, Set<ClassName>>() {
- @Override
- public Set<ClassName> apply(HasClassReferences input) {
- return input.referencedClasses();
- }
- })
- .toSet();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java b/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java
deleted file mode 100644
index bd0791f..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Ascii;
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.base.Optional;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.NestingKind;
-import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.TypeElement;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static javax.lang.model.element.NestingKind.MEMBER;
-import static javax.lang.model.element.NestingKind.TOP_LEVEL;
-
-/**
- * Represents a fully-qualified class name for {@link NestingKind#TOP_LEVEL} and
- * {@link NestingKind#MEMBER} classes.
- *
- * @since 2.0
- */
-public final class ClassName implements TypeName, Comparable<ClassName> {
- private String fullyQualifiedName = null;
- private final String packageName;
- /* From top to bottom. E.g.: this field will contain ["A", "B"] for pgk.A.B.C */
- private final ImmutableList<String> enclosingSimpleNames;
- private final String simpleName;
-
- private ClassName(String packageName, ImmutableList<String> enclosingSimpleNames,
- String simpleName) {
- this.packageName = packageName;
- this.enclosingSimpleNames = enclosingSimpleNames;
- this.simpleName = simpleName;
- }
-
- public String packageName() {
- return packageName;
- }
-
- public ImmutableList<String> enclosingSimpleNames() {
- return enclosingSimpleNames;
- }
-
- public Optional<ClassName> enclosingClassName() {
- return enclosingSimpleNames.isEmpty()
- ? Optional.<ClassName>absent()
- : Optional.of(new ClassName(packageName,
- enclosingSimpleNames.subList(0, enclosingSimpleNames.size() - 1),
- enclosingSimpleNames.get(enclosingSimpleNames.size() - 1)));
- }
-
- public String simpleName() {
- return simpleName;
- }
-
- public String canonicalName() {
- if (fullyQualifiedName == null) {
- StringBuilder builder = new StringBuilder(packageName());
- if (builder.length() > 0) {
- builder.append('.');
- }
- for (String enclosingSimpleName : enclosingSimpleNames()) {
- builder.append(enclosingSimpleName).append('.');
- }
- fullyQualifiedName = builder.append(simpleName()).toString();
- }
- return fullyQualifiedName;
- }
-
- /**
- * Equivalent to {@link #classFileName(char) classFileName('$')}
- */
- public String classFileName() {
- return classFileName('$');
- }
-
- /**
- * Returns the class name (excluding package).
- *
- * <p>The returned value includes the names of its enclosing classes (if any) but not the package
- * name. e.g. {@code fromClass(Map.Entry.class).classFileName('_')} will return {@code Map_Entry}.
- */
- public String classFileName(char separator) {
- StringBuilder builder = new StringBuilder();
- for (String enclosingSimpleName : enclosingSimpleNames) {
- builder.append(enclosingSimpleName).append(separator);
- }
- return builder.append(simpleName()).toString();
- }
-
- public ClassName topLevelClassName() {
- Iterator<String> enclosingIterator = enclosingSimpleNames().iterator();
- return enclosingIterator.hasNext()
- ? new ClassName(packageName(), ImmutableList.<String>of(),
- enclosingIterator.next())
- : this;
- }
-
- public ClassName nestedClassNamed(String memberClassName) {
- checkNotNull(memberClassName);
- checkArgument(SourceVersion.isIdentifier(memberClassName));
- return new ClassName(packageName(),
- new ImmutableList.Builder<String>()
- .addAll(enclosingSimpleNames())
- .add(simpleName())
- .build(),
- memberClassName);
- }
-
- public ClassName peerNamed(String peerClassName) {
- checkNotNull(peerClassName);
- checkArgument(SourceVersion.isIdentifier(peerClassName));
- return new ClassName(packageName(), enclosingSimpleNames(), peerClassName);
- }
-
- /**
- * Returns a parameterized type name with this as its raw type if {@code parameters} is not empty.
- * If {@code parameters} is empty, returns this object.
- */
- public TypeName withTypeParameters(List<? extends TypeName> parameters) {
- return parameters.isEmpty() ? this : ParameterizedTypeName.create(this, parameters);
- }
-
- private static final ImmutableSet<NestingKind> ACCEPTABLE_NESTING_KINDS =
- Sets.immutableEnumSet(TOP_LEVEL, MEMBER);
-
- public static ClassName fromTypeElement(TypeElement element) {
- checkNotNull(element);
- checkArgument(ACCEPTABLE_NESTING_KINDS.contains(element.getNestingKind()));
- String simpleName = element.getSimpleName().toString();
- List<String> enclosingNames = new ArrayList<String>();
- Element current = element.getEnclosingElement();
- while (current.getKind().isClass() || current.getKind().isInterface()) {
- checkArgument(ACCEPTABLE_NESTING_KINDS.contains(element.getNestingKind()));
- enclosingNames.add(current.getSimpleName().toString());
- current = current.getEnclosingElement();
- }
- PackageElement packageElement = getPackage(current);
- Collections.reverse(enclosingNames);
- return new ClassName(packageElement.getQualifiedName().toString(),
- ImmutableList.copyOf(enclosingNames), simpleName);
- }
-
- public static ClassName fromClass(Class<?> clazz) {
- checkNotNull(clazz);
- List<String> enclosingNames = new ArrayList<String>();
- Class<?> current = clazz.getEnclosingClass();
- while (current != null) {
- enclosingNames.add(current.getSimpleName());
- current = current.getEnclosingClass();
- }
- Collections.reverse(enclosingNames);
- return create(clazz.getPackage().getName(), enclosingNames, clazz.getSimpleName());
- }
-
- private static PackageElement getPackage(Element type) {
- while (type.getKind() != ElementKind.PACKAGE) {
- type = type.getEnclosingElement();
- }
- return (PackageElement) type;
- }
-
- /**
- * Returns a new {@link ClassName} instance for the given fully-qualified class name string. This
- * method assumes that the input is ASCII and follows typical Java style (lower-case package
- * names, upper-camel-case class names) and may produce incorrect results or throw
- * {@link IllegalArgumentException} otherwise. For that reason, {@link #fromClass(Class)} and
- * {@link #fromClass(Class)} should be preferred as they can correctly create {@link ClassName}
- * instances without such restrictions.
- */
- public static ClassName bestGuessFromString(String classNameString) {
- checkNotNull(classNameString);
- List<String> parts = Splitter.on('.').splitToList(classNameString);
- int firstClassPartIndex = -1;
- for (int i = 0; i < parts.size(); i++) {
- String part = parts.get(i);
- checkArgument(SourceVersion.isIdentifier(part));
- char firstChar = part.charAt(0);
- if (Ascii.isLowerCase(firstChar)) {
- // looks like a package part
- if (firstClassPartIndex >= 0) {
- throw new IllegalArgumentException("couldn't make a guess for " + classNameString);
- }
- } else if (Ascii.isUpperCase(firstChar)) {
- // looks like a class part
- if (firstClassPartIndex < 0) {
- firstClassPartIndex = i;
- }
- } else {
- throw new IllegalArgumentException("couldn't make a guess for " + classNameString);
- }
- }
- int lastIndex = parts.size() - 1;
- return new ClassName(
- Joiner.on('.').join(parts.subList(0, firstClassPartIndex)),
- firstClassPartIndex == lastIndex
- ? ImmutableList.<String>of()
- : ImmutableList.copyOf(parts.subList(firstClassPartIndex, lastIndex)),
- parts.get(lastIndex));
- }
-
- public static ClassName create(
- String packageName, List<String> enclosingSimpleNames, String simpleName) {
- return new ClassName(packageName, ImmutableList.copyOf(enclosingSimpleNames),
- simpleName);
- }
-
- public static ClassName create(String packageName, String simpleName) {
- return new ClassName(packageName, ImmutableList.<String>of(), simpleName);
- }
-
- @Override
- public String toString() {
- return canonicalName();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- appendable.append(context.sourceReferenceForClassName(this));
- return appendable;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- } else if (obj instanceof ClassName) {
- ClassName that = (ClassName) obj;
- return this.packageName.equals(that.packageName)
- && this.enclosingSimpleNames.equals(that.enclosingSimpleNames)
- && this.simpleName.equals(that.simpleName);
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(packageName, enclosingSimpleNames, simpleName);
- }
-
- @Override
- public int compareTo(ClassName o) {
- return canonicalName().compareTo(o.canonicalName());
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return ImmutableSet.of(this);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/ClassWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/ClassWriter.java
deleted file mode 100644
index edaba3a..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/ClassWriter.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import java.io.IOException;
-import java.util.List;
-import java.util.Set;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-
-import static com.google.common.base.Preconditions.checkState;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-public final class ClassWriter extends TypeWriter {
- private Optional<TypeName> superclass;
- private final List<ConstructorWriter> constructorWriters;
- private final List<TypeVariableName> typeParameters;
-
- ClassWriter(ClassName className) {
- super(className);
- this.superclass = Optional.absent();
- this.constructorWriters = Lists.newArrayList();
- this.typeParameters = Lists.newArrayList();
- }
-
- public void setSuperclass(TypeName typeReference) {
- checkState(!superclass.isPresent());
- superclass = Optional.of(typeReference);
- }
-
- /**
- * If {@code supertype} is a class, makes this class extend it; if it is an interface, makes this
- * class implement it.
- */
- public void setSupertype(TypeElement supertype) {
- switch (supertype.getKind()) {
- case CLASS:
- setSuperclass(ClassName.fromTypeElement(supertype));
- break;
- case INTERFACE:
- addImplementedType(supertype);
- break;
- default:
- throw new IllegalArgumentException(supertype + " must be a class or interface");
- }
- }
-
- public ConstructorWriter addConstructor() {
- ConstructorWriter constructorWriter = new ConstructorWriter(name.simpleName());
- constructorWriters.add(constructorWriter);
- return constructorWriter;
- }
-
- public void addTypeParameter(TypeVariableName typeVariableName) {
- this.typeParameters.add(typeVariableName);
- }
-
- public void addTypeParameters(Iterable<TypeVariableName> typeVariableNames) {
- Iterables.addAll(typeParameters, typeVariableNames);
- }
-
- public List<TypeVariableName> typeParameters() {
- return ImmutableList.copyOf(typeParameters);
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- context = context.createSubcontext(FluentIterable.from(nestedTypeWriters)
- .transform(new Function<TypeWriter, ClassName>() {
- @Override public ClassName apply(TypeWriter input) {
- return input.name;
- }
- })
- .toSet());
- writeAnnotations(appendable, context);
- writeModifiers(appendable).append("class ").append(name.simpleName());
- Writables.join(", ", typeParameters, "<", ">", appendable, context);
- if (superclass.isPresent()) {
- appendable.append(" extends ");
- superclass.get().write(appendable, context);
- }
- Writables.join(", ", implementedTypes, " implements ", "", appendable, context);
- appendable.append(" {");
- if (!fieldWriters.isEmpty()) {
- appendable.append('\n');
- }
- for (VariableWriter fieldWriter : fieldWriters.values()) {
- fieldWriter.write(new IndentingAppendable(appendable), context).append("\n");
- }
- for (ConstructorWriter constructorWriter : constructorWriters) {
- appendable.append('\n');
- if (!isDefaultConstructor(constructorWriter)) {
- constructorWriter.write(new IndentingAppendable(appendable), context);
- }
- }
- for (MethodWriter methodWriter : methodWriters) {
- appendable.append('\n');
- methodWriter.write(new IndentingAppendable(appendable), context);
- }
- for (TypeWriter nestedTypeWriter : nestedTypeWriters) {
- appendable.append('\n');
- nestedTypeWriter.write(new IndentingAppendable(appendable), context);
- }
- appendable.append("}\n");
- return appendable;
- }
-
- private static final Set<Modifier> VISIBILIY_MODIFIERS =
- Sets.immutableEnumSet(PUBLIC, PROTECTED, PRIVATE);
-
- private boolean isDefaultConstructor(ConstructorWriter constructorWriter) {
- return Sets.intersection(VISIBILIY_MODIFIERS, modifiers)
- .equals(Sets.intersection(VISIBILIY_MODIFIERS, constructorWriter.modifiers))
- && constructorWriter.body().isEmpty();
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(ImmutableList.<HasClassReferences>of())
- .append(nestedTypeWriters)
- .append(fieldWriters.values())
- .append(constructorWriters)
- .append(methodWriters)
- .append(implementedTypes)
- .append(superclass.asSet())
- .append(annotations)
- .append(typeParameters)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/ConstructorWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/ConstructorWriter.java
deleted file mode 100644
index 387c1dd..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/ConstructorWriter.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.element.TypeElement;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-public final class ConstructorWriter extends Modifiable implements Writable, HasClassReferences {
- private final String name;
- private final Map<String, VariableWriter> parameterWriters;
- private final BlockWriter blockWriter;
-
- ConstructorWriter(String name) {
- this.name = name;
- this.parameterWriters = Maps.newLinkedHashMap();
- this.blockWriter = new BlockWriter();
- }
-
- public VariableWriter addParameter(Class<?> type, String name) {
- return addParameter(ClassName.fromClass(type), name);
- }
-
- public VariableWriter addParameter(TypeElement type, String name) {
- return addParameter(ClassName.fromTypeElement(type), name);
- }
-
- public VariableWriter addParameter(TypeWriter type, String name) {
- return addParameter(type.name, name);
- }
-
- public VariableWriter addParameter(TypeName type, String name) {
- VariableWriter parameterWriter = new VariableWriter(type, name);
- parameterWriters.put(name, parameterWriter);
- return parameterWriter;
- }
-
- public Map<String, TypeName> parameters() {
- ImmutableMap.Builder<String, TypeName> params = ImmutableMap.builder();
- for (Map.Entry<String, VariableWriter> entry : parameterWriters.entrySet()) {
- params.put(entry.getKey(), entry.getValue().type());
- }
- return params.build();
- }
-
- public BlockWriter body() {
- return blockWriter;
- }
-
- private VariableWriter addParameter(ClassName type, String name) {
- checkArgument(!parameterWriters.containsKey(name));
- VariableWriter parameterWriter = new VariableWriter(type, name);
- parameterWriters.put(name, parameterWriter);
- return parameterWriter;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(ImmutableList.<HasClassReferences>of())
- .append(parameterWriters.values())
- .append(annotations)
- .append(blockWriter)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- writeAnnotations(appendable, context);
- writeModifiers(appendable).append(name).append('(');
- Writables.join(", ", parameterWriters.values(), appendable, context);
- appendable.append(") {");
- blockWriter.write(new IndentingAppendable(appendable), context);
- return appendable.append("}\n");
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/EnumWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/EnumWriter.java
deleted file mode 100644
index 4ab017d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/EnumWriter.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.element.Modifier;
-
-import static com.google.common.base.Preconditions.checkState;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-public final class EnumWriter extends TypeWriter {
- private final Map<String, ConstantWriter> constantWriters = Maps.newLinkedHashMap();
- private final List<ConstructorWriter> constructorWriters = Lists.newArrayList();
-
- EnumWriter(ClassName name) {
- super(name);
- }
-
- public ConstantWriter addConstant(String name) {
- ConstantWriter constantWriter = new ConstantWriter(name);
- constantWriters.put(name, constantWriter);
- return constantWriter;
- }
-
- public ConstructorWriter addConstructor() {
- ConstructorWriter constructorWriter = new ConstructorWriter(name.simpleName());
- constructorWriters.add(constructorWriter);
- return constructorWriter;
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- context = context.createSubcontext(FluentIterable.from(nestedTypeWriters)
- .transform(new Function<TypeWriter, ClassName>() {
- @Override public ClassName apply(TypeWriter input) {
- return input.name;
- }
- })
- .toSet());
- writeAnnotations(appendable, context);
- writeModifiers(appendable).append("enum ").append(name.simpleName());
- Iterator<TypeName> implementedTypesIterator = implementedTypes.iterator();
- if (implementedTypesIterator.hasNext()) {
- appendable.append(" implements ");
- implementedTypesIterator.next().write(appendable, context);
- while (implementedTypesIterator.hasNext()) {
- appendable.append(", ");
- implementedTypesIterator.next().write(appendable, context);
- }
- }
- appendable.append(" {");
-
- checkState(!constantWriters.isEmpty(), "Cannot write an enum with no constants.");
- appendable.append('\n');
- ImmutableList<ConstantWriter> constantWriterList =
- ImmutableList.copyOf(constantWriters.values());
- for (ConstantWriter constantWriter
- : constantWriterList.subList(0, constantWriterList.size() - 1)) {
- constantWriter.write(appendable, context);
- appendable.append(",\n");
- }
- constantWriterList.get(constantWriterList.size() - 1).write(appendable, context);
- appendable.append(";\n");
-
- if (!fieldWriters.isEmpty()) {
- appendable.append('\n');
- }
- for (VariableWriter fieldWriter : fieldWriters.values()) {
- fieldWriter.write(new IndentingAppendable(appendable), context).append("\n");
- }
- for (ConstructorWriter constructorWriter : constructorWriters) {
- appendable.append('\n');
- if (!isDefaultConstructor(constructorWriter)) {
- constructorWriter.write(new IndentingAppendable(appendable), context);
- }
- }
- for (MethodWriter methodWriter : methodWriters) {
- appendable.append('\n');
- methodWriter.write(new IndentingAppendable(appendable), context);
- }
- for (TypeWriter nestedTypeWriter : nestedTypeWriters) {
- appendable.append('\n');
- nestedTypeWriter.write(new IndentingAppendable(appendable), context);
- }
- appendable.append("}\n");
- return appendable;
- }
-
- private static final Set<Modifier> VISIBILIY_MODIFIERS =
- Sets.immutableEnumSet(PUBLIC, PROTECTED, PRIVATE);
-
- private boolean isDefaultConstructor(ConstructorWriter constructorWriter) {
- return Sets.intersection(VISIBILIY_MODIFIERS, modifiers)
- .equals(Sets.intersection(VISIBILIY_MODIFIERS, constructorWriter.modifiers))
- && constructorWriter.body().isEmpty();
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(ImmutableList.<HasClassReferences>of())
- .append(nestedTypeWriters)
- .append(constantWriters.values())
- .append(fieldWriters.values())
- .append(constructorWriters)
- .append(methodWriters)
- .append(implementedTypes)
- .append(annotations)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
-
- public static final class ConstantWriter implements Writable, HasClassReferences {
- private final String name;
- private final List<Snippet> constructorSnippets;
-
- private ConstantWriter(String name) {
- this.name = name;
- this.constructorSnippets = Lists.newArrayList();
- }
-
- ConstantWriter addArgument(Snippet snippet) {
- constructorSnippets.add(snippet);
- return this;
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- appendable.append(name);
- Iterator<Snippet> snippetIterator = constructorSnippets.iterator();
- if (snippetIterator.hasNext()) {
- appendable.append('(');
- snippetIterator.next().write(appendable, context);
- while (snippetIterator.hasNext()) {
- appendable.append(", ");
- snippetIterator.next().write(appendable, context);
- }
- appendable.append(')');
- }
- return appendable;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(constructorSnippets)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/FieldWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/FieldWriter.java
deleted file mode 100644
index b45e5d9..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/FieldWriter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import java.io.IOException;
-import java.util.Set;
-
-public final class FieldWriter extends VariableWriter {
- private Optional<Snippet> initializer;
-
- FieldWriter(TypeName type, String name) {
- super(type, name);
- this.initializer = Optional.absent();
- }
-
- public void setInitializer(Snippet initializer) {
- this.initializer = Optional.of(initializer);
- }
-
- public void setInitializer(String initializer, Object... args) {
- this.initializer = Optional.of(Snippet.format(initializer, args));
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- super.write(appendable, context);
- if (initializer.isPresent()) {
- appendable.append(" = ");
- initializer.get().write(appendable, context);
- }
- appendable.append(';');
- return appendable;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- Iterable<? extends HasClassReferences> concat =
- Iterables.concat(ImmutableList.of(type()), initializer.asSet(), annotations);
- return FluentIterable.from(concat)
- .transformAndConcat(new Function<HasClassReferences, Set<ClassName>>() {
- @Override
- public Set<ClassName> apply(HasClassReferences input) {
- return input.referencedClasses();
- }
- })
- .toSet();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/HasClassReferences.java b/compiler/src/main/java/dagger/internal/codegen/writer/HasClassReferences.java
deleted file mode 100644
index e463ea2..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/HasClassReferences.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import java.util.Set;
-
-public interface HasClassReferences {
- Set<ClassName> referencedClasses();
-
- static final Function<HasClassReferences, Set<ClassName>> COMBINER =
- new Function<HasClassReferences, Set<ClassName>>() {
- @Override
- public Set<ClassName> apply(HasClassReferences input) {
- return input.referencedClasses();
- }
- };
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/HasTypeName.java b/compiler/src/main/java/dagger/internal/codegen/writer/HasTypeName.java
deleted file mode 100644
index a6909ed..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/HasTypeName.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-interface HasTypeName {
- TypeName name();
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/IndentingAppendable.java b/compiler/src/main/java/dagger/internal/codegen/writer/IndentingAppendable.java
deleted file mode 100644
index d96f8a3..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/IndentingAppendable.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.AbstractIterator;
-import java.io.IOException;
-import java.util.Iterator;
-
-final class IndentingAppendable implements Appendable {
- private final String indentation;
- private final Appendable delegate;
- private boolean requiresIndent = true;
-
- IndentingAppendable(Appendable delegate) {
- this(" ", delegate);
- }
-
- IndentingAppendable(String indentation, Appendable delegate) {
- this.indentation = indentation;
- this.delegate = delegate;
- }
-
- @Override
- public Appendable append(CharSequence csq) throws IOException {
- return append(csq, 0, csq.length());
- }
-
- @Override
- public Appendable append(CharSequence csq, int start, int end) throws IOException {
- Iterator<CharSequence> lines = lines(csq, start, end);
- while (lines.hasNext()) {
- CharSequence line = lines.next();
- maybeIndent();
- delegate.append(line);
- if (line.charAt(line.length() - 1) == '\n') {
- requiresIndent = true;
- }
- }
- return this;
- }
-
- @Override
- public Appendable append(char c) throws IOException {
- maybeIndent();
- delegate.append(c);
- if (c == '\n') {
- requiresIndent = true;
- }
- return this;
- }
-
- void maybeIndent() throws IOException {
- if (requiresIndent) {
- delegate.append(indentation);
- }
- requiresIndent = false;
- }
-
- private static Iterator<CharSequence> lines(
- final CharSequence csq, final int start, final int end) {
- return new AbstractIterator<CharSequence>() {
- int index = start;
-
- @Override protected CharSequence computeNext() {
- int nextStart = index;
- while (index < end && csq.charAt(index) != '\n') {
- index++;
- }
- if (index < end && csq.charAt(index) == '\n') {
- index++;
- }
- int nextEnd = index;
- return nextStart >= end
- ? endOfData()
- : csq.subSequence(nextStart, nextEnd);
- }
- };
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/InterfaceWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/InterfaceWriter.java
deleted file mode 100644
index ffcfc75..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/InterfaceWriter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-public final class InterfaceWriter extends TypeWriter {
- private final List<TypeVariableName> typeVariables;
- InterfaceWriter(ClassName name) {
- super(name);
- this.typeVariables = Lists.newArrayList();
- }
-
- public void addTypeVariable(TypeVariableName typeVariable) {
- this.typeVariables.add(typeVariable);
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- context = context.createSubcontext(FluentIterable.from(nestedTypeWriters)
- .transform(new Function<TypeWriter, ClassName>() {
- @Override public ClassName apply(TypeWriter input) {
- return input.name;
- }
- })
- .toSet());
- writeAnnotations(appendable, context);
- writeModifiers(appendable).append("interface ").append(name.simpleName());
- if (!typeVariables.isEmpty()) {
- appendable.append('<');
- Joiner.on(", ").appendTo(appendable, typeVariables);
- appendable.append('>');
- }
- Iterator<TypeName> implementedTypesIterator = implementedTypes.iterator();
- if (implementedTypesIterator.hasNext()) {
- appendable.append(" extends ");
- implementedTypesIterator.next().write(appendable, context);
- while (implementedTypesIterator.hasNext()) {
- appendable.append(", ");
- implementedTypesIterator.next().write(appendable, context);
- }
- }
- appendable.append(" {");
- for (MethodWriter methodWriter : methodWriters) {
- appendable.append('\n');
- methodWriter.write(new IndentingAppendable(appendable), context);
- }
- for (TypeWriter nestedTypeWriter : nestedTypeWriters) {
- appendable.append('\n');
- nestedTypeWriter.write(new IndentingAppendable(appendable), context);
- }
- appendable.append("}\n");
- return appendable;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(ImmutableList.<HasClassReferences>of())
- .append(nestedTypeWriters)
- .append(methodWriters)
- .append(implementedTypes)
- .append(annotations)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/JavaWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/JavaWriter.java
deleted file mode 100644
index 5977371..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/JavaWriter.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import com.google.common.io.CharSink;
-import com.google.common.io.CharSource;
-import com.google.googlejavaformat.java.Formatter;
-import com.google.googlejavaformat.java.FormatterException;
-import dagger.internal.codegen.writer.Writable.Context;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.List;
-import java.util.Set;
-import javax.annotation.processing.Filer;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.PackageElement;
-import javax.tools.JavaFileObject;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.Collections.unmodifiableList;
-
-/**
- * Writes a single compilation unit.
- */
-public final class JavaWriter {
- public static JavaWriter inPackage(String packageName) {
- return new JavaWriter(packageName);
- }
-
- public static JavaWriter inPackage(Package enclosingPackage) {
- return new JavaWriter(enclosingPackage.getName());
- }
-
- public static JavaWriter inPackage(PackageElement packageElement) {
- return new JavaWriter(packageElement.getQualifiedName().toString());
- }
-
- private final String packageName;
- // TODO(gak): disallow multiple types in a file?
- private final List<TypeWriter> typeWriters;
- private final List<ClassName> explicitImports;
-
- private JavaWriter(String packageName) {
- this.packageName = packageName;
- this.typeWriters = Lists.newArrayList();
- this.explicitImports = Lists.newArrayList();
- }
-
- public List<TypeWriter> getTypeWriters() {
- return unmodifiableList(typeWriters);
- }
-
- public JavaWriter addImport(Class<?> importedClass) {
- explicitImports.add(ClassName.fromClass(importedClass));
- return this;
- }
-
- public ClassWriter addClass(String simpleName) {
- checkNotNull(simpleName);
- ClassWriter classWriter = new ClassWriter(ClassName.create(packageName, simpleName));
- typeWriters.add(classWriter);
- return classWriter;
- }
-
- public EnumWriter addEnum(String simpleName) {
- checkNotNull(simpleName);
- EnumWriter writer = new EnumWriter(ClassName.create(packageName, simpleName));
- typeWriters.add(writer);
- return writer;
- }
-
- public InterfaceWriter addInterface(String simpleName) {
- InterfaceWriter writer = new InterfaceWriter(ClassName.create(packageName, simpleName));
- typeWriters.add(writer);
- return writer;
- }
-
- public <A extends Appendable> A write(A appendable) throws IOException {
- if (!packageName.isEmpty()) {
- appendable.append("package ").append(packageName).append(";\n\n");
- }
-
- // write imports
- ImmutableSet<ClassName> classNames = FluentIterable.from(typeWriters)
- .transformAndConcat(new Function<HasClassReferences, Set<ClassName>>() {
- @Override
- public Set<ClassName> apply(HasClassReferences input) {
- return input.referencedClasses();
- }
- })
- .toSet();
-
- ImmutableSortedSet<ClassName> importCandidates = ImmutableSortedSet.<ClassName>naturalOrder()
- .addAll(explicitImports)
- .addAll(classNames)
- .build();
- ImmutableSet<ClassName> typeNames = FluentIterable.from(typeWriters)
- .transform(new Function<TypeWriter, ClassName>() {
- @Override public ClassName apply(TypeWriter input) {
- return input.name;
- }
- })
- .toSet();
-
- ImmutableSet.Builder<String> declaredSimpleNamesBuilder = ImmutableSet.builder();
- Deque<TypeWriter> declaredTypes = new ArrayDeque<>(typeWriters);
- while (!declaredTypes.isEmpty()) {
- TypeWriter currentType = declaredTypes.pop();
- declaredSimpleNamesBuilder.add(currentType.name().simpleName());
- declaredTypes.addAll(currentType.nestedTypeWriters);
- }
-
- ImmutableSet<String> declaredSimpleNames = declaredSimpleNamesBuilder.build();
-
- BiMap<String, ClassName> importedClassIndex = HashBiMap.create();
- for (ClassName className : importCandidates) {
- if (!(className.packageName().equals(packageName)
- && !className.enclosingClassName().isPresent())
- && !(className.packageName().equals("java.lang")
- && className.enclosingSimpleNames().isEmpty())
- && !typeNames.contains(className.topLevelClassName())) {
- Optional<ClassName> importCandidate = Optional.of(className);
- while (importCandidate.isPresent()
- && (importedClassIndex.containsKey(importCandidate.get().simpleName())
- || declaredSimpleNames.contains(importCandidate.get().simpleName()))) {
- importCandidate = importCandidate.get().enclosingClassName();
- }
- if (importCandidate.isPresent()) {
- appendable.append("import ").append(importCandidate.get().canonicalName()).append(";\n");
- importedClassIndex.put(importCandidate.get().simpleName(), importCandidate.get());
- }
- }
- }
-
- appendable.append('\n');
-
- CompilationUnitContext context =
- new CompilationUnitContext(packageName, ImmutableSet.copyOf(importedClassIndex.values()));
-
- // write types
- for (TypeWriter typeWriter : typeWriters) {
- typeWriter.write(appendable, context.createSubcontext(typeNames)).append('\n');
- }
- return appendable;
- }
-
- public void file(Filer filer, Iterable<? extends Element> originatingElements)
- throws IOException {
- file(filer, Iterables.getOnlyElement(typeWriters).name.canonicalName(), originatingElements);
- }
-
- public void file(Filer filer, CharSequence name, Iterable<? extends Element> originatingElements)
- throws IOException {
- final JavaFileObject sourceFile = filer.createSourceFile(name,
- Iterables.toArray(originatingElements, Element.class));
- try {
- new Formatter().formatSource(
- CharSource.wrap(write(new StringBuilder())),
- new CharSink() {
- @Override public Writer openStream() throws IOException {
- return sourceFile.openWriter();
- }
- });
- } catch (FormatterException e) {
- throw new IllegalStateException(
- "The writer produced code that could not be parsed by the formatter", e);
- }
- }
-
- @Override
- public String toString() {
- try {
- return write(new StringBuilder()).toString();
- } catch (IOException e) {
- throw new AssertionError();
- }
- }
-
- static final class CompilationUnitContext implements Context {
- private final String packageName;
- private final ImmutableSortedSet<ClassName> visibleClasses;
-
- CompilationUnitContext(String packageName, Set<ClassName> visibleClasses) {
- this.packageName = packageName;
- this.visibleClasses =
- ImmutableSortedSet.copyOf(Ordering.natural().reverse(), visibleClasses);
- }
-
- @Override
- public Context createSubcontext(Set<ClassName> newTypes) {
- return new CompilationUnitContext(packageName, Sets.union(visibleClasses, newTypes));
- }
-
- @Override
- public String sourceReferenceForClassName(ClassName className) {
- if (isImported(className)) {
- return className.simpleName();
- }
- Optional<ClassName> enclosingClassName = className.enclosingClassName();
- while (enclosingClassName.isPresent()) {
- if (isImported(enclosingClassName.get())) {
- return enclosingClassName.get().simpleName()
- + className.canonicalName()
- .substring(enclosingClassName.get().canonicalName().length());
- }
- enclosingClassName = enclosingClassName.get().enclosingClassName();
- }
- return className.canonicalName();
- }
-
- private boolean collidesWithVisibleClass(ClassName className) {
- return collidesWithVisibleClass(className.simpleName());
- }
-
- private boolean collidesWithVisibleClass(String simpleName) {
- return FluentIterable.from(visibleClasses)
- .transform(new Function<ClassName, String>() {
- @Override public String apply(ClassName input) {
- return input.simpleName();
- }
- })
- .contains(simpleName);
- }
-
- private boolean isImported(ClassName className) {
- return (packageName.equals(className.packageName())
- && !className.enclosingClassName().isPresent()
- && !collidesWithVisibleClass(className)) // need to account for scope & hiding
- || visibleClasses.contains(className)
- || (className.packageName().equals("java.lang")
- && className.enclosingSimpleNames().isEmpty());
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/MethodWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/MethodWriter.java
deleted file mode 100644
index eb4ff8d..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/MethodWriter.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.element.TypeElement;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-public final class MethodWriter extends Modifiable implements HasClassReferences, Writable {
- private final TypeName returnType;
- private final String name;
- private final Map<String, VariableWriter> parameterWriters;
- private final List<TypeVariableName> typeParameters;
- private Optional<BlockWriter> body;
-
- MethodWriter(TypeName returnType, String name) {
- this.returnType = returnType;
- this.name = name;
- this.parameterWriters = Maps.newLinkedHashMap();
- this.typeParameters = Lists.newArrayList();
- this.body = Optional.absent();
- }
-
- public String name() {
- return name;
- }
-
- public TypeName returnType() {
- return returnType;
- }
-
- public void addTypeParameter(TypeVariableName typeVariableName) {
- this.typeParameters.add(typeVariableName);
- }
-
- public void addTypeParameters(Iterable<TypeVariableName> typeVariableNames) {
- Iterables.addAll(typeParameters, typeVariableNames);
- }
-
- public VariableWriter addParameter(Class<?> type, String name) {
- return addParameter(ClassName.fromClass(type), name);
- }
-
- public VariableWriter addParameter(TypeElement type, String name) {
- return addParameter(ClassName.fromTypeElement(type), name);
- }
-
- public VariableWriter addParameter(TypeWriter type, String name) {
- return addParameter(type.name, name);
- }
-
- public VariableWriter addParameter(TypeName type, String name) {
- checkArgument(!parameterWriters.containsKey(name));
- VariableWriter parameterWriter = new VariableWriter(type, name);
- parameterWriters.put(name, parameterWriter);
- return parameterWriter;
- }
-
- public BlockWriter body() {
- if (body.isPresent()) {
- return body.get();
- } else {
- BlockWriter blockWriter = new BlockWriter();
- body = Optional.of(blockWriter);
- return blockWriter;
- }
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- writeAnnotations(appendable, context);
- writeModifiers(appendable);
- Writables.join(", ", typeParameters, "<", "> ", appendable, context);
- returnType.write(appendable, context);
- appendable.append(' ').append(name).append('(');
- Writables.join(", ", parameterWriters.values(), appendable, context);
- appendable.append(")");
- if (body.isPresent()) {
- appendable.append(" {");
- body.get().write(new IndentingAppendable(appendable), context);
- appendable.append("}\n");
- } else {
- appendable.append(";\n");
- }
- return appendable;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(ImmutableList.<HasClassReferences>of())
- .append(parameterWriters.values())
- .append(returnType)
- .append(body.asSet())
- .append(annotations)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/Modifiable.java b/compiler/src/main/java/dagger/internal/codegen/writer/Modifiable.java
deleted file mode 100644
index bb4c6ff..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/Modifiable.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import dagger.internal.codegen.writer.Writable.Context;
-import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-import javax.lang.model.element.Modifier;
-
-public abstract class Modifiable {
- final Set<Modifier> modifiers;
- final List<AnnotationWriter> annotations;
-
- Modifiable() {
- this.modifiers = EnumSet.noneOf(Modifier.class);
- this.annotations = Lists.newArrayList();
- }
-
- public void addModifiers(Modifier first, Modifier... rest) {
- addModifiers(Lists.asList(first, rest));
- }
-
- public void addModifiers(Iterable<Modifier> modifiers) {
- Iterables.addAll(this.modifiers, modifiers);
- }
-
- public AnnotationWriter annotate(ClassName annotation) {
- AnnotationWriter annotationWriter = new AnnotationWriter(annotation);
- this.annotations.add(annotationWriter);
- return annotationWriter;
- }
-
- public AnnotationWriter annotate(Class<? extends Annotation> annotation) {
- return annotate(ClassName.fromClass(annotation));
- }
-
- Appendable writeModifiers(Appendable appendable) throws IOException {
- for (Modifier modifier : modifiers) {
- appendable.append(modifier.toString()).append(' ');
- }
- return appendable;
- }
-
- Appendable writeAnnotations(Appendable appendable, Context context) throws IOException {
- for (AnnotationWriter annotationWriter : annotations) {
- annotationWriter.write(appendable, context).append('\n');
- }
- return appendable;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/NullName.java b/compiler/src/main/java/dagger/internal/codegen/writer/NullName.java
deleted file mode 100644
index 0d3588b..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/NullName.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-import java.util.Set;
-
-enum NullName implements TypeName {
- NULL;
-
- @Override
- public Set<ClassName> referencedClasses() {
- return ImmutableSet.of();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- return appendable.append("null");
- }
-
- @Override
- public String toString() {
- return "null";
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/ParameterizedTypeName.java b/compiler/src/main/java/dagger/internal/codegen/writer/ParameterizedTypeName.java
deleted file mode 100644
index e46a961..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/ParameterizedTypeName.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Verify.verify;
-
-public final class ParameterizedTypeName implements TypeName {
- private final ClassName type;
- private final ImmutableList<TypeName> parameters;
-
- ParameterizedTypeName(ClassName type, Iterable<? extends TypeName> parameters) {
- this.type = type;
- this.parameters = ImmutableList.<TypeName>copyOf(parameters);
- }
-
- public ClassName type() {
- return type;
- }
-
- public ImmutableList<TypeName> parameters() {
- return parameters;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- ImmutableSet.Builder<ClassName> builder = new ImmutableSet.Builder<ClassName>()
- .add(type);
- for (TypeName parameter : parameters) {
- builder.addAll(parameter.referencedClasses());
- }
- return builder.build();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- appendable.append(context.sourceReferenceForClassName(type));
- Iterator<? extends TypeName> parameterIterator = parameters.iterator();
- verify(parameterIterator.hasNext(), type.toString());
- appendable.append('<');
- parameterIterator.next().write(appendable, context);
- while (parameterIterator.hasNext()) {
- appendable.append(", ");
- parameterIterator.next().write(appendable, context);
- }
- appendable.append('>');
- return appendable;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof ParameterizedTypeName) {
- ParameterizedTypeName that = (ParameterizedTypeName) obj;
- return this.type.equals(that.type)
- && this.parameters.equals(that.parameters);
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(type, parameters);
- }
-
- @Override
- public String toString() {
- return Writables.writeToString(this);
- }
-
- public static ParameterizedTypeName create(ClassName className,
- TypeName... parameters) {
- return new ParameterizedTypeName(className, ImmutableList.copyOf(parameters));
- }
-
- public static ParameterizedTypeName create(ClassName className,
- Iterable<? extends TypeName> parameters) {
- return new ParameterizedTypeName(className, ImmutableList.copyOf(parameters));
- }
-
- public static ParameterizedTypeName create(Class<?> parameterizedClass,
- TypeName... parameters) {
- checkArgument(parameterizedClass.getTypeParameters().length == parameters.length);
- return new ParameterizedTypeName(ClassName.fromClass(parameterizedClass),
- ImmutableList.copyOf(parameters));
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/PrimitiveName.java b/compiler/src/main/java/dagger/internal/codegen/writer/PrimitiveName.java
deleted file mode 100644
index 94961dd..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/PrimitiveName.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-import java.util.Set;
-import javax.lang.model.type.PrimitiveType;
-
-public enum PrimitiveName implements TypeName {
- BOOLEAN, BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE;
-
- @Override
- public Set<ClassName> referencedClasses() {
- return ImmutableSet.of();
- }
-
- @Override
- public String toString() {
- return Ascii.toLowerCase(name());
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- return appendable.append(toString());
- }
-
- static PrimitiveName forTypeMirror(PrimitiveType mirror) {
- switch (mirror.getKind()) {
- case BOOLEAN:
- return BOOLEAN;
- case BYTE:
- return BYTE;
- case SHORT:
- return SHORT;
- case INT:
- return INT;
- case LONG:
- return LONG;
- case CHAR:
- return CHAR;
- case FLOAT:
- return FLOAT;
- case DOUBLE:
- return DOUBLE;
- default:
- throw new AssertionError();
- }
- }
-
- static PrimitiveName forClass(Class<?> primitiveClass) {
- if (boolean.class.equals(primitiveClass)) {
- return BOOLEAN;
- }
- if (byte.class.equals(primitiveClass)) {
- return BYTE;
- }
- if (short.class.equals(primitiveClass)) {
- return SHORT;
- }
- if (int.class.equals(primitiveClass)) {
- return INT;
- }
- if (long.class.equals(primitiveClass)) {
- return LONG;
- }
- if (char.class.equals(primitiveClass)) {
- return CHAR;
- }
- if (float.class.equals(primitiveClass)) {
- return FLOAT;
- }
- if (double.class.equals(primitiveClass)) {
- return DOUBLE;
- }
- throw new IllegalArgumentException(primitiveClass + " is not a primitive type");
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java b/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java
deleted file mode 100644
index 80ab944..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Formatter;
-import java.util.Iterator;
-import java.util.Set;
-
-public abstract class Snippet implements HasClassReferences, Writable {
-
- abstract ImmutableSet<TypeName> types();
-
- @Override
- public String toString() {
- return Writables.writeToString(this);
- }
-
- @Override
- public final Set<ClassName> referencedClasses() {
- return FluentIterable.from(types())
- .transformAndConcat(
- new Function<TypeName, Set<ClassName>>() {
- @Override
- public Set<ClassName> apply(TypeName input) {
- return input.referencedClasses();
- }
- })
- .toSet();
- }
-
- private static final class BasicSnippet extends Snippet {
- final String format;
- final ImmutableSet<TypeName> types;
- final ImmutableList<Object> args;
-
- BasicSnippet(String format, ImmutableSet<TypeName> types, ImmutableList<Object> args) {
- this.format = format;
- this.types = types;
- this.args = args;
- }
-
- @Override
- ImmutableSet<TypeName> types() {
- return types;
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- ImmutableList.Builder<Object> formattedArgsBuilder = ImmutableList.builder();
- for (Object arg : args) {
- if (arg instanceof Writable) {
- formattedArgsBuilder.add(((Writable) arg).write(new StringBuilder(), context).toString());
- } else {
- formattedArgsBuilder.add(arg);
- }
- }
-
- @SuppressWarnings("resource") // intentionally don't close the formatter
- Formatter formatter = new Formatter(appendable);
- formatter.format(format, Iterables.toArray(formattedArgsBuilder.build(), Object.class));
-
- return appendable;
- }
- }
-
- private static final class CompoundSnippet extends Snippet {
- final String joinToken;
- final ImmutableList<Snippet> snippets;
-
- CompoundSnippet(String joinToken, ImmutableList<Snippet> snippets) {
- this.joinToken = joinToken;
- this.snippets = snippets;
- }
-
- @Override
- ImmutableSet<TypeName> types() {
- return FluentIterable.from(snippets)
- .transformAndConcat(
- new Function<Snippet, Iterable<TypeName>>() {
- @Override
- public Iterable<TypeName> apply(Snippet input) {
- return input.types();
- }
- })
- .toSet();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- Iterator<Snippet> snippetIterator = snippets.iterator();
- if (snippetIterator.hasNext()) {
- Snippet firstSnippet = snippetIterator.next();
- firstSnippet.write(appendable, context);
- while (snippetIterator.hasNext()) {
- Snippet nextSnippet = snippetIterator.next();
- appendable.append(joinToken);
- nextSnippet.write(appendable, context);
- }
- }
- return appendable;
- }
- }
-
- public static Snippet format(String format, Object... args) {
- ImmutableSet.Builder<TypeName> types = ImmutableSet.builder();
- for (Object arg : args) {
- if (arg instanceof Snippet) {
- types.addAll(((Snippet) arg).types());
- }
- if (arg instanceof TypeName) {
- types.add((TypeName) arg);
- }
- if (arg instanceof HasTypeName) {
- types.add(((HasTypeName) arg).name());
- }
- }
- return new BasicSnippet(format, types.build(), ImmutableList.copyOf(args));
- }
-
- public static Snippet format(String format, Iterable<? extends Object> args) {
- return format(format, Iterables.toArray(args, Object.class));
- }
-
- public static Snippet memberSelectSnippet(Iterable<? extends Object> selectors) {
- return format(Joiner.on('.').join(Collections.nCopies(Iterables.size(selectors), "%s")),
- selectors);
- }
-
- public static Snippet nullCheck(Object thingToCheck) {
- return format("if (%s == null) { throw new NullPointerException(); } ", thingToCheck);
- }
-
- public static Snippet nullCheck(Object thingToCheck, String message) {
- return format("if (%s == null) { throw new NullPointerException(%s); } ",
- thingToCheck,
- StringLiteral.forValue(message));
- }
-
- public static Snippet makeParametersSnippet(Iterable<Snippet> parameterSnippets) {
- return join(", ", parameterSnippets);
- }
-
- /**
- * A snippet that concatenates its arguments with each snippet separated by a new line.
- */
- public static Snippet concat(Iterable<Snippet> snippets) {
- return join("\n", snippets);
- }
-
- /**
- * A snippet that joins its arguments with {@code joiner}.
- */
- public static Snippet join(String joinToken, Iterable<Snippet> snippets) {
- return new CompoundSnippet(joinToken, ImmutableList.copyOf(snippets));
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/StringLiteral.java b/compiler/src/main/java/dagger/internal/codegen/writer/StringLiteral.java
deleted file mode 100644
index 2d059f9..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/StringLiteral.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2014 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import java.util.Formatter;
-
-/**
- * Represents a string literal as found in Java source code.
- */
-public final class StringLiteral {
- /** Returns a new {@link StringLiteral} instance for the intended value of the literal. */
- public static StringLiteral forValue(String value) {
- return new StringLiteral(value, stringLiteral(value));
- }
-
- /** Returns the string literal representing {@code data}, including wrapping quotes. */
- private static String stringLiteral(String value) {
- StringBuilder result = new StringBuilder();
- result.append('"');
- for (int i = 0; i < value.length(); i++) {
- char c = value.charAt(i);
- switch (c) {
- case '"':
- result.append("\\\"");
- break;
- case '\\':
- result.append("\\\\");
- break;
- case '\b':
- result.append("\\b");
- break;
- case '\t':
- result.append("\\t");
- break;
- case '\n':
- result.append("\\n");
- break;
- case '\f':
- result.append("\\f");
- break;
- case '\r':
- result.append("\\r");
- break;
- default:
- if (Character.isISOControl(c)) {
- new Formatter(result).format("\\u%04x", (int) c);
- } else {
- result.append(c);
- }
- }
- }
- result.append('"');
- return result.toString();
- }
-
- private final String value;
- private final String literal;
-
- private StringLiteral(String value, String literal) {
- this.value = value;
- this.literal = literal;
- }
-
- public String value() {
- return value;
- }
-
- public String literal() {
- return literal;
- }
-
- @Override
- public String toString() {
- return literal;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- } else if (obj instanceof StringLiteral) {
- return this.value.equals(((StringLiteral) obj).value);
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/TypeName.java b/compiler/src/main/java/dagger/internal/codegen/writer/TypeName.java
deleted file mode 100644
index e0daf53..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/TypeName.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-public interface TypeName extends HasClassReferences, Writable {
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/TypeNames.java b/compiler/src/main/java/dagger/internal/codegen/writer/TypeNames.java
deleted file mode 100644
index 4bc2347..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/TypeNames.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.NoType;
-import javax.lang.model.type.NullType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.TypeVariable;
-import javax.lang.model.type.WildcardType;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-public final class TypeNames {
- static final Function<TypeMirror, TypeName> FOR_TYPE_MIRROR =
- new Function<TypeMirror, TypeName>() {
- @Override public TypeName apply(TypeMirror input) {
- return forTypeMirror(input);
- }
- };
-
- public static TypeName forClass(Class<?> clazz) {
- if (clazz.isPrimitive()) {
- return PrimitiveName.forClass(clazz);
- } else if (void.class.equals(clazz)) {
- return VoidName.VOID;
- } else if (clazz.isArray()) {
- return new ArrayTypeName(forClass(clazz.getComponentType()));
- } else {
- return ClassName.fromClass(clazz);
- }
- }
-
- public static TypeName forTypeMirror(TypeMirror mirror) {
- return mirror.accept(new SimpleTypeVisitor6<TypeName, Void>() {
- @Override
- protected TypeName defaultAction(TypeMirror e, Void p) {
- throw new IllegalArgumentException(e.toString());
- }
-
- @Override
- public TypeName visitTypeVariable(TypeVariable t, Void p) {
- return TypeVariableName.fromTypeVariable(t);
- }
-
- @Override
- public ArrayTypeName visitArray(ArrayType t, Void p) {
- return new ArrayTypeName(t.getComponentType().accept(this, null));
- }
-
- @Override
- public TypeName visitDeclared(DeclaredType t, Void p) {
- return t.getTypeArguments().isEmpty()
- ? ClassName.fromTypeElement((TypeElement) t.asElement())
- : new ParameterizedTypeName(
- ClassName.fromTypeElement((TypeElement) t.asElement()),
- FluentIterable.from(t.getTypeArguments()).transform(FOR_TYPE_MIRROR));
- }
-
- @Override
- public PrimitiveName visitPrimitive(PrimitiveType t, Void p) {
- return PrimitiveName.forTypeMirror(t);
- }
-
- @Override
- public WildcardName visitWildcard(WildcardType t, Void p) {
- return WildcardName.forTypeMirror(t);
- }
-
- @Override
- public NullName visitNull(NullType t, Void p) {
- return NullName.NULL;
- }
-
- @Override
- public TypeName visitNoType(NoType t, Void p) {
- switch (t.getKind()) {
- case VOID:
- return VoidName.VOID;
- case PACKAGE:
- throw new IllegalArgumentException();
- default:
- throw new IllegalStateException();
- }
- }
- }, null);
- }
-
- private TypeNames() {
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/TypeVariableName.java b/compiler/src/main/java/dagger/internal/codegen/writer/TypeVariableName.java
deleted file mode 100644
index c6ee533..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/TypeVariableName.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Set;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.TypeVariable;
-
-public final class TypeVariableName implements TypeName {
- private final CharSequence name;
- private final Iterable<? extends TypeName> extendsBounds;
-
- TypeVariableName(CharSequence name, Iterable<? extends TypeName> extendsBounds) {
- this.name = name;
- this.extendsBounds = extendsBounds;
- }
-
- public CharSequence name() {
- return name;
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- ImmutableSet.Builder<ClassName> builder = new ImmutableSet.Builder<ClassName>();
- for (TypeName bound : extendsBounds) {
- builder.addAll(bound.referencedClasses());
- }
- return builder.build();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- appendable.append(name);
- if (!Iterables.isEmpty(extendsBounds)) {
- appendable.append(" extends ");
- Iterator<? extends TypeName> iter = extendsBounds.iterator();
- iter.next().write(appendable, context);
- while (iter.hasNext()) {
- appendable.append(" & ");
- iter.next().write(appendable, context);
- }
- }
- return appendable;
- }
-
- @Override
- public String toString() {
- return Writables.writeToString(this);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof TypeVariableName) {
- TypeVariableName that = (TypeVariableName) obj;
- return this.name.toString().equals(that.name.toString())
- && this.extendsBounds.equals(that.extendsBounds);
- } else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(name, extendsBounds);
- }
-
- static TypeVariableName named(CharSequence name) {
- return new TypeVariableName(name, ImmutableList.<TypeName>of());
- }
-
- public static TypeVariableName fromTypeVariable(TypeVariable variable) {
- // Note: We don't have any use right now for the bounds because these are references
- // to the type & not the specification of the type itself. We never generate
- // code with type variables that include upper or lower bounds.
- return named(variable.asElement().getSimpleName());
- }
-
- // TODO(sameb): Consider making this a whole different thing: TypeParameterName since it
- // has different semantics than a TypeVariable (parameters only have upper bounds).
- public static TypeVariableName fromTypeParameterElement(TypeParameterElement element) {
- // We filter out bounds of type Object because those would just clutter the generated code.
- Iterable<? extends TypeName> bounds =
- FluentIterable.from(element.getBounds())
- .filter(new Predicate<TypeMirror>() {
- @Override public boolean apply(TypeMirror input) {
- return !MoreTypes.isType(input) || !MoreTypes.isTypeOf(Object.class, input);
- }
- })
- .transform(TypeNames.FOR_TYPE_MIRROR);
- return new TypeVariableName(element.getSimpleName(), bounds);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/TypeWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/TypeWriter.java
deleted file mode 100644
index b13d083..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/TypeWriter.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import java.util.List;
-import java.util.Map;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Only named types. Doesn't cover anonymous inner classes.
- */
-public abstract class TypeWriter /* ha ha */ extends Modifiable
- implements Writable, HasTypeName, HasClassReferences {
- final ClassName name;
- final List<TypeName> implementedTypes;
- final List<MethodWriter> methodWriters;
- final List<TypeWriter> nestedTypeWriters;
- final Map<String, FieldWriter> fieldWriters;
-
- TypeWriter(ClassName name) {
- this.name = name;
- this.implementedTypes = Lists.newArrayList();
- this.methodWriters = Lists.newArrayList();
- this.nestedTypeWriters = Lists.newArrayList();
- this.fieldWriters = Maps.newLinkedHashMap();
- }
-
- @Override
- public ClassName name() {
- return name;
- }
-
- public MethodWriter addMethod(TypeWriter returnType, String name) {
- MethodWriter methodWriter = new MethodWriter(returnType.name, name);
- methodWriters.add(methodWriter);
- return methodWriter;
- }
-
- public MethodWriter addMethod(TypeMirror returnType, String name) {
- MethodWriter methodWriter =
- new MethodWriter(TypeNames.forTypeMirror(returnType), name);
- methodWriters.add(methodWriter);
- return methodWriter;
- }
-
- public MethodWriter addMethod(TypeName returnType, String name) {
- MethodWriter methodWriter = new MethodWriter(returnType, name);
- methodWriters.add(methodWriter);
- return methodWriter;
- }
-
- public MethodWriter addMethod(Class<?> returnType, String name) {
- MethodWriter methodWriter =
- new MethodWriter(ClassName.fromClass(returnType), name);
- methodWriters.add(methodWriter);
- return methodWriter;
- }
-
- public ClassWriter addNestedClass(String name) {
- ClassWriter innerClassWriter = new ClassWriter(this.name.nestedClassNamed(name));
- nestedTypeWriters.add(innerClassWriter);
- return innerClassWriter;
- }
-
- public void addImplementedType(TypeName typeReference) {
- implementedTypes.add(typeReference);
- }
-
- public void addImplementedType(TypeElement typeElement) {
- implementedTypes.add(ClassName.fromTypeElement(typeElement));
- }
-
- public FieldWriter addField(Class<?> type, String name) {
- return addField(ClassName.fromClass(type), name);
- }
-
- public FieldWriter addField(TypeElement type, String name) {
- return addField(ClassName.fromTypeElement(type), name);
- }
-
- public FieldWriter addField(TypeName type, String name) {
- String candidateName = name;
- int differentiator = 1;
- while (fieldWriters.containsKey(candidateName)) {
- candidateName = name + differentiator;
- differentiator++;
- }
- FieldWriter fieldWriter = new FieldWriter(type, candidateName);
- fieldWriters.put(candidateName, fieldWriter);
- return fieldWriter;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/VariableWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/VariableWriter.java
deleted file mode 100644
index 58ee1e4..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/VariableWriter.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-public class VariableWriter extends Modifiable implements Writable, HasClassReferences {
- private final TypeName type;
- private final String name;
-
- VariableWriter(TypeName type, String name) {
- this.type = checkNotNull(type);
- this.name = checkNotNull(name);
- }
-
- public TypeName type() {
- return type;
- }
-
- public String name() {
- return name;
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- writeAnnotations(appendable, context);
- writeModifiers(appendable);
- type.write(appendable, context);
- return appendable.append(' ').append(name);
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(ImmutableList.<HasClassReferences>of())
- .append(annotations)
- .append(type)
- .transformAndConcat(HasClassReferences.COMBINER)
- .toSet();
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/VoidName.java b/compiler/src/main/java/dagger/internal/codegen/writer/VoidName.java
deleted file mode 100644
index f82a4ca..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/VoidName.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-import java.util.Set;
-
-public enum VoidName implements TypeName {
- VOID;
-
- @Override
- public Set<ClassName> referencedClasses() {
- return ImmutableSet.of();
- }
-
- @Override
- public String toString() {
- return "void";
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- return appendable.append("void");
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/WildcardName.java b/compiler/src/main/java/dagger/internal/codegen/writer/WildcardName.java
deleted file mode 100644
index 7756f93..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/WildcardName.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-import java.util.Set;
-import javax.lang.model.type.WildcardType;
-
-import static dagger.internal.codegen.writer.TypeNames.FOR_TYPE_MIRROR;
-
-public final class WildcardName implements TypeName {
- private final Optional<TypeName> extendsBound;
- private final Optional<TypeName> superBound;
-
- WildcardName(Optional<TypeName> extendsBound,
- Optional<TypeName> superBound) {
- this.extendsBound = extendsBound;
- this.superBound = superBound;
- }
-
- static WildcardName forTypeMirror(WildcardType mirror) {
- return new WildcardName(
- Optional.fromNullable(mirror.getExtendsBound()).transform(FOR_TYPE_MIRROR),
- Optional.fromNullable(mirror.getSuperBound()).transform(FOR_TYPE_MIRROR));
- }
-
- @Override
- public Set<ClassName> referencedClasses() {
- ImmutableSet.Builder<ClassName> builder = new ImmutableSet.Builder<ClassName>();
- if (extendsBound.isPresent()) {
- builder.addAll(extendsBound.get().referencedClasses());
- }
- if (superBound.isPresent()) {
- builder.addAll(superBound.get().referencedClasses());
- }
- return builder.build();
- }
-
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- appendable.append('?');
- if (extendsBound.isPresent()) {
- appendable.append(" extends ");
- extendsBound.get().write(appendable, context);
- }
- if (superBound.isPresent()) {
- appendable.append(" super ");
- superBound.get().write(appendable, context);
- }
- return appendable;
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/Writable.java b/compiler/src/main/java/dagger/internal/codegen/writer/Writable.java
deleted file mode 100644
index 9a88f43..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/Writable.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import java.io.IOException;
-import java.util.Set;
-
-interface Writable {
- interface Context {
- String sourceReferenceForClassName(ClassName className);
- Context createSubcontext(Set<ClassName> newTypes);
- }
-
- Appendable write(Appendable appendable, Context context) throws IOException;
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/Writables.java b/compiler/src/main/java/dagger/internal/codegen/writer/Writables.java
deleted file mode 100644
index 0186cbf..0000000
--- a/compiler/src/main/java/dagger/internal/codegen/writer/Writables.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import dagger.internal.codegen.writer.Writable.Context;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Set;
-
-final class Writables {
-
- /**
- * Joins the writables by the given delimiter, writing out the
- * prefix & suffix if there's at least one element.
- */
- static void join(String delimiter, Iterable<? extends Writable> writables,
- String prefix, String suffix,
- Appendable appendable, Context context) throws IOException {
- Iterator<? extends Writable> iter = writables.iterator();
- if (iter.hasNext()) {
- appendable.append(prefix);
- iter.next().write(appendable, context);
- while (iter.hasNext()) {
- appendable.append(delimiter);
- iter.next().write(appendable, context);
- }
- appendable.append(suffix);
- }
- }
-
- /** Joins the writables by the given delimiter. */
- static void join(String delimiter, Iterable<? extends Writable> writables,
- Appendable appendable, Context context) throws IOException {
- join(delimiter, writables, "", "", appendable, context);
- }
-
- static Writable toStringWritable(final Object object) {
- return new Writable() {
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- return appendable.append(object.toString());
- }
- };
- }
-
- private static final Context DEFAULT_CONTEXT = new Context() {
- @Override
- public String sourceReferenceForClassName(ClassName className) {
- return className.canonicalName();
- }
-
- @Override
- public Context createSubcontext(Set<ClassName> newTypes) {
- throw new UnsupportedOperationException();
- }
- };
-
- static String writeToString(Writable writable) {
- StringBuilder builder = new StringBuilder();
- try {
- writable.write(builder, DEFAULT_CONTEXT);
- } catch (IOException e) {
- throw new AssertionError("StringBuilder doesn't throw IOException" + e);
- }
- return builder.toString();
- }
-
- private Writables() {
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/BindingFieldTest.java b/compiler/src/test/java/dagger/internal/codegen/BindingFieldTest.java
deleted file mode 100644
index eaaa595..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/BindingFieldTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.Iterables;
-import com.google.testing.compile.CompilationRule;
-import dagger.MembersInjector;
-import dagger.internal.codegen.writer.ClassName;
-import dagger.internal.codegen.writer.ParameterizedTypeName;
-import dagger.internal.codegen.writer.TypeName;
-import dagger.internal.codegen.writer.TypeNames;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-/**
- * Test case for {@link FrameworkField}.
- */
-@RunWith(JUnit4.class)
-public class BindingFieldTest {
- @Rule public CompilationRule compilationRule = new CompilationRule();
-
- private Elements elements;
- private Types types;
- private Key.Factory keyFactory;
-
- @Before public void setUp() {
- this.types = compilationRule.getTypes();
- this.elements = compilationRule.getElements();
- this.keyFactory = new Key.Factory(types, elements);
- }
-
- private ExecutableElement getXConstructor() {
- TypeElement classElement = elements.getTypeElement(X.class.getCanonicalName());
- return Iterables.getOnlyElement(
- ElementFilter.constructorsIn(classElement.getEnclosedElements()));
- }
-
- @Test public void frameworkType() {
- Key key = keyFactory.forInjectConstructorWithResolvedType(
- getXConstructor().getEnclosingElement().asType());
- TypeName xClass = TypeNames.forTypeMirror(key.type());
- assertThat(FrameworkField.createWithTypeFromKey(Provider.class,
- BindingKey.create(BindingKey.Kind.CONTRIBUTION, key), "test")
- .frameworkType())
- .isEqualTo(ParameterizedTypeName.create(
- ClassName.fromClass(Provider.class), xClass));
- assertThat(FrameworkField.createWithTypeFromKey(MembersInjector.class,
- BindingKey.create(BindingKey.Kind.MEMBERS_INJECTION, key), "test")
- .frameworkType())
- .isEqualTo(ParameterizedTypeName.create(
- ClassName.fromClass(MembersInjector.class), xClass));
- }
-
- @Test public void nameSuffix() {
- Key key = keyFactory.forInjectConstructorWithResolvedType(
- getXConstructor().getEnclosingElement().asType());
- assertThat(FrameworkField.createWithTypeFromKey(Provider.class,
- BindingKey.create(BindingKey.Kind.CONTRIBUTION, key), "foo").name())
- .isEqualTo("fooProvider");
- assertThat(FrameworkField.createWithTypeFromKey(Provider.class,
- BindingKey.create(BindingKey.Kind.CONTRIBUTION, key), "fooProvider").name())
- .isEqualTo("fooProvider");
-
- }
-
- static final class X {
- @Inject X() {}
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java b/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java
deleted file mode 100644
index cf0e69d..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java
+++ /dev/null
@@ -1,941 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-/** Tests for {@link dagger.Component.Builder} */
-@RunWith(JUnit4.class)
-public class ComponentBuilderTest {
-
- private static final ErrorMessages.ComponentBuilderMessages MSGS =
- ErrorMessages.ComponentBuilderMessages.INSTANCE;
-
- @Test
- public void testEmptyBuilder() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " SomeInjectableType someInjectableType();",
- "",
- " @Component.Builder",
- " static interface Builder {",
- " SimpleComponent build();",
- " }",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import test.SimpleComponent",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static SimpleComponent.Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " }",
- "",
- " @Override",
- " public SomeInjectableType someInjectableType() {",
- " return SomeInjectableType_Factory.create().get();",
- " }",
- "",
- " private static final class Builder implements SimpleComponent.Builder {",
- " @Override",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(injectableTypeFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test
- public void testUsesBuildAndSetterNames() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides String string() { return null; }",
- "}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " String string();",
- "",
- " @Component.Builder",
- " interface Builder {",
- " Builder setTestModule(TestModule testModule);",
- " TestComponent create();",
- " }",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import test.TestComponent;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<String> stringProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static TestComponent.Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().create();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.stringProvider = TestModule_StringFactory.create(builder.testModule);",
- " }",
- "",
- " @Override",
- " public String string() {",
- " return stringProvider.get();",
- " }",
- "",
- " private static final class Builder implements TestComponent.Builder {",
- " private TestModule testModule;",
- "",
- " @Override",
- " public TestComponent create() {",
- " if (testModule == null) {",
- " this.testModule = new TestModule();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " @Override",
- " public Builder setTestModule(TestModule testModule) {",
- " if (testModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.testModule = testModule;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(moduleFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test
- public void testIgnoresModulesNotInApi() {
- JavaFileObject module1 = JavaFileObjects.forSourceLines("test.TestModule1",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule1 {",
- " @Provides String string() { return null; }",
- "}");
- JavaFileObject module2 = JavaFileObjects.forSourceLines("test.TestModule2",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule2 {",
- " @Provides Integer integer() { return null; }",
- "}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = {TestModule1.class, TestModule2.class})",
- "interface TestComponent {",
- " String string();",
- " Integer integer();",
- "",
- " @Component.Builder",
- " interface Builder {",
- " Builder testModule1(TestModule1 testModule1);",
- " TestComponent build();",
- " }",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import test.TestComponent;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<String> stringProvider;",
- " private Provider<Integer> integerProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static TestComponent.Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.stringProvider = TestModule1_StringFactory.create(builder.testModule1);",
- " this.integerProvider = TestModule2_IntegerFactory.create(builder.testModule2);",
- " }",
- "",
- " @Override",
- " public String string() {",
- " return stringProvider.get();",
- " }",
- "",
- " @Override",
- " public Integer integer() {",
- " return integerProvider.get();",
- " }",
- "",
- " private static final class Builder implements TestComponent.Builder {",
- " private TestModule1 testModule1;",
- " private TestModule2 testModule2;",
- "",
- " @Override",
- " public TestComponent build() {",
- " if (testModule1 == null) {",
- " this.testModule1 = new TestModule1();",
- " }",
- " if (testModule2 == null) {",
- " this.testModule2 = new TestModule2();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " @Override",
- " public Builder testModule1(TestModule1 testModule1) {",
- " if (testModule1 == null) {",
- " throw new NullPointerException();",
- " }",
- " this.testModule1 = testModule1;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(module1, module2, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test
- public void testMoreThanOneBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " @Component.Builder",
- " static interface Builder {",
- " SimpleComponent build();",
- " }",
- "",
- " @Component.Builder",
- " interface Builder2 {",
- " SimpleComponent build();",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(MSGS.moreThanOne(),
- "[test.SimpleComponent.Builder, test.SimpleComponent.Builder2]"))
- .in(componentFile);
- }
-
- @Test
- public void testBuilderGenericsFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " @Component.Builder",
- " interface Builder<T> {",
- " SimpleComponent build();",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.generics())
- .in(componentFile);
- }
-
- @Test
- public void testBuilderNotInComponentFails() {
- JavaFileObject builder = JavaFileObjects.forSourceLines("test.Builder",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component.Builder",
- "interface Builder {}");
- assertAbout(javaSource()).that(builder)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeInComponent())
- .in(builder);
- }
-
- @Test
- public void testBuilderMissingBuildMethodFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " @Component.Builder",
- " interface Builder {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.missingBuildMethod())
- .in(componentFile);
- }
-
- @Test
- public void testPrivateBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " private interface Builder {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.isPrivate())
- .in(componentFile);
- }
-
- @Test
- public void testNonStaticBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " abstract class Builder {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeStatic())
- .in(componentFile);
- }
-
- @Test
- public void testNonAbstractBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " static class Builder {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeAbstract());
- }
-
- @Test
- public void testBuilderOneCxtorWithArgsFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " static abstract class Builder {",
- " Builder(String unused) {}",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.cxtorOnlyOneAndNoArgs())
- .in(componentFile);
- }
-
- @Test
- public void testBuilderMoreThanOneCxtorFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " static abstract class Builder {",
- " Builder() {}",
- " Builder(String unused) {}",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.cxtorOnlyOneAndNoArgs())
- .in(componentFile);
- }
-
- @Test
- public void testBuilderEnumFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " enum Builder {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeClassOrInterface())
- .in(componentFile);
- }
-
- @Test
- public void testBuilderBuildReturnsWrongTypeFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " interface Builder {",
- " String build();",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.buildMustReturnComponentType())
- .in(componentFile).onLine(11);
- }
-
- @Test
- public void testInheritedBuilderBuildReturnsWrongTypeFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " interface Parent {",
- " String build();",
- " }",
- "",
- " @Component.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedBuildMustReturnComponentType(), "build"))
- .in(componentFile).onLine(14);
- }
-
- @Test
- public void testTwoBuildMethodsFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " interface Builder {",
- " SimpleComponent build();",
- " SimpleComponent create();",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(MSGS.twoBuildMethods(), "build()"))
- .in(componentFile).onLine(12);
- }
-
- @Test
- public void testInheritedTwoBuildMethodsFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " interface Parent {",
- " SimpleComponent build();",
- " SimpleComponent create();",
- " }",
- "",
- " @Component.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedTwoBuildMethods(), "create()", "build()"))
- .in(componentFile).onLine(15);
- }
-
- @Test
- public void testMoreThanOneArgFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " interface Builder {",
- " SimpleComponent build();",
- " Builder set(String s, Integer i);",
- " Builder set(Number n, Double d);",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.methodsMustTakeOneArg())
- .in(componentFile).onLine(12)
- .and().withErrorContaining(MSGS.methodsMustTakeOneArg())
- .in(componentFile).onLine(13);
- }
-
- @Test
- public void testInheritedMoreThanOneArgFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " interface Parent {",
- " SimpleComponent build();",
- " Builder set1(String s, Integer i);",
- " }",
- "",
- " @Component.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedMethodsMustTakeOneArg(),
- "set1(java.lang.String,java.lang.Integer)"))
- .in(componentFile).onLine(15);
- }
-
- @Test
- public void testSetterReturningNonVoidOrBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " interface Builder {",
- " SimpleComponent build();",
- " String set(Integer i);",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.methodsMustReturnVoidOrBuilder())
- .in(componentFile).onLine(12);
- }
-
- @Test
- public void testInheritedSetterReturningNonVoidOrBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " interface Parent {",
- " SimpleComponent build();",
- " String set(Integer i);",
- " }",
- "",
- " @Component.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedMethodsMustReturnVoidOrBuilder(),
- "set(java.lang.Integer)"))
- .in(componentFile).onLine(15);
- }
-
- @Test
- public void testGenericsOnSetterMethodFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " interface Builder {",
- " SimpleComponent build();",
- " <T> Builder set(T t);",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.methodsMayNotHaveTypeParameters())
- .in(componentFile).onLine(12);
- }
-
- @Test
- public void testGenericsOnInheritedSetterMethodFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " interface Parent {",
- " SimpleComponent build();",
- " <T> Builder set(T t);",
- " }",
- "",
- " @Component.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedMethodsMayNotHaveTypeParameters(), "<T>set(T)"))
- .in(componentFile).onLine(15);
- }
-
- @Test
- public void testMultipleSettersPerTypeFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " interface Builder {",
- " SimpleComponent build();",
- " void set1(String s);",
- " void set2(String s);",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.manyMethodsForType(),
- "java.lang.String", "[set1(java.lang.String), set2(java.lang.String)]"))
- .in(componentFile).onLine(10);
- }
-
- @Test
- public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " interface Parent<T> {",
- " void set1(T t);",
- " }",
- "",
- " @Component.Builder",
- " interface Builder extends Parent<String> {",
- " SimpleComponent build();",
- " void set2(String s);",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.manyMethodsForType(),
- "java.lang.String", "[set1(T), set2(java.lang.String)]"))
- .in(componentFile).onLine(14);
- }
-
- @Test
- public void testExtraSettersFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "abstract class SimpleComponent {",
- " @Component.Builder",
- " interface Builder {",
- " SimpleComponent build();",
- " void set1(String s);",
- " void set2(Integer s);",
- " }",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.extraSetters(),
- "[void test.SimpleComponent.Builder.set1(String),"
- + " void test.SimpleComponent.Builder.set2(Integer)]"))
- .in(componentFile).onLine(10);
-
- }
-
- @Test
- public void testMissingSettersFail() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " TestModule(String unused) {}",
- " @Provides String s() { return null; }",
- "}");
- JavaFileObject module2File = JavaFileObjects.forSourceLines("test.Test2Module",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class Test2Module {",
- " @Provides Integer i() { return null; }",
- "}");
- JavaFileObject module3File = JavaFileObjects.forSourceLines("test.Test3Module",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class Test3Module {",
- " Test3Module(String unused) {}",
- " @Provides Double d() { return null; }",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = {TestModule.class, Test2Module.class, Test3Module.class},",
- " dependencies = OtherComponent.class)",
- "interface TestComponent {",
- " String string();",
- " Integer integer();",
- "",
- " @Component.Builder",
- " interface Builder {",
- " TestComponent create();",
- " }",
- "}");
- JavaFileObject otherComponent = JavaFileObjects.forSourceLines("test.OtherComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface OtherComponent {}");
- assertAbout(javaSources())
- .that(ImmutableList.of(moduleFile, module2File, module3File, componentFile, otherComponent))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- // Ignores Test2Module because we can construct it ourselves.
- // TODO(sameb): Ignore Test3Module because it's not used within transitive dependencies.
- String.format(MSGS.missingSetters(),
- "[test.TestModule, test.Test3Module, test.OtherComponent]"))
- .in(componentFile).onLine(12);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java
deleted file mode 100644
index 28e3b45..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java
+++ /dev/null
@@ -1,2185 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.testing.compile.JavaFileObjects;
-import dagger.internal.codegen.writer.StringLiteral;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Set;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Processor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.TypeElement;
-import javax.tools.JavaFileObject;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static dagger.internal.codegen.ErrorMessages.REFERENCED_MODULES_MUST_NOT_BE_ABSTRACT;
-import static javax.tools.StandardLocation.SOURCE_OUTPUT;
-
-@RunWith(JUnit4.class)
-public class ComponentProcessorTest {
- private static final StringLiteral NPE_LITERAL =
- StringLiteral.forValue(ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD);
-
- @Test public void componentOnConcreteClass() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "final class NotAComponent {}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("interface");
- }
-
- @Test public void componentOnEnum() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "enum NotAComponent {",
- " INSTANCE",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("interface");
- }
-
- @Test public void componentOnAnnotation() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "@interface NotAComponent {}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("interface");
- }
-
- @Test public void nonModuleModule() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = Object.class)",
- "interface NotAComponent {}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("is not annotated with @Module");
- }
-
- private void checkCannotReferToModuleOfType(String moduleType) {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- moduleType + " TestModule {}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface BadComponent {}");
- assertAbout(javaSources()).that(ImmutableList.of(moduleFile, componentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(REFERENCED_MODULES_MUST_NOT_BE_ABSTRACT, "test.TestModule"));
- }
-
- @Test public void cannotReferToAbstractClassModules() {
- checkCannotReferToModuleOfType("abstract class");
- }
-
- @Test public void cannotReferToInterfaceModules() {
- checkCannotReferToModuleOfType("interface");
- }
-
- @Test public void doubleBindingFromResolvedModules() {
- JavaFileObject parent = JavaFileObjects.forSourceLines("test.ParentModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.List;",
- "",
- "@Module",
- "abstract class ParentModule<A> {",
- " @Provides List<A> provideListB(A a) { return null; }",
- "}");
- JavaFileObject child = JavaFileObjects.forSourceLines("test.ChildModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class ChildNumberModule extends ParentModule<Integer> {",
- " @Provides Integer provideInteger() { return null; }",
- "}");
- JavaFileObject another = JavaFileObjects.forSourceLines("test.AnotherModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.List;",
- "",
- "@Module",
- "class AnotherModule {",
- " @Provides List<Integer> provideListOfInteger() { return null; }",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.List;",
- "",
- "@Component(modules = {ChildNumberModule.class, AnotherModule.class})",
- "interface BadComponent {",
- " List<Integer> listOfInteger();",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(parent, child, another, componentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile().withErrorContaining(
- "java.util.List<java.lang.Integer> is bound multiple times")
- .and().withErrorContaining(
- "@Provides List<Integer> test.ChildNumberModule.provideListB(Integer)")
- .and().withErrorContaining(
- "@Provides List<Integer> test.AnotherModule.provideListOfInteger()");
- }
-
- @Test public void simpleComponent() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " SomeInjectableType someInjectableType();",
- " Lazy<SomeInjectableType> lazySomeInjectableType();",
- " Provider<SomeInjectableType> someInjectableTypeProvider();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import dagger.Lazy;",
- "import dagger.internal.DoubleCheckLazy;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " }",
- "",
- " @Override",
- " public SomeInjectableType someInjectableType() {",
- " return SomeInjectableType_Factory.create().get();",
- " }",
- "",
- " @Override",
- " public Lazy<SomeInjectableType> lazySomeInjectableType() {",
- " return DoubleCheckLazy.create(SomeInjectableType_Factory.create());",
- " }",
- "",
- " @Override",
- " public Provider<SomeInjectableType> someInjectableTypeProvider() {",
- " return SomeInjectableType_Factory.create();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(injectableTypeFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void componentWithScope() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "import javax.inject.Provider;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "@Component",
- "interface SimpleComponent {",
- " SomeInjectableType someInjectableType();",
- " Lazy<SomeInjectableType> lazySomeInjectableType();",
- " Provider<SomeInjectableType> someInjectableTypeProvider();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import dagger.Lazy;",
- "import dagger.internal.DoubleCheckLazy;",
- "import dagger.internal.ScopedProvider;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private Provider<SomeInjectableType> someInjectableTypeProvider;",
- "",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.someInjectableTypeProvider =",
- " ScopedProvider.create(SomeInjectableType_Factory.create());",
- " }",
- "",
- " @Override",
- " public SomeInjectableType someInjectableType() {",
- " return someInjectableTypeProvider.get();",
- " }",
- "",
- " @Override",
- " public Lazy<SomeInjectableType> lazySomeInjectableType() {",
- " return DoubleCheckLazy.create(someInjectableTypeProvider);",
- " }",
- "",
- " @Override",
- " public Provider<SomeInjectableType> someInjectableTypeProvider() {",
- " return someInjectableTypeProvider;",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(injectableTypeFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void simpleComponentWithNesting() {
- JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines("test.OuterType",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Inject;",
- "",
- "final class OuterType {",
- " static class A {",
- " @Inject A() {}",
- " }",
- " static class B {",
- " @Inject A a;",
- " }",
- " @Component interface SimpleComponent {",
- " A a();",
- " void inject(B b);",
- " }",
- "}");
-
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerOuterType_SimpleComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import test.OuterType.A;",
- "import test.OuterType.B;",
- "import test.OuterType.SimpleComponent;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerOuterType_SimpleComponent implements SimpleComponent {",
- " private MembersInjector<B> bMembersInjector;",
- "",
- " private DaggerOuterType_SimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.bMembersInjector =",
- " OuterType$B_MembersInjector.create(OuterType$A_Factory.create());",
- " }",
- "",
- " @Override",
- " public A a() {",
- " return OuterType$A_Factory.create().get();",
- " }",
- "",
- " @Override",
- " public void inject(B b) {",
- " bMembersInjector.injectMembers(b);",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerOuterType_SimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(nestedTypesFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void componentWithModule() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A(B b) {}",
- "}");
- JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
- "package test;",
- "",
- "interface B {}");
- JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class C {",
- " @Inject C() {}",
- "}");
-
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides B b(C c) { return null; }",
- "}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<B> bProvider;",
- " private Provider<A> aProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.bProvider = TestModule_BFactory.create(builder.testModule,",
- " C_Factory.create());",
- " this.aProvider = A_Factory.create(bProvider);",
- " }",
- "",
- " @Override",
- " public A a() {",
- " return aProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private TestModule testModule;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (testModule == null) {",
- " this.testModule = new TestModule();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder testModule(TestModule testModule) {",
- " if (testModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.testModule = testModule;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(aFile, bFile, cFile, moduleFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void transitiveModuleDeps() {
- JavaFileObject always = JavaFileObjects.forSourceLines("test.AlwaysIncluded",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class AlwaysIncluded {}");
- JavaFileObject testModule = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = {DepModule.class, AlwaysIncluded.class})",
- "final class TestModule extends ParentTestModule {}");
- JavaFileObject parentTest = JavaFileObjects.forSourceLines("test.ParentTestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = {ParentTestIncluded.class, AlwaysIncluded.class})",
- "class ParentTestModule {}");
- JavaFileObject parentTestIncluded = JavaFileObjects.forSourceLines("test.ParentTestIncluded",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = AlwaysIncluded.class)",
- "final class ParentTestIncluded {}");
- JavaFileObject depModule = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = {RefByDep.class, AlwaysIncluded.class})",
- "final class DepModule extends ParentDepModule {}");
- JavaFileObject refByDep = JavaFileObjects.forSourceLines("test.RefByDep",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = AlwaysIncluded.class)",
- "final class RefByDep extends ParentDepModule {}");
- JavaFileObject parentDep = JavaFileObjects.forSourceLines("test.ParentDepModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = {ParentDepIncluded.class, AlwaysIncluded.class})",
- "class ParentDepModule {}");
- JavaFileObject parentDepIncluded = JavaFileObjects.forSourceLines("test.ParentDepIncluded",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = AlwaysIncluded.class)",
- "final class ParentDepIncluded {}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- "}");
- // Generated code includes all includes, but excludes the parent modules.
- // The "always" module should only be listed once.
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " @Deprecated",
- " public Builder testModule(TestModule testModule) {",
- " if (testModule == null) {",
- " throw new NullPointerException();",
- " }",
- " return this;",
- " }",
- "",
- " @Deprecated",
- " public Builder parentTestIncluded(ParentTestIncluded parentTestIncluded) {",
- " if (parentTestIncluded == null) {",
- " throw new NullPointerException();",
- " }",
- " return this;",
- " }",
- "",
- " @Deprecated",
- " public Builder alwaysIncluded(AlwaysIncluded alwaysIncluded) {",
- " if (alwaysIncluded == null) {",
- " throw new NullPointerException();",
- " }",
- " return this;",
- " }",
- "",
- " @Deprecated",
- " public Builder depModule(DepModule depModule) {",
- " if (depModule == null) {",
- " throw new NullPointerException();",
- " }",
- " return this;",
- " }",
- "",
- " @Deprecated",
- " public Builder parentDepIncluded(ParentDepIncluded parentDepIncluded) {",
- " if (parentDepIncluded == null) {",
- " throw new NullPointerException();",
- " }",
- " return this;",
- " }",
- "",
- " @Deprecated",
- " public Builder refByDep(RefByDep refByDep) {",
- " if (refByDep == null) {",
- " throw new NullPointerException();",
- " }",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(always,
- testModule,
- parentTest,
- parentTestIncluded,
- depModule,
- refByDep,
- parentDep,
- parentDepIncluded,
- componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test
- public void generatedTransitiveModule() {
- JavaFileObject rootModule = JavaFileObjects.forSourceLines("test.RootModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = GeneratedModule.class)",
- "final class RootModule {}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RootModule.class)",
- "interface TestComponent {}");
- assertAbout(javaSources())
- .that(ImmutableList.of(rootModule, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile();
- assertAbout(javaSources())
- .that(ImmutableList.of(rootModule, component))
- .processedWith(
- new ComponentProcessor(),
- new GeneratingProcessor(
- "test.GeneratedModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class GeneratedModule {}"))
- .compilesWithoutError();
- }
-
- @Test
- public void generatedModuleInSubcomponent() {
- JavaFileObject subcomponent =
- JavaFileObjects.forSourceLines(
- "test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = GeneratedModule.class)",
- "interface ChildComponent {}");
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " ChildComponent childComponent();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(subcomponent, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile();
- assertAbout(javaSources())
- .that(ImmutableList.of(subcomponent, component))
- .processedWith(
- new ComponentProcessor(),
- new GeneratingProcessor(
- "test.GeneratedModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class GeneratedModule {}"))
- .compilesWithoutError();
- }
-
- @Test
- public void subcomponentOmitsInheritedBindings() {
- JavaFileObject parent =
- JavaFileObjects.forSourceLines(
- "test.Parent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = ParentModule.class)",
- "interface Parent {",
- " Child child();",
- "}");
- JavaFileObject parentModule =
- JavaFileObjects.forSourceLines(
- "test.ParentModule",
- "package test;",
- "",
- "import dagger.mapkeys.StringKey;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "import static dagger.Provides.Type.SET;",
- "import static dagger.Provides.Type.MAP;",
- "",
- "@Module",
- "class ParentModule {",
- " @Provides(type = SET) static Object parentObject() {",
- " return \"parent object\";",
- " }",
- "",
- " @Provides(type = MAP) @StringKey(\"parent key\") Object parentKeyObject() {",
- " return \"parent value\";",
- " }",
- "}");
- JavaFileObject child =
- JavaFileObjects.forSourceLines(
- "test.Child",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "import java.util.Set;",
- "",
- "@Subcomponent",
- "interface Child {",
- " Set<Object> objectSet();",
- " Map<String, Object> objectMap();",
- "}");
- JavaFileObject expected =
- JavaFileObjects.forSourceLines(
- "test.DaggerParent",
- "package test;",
- "",
- "import dagger.internal.MapFactory;",
- "import dagger.internal.MapProviderFactory;",
- "import dagger.internal.SetFactory;",
- "import java.util.Map;",
- "import java.util.Set;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerParent implements Parent {",
- " private Provider<Set<Object>> setOfObjectContribution1Provider;",
- " private Provider<Set<Object>> setOfObjectProvider;",
- " private Provider<Object> mapOfStringAndProviderOfObjectContribution1;",
- " private Provider<Map<String, Provider<Object>>>",
- " mapOfStringAndProviderOfObjectProvider;",
- "",
- " private DaggerParent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Parent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.setOfObjectContribution1Provider =",
- " ParentModule_ParentObjectFactory.create();",
- " this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider);",
- " this.mapOfStringAndProviderOfObjectContribution1 =",
- " ParentModule_ParentKeyObjectFactory.create(builder.parentModule);",
- " this.mapOfStringAndProviderOfObjectProvider =",
- " MapProviderFactory.<String, Object>builder(1)",
- " .put(\"parent key\", mapOfStringAndProviderOfObjectContribution1)",
- " .build();",
- " }",
- "",
- " @Override",
- " public Child child() {",
- " return new ChildImpl();",
- " }",
- "",
- " public static final class Builder {",
- " private ParentModule parentModule;",
- "",
- " private Builder() {}",
- "",
- " public Parent build() {",
- " if (parentModule == null) {",
- " this.parentModule = new ParentModule();",
- " }",
- " return new DaggerParent(this);",
- " }",
- "",
- " public Builder parentModule(ParentModule parentModule) {",
- " if (parentModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.parentModule = parentModule;",
- " return this;",
- " }",
- " }",
- "",
- " private final class ChildImpl implements Child {",
- " private Provider<Map<String, Object>> mapOfStringAndObjectProvider;",
- "",
- " private ChildImpl() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.mapOfStringAndObjectProvider = MapFactory.create(",
- " DaggerParent.this.mapOfStringAndProviderOfObjectProvider);",
- " }",
- "",
- " @Override",
- " public Set<Object> objectSet() {",
- " return DaggerParent.this.setOfObjectProvider.get();",
- " }",
- "",
- " @Override",
- " public Map<String, Object> objectMap() {",
- " return mapOfStringAndObjectProvider.get();",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(parent, parentModule, child))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expected);
- }
-
- @Test public void testDefaultPackage() {
- JavaFileObject aClass = JavaFileObjects.forSourceLines("AClass", "class AClass {}");
- JavaFileObject bClass = JavaFileObjects.forSourceLines("BClass",
- "import javax.inject.Inject;",
- "",
- "class BClass {",
- " @Inject BClass(AClass a) {}",
- "}");
- JavaFileObject aModule = JavaFileObjects.forSourceLines("AModule",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module class AModule {",
- " @Provides AClass aClass() {",
- " return new AClass();",
- " }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("SomeComponent",
- "import dagger.Component;",
- "",
- "@Component(modules = AModule.class)",
- "interface SomeComponent {",
- " BClass bClass();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(aModule, aClass, bClass, component))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test public void setBindings() {
- JavaFileObject emptySetModuleFile = JavaFileObjects.forSourceLines("test.EmptySetModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET_VALUES;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.Collections;",
- "import java.util.Set;",
- "",
- "@Module",
- "final class EmptySetModule {",
- " @Provides(type = SET_VALUES) Set<String> emptySet() { return Collections.emptySet(); }",
- "}");
- JavaFileObject setModuleFile = JavaFileObjects.forSourceLines("test.SetModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class SetModule {",
- " @Provides(type = SET) String string() { return \"\"; }",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Set;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {EmptySetModule.class, SetModule.class})",
- "interface TestComponent {",
- " Set<String> strings();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.internal.SetFactory;",
- "import java.util.Set;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Set<String>> setOfStringContribution1Provider;",
- " private Provider<Set<String>> setOfStringContribution2Provider;",
- " private Provider<Set<String>> setOfStringProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.setOfStringContribution1Provider =",
- " EmptySetModule_EmptySetFactory.create(builder.emptySetModule);",
- " this.setOfStringContribution2Provider =",
- " SetModule_StringFactory.create(builder.setModule);",
- " this.setOfStringProvider = SetFactory.create(",
- " setOfStringContribution1Provider, setOfStringContribution2Provider);",
- " }",
- "",
- " @Override",
- " public Set<String> strings() {",
- " return setOfStringProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private EmptySetModule emptySetModule;",
- " private SetModule setModule;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (emptySetModule == null) {",
- " this.emptySetModule = new EmptySetModule();",
- " }",
- " if (setModule == null) {",
- " this.setModule = new SetModule();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder emptySetModule(EmptySetModule emptySetModule) {",
- " if (emptySetModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.emptySetModule = emptySetModule;",
- " return this;",
- " }",
- "",
- " public Builder setModule(SetModule setModule) {",
- " if (setModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.setModule = setModule;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(emptySetModuleFile, setModuleFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void membersInjection() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectedType {",
- " @Inject SomeInjectableType injectedField;",
- " SomeInjectedType() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " void inject(SomeInjectedType instance);",
- " SomeInjectedType injectAndReturn(SomeInjectedType instance);",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private MembersInjector<SomeInjectedType> someInjectedTypeMembersInjector;",
- "",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.someInjectedTypeMembersInjector =",
- " SomeInjectedType_MembersInjector.create(SomeInjectableType_Factory.create());",
- " }",
- "",
- " @Override",
- " public void inject(SomeInjectedType instance) {",
- " someInjectedTypeMembersInjector.injectMembers(instance);",
- " }",
- "",
- " @Override",
- " public SomeInjectedType injectAndReturn(SomeInjectedType instance) {",
- " someInjectedTypeMembersInjector.injectMembers(instance);",
- " return instance;",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(injectableTypeFile, injectedTypeFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void componentInjection() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType(SimpleComponent component) {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " SomeInjectableType someInjectableType();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import dagger.internal.InstanceFactory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private Provider<SimpleComponent> simpleComponentProvider;",
- " private Provider<SomeInjectableType> someInjectableTypeProvider;",
- "",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.simpleComponentProvider = InstanceFactory.<SimpleComponent>create(this);",
- " this.someInjectableTypeProvider =",
- " SomeInjectableType_Factory.create(simpleComponentProvider);",
- " }",
- "",
- " @Override",
- " public SomeInjectableType someInjectableType() {",
- " return someInjectableTypeProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(injectableTypeFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void membersInjectionInsideProvision() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectedType {",
- " @Inject SomeInjectableType injectedField;",
- " @Inject SomeInjectedType() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " SomeInjectedType createAndInject();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private MembersInjector<SomeInjectedType> someInjectedTypeMembersInjector;",
- " private Provider<SomeInjectedType> someInjectedTypeProvider;",
- "",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.someInjectedTypeMembersInjector =",
- " SomeInjectedType_MembersInjector.create(SomeInjectableType_Factory.create());",
- " this.someInjectedTypeProvider =",
- " SomeInjectedType_Factory.create(someInjectedTypeMembersInjector);",
- " }",
- "",
- " @Override",
- " public SomeInjectedType createAndInject() {",
- " return someInjectedTypeProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(injectableTypeFile, injectedTypeFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void injectionWithGenericBaseClass() {
- JavaFileObject genericType = JavaFileObjects.forSourceLines("test.AbstractGenericType",
- "package test;",
- "",
- "abstract class AbstractGenericType<T> {",
- "}");
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType extends AbstractGenericType<String> {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " SomeInjectableType someInjectableType();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.MembersInjectors;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private Provider<SomeInjectableType> someInjectableTypeProvider;",
- "",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.someInjectableTypeProvider =",
- " SomeInjectableType_Factory.create((MembersInjector) MembersInjectors.noOp());",
- " }",
- "",
- " @Override",
- " public SomeInjectableType someInjectableType() {",
- " return someInjectableTypeProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(genericType, injectableTypeFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void componentDependency() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A() {}",
- "}");
- JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class B {",
- " @Inject B(A a) {}",
- "}");
- JavaFileObject aComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface AComponent {",
- " A a();",
- "}");
- JavaFileObject bComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component(dependencies = AComponent.class)",
- "interface BComponent {",
- " B b();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerBComponent",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerBComponent implements BComponent {",
- " private Provider<A> aProvider;",
- " private Provider<B> bProvider;",
- "",
- " private DaggerBComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.aProvider = new Factory<A>() {",
- " private final AComponent aComponent = builder.aComponent;",
- " @Override public A get() {",
- " A provided = aComponent.a();",
- " if (provided == null) {",
- " throw new NullPointerException(" + NPE_LITERAL + ");",
- " }",
- " return provided;",
- " }",
- " };",
- " this.bProvider = B_Factory.create(aProvider);",
- " }",
- "",
- " @Override",
- " public B b() {",
- " return bProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private AComponent aComponent;",
- "",
- " private Builder() {",
- " }",
- "",
- " public BComponent build() {",
- " if (aComponent == null) {",
- " throw new IllegalStateException(AComponent.class.getCanonicalName()",
- " + \" must be set\");",
- " }",
- " return new DaggerBComponent(this);",
- " }",
- "",
- " public Builder aComponent(AComponent aComponent) {",
- " if (aComponent == null) {",
- " throw new NullPointerException();",
- " }",
- " this.aComponent = aComponent;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(aFile, bFile, aComponentFile, bComponentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void moduleNameCollision() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "public final class A {}");
- JavaFileObject otherAFile = JavaFileObjects.forSourceLines("other.test.A",
- "package other.test;",
- "",
- "public final class A {}");
-
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "public final class TestModule {",
- " @Provides A a() { return null; }",
- "}");
- JavaFileObject otherModuleFile = JavaFileObjects.forSourceLines("other.test.TestModule",
- "package other.test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "public final class TestModule {",
- " @Provides A a() { return null; }",
- "}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {TestModule.class, other.test.TestModule.class})",
- "interface TestComponent {",
- " A a();",
- " other.test.A otherA();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import other.test.A;",
- "import other.test.TestModule;",
- "import other.test.TestModule_AFactory;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<test.A> aProvider;",
- " private Provider<A> aProvider1;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.aProvider = test.TestModule_AFactory.create(builder.testModule);",
- " this.aProvider1 = TestModule_AFactory.create(builder.testModule1);",
- " }",
- "",
- " @Override",
- " public test.A a() {",
- " return aProvider.get();",
- " }",
- "",
- " @Override",
- " public A otherA() {",
- " return aProvider1.get();",
- " }",
- "",
- " public static final class Builder {",
- " private test.TestModule testModule;",
- " private TestModule testModule1;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (testModule == null) {",
- " this.testModule = new test.TestModule();",
- " }",
- " if (testModule1 == null) {",
- " this.testModule1 = new TestModule();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder testModule(test.TestModule testModule) {",
- " if (testModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.testModule = testModule;",
- " return this;",
- " }",
- "",
- " public Builder testModule(TestModule testModule) {",
- " if (testModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.testModule1 = testModule;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(aFile, otherAFile, moduleFile, otherModuleFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void resolutionOrder() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A(B b) {}",
- "}");
- JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class B {",
- " @Inject B(C c) {}",
- "}");
- JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class C {",
- " @Inject C() {}",
- "}");
- JavaFileObject xFile = JavaFileObjects.forSourceLines("test.X",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class X {",
- " @Inject X(C c) {}",
- "}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface TestComponent {",
- " A a();",
- " C c();",
- " X x();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<B> bProvider;",
- " private Provider<A> aProvider;",
- " private Provider<X> xProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.bProvider = B_Factory.create(C_Factory.create());",
- " this.aProvider = A_Factory.create(bProvider);",
- " this.xProvider = X_Factory.create(C_Factory.create());",
- " }",
- "",
- " @Override",
- " public A a() {",
- " return aProvider.get();",
- " }",
- "",
- " @Override",
- " public C c() {",
- " return C_Factory.create().get();",
- " }",
- "",
- " @Override",
- " public X x() {",
- " return xProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " return new DaggerTestComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(aFile, bFile, cFile, xFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void simpleComponent_redundantComponentMethod() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject componentSupertypeAFile = JavaFileObjects.forSourceLines("test.SupertypeA",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SupertypeA {",
- " SomeInjectableType someInjectableType();",
- "}");
- JavaFileObject componentSupertypeBFile = JavaFileObjects.forSourceLines("test.SupertypeB",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SupertypeB {",
- " SomeInjectableType someInjectableType();",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent extends SupertypeA, SupertypeB {",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {}",
- "",
- " @Override",
- " public SomeInjectableType someInjectableType() {",
- " return SomeInjectableType_Factory.create().get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(
- injectableTypeFile, componentSupertypeAFile, componentSupertypeBFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void simpleComponent_inheritedComponentMethodDep() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType() {}",
- "}");
- JavaFileObject componentSupertype = JavaFileObjects.forSourceLines("test.Supertype",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface Supertype {",
- " SomeInjectableType someInjectableType();",
- "}");
- JavaFileObject depComponentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent extends Supertype {",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ComponentWithDep",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component(dependencies = SimpleComponent.class)",
- "interface ComponentWithDep {",
- " SomeInjectableType someInjectableType();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {}",
- "",
- " @Override",
- " public SomeInjectableType someInjectableType() {",
- " return SomeInjectableType_Factory.create().get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(
- injectableTypeFile, componentSupertype, depComponentFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void wildcardGenericsRequiresAtProvides() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A() {}",
- "}");
- JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class B<T> {",
- " @Inject B(T t) {}",
- "}");
- JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class C {",
- " @Inject C(B<? extends A> bA) {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " C c();",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(aFile, bFile, cFile, componentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- "test.B<? extends test.A> cannot be provided without an @Provides-annotated method");
- }
- @Test
- public void componentImplicitlyDependsOnGeneratedType() {
- JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SomeInjectableType {",
- " @Inject SomeInjectableType(GeneratedType generatedType) {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " SomeInjectableType someInjectableType();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(injectableTypeFile, componentFile))
- .processedWith(
- new ComponentProcessor(),
- new GeneratingProcessor(
- "test.GeneratedType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class GeneratedType {",
- " @Inject GeneratedType() {}",
- "}"))
- .compilesWithoutError()
- .and()
- .generatesFileNamed(SOURCE_OUTPUT, "test", "DaggerSimpleComponent.java");
- }
- @Test
- public void componentSupertypeDependsOnGeneratedType() {
- JavaFileObject componentFile =
- JavaFileObjects.forSourceLines(
- "test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface SimpleComponent extends SimpleComponentInterface {}");
- JavaFileObject interfaceFile =
- JavaFileObjects.forSourceLines(
- "test.SimpleComponentInterface",
- "package test;",
- "",
- "interface SimpleComponentInterface {",
- " GeneratedType generatedType();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(componentFile, interfaceFile))
- .processedWith(
- new ComponentProcessor(),
- new GeneratingProcessor(
- "test.GeneratedType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class GeneratedType {",
- " @Inject GeneratedType() {}",
- "}"))
- .compilesWithoutError()
- .and()
- .generatesFileNamed(SOURCE_OUTPUT, "test", "DaggerSimpleComponent.java");
- }
-
- @Test
- @Ignore // modify this test as necessary while debugging for your situation.
- @SuppressWarnings("unused")
- public void genericTestToLetMeDebugInEclipse() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "public final class A {",
- " @Inject A() {}",
- "}");
- JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "public class B<T> {",
- " @Inject B() {}",
- "}");
- JavaFileObject dFile = JavaFileObjects.forSourceLines("test.sub.D",
- "package test.sub;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "import test.B;",
- "",
- "public class D {",
- " @Inject D(B<A.InA> ba) {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Lazy;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " B<A> d();",
- " Provider<B<A>> d2();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerSimpleComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerSimpleComponent implements SimpleComponent {",
- " private Provider<D> dProvider;",
- "",
- " private DaggerSimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static SimpleComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.dProvider = new D_Factory(B_Factory.INSTANCE);",
- " }",
- "",
- " @Override",
- " public D d() {",
- " return dProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " return new DaggerSimpleComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(aFile, bFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- /**
- * A simple {@link Processor} that generates one source file.
- */
- private static final class GeneratingProcessor extends AbstractProcessor {
- private final String generatedClassName;
- private final String generatedSource;
- private boolean processed;
-
- GeneratingProcessor(String generatedClassName, String... source) {
- this.generatedClassName = generatedClassName;
- this.generatedSource = Joiner.on("\n").join(source);
- }
-
- @Override
- public Set<String> getSupportedAnnotationTypes() {
- return ImmutableSet.of("*");
- }
-
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- if (!processed) {
- processed = true;
- try (Writer writer =
- processingEnv.getFiler().createSourceFile(generatedClassName).openWriter()) {
- writer.append(generatedSource);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- return false;
- }
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java b/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java
deleted file mode 100644
index b47a43c..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.Iterables;
-import com.google.testing.compile.CompilationRule;
-import dagger.Lazy;
-import dagger.MembersInjector;
-import dagger.Module;
-import dagger.Provides;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.util.List;
-import javax.inject.Provider;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-/**
- * Test case for {@link DependencyRequestMapper}.
- */
-@RunWith(JUnit4.class)
-public class DependencyRequestMapperTest {
- @Rule public CompilationRule compilationRule = new CompilationRule();
-
- private Elements elements;
- private Types types;
- private Key.Factory keyFactory;
- private DependencyRequest.Factory dependencyRequestFactory;
-
- @Before public void setUp() {
- this.types = compilationRule.getTypes();
- this.elements = compilationRule.getElements();
- this.keyFactory = new Key.Factory(types, elements);
- this.dependencyRequestFactory = new DependencyRequest.Factory(elements, keyFactory);
- }
-
- private List<? extends VariableElement> sampleProviderParameters() {
- TypeElement moduleElement =
- elements.getTypeElement(ProvidesMethodModule.class.getCanonicalName());
- ExecutableElement providesMethod =
- Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
- return providesMethod.getParameters();
- }
-
- private List<? extends VariableElement> sampleProducerParameters() {
- TypeElement moduleElement =
- elements.getTypeElement(ProducesMethodModule.class.getCanonicalName());
- ExecutableElement producesMethod =
- Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
- return producesMethod.getParameters();
- }
-
- private DependencyRequest dependencyRequestForInstance() {
- return dependencyRequestFactory.forRequiredVariable(sampleProviderParameters().get(0));
- }
-
- private DependencyRequest dependencyRequestForLazy() {
- return dependencyRequestFactory.forRequiredVariable(sampleProviderParameters().get(1));
- }
-
- private DependencyRequest dependencyRequestForProvider() {
- return dependencyRequestFactory.forRequiredVariable(sampleProviderParameters().get(2));
- }
-
- private DependencyRequest dependencyRequestForMembersInjector() {
- return dependencyRequestFactory.forRequiredVariable(sampleProviderParameters().get(3));
- }
-
- private DependencyRequest dependencyRequestForProducer() {
- return dependencyRequestFactory.forRequiredVariable(sampleProducerParameters().get(0));
- }
-
- private DependencyRequest dependencyRequestForProduced() {
- return dependencyRequestFactory.forRequiredVariable(sampleProducerParameters().get(1));
- }
-
- @Test public void forProvider() {
- DependencyRequestMapper mapper = DependencyRequestMapper.FOR_PROVIDER;
- assertThat(mapper.getFrameworkClass(dependencyRequestForInstance()))
- .isEqualTo(Provider.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForLazy()))
- .isEqualTo(Provider.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForProvider()))
- .isEqualTo(Provider.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForMembersInjector()))
- .isEqualTo(MembersInjector.class);
- }
-
- @Test public void forProducer() {
- DependencyRequestMapper mapper = DependencyRequestMapper.FOR_PRODUCER;
- assertThat(mapper.getFrameworkClass(dependencyRequestForInstance()))
- .isEqualTo(Producer.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForLazy()))
- .isEqualTo(Provider.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForProvider()))
- .isEqualTo(Provider.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForMembersInjector()))
- .isEqualTo(MembersInjector.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForProducer()))
- .isEqualTo(Producer.class);
- assertThat(mapper.getFrameworkClass(dependencyRequestForProduced()))
- .isEqualTo(Producer.class);
- }
-
- @Module
- static final class ProvidesMethodModule {
- @Provides String provideString(
- Integer a, Lazy<Integer> b, Provider<Integer> c, MembersInjector<Y> d) {
- return null;
- }
- }
-
- @ProducerModule
- static final class ProducesMethodModule {
- @Produces String produceString(Producer<Integer> a, Produced<Integer> b) {
- return null;
- }
- }
-
- static final class Y {}
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ErrorMessagesTest.java b/compiler/src/test/java/dagger/internal/codegen/ErrorMessagesTest.java
deleted file mode 100644
index 141d5c4..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ErrorMessagesTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class ErrorMessagesTest {
- @Test public void stripCommonTypePrefixes() {
- String typeName = "com.google.common.collect.ImmutableList<java.lang.Boolean>";
- assertThat(ErrorMessages.stripCommonTypePrefixes(typeName)).isEqualTo("ImmutableList<Boolean>");
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/GraphValidationScopingTest.java b/compiler/src/test/java/dagger/internal/codegen/GraphValidationScopingTest.java
deleted file mode 100644
index e207fe0..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/GraphValidationScopingTest.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assert_;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static java.util.Arrays.asList;
-
-@RunWith(JUnit4.class)
-public class GraphValidationScopingTest {
- @Test public void componentWithoutScopeIncludesScopedBindings_Fail() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.MyComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Component(modules = ScopedModule.class)",
- "interface MyComponent {",
- " ScopedType string();",
- "}");
- JavaFileObject typeFile = JavaFileObjects.forSourceLines("test.ScopedType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "class ScopedType {",
- " @Inject ScopedType(String s, long l, float f) {}",
- "}");
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ScopedModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Singleton;",
- "",
- "@Module",
- "class ScopedModule {",
- " @Provides @Singleton String string() { return \"a string\"; }",
- " @Provides long integer() { return 0L; }",
- " @Provides float floatingPoint() { return 0.0f; }",
- "}");
- String errorMessage = "test.MyComponent (unscoped) may not reference scoped bindings:\n"
- + " @Provides @Singleton String test.ScopedModule.string()\n"
- + " @Singleton class test.ScopedType";
- assert_().about(javaSources()).that(asList(componentFile, typeFile, moduleFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage);
- }
-
- @Test public void componentWithScopeIncludesIncompatiblyScopedBindings_Fail() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.MyComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "@Component(modules = ScopedModule.class)",
- "interface MyComponent {",
- " ScopedType string();",
- "}");
- JavaFileObject scopeFile = JavaFileObjects.forSourceLines("test.PerTest",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope",
- "@interface PerTest {}");
- JavaFileObject typeFile = JavaFileObjects.forSourceLines("test.ScopedType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "@PerTest", // incompatible scope
- "class ScopedType {",
- " @Inject ScopedType(String s, long l, float f) {}",
- "}");
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ScopedModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Singleton;",
- "",
- "@Module",
- "class ScopedModule {",
- " @Provides @PerTest String string() { return \"a string\"; }", // incompatible scope
- " @Provides long integer() { return 0L; }", // unscoped - valid
- " @Provides @Singleton float floatingPoint() { return 0.0f; }", // same scope - valid
- "}");
- String errorMessage = "test.MyComponent scoped with @Singleton "
- + "may not reference bindings with different scopes:\n"
- + " @Provides @test.PerTest String test.ScopedModule.string()\n"
- + " @test.PerTest class test.ScopedType";
- assert_().about(javaSources()).that(asList(componentFile, scopeFile, typeFile, moduleFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage);
- }
-
- @Test public void componentWithScopeMayDependOnOnlyOneScopedComponent() {
- // If a scoped component will have dependencies, they must only include, at most, a single
- // scoped component
- JavaFileObject type = JavaFileObjects.forSourceLines("test.SimpleType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class SimpleType {",
- " @Inject SimpleType() {}",
- " static class A { @Inject A() {} }",
- " static class B { @Inject B() {} }",
- "}");
- JavaFileObject simpleScope = JavaFileObjects.forSourceLines("test.SimpleScope",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope @interface SimpleScope {}");
- JavaFileObject singletonScopedA = JavaFileObjects.forSourceLines("test.SingletonComponentA",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "@Component",
- "interface SingletonComponentA {",
- " SimpleType.A type();",
- "}");
- JavaFileObject singletonScopedB = JavaFileObjects.forSourceLines("test.SingletonComponentB",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "@Component",
- "interface SingletonComponentB {",
- " SimpleType.B type();",
- "}");
- JavaFileObject scopeless = JavaFileObjects.forSourceLines("test.ScopelessComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface ScopelessComponent {",
- " SimpleType type();",
- "}");
- JavaFileObject simpleScoped = JavaFileObjects.forSourceLines("test.SimpleScopedComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@SimpleScope",
- "@Component(dependencies = {SingletonComponentA.class, SingletonComponentB.class})",
- "interface SimpleScopedComponent {",
- " SimpleType.A type();",
- "}");
- String errorMessage =
- "@test.SimpleScope test.SimpleScopedComponent depends on more than one scoped component:\n"
- + " @Singleton test.SingletonComponentA\n"
- + " @Singleton test.SingletonComponentB";
- assert_().about(javaSources())
- .that(
- asList(type, simpleScope, simpleScoped, singletonScopedA, singletonScopedB, scopeless))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage);
- }
-
- @Test public void componentWithoutScopeCannotDependOnScopedComponent() {
- JavaFileObject type = JavaFileObjects.forSourceLines("test.SimpleType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class SimpleType {",
- " @Inject SimpleType() {}",
- "}");
- JavaFileObject scopedComponent = JavaFileObjects.forSourceLines("test.ScopedComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "@Component",
- "interface ScopedComponent {",
- " SimpleType type();",
- "}");
- JavaFileObject unscopedComponent = JavaFileObjects.forSourceLines("test.UnscopedComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Component(dependencies = ScopedComponent.class)",
- "interface UnscopedComponent {",
- " SimpleType type();",
- "}");
- String errorMessage =
- "test.UnscopedComponent (unscoped) cannot depend on scoped components:\n"
- + " @Singleton test.ScopedComponent";
- assert_().about(javaSources())
- .that(asList(type, scopedComponent, unscopedComponent))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage);
- }
-
- @Test public void componentWithSingletonScopeMayNotDependOnOtherScope() {
- // Singleton must be the widest lifetime of present scopes.
- JavaFileObject type = JavaFileObjects.forSourceLines("test.SimpleType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class SimpleType {",
- " @Inject SimpleType() {}",
- "}");
- JavaFileObject simpleScope = JavaFileObjects.forSourceLines("test.SimpleScope",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope @interface SimpleScope {}");
- JavaFileObject simpleScoped = JavaFileObjects.forSourceLines("test.SimpleScopedComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@SimpleScope",
- "@Component",
- "interface SimpleScopedComponent {",
- " SimpleType type();",
- "}");
- JavaFileObject singletonScoped = JavaFileObjects.forSourceLines("test.SingletonComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Singleton",
- "@Component(dependencies = SimpleScopedComponent.class)",
- "interface SingletonComponent {",
- " SimpleType type();",
- "}");
- String errorMessage =
- "This @Singleton component cannot depend on scoped components:\n"
- + " @test.SimpleScope test.SimpleScopedComponent";
- assert_().about(javaSources())
- .that(asList(type, simpleScope, simpleScoped, singletonScoped))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage);
- }
-
- @Test public void componentScopeAncestryMustNotCycle() {
- // The dependency relationship of components is necessarily from shorter lifetimes to
- // longer lifetimes. The scoping annotations must reflect this, and so one cannot declare
- // scopes on components such that they cycle.
- JavaFileObject type = JavaFileObjects.forSourceLines("test.SimpleType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class SimpleType {",
- " @Inject SimpleType() {}",
- "}");
- JavaFileObject scopeA = JavaFileObjects.forSourceLines("test.ScopeA",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope @interface ScopeA {}");
- JavaFileObject scopeB = JavaFileObjects.forSourceLines("test.ScopeB",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope @interface ScopeB {}");
- JavaFileObject longLifetime = JavaFileObjects.forSourceLines("test.ComponentLong",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@ScopeA",
- "@Component",
- "interface ComponentLong {",
- " SimpleType type();",
- "}");
- JavaFileObject mediumLifetime = JavaFileObjects.forSourceLines("test.ComponentMedium",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@ScopeB",
- "@Component(dependencies = ComponentLong.class)",
- "interface ComponentMedium {",
- " SimpleType type();",
- "}");
- JavaFileObject shortLifetime = JavaFileObjects.forSourceLines("test.ComponentShort",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@ScopeA",
- "@Component(dependencies = ComponentMedium.class)",
- "interface ComponentShort {",
- " SimpleType type();",
- "}");
- String errorMessage =
- "test.ComponentShort depends on scoped components in a non-hierarchical scope ordering:\n"
- + " @test.ScopeA test.ComponentLong\n"
- + " @test.ScopeB test.ComponentMedium\n"
- + " @test.ScopeA test.ComponentShort";
- assert_().about(javaSources())
- .that(asList(type, scopeA, scopeB, longLifetime, mediumLifetime, shortLifetime))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java
deleted file mode 100644
index 6001ea7..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java
+++ /dev/null
@@ -1,1143 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import java.util.Arrays;
-import javax.tools.JavaFileObject;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static dagger.internal.codegen.ErrorMessages.nullableToNonNullable;
-
-@RunWith(JUnit4.class)
-public class GraphValidationTest {
- private final JavaFileObject NULLABLE = JavaFileObjects.forSourceLines("test.Nullable",
- "package test;",
- "public @interface Nullable {}");
-
- @Test public void componentOnConcreteClass() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.MyComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface MyComponent {",
- " Foo getFoo();",
- "}");
- JavaFileObject injectable = JavaFileObjects.forSourceLines("test.Foo",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class Foo {",
- " @Inject Foo(Bar bar) {}",
- "}");
- JavaFileObject nonInjectable = JavaFileObjects.forSourceLines("test.Bar",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "interface Bar {}");
- assertAbout(javaSources()).that(Arrays.asList(component, injectable, nonInjectable))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("test.Bar cannot be provided without an @Provides-annotated method.")
- .in(component).onLine(7);
- }
-
- @Test public void componentProvisionWithNoDependencyChain() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Qualifier;",
- "",
- "final class TestClass {",
- " @Qualifier @interface Q {}",
- " interface A {}",
- "",
- " @Component()",
- " interface AComponent {",
- " A getA();",
- " @Q A qualifiedA();",
- " }",
- "}");
- assertAbout(javaSource())
- .that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- "test.TestClass.A cannot be provided without an @Provides-annotated method.")
- .in(component)
- .onLine(12)
- .and()
- .withErrorContaining(
- "@test.TestClass.Q test.TestClass.A "
- + "cannot be provided without an @Provides-annotated method.")
- .in(component)
- .onLine(13);
- }
-
- @Test public void constructorInjectionWithoutAnnotation() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "final class TestClass {",
- " static class A {",
- " A() {}",
- " }",
- "",
- " @Component()",
- " interface AComponent {",
- " A getA();",
- " }",
- "}");
- String expectedError = "test.TestClass.A cannot be provided without an "
- + "@Inject constructor or from an @Provides-annotated method.";
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(15);
- }
-
- @Test public void membersInjectWithoutProvision() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "final class TestClass {",
- " static class A {",
- " @Inject A() {}",
- " }",
- "",
- " static class B {",
- " @Inject A a;",
- " }",
- "",
- " @Component()",
- " interface AComponent {",
- " B getB();",
- " }",
- "}");
- String expectedError = "test.TestClass.B cannot be provided without an "
- + "@Inject constructor or from an @Provides-annotated method. "
- + "This type supports members injection but cannot be implicitly provided.";
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(19);
- }
-
- @Test public void cyclicDependency() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "final class Outer {",
- " static class A {",
- " @Inject A(C cParam) {}",
- " }",
- "",
- " static class B {",
- " @Inject B(A aParam) {}",
- " }",
- "",
- " static class C {",
- " @Inject C(B bParam) {}",
- " }",
- "",
- " @Component()",
- " interface CComponent {",
- " C getC();",
- " }",
- "}");
-
- String expectedError = "test.Outer.CComponent.getC() contains a dependency cycle:\n"
- + " test.Outer.C.<init>(test.Outer.B bParam)\n"
- + " [parameter: test.Outer.B bParam]\n"
- + " test.Outer.B.<init>(test.Outer.A aParam)\n"
- + " [parameter: test.Outer.A aParam]\n"
- + " test.Outer.A.<init>(test.Outer.C cParam)\n"
- + " [parameter: test.Outer.C cParam]";
-
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(23);
- }
-
- @Test public void cyclicDependencyNotIncludingEntryPoint() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "final class Outer {",
- " static class A {",
- " @Inject A(C cParam) {}",
- " }",
- "",
- " static class B {",
- " @Inject B(A aParam) {}",
- " }",
- "",
- " static class C {",
- " @Inject C(B bParam) {}",
- " }",
- "",
- " static class D {",
- " @Inject D(C cParam) {}",
- " }",
- "",
- " @Component()",
- " interface DComponent {",
- " D getD();",
- " }",
- "}");
-
- String expectedError = "test.Outer.DComponent.getD() contains a dependency cycle:\n"
- + " test.Outer.D.<init>(test.Outer.C cParam)\n"
- + " [parameter: test.Outer.C cParam]\n"
- + " test.Outer.C.<init>(test.Outer.B bParam)\n"
- + " [parameter: test.Outer.B bParam]\n"
- + " test.Outer.B.<init>(test.Outer.A aParam)\n"
- + " [parameter: test.Outer.A aParam]\n"
- + " test.Outer.A.<init>(test.Outer.C cParam)\n"
- + " [parameter: test.Outer.C cParam]";
-
- assertAbout(javaSource())
- .that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError)
- .in(component)
- .onLine(27);
- }
-
- @Test
- public void cyclicDependencyNotBrokenByMapBinding() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.MapKey;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.Map;",
- "import javax.inject.Inject;",
- "",
- "final class Outer {",
- " static class A {",
- " @Inject A(Map<String, C> cMap) {}",
- " }",
- "",
- " static class B {",
- " @Inject B(A aParam) {}",
- " }",
- "",
- " static class C {",
- " @Inject C(B bParam) {}",
- " }",
- "",
- " @Component(modules = CModule.class)",
- " interface CComponent {",
- " C getC();",
- " }",
- "",
- " @Module",
- " static class CModule {",
- " @Provides(type = Provides.Type.MAP)",
- " @StringKey(\"C\")",
- " static C c(C c) {",
- " return c;",
- " }",
- " }",
- "",
- " @MapKey",
- " @interface StringKey {",
- " String value();",
- " }",
- "}");
-
- String expectedError =
- Joiner.on('\n')
- .join(
- "test.Outer.CComponent.getC() contains a dependency cycle:",
- " test.Outer.C.<init>(test.Outer.B bParam)",
- " [parameter: test.Outer.B bParam]",
- " test.Outer.B.<init>(test.Outer.A aParam)",
- " [parameter: test.Outer.A aParam]",
- " test.Outer.A.<init>(java.util.Map<java.lang.String,test.Outer.C> cMap)",
- " [parameter: java.util.Map<java.lang.String,test.Outer.C> cMap]",
- " test.Outer.A.<init>(java.util.Map<java.lang.String,test.Outer.C> cMap)",
- " [parameter: java.util.Map<java.lang.String,test.Outer.C> cMap]",
- " test.Outer.CModule.c(test.Outer.C c)",
- " [parameter: test.Outer.C c]");
-
- assertAbout(javaSource())
- .that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError)
- .in(component)
- .onLine(25);
- }
-
- @Test
- public void falsePositiveCyclicDependencyIndirectionDetected() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class Outer {",
- " static class A {",
- " @Inject A(C cParam) {}",
- " }",
- "",
- " static class B {",
- " @Inject B(A aParam) {}",
- " }",
- "",
- " static class C {",
- " @Inject C(B bParam) {}",
- " }",
- "",
- " static class D {",
- " @Inject D(Provider<C> cParam) {}",
- " }",
- "",
- " @Component()",
- " interface DComponent {",
- " D getD();",
- " }",
- "}");
-
- String expectedError =
- "test.Outer.DComponent.getD() contains a dependency cycle:\n"
- + " test.Outer.D.<init>(javax.inject.Provider<test.Outer.C> cParam)\n"
- + " [parameter: javax.inject.Provider<test.Outer.C> cParam]\n"
- + " test.Outer.C.<init>(test.Outer.B bParam)\n"
- + " [parameter: test.Outer.B bParam]\n"
- + " test.Outer.B.<init>(test.Outer.A aParam)\n"
- + " [parameter: test.Outer.A aParam]\n"
- + " test.Outer.A.<init>(test.Outer.C cParam)\n"
- + " [parameter: test.Outer.C cParam]";
-
- assertAbout(javaSource())
- .that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError)
- .in(component)
- .onLine(28);
- }
-
- @Ignore @Test public void cyclicDependencySimpleProviderIndirectionWarning() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class Outer {",
- " static class A {",
- " @Inject A(B bParam) {}",
- " }",
- "",
- " static class B {",
- " @Inject B(C bParam, D dParam) {}",
- " }",
- "",
- " static class C {",
- " @Inject C(Provider<A> aParam) {}",
- " }",
- "",
- " static class D {",
- " @Inject D() {}",
- " }",
- "",
- " @Component()",
- " interface CComponent {",
- " C get();",
- " }",
- "}");
-
- /* String expectedWarning =
- "test.Outer.CComponent.get() contains a dependency cycle:"
- + " test.Outer.C.<init>(javax.inject.Provider<test.Outer.A> aParam)"
- + " [parameter: javax.inject.Provider<test.Outer.A> aParam]"
- + " test.Outer.A.<init>(test.Outer.B bParam)"
- + " [parameter: test.Outer.B bParam]"
- + " test.Outer.B.<init>(test.Outer.C bParam, test.Outer.D dParam)"
- + " [parameter: test.Outer.C bParam]";
- */
- assertAbout(javaSource()) // TODO(cgruber): Implement warning checks.
- .that(component)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- //.withWarningContaining(expectedWarning).in(component).onLine(X);
- }
-
- @Ignore @Test public void cyclicDependencySimpleProviderIndirectionWarningSuppressed() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class Outer {",
- " static class A {",
- " @Inject A(B bParam) {}",
- " }",
- "",
- " static class B {",
- " @Inject B(C bParam, D dParam) {}",
- " }",
- "",
- " static class C {",
- " @Inject C(Provider<A> aParam) {}",
- " }",
- "",
- " static class D {",
- " @Inject D() {}",
- " }",
- "",
- " @SuppressWarnings(\"dependency-cycle\")",
- " @Component()",
- " interface CComponent {",
- " C get();",
- " }",
- "}");
-
- assertAbout(javaSource())
- .that(component)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- //.compilesWithoutWarning(); //TODO(cgruber)
- }
-
- @Test public void duplicateExplicitBindings_ProvidesAndComponentProvision() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "final class Outer {",
- " interface A {}",
- "",
- " interface B {}",
- "",
- " @Module",
- " static class AModule {",
- " @Provides String provideString() { return \"\"; }",
- " @Provides A provideA(String s) { return new A() {}; }",
- " }",
- "",
- " @Component(modules = AModule.class)",
- " interface Parent {",
- " A getA();",
- " }",
- "",
- " @Module",
- " static class BModule {",
- " @Provides B provideB(A a) { return new B() {}; }",
- " }",
- "",
- " @Component(dependencies = Parent.class, modules = { BModule.class, AModule.class})",
- " interface Child {",
- " B getB();",
- " }",
- "}");
-
- String expectedError = "test.Outer.A is bound multiple times:\n"
- + " test.Outer.A test.Outer.Parent.getA()\n"
- + " @Provides test.Outer.A test.Outer.AModule.provideA(String)";
-
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(30);
- }
-
- @Test public void duplicateExplicitBindings_TwoProvidesMethods() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "final class Outer {",
- " interface A {}",
- "",
- " @Module",
- " static class Module1 {",
- " @Provides A provideA1() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module2 {",
- " @Provides String provideString() { return \"\"; }",
- " @Provides A provideA2(String s) { return new A() {}; }",
- " }",
- "",
- " @Component(modules = { Module1.class, Module2.class})",
- " interface TestComponent {",
- " A getA();",
- " }",
- "}");
-
- String expectedError = "test.Outer.A is bound multiple times:\n"
- + " @Provides test.Outer.A test.Outer.Module1.provideA1()\n"
- + " @Provides test.Outer.A test.Outer.Module2.provideA2(String)";
-
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(24);
- }
-
- @Test public void duplicateExplicitBindings_MultipleProvisionTypes() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.MapKey;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.MapKey;",
- "import java.util.HashMap;",
- "import java.util.HashSet;",
- "import java.util.Map;",
- "import java.util.Set;",
- "",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "import static dagger.Provides.Type.MAP;",
- "import static dagger.Provides.Type.SET;",
- "",
- "final class Outer {",
- " @MapKey(unwrapValue = true)",
- " @interface StringKey {",
- " String value();",
- " }",
- "",
- " @Module",
- " static class TestModule1 {",
- " @Provides(type = MAP)",
- " @StringKey(\"foo\")",
- " String stringMapEntry() { return \"\"; }",
- "",
- " @Provides(type = SET) String stringSetElement() { return \"\"; }",
- " }",
- "",
- " @Module",
- " static class TestModule2 {",
- " @Provides Set<String> stringSet() { return new HashSet<String>(); }",
- "",
- " @Provides Map<String, String> stringMap() {",
- " return new HashMap<String, String>();",
- " }",
- " }",
- "",
- " @Component(modules = { TestModule1.class, TestModule2.class })",
- " interface TestComponent {",
- " Set<String> getStringSet();",
- " Map<String, String> getStringMap();",
- " }",
- "}");
-
- String expectedSetError =
- "java.util.Set<java.lang.String> has incompatible bindings:\n"
- + " Set bindings:\n"
- + " @Provides(type=SET) String test.Outer.TestModule1.stringSetElement()\n"
- + " Unique bindings:\n"
- + " @Provides Set<String> test.Outer.TestModule2.stringSet()";
-
- String expectedMapError =
- "java.util.Map<java.lang.String,java.lang.String> has incompatible bindings:\n"
- + " Map bindings:\n"
- + " @Provides(type=MAP) @test.Outer.StringKey(\"foo\") String"
- + " test.Outer.TestModule1.stringMapEntry()\n"
- + " Unique bindings:\n"
- + " @Provides Map<String,String> test.Outer.TestModule2.stringMap()";
-
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedSetError).in(component).onLine(43)
- .and().withErrorContaining(expectedMapError).in(component).onLine(44);
- }
-
- @Test public void duplicateBindings_TruncateAfterLimit() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "final class Outer {",
- " interface A {}",
- "",
- " @Module",
- " static class Module1 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module2 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module3 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module4 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module5 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module6 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module7 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module8 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module9 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module10 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module11 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Module",
- " static class Module12 {",
- " @Provides A provideA() { return new A() {}; }",
- " }",
- "",
- " @Component(modules = {",
- " Module1.class,",
- " Module2.class,",
- " Module3.class,",
- " Module4.class,",
- " Module5.class,",
- " Module6.class,",
- " Module7.class,",
- " Module8.class,",
- " Module9.class,",
- " Module10.class,",
- " Module11.class,",
- " Module12.class",
- " })",
- " interface TestComponent {",
- " A getA();",
- " }",
- "}");
-
- String expectedError = "test.Outer.A is bound multiple times:\n"
- + " @Provides test.Outer.A test.Outer.Module1.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module2.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module3.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module4.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module5.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module6.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module7.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module8.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module9.provideA()\n"
- + " @Provides test.Outer.A test.Outer.Module10.provideA()\n"
- + " and 2 others";
-
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(86);
- }
-
- @Test public void longChainOfDependencies() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import dagger.Component;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "final class TestClass {",
- " interface A {}",
- "",
- " static class B {",
- " @Inject B(A a) {}",
- " }",
- "",
- " static class C {",
- " @Inject B b;",
- " @Inject C(B b) {}",
- " }",
- "",
- " interface D { }",
- "",
- " static class DImpl implements D {",
- " @Inject DImpl(C c, B b) {}",
- " }",
- "",
- " @Module",
- " static class DModule {",
- " @Provides D d(DImpl impl) { return impl; }",
- " }",
- "",
- " @Component(modules = { DModule.class })",
- " interface AComponent {",
- " D getFoo();",
- " C injectC(C c);",
- " }",
- "}");
- String errorText =
- "test.TestClass.A cannot be provided without an @Provides-annotated method.\n";
- String firstError = errorText
- + " test.TestClass.DModule.d(test.TestClass.DImpl impl)\n"
- + " [parameter: test.TestClass.DImpl impl]\n"
- + " test.TestClass.DImpl.<init>(test.TestClass.C c, test.TestClass.B b)\n"
- + " [parameter: test.TestClass.C c]\n"
- + " test.TestClass.C.b\n"
- + " [injected field of type: test.TestClass.B b]\n"
- + " test.TestClass.B.<init>(test.TestClass.A a)\n"
- + " [parameter: test.TestClass.A a]";
- String secondError = errorText
- + " test.TestClass.C.b\n"
- + " [injected field of type: test.TestClass.B b]\n"
- + " test.TestClass.B.<init>(test.TestClass.A a)\n"
- + " [parameter: test.TestClass.A a]";
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(firstError).in(component).onLine(33)
- .and().withErrorContaining(secondError).in(component).onLine(34);
- }
-
- @Test public void resolvedParametersInDependencyTrace() {
- JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class Generic<T> {",
- " @Inject Generic(T t) {}",
- "}");
- JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import java.util.List;",
- "",
- "final class TestClass {",
- " @Inject TestClass(List list) {}",
- "}");
- JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class UsesTest {",
- " @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " UsesTest usesTest();",
- "}");
- String expectedMsg = Joiner.on("\n").join(
- "java.util.List cannot be provided without an @Provides-annotated method.",
- " test.UsesTest.<init>(test.Generic<test.TestClass> genericTestClass)",
- " [parameter: test.Generic<test.TestClass> genericTestClass]",
- " test.Generic.<init>(test.TestClass t)",
- " [parameter: test.TestClass t]",
- " test.TestClass.<init>(java.util.List list)",
- " [parameter: java.util.List list]");
- assertAbout(javaSources()).that(ImmutableList.of(generic, testClass, usesTest, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedMsg);
- }
-
- @Test public void resolvedVariablesInDependencyTrace() {
- JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class Generic<T> {",
- " @Inject T t;",
- " @Inject Generic() {}",
- "}");
- JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import java.util.List;",
- "",
- "final class TestClass {",
- " @Inject TestClass(List list) {}",
- "}");
- JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class UsesTest {",
- " @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " UsesTest usesTest();",
- "}");
- String expectedMsg = Joiner.on("\n").join(
- "java.util.List cannot be provided without an @Provides-annotated method.",
- " test.UsesTest.<init>(test.Generic<test.TestClass> genericTestClass)",
- " [parameter: test.Generic<test.TestClass> genericTestClass]",
- " test.Generic.t",
- " [injected field of type: test.TestClass t]",
- " test.TestClass.<init>(java.util.List list)",
- " [parameter: java.util.List list]");
- assertAbout(javaSources()).that(ImmutableList.of(generic, testClass, usesTest, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedMsg);
- }
-
- @Test public void nullCheckForConstructorParameters() {
- JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A(String string) {}",
- "}");
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
- .withCompilerOptions("-Adagger.nullableValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test public void nullCheckForMembersInjectParam() {
- JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A() {}",
- " @Inject void register(String string) {}",
- "}");
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
- .withCompilerOptions("-Adagger.nullableValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test public void nullCheckForVariable() {
- JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject String string;",
- " @Inject A() {}",
- "}");
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
- .withCompilerOptions("-Adagger.nullableValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test public void nullCheckForComponentReturn() {
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " String string();",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, module, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, module, component))
- .withCompilerOptions("-Adagger.nullableValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test public void componentDependencyMustNotCycle_Direct() {
- JavaFileObject shortLifetime = JavaFileObjects.forSourceLines("test.ComponentShort",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(dependencies = ComponentShort.class)",
- "interface ComponentShort {",
- "}");
- String errorMessage =
- "test.ComponentShort contains a cycle in its component dependencies:\n"
- + " test.ComponentShort";
- assertAbout(javaSource())
- .that(shortLifetime)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage);
- }
-
- @Test public void componentDependencyMustNotCycle_Indirect() {
- JavaFileObject longLifetime = JavaFileObjects.forSourceLines("test.ComponentLong",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(dependencies = ComponentMedium.class)",
- "interface ComponentLong {",
- "}");
- JavaFileObject mediumLifetime = JavaFileObjects.forSourceLines("test.ComponentMedium",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(dependencies = ComponentLong.class)",
- "interface ComponentMedium {",
- "}");
- JavaFileObject shortLifetime = JavaFileObjects.forSourceLines("test.ComponentShort",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(dependencies = ComponentMedium.class)",
- "interface ComponentShort {",
- "}");
- String longErrorMessage =
- "test.ComponentLong contains a cycle in its component dependencies:\n"
- + " test.ComponentLong\n"
- + " test.ComponentMedium\n"
- + " test.ComponentLong";
- String mediumErrorMessage =
- "test.ComponentMedium contains a cycle in its component dependencies:\n"
- + " test.ComponentMedium\n"
- + " test.ComponentLong\n"
- + " test.ComponentMedium";
- String shortErrorMessage =
- "test.ComponentShort contains a cycle in its component dependencies:\n"
- + " test.ComponentMedium\n"
- + " test.ComponentLong\n"
- + " test.ComponentMedium\n"
- + " test.ComponentShort";
- assertAbout(javaSources())
- .that(ImmutableList.of(longLifetime, mediumLifetime, shortLifetime))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(longErrorMessage).in(longLifetime)
- .and()
- .withErrorContaining(mediumErrorMessage).in(mediumLifetime)
- .and()
- .withErrorContaining(shortErrorMessage).in(shortLifetime);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/InaccessibleTypeTest.java b/compiler/src/test/java/dagger/internal/codegen/InaccessibleTypeTest.java
deleted file mode 100644
index 6355922..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/InaccessibleTypeTest.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-@RunWith(JUnit4.class)
-public class InaccessibleTypeTest {
- @Test public void basicInjectedType() {
- JavaFileObject noDepClassFile = JavaFileObjects.forSourceLines("foreign.NoDepClass",
- "package foreign;",
- "",
- "import javax.inject.Inject;",
- "",
- "public final class NoDepClass {",
- " @Inject NoDepClass() {}",
- "}");
- JavaFileObject publicClassFile = JavaFileObjects.forSourceLines("foreign.PublicClass",
- "package foreign;",
- "",
- "import javax.inject.Inject;",
- "",
- "public final class PublicClass {",
- " @Inject PublicClass(NonPublicClass1 dep1, NonPublicClass2 dep2, NoDepClass dep3) {}",
- "}");
- JavaFileObject nonPublicClass1File = JavaFileObjects.forSourceLines("foreign.NonPublicClass1",
- "package foreign;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class NonPublicClass1 {",
- " @Inject NonPublicClass1(NoDepClass dep) {}",
- "}");
- JavaFileObject nonPublicClass2File = JavaFileObjects.forSourceLines("foreign.NonPublicClass2",
- "package foreign;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class NonPublicClass2 {",
- " @Inject NonPublicClass2(NoDepClass dep) {}",
- "}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import foreign.PublicClass;",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface TestComponent {",
- " PublicClass publicClass();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import foreign.NoDepClass_Factory;",
- "import foreign.NonPublicClass1_Factory;",
- "import foreign.NonPublicClass2_Factory;",
- "import foreign.PublicClass;",
- "import foreign.PublicClass_Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " @SuppressWarnings(\"rawtypes\")",
- " private Provider nonPublicClass1Provider;",
- " @SuppressWarnings(\"rawtypes\")",
- " private Provider nonPublicClass2Provider;",
- " private Provider<PublicClass> publicClassProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.nonPublicClass1Provider =",
- " NonPublicClass1_Factory.create(NoDepClass_Factory.create());",
- " this.nonPublicClass2Provider =",
- " NonPublicClass2_Factory.create(NoDepClass_Factory.create());",
- " this.publicClassProvider = PublicClass_Factory.create(",
- " nonPublicClass1Provider,",
- " nonPublicClass2Provider,",
- " NoDepClass_Factory.create());",
- " }",
- "",
- " @Override",
- " public PublicClass publicClass() {",
- " return publicClassProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " return new DaggerTestComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(
- noDepClassFile,
- publicClassFile,
- nonPublicClass1File,
- nonPublicClass2File,
- componentFile))
- .withCompilerOptions("-Xlint:rawtypes", "-Xlint:unchecked", "-Werror")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test public void memberInjectedType() {
- JavaFileObject noDepClassFile = JavaFileObjects.forSourceLines("test.NoDepClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "public final class NoDepClass {",
- " @Inject NoDepClass() {}",
- "}");
- JavaFileObject aClassFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import foreign.B;",
- "import javax.inject.Inject;",
- "",
- "final class A extends B {",
- " @Inject NoDepClass dep;",
- "}");
- JavaFileObject bClassFile = JavaFileObjects.forSourceLines("foreign.B",
- "package foreign;",
- "",
- "import test.NoDepClass;",
- "import javax.inject.Inject;",
- "",
- "public class B extends C {",
- " @Inject NoDepClass dep;",
- "}");
- JavaFileObject cClassFile = JavaFileObjects.forSourceLines("foreign.C",
- "package foreign;",
- "",
- "import test.D;",
- "import test.NoDepClass;",
- "import javax.inject.Inject;",
- "",
- "class C extends D {",
- " @Inject NoDepClass dep;",
- "}");
- JavaFileObject dClassFile = JavaFileObjects.forSourceLines("test.D",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "public class D {",
- " @Inject NoDepClass dep;",
- "}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface TestComponent {",
- " void injectA(A a);",
- "}");
- JavaFileObject generatedComponent =
- JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private MembersInjector<A> aMembersInjector;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.aMembersInjector = A_MembersInjector.create(NoDepClass_Factory.create());",
- " }",
- "",
- " @Override",
- " public void injectA(A a) {",
- " aMembersInjector.injectMembers(a);",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " return new DaggerTestComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(
- noDepClassFile,
- aClassFile,
- bClassFile,
- cClassFile,
- dClassFile,
- componentFile))
- .withCompilerOptions("-Xlint:rawtypes", "-Xlint:unchecked", "-Werror")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java b/compiler/src/test/java/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
deleted file mode 100644
index ca0494e..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
+++ /dev/null
@@ -1,1128 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static dagger.internal.codegen.ErrorMessages.ABSTRACT_INJECT_METHOD;
-import static dagger.internal.codegen.ErrorMessages.FINAL_INJECT_FIELD;
-import static dagger.internal.codegen.ErrorMessages.GENERIC_INJECT_METHOD;
-import static dagger.internal.codegen.ErrorMessages.INJECT_CONSTRUCTOR_ON_ABSTRACT_CLASS;
-import static dagger.internal.codegen.ErrorMessages.INJECT_CONSTRUCTOR_ON_INNER_CLASS;
-import static dagger.internal.codegen.ErrorMessages.INJECT_ON_PRIVATE_CONSTRUCTOR;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_INJECT_CONSTRUCTORS;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_QUALIFIERS;
-import static dagger.internal.codegen.ErrorMessages.MULTIPLE_SCOPES;
-import static dagger.internal.codegen.ErrorMessages.PRIVATE_INJECT_FIELD;
-import static dagger.internal.codegen.ErrorMessages.PRIVATE_INJECT_METHOD;
-import static dagger.internal.codegen.ErrorMessages.QUALIFIER_ON_INJECT_CONSTRUCTOR;
-import static dagger.internal.codegen.ErrorMessages.STATIC_INJECT_FIELD;
-import static dagger.internal.codegen.ErrorMessages.STATIC_INJECT_METHOD;
-
-@RunWith(JUnit4.class)
-// TODO(gak): add tests for generation in the default package.
-public final class InjectConstructorFactoryGeneratorTest {
- private static final JavaFileObject QUALIFIER_A =
- JavaFileObjects.forSourceLines("test.QualifierA",
- "package test;",
- "",
- "import javax.inject.Qualifier;",
- "",
- "@Qualifier @interface QualifierA {}");
- private static final JavaFileObject QUALIFIER_B =
- JavaFileObjects.forSourceLines("test.QualifierB",
- "package test;",
- "",
- "import javax.inject.Qualifier;",
- "",
- "@Qualifier @interface QualifierB {}");
- private static final JavaFileObject SCOPE_A =
- JavaFileObjects.forSourceLines("test.ScopeA",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope @interface ScopeA {}");
- private static final JavaFileObject SCOPE_B =
- JavaFileObjects.forSourceLines("test.ScopeB",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope @interface ScopeB {}");
-
- @Test public void injectOnPrivateConstructor() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateConstructor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class PrivateConstructor {",
- " @Inject private PrivateConstructor() {}",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(INJECT_ON_PRIVATE_CONSTRUCTOR).in(file).onLine(6);
- }
-
- @Test public void injectConstructorOnInnerClass() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class OuterClass {",
- " class InnerClass {",
- " @Inject InnerClass() {}",
- " }",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(INJECT_CONSTRUCTOR_ON_INNER_CLASS).in(file).onLine(7);
- }
-
- @Test public void injectConstructorOnAbstractClass() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.AbstractClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "abstract class AbstractClass {",
- " @Inject AbstractClass() {}",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(INJECT_CONSTRUCTOR_ON_ABSTRACT_CLASS).in(file).onLine(6);
- }
-
- @Test public void injectConstructorOnGenericClass() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class GenericClass<T> {",
- " @Inject GenericClass(T t) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines("test.GenericClass_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class GenericClass_Factory<T> implements Factory<GenericClass<T>> {",
- " private final Provider<T> tProvider;",
- "",
- " public GenericClass_Factory(Provider<T> tProvider) {",
- " assert tProvider != null;",
- " this.tProvider = tProvider;",
- " }",
- "",
- " @Override",
- " public GenericClass<T> get() {",
- " return new GenericClass<T>(tProvider.get());",
- " }",
- "",
- " public static <T> Factory<GenericClass<T>> create(Provider<T> tProvider) {",
- " return new GenericClass_Factory<T>(tProvider);",
- " }",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test public void fieldAndMethodGenerics() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class GenericClass<A, B> {",
- " @Inject A a;",
- "",
- " @Inject GenericClass() {}",
- "",
- " @Inject void register(B b) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines("test.GenericClass_Factory",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class GenericClass_Factory<A, B> implements Factory<GenericClass<A, B>> {",
- " private final MembersInjector<GenericClass<A, B>> membersInjector;",
- "",
- " public GenericClass_Factory(MembersInjector<GenericClass<A, B>> membersInjector) {",
- " assert membersInjector != null;",
- " this.membersInjector = membersInjector;",
- " }",
- "",
- " @Override",
- " public GenericClass<A, B> get() {",
- " GenericClass<A, B> instance = new GenericClass<A, B>();",
- " membersInjector.injectMembers(instance);",
- " return instance;",
- " }",
- "",
- " public static <A, B> Factory<GenericClass<A, B>> create(",
- " MembersInjector<GenericClass<A, B>> membersInjector) {",
- " return new GenericClass_Factory<A, B>(membersInjector);",
- " }",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test public void genericClassWithNoDependencies() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class GenericClass<T> {",
- " @Inject GenericClass() {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines("test.GenericClass_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@SuppressWarnings(\"rawtypes\")",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public enum GenericClass_Factory implements Factory<GenericClass> {",
- " INSTANCE;",
- "",
- " @Override",
- " public GenericClass get() {",
- " return new GenericClass();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " public static <T> Factory<GenericClass<T>> create() {",
- " return (Factory) INSTANCE;",
- " }",
- "",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test public void twoGenericTypes() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class GenericClass<A, B> {",
- " @Inject GenericClass(A a, B b) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines("test.GenericClass_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class GenericClass_Factory<A, B> implements Factory<GenericClass<A, B>> {",
- " private final Provider<A> aProvider;",
- " private final Provider<B> bProvider;",
- "",
- " public GenericClass_Factory(Provider<A> aProvider, Provider<B> bProvider) {",
- " assert aProvider != null;",
- " this.aProvider = aProvider;",
- " assert bProvider != null;",
- " this.bProvider = bProvider;",
- " }",
- "",
- " @Override",
- " public GenericClass<A, B> get() {",
- " return new GenericClass<A, B>(aProvider.get(), bProvider.get());",
- " }",
- "",
- " public static <A, B> Factory<GenericClass<A, B>> create(",
- " Provider<A> aProvider, Provider<B> bProvider) {",
- " return new GenericClass_Factory<A, B>(aProvider, bProvider);",
- " }",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test public void boundedGenerics() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import java.util.List;",
- "",
- "class GenericClass<A extends Number & Comparable<A>,",
- " B extends List<? extends String>,",
- " C extends List<? super String>> {",
- " @Inject GenericClass(A a, B b, C c) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines("test.GenericClass_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import java.util.List;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class GenericClass_Factory<A extends Number & Comparable<A>,",
- " B extends List<? extends String>,",
- " C extends List<? super String>>",
- " implements Factory<GenericClass<A, B, C>> {",
- " private final Provider<A> aProvider;",
- " private final Provider<B> bProvider;",
- " private final Provider<C> cProvider;",
- "",
- " public GenericClass_Factory(Provider<A> aProvider,",
- " Provider<B> bProvider,",
- " Provider<C> cProvider) {",
- " assert aProvider != null;",
- " this.aProvider = aProvider;",
- " assert bProvider != null;",
- " this.bProvider = bProvider;",
- " assert cProvider != null;",
- " this.cProvider = cProvider;",
- " }",
- "",
- " @Override",
- " public GenericClass<A, B, C> get() {",
- " return new GenericClass<A, B, C>(aProvider.get(), bProvider.get(), cProvider.get());",
- " }",
- "",
- " public static <A extends Number & Comparable<A>,",
- " B extends List<? extends String>,",
- " C extends List<? super String>> Factory<GenericClass<A, B, C>> create(",
- " Provider<A> aProvider, Provider<B> bProvider, Provider<C> cProvider) {",
- " return new GenericClass_Factory<A, B, C>(aProvider, bProvider, cProvider);",
- " }",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test public void multipleSameTypesWithGenericsAndQualifiersAndLazies() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "import dagger.Lazy;",
- "",
- "class GenericClass<A, B> {",
- " @Inject GenericClass(A a, A a2, Provider<A> pa, @QualifierA A qa, Lazy<A> la, ",
- " String s, String s2, Provider<String> ps, ",
- " @QualifierA String qs, Lazy<String> ls,",
- " B b, B b2, Provider<B> pb, @QualifierA B qb, Lazy<B> lb) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines("test.GenericClass_Factory",
- "package test;",
- "",
- "import dagger.internal.DoubleCheckLazy;",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class GenericClass_Factory<A, B> implements Factory<GenericClass<A, B>> {",
- " private final Provider<A> aAndA2AndPaAndLaProvider;",
- " private final Provider<A> qaProvider;",
- " private final Provider<String> sAndS2AndPsAndLsProvider;",
- " private final Provider<String> qsProvider;",
- " private final Provider<B> bAndB2AndPbAndLbProvider;",
- " private final Provider<B> qbProvider;",
- "",
- " public GenericClass_Factory(Provider<A> aAndA2AndPaAndLaProvider,",
- " Provider<A> qaProvider,",
- " Provider<String> sAndS2AndPsAndLsProvider,",
- " Provider<String> qsProvider,",
- " Provider<B> bAndB2AndPbAndLbProvider,",
- " Provider<B> qbProvider) {",
- " assert aAndA2AndPaAndLaProvider != null;",
- " this.aAndA2AndPaAndLaProvider = aAndA2AndPaAndLaProvider;",
- " assert qaProvider != null;",
- " this.qaProvider = qaProvider;",
- " assert sAndS2AndPsAndLsProvider != null;",
- " this.sAndS2AndPsAndLsProvider = sAndS2AndPsAndLsProvider;",
- " assert qsProvider != null;",
- " this.qsProvider = qsProvider;",
- " assert bAndB2AndPbAndLbProvider != null;",
- " this.bAndB2AndPbAndLbProvider = bAndB2AndPbAndLbProvider;",
- " assert qbProvider != null;",
- " this.qbProvider = qbProvider;",
- " }",
- "",
- " @Override",
- " public GenericClass<A, B> get() {",
- " return new GenericClass<A, B>(",
- " aAndA2AndPaAndLaProvider.get(),",
- " aAndA2AndPaAndLaProvider.get(),",
- " aAndA2AndPaAndLaProvider,",
- " qaProvider.get(),",
- " DoubleCheckLazy.create(aAndA2AndPaAndLaProvider),",
- " sAndS2AndPsAndLsProvider.get(),",
- " sAndS2AndPsAndLsProvider.get(),",
- " sAndS2AndPsAndLsProvider,",
- " qsProvider.get(),",
- " DoubleCheckLazy.create(sAndS2AndPsAndLsProvider),",
- " bAndB2AndPbAndLbProvider.get(),",
- " bAndB2AndPbAndLbProvider.get(),",
- " bAndB2AndPbAndLbProvider,",
- " qbProvider.get(),",
- " DoubleCheckLazy.create(bAndB2AndPbAndLbProvider));",
- " }",
- "",
- " public static <A, B> Factory<GenericClass<A, B>> create(",
- " Provider<A> aAndA2AndPaAndLaProvider,",
- " Provider<A> qaProvider,",
- " Provider<String> sAndS2AndPsAndLsProvider,",
- " Provider<String> qsProvider,",
- " Provider<B> bAndB2AndPbAndLbProvider,",
- " Provider<B> qbProvider) {",
- " return new GenericClass_Factory<A, B>(",
- " aAndA2AndPaAndLaProvider,",
- " qaProvider,",
- " sAndS2AndPsAndLsProvider,",
- " qsProvider,",
- " bAndB2AndPbAndLbProvider,",
- " qbProvider);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(file, QUALIFIER_A))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test public void multipleInjectConstructors() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.TooManyInjectConstructors",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class TooManyInjectConstructors {",
- " @Inject TooManyInjectConstructors() {}",
- " TooManyInjectConstructors(int i) {}",
- " @Inject TooManyInjectConstructors(String s) {}",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MULTIPLE_INJECT_CONSTRUCTORS).in(file).onLine(6)
- .and().withErrorContaining(MULTIPLE_INJECT_CONSTRUCTORS).in(file).onLine(8);
- }
-
- @Test public void multipleQualifiersOnInjectConstructorParameter() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleQualifierConstructorParam",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class MultipleQualifierConstructorParam {",
- " @Inject MultipleQualifierConstructorParam(@QualifierA @QualifierB String s) {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(file, QUALIFIER_A, QUALIFIER_B))
- .processedWith(new ComponentProcessor()).failsToCompile()
- // for whatever reason, javac only reports the error once on the constructor
- .withErrorContaining(MULTIPLE_QUALIFIERS).in(file).onLine(6);
- }
-
- @Test public void injectConstructorOnClassWithMultipleScopes() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleScopeClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "@ScopeA @ScopeB class MultipleScopeClass {",
- " @Inject MultipleScopeClass() {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(file, SCOPE_A, SCOPE_B))
- .processedWith(new ComponentProcessor()).failsToCompile()
- .withErrorContaining(MULTIPLE_SCOPES).in(file).onLine(5).atColumn(1)
- .and().withErrorContaining(MULTIPLE_SCOPES).in(file).onLine(5).atColumn(9);
- }
-
- @Test public void injectConstructorWithQualifier() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleScopeClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class MultipleScopeClass {",
- " @Inject",
- " @QualifierA",
- " @QualifierB",
- " MultipleScopeClass() {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(file, QUALIFIER_A, QUALIFIER_B))
- .processedWith(new ComponentProcessor()).failsToCompile()
- .withErrorContaining(QUALIFIER_ON_INJECT_CONSTRUCTOR).in(file).onLine(7)
- .and().withErrorContaining(QUALIFIER_ON_INJECT_CONSTRUCTOR).in(file).onLine(8);
- }
-
- @Test public void finalInjectField() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.FinalInjectField",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class FinalInjectField {",
- " @Inject final String s;",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(FINAL_INJECT_FIELD).in(file).onLine(6);
- }
-
- @Test public void privateInjectFieldError() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectField",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class PrivateInjectField {",
- " @Inject private String s;",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRIVATE_INJECT_FIELD).in(file).onLine(6);
- }
-
- @Test public void privateInjectFieldWarning() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectField",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class PrivateInjectField {",
- " @Inject private String s;",
- "}");
- assertAbout(javaSource()).that(file)
- .withCompilerOptions("-Adagger.privateMemberValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError(); // TODO: Verify warning message when supported
- }
-
- @Test public void staticInjectFieldError() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectField",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class StaticInjectField {",
- " @Inject static String s;",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(STATIC_INJECT_FIELD).in(file).onLine(6);
- }
-
- @Test public void staticInjectFieldWarning() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectField",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class StaticInjectField {",
- " @Inject static String s;",
- "}");
- assertAbout(javaSource()).that(file)
- .withCompilerOptions("-Adagger.staticMemberValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError(); // TODO: Verify warning message when supported
- }
-
- @Test public void multipleQualifiersOnField() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleQualifierInjectField",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class MultipleQualifierInjectField {",
- " @Inject @QualifierA @QualifierB String s;",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(file, QUALIFIER_A, QUALIFIER_B))
- .processedWith(new ComponentProcessor()).failsToCompile()
- .withErrorContaining(MULTIPLE_QUALIFIERS).in(file).onLine(6).atColumn(11)
- .and().withErrorContaining(MULTIPLE_QUALIFIERS).in(file).onLine(6).atColumn(23);
- }
-
- @Test public void abstractInjectMethod() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.AbstractInjectMethod",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "abstract class AbstractInjectMethod {",
- " @Inject abstract void method();",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(ABSTRACT_INJECT_METHOD).in(file).onLine(6);
- }
-
- @Test public void privateInjectMethodError() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectMethod",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class PrivateInjectMethod {",
- " @Inject private void method(){}",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRIVATE_INJECT_METHOD).in(file).onLine(6);
- }
-
- @Test public void privateInjectMethodWarning() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectMethod",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class PrivateInjectMethod {",
- " @Inject private void method(){}",
- "}");
- assertAbout(javaSource()).that(file)
- .withCompilerOptions("-Adagger.privateMemberValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError(); // TODO: Verify warning message when supported
- }
-
- @Test public void staticInjectMethodError() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectMethod",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class StaticInjectMethod {",
- " @Inject static void method(){}",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(STATIC_INJECT_METHOD).in(file).onLine(6);
- }
-
- @Test public void staticInjectMethodWarning() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectMethod",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class StaticInjectMethod {",
- " @Inject static void method(){}",
- "}");
- assertAbout(javaSource()).that(file)
- .withCompilerOptions("-Adagger.staticMemberValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError(); // TODO: Verify warning message when supported
- }
-
- @Test public void genericInjectMethod() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericInjectMethod",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class AbstractInjectMethod {",
- " @Inject <T> void method();",
- "}");
- assertAbout(javaSource()).that(file)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(GENERIC_INJECT_METHOD).in(file).onLine(6);
- }
-
- @Test public void multipleQualifiersOnInjectMethodParameter() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleQualifierMethodParam",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class MultipleQualifierMethodParam {",
- " @Inject void method(@QualifierA @QualifierB String s) {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(file, QUALIFIER_A, QUALIFIER_B))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- // for whatever reason, javac only reports the error once on the method
- .withErrorContaining(MULTIPLE_QUALIFIERS).in(file).onLine(6);
- }
-
- @Test public void injectConstructor() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class InjectConstructor {",
- " @Inject InjectConstructor(String s) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.InjectConstructor_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class InjectConstructor_Factory ",
- " implements Factory<InjectConstructor> {",
- "",
- " private final Provider<String> sProvider;",
- "",
- " public InjectConstructor_Factory(Provider<String> sProvider) {",
- " assert sProvider != null;",
- " this.sProvider = sProvider;",
- " }",
- "",
- " @Override public InjectConstructor get() {",
- " return new InjectConstructor(sProvider.get());",
- " }",
- "",
- " public static Factory<InjectConstructor> create(Provider<String> sProvider) {",
- " return new InjectConstructor_Factory(sProvider);",
- " }",
- "}");
- assertAbout(javaSource()).that(file).processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test public void injectConstructorAndMembersInjection() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.AllInjections",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class AllInjections {",
- " @Inject String s;",
- " @Inject AllInjections(String s) {}",
- " @Inject void s(String s) {}",
- "}");
- JavaFileObject expectedFactory = JavaFileObjects.forSourceLines(
- "test.AllInjections_Factory",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class AllInjections_Factory ",
- " implements Factory<AllInjections> {",
- "",
- " private final MembersInjector<AllInjections> membersInjector;",
- " private final Provider<String> sProvider;",
- "",
- " public AllInjections_Factory(MembersInjector<AllInjections> membersInjector, ",
- " Provider<String> sProvider) {",
- " assert membersInjector != null;",
- " this.membersInjector = membersInjector;",
- " assert sProvider != null;",
- " this.sProvider = sProvider;",
- " }",
- "",
- " @Override public AllInjections get() {",
- " AllInjections instance = new AllInjections(sProvider.get());",
- " membersInjector.injectMembers(instance);",
- " return instance;",
- " }",
- "",
- " public static Factory<AllInjections> create(",
- " MembersInjector<AllInjections> membersInjector, ",
- " Provider<String> sProvider) {",
- " return new AllInjections_Factory(membersInjector, sProvider);",
- " }",
- "}");
- assertAbout(javaSource()).that(file).processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expectedFactory);
- }
-
- @Test public void supertypeRequiresMemberInjection() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "class A {}");
- JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class B extends A {",
- " @Inject B() {}",
- "}");
- JavaFileObject expectedFactory = JavaFileObjects.forSourceLines(
- "test.B_Factory",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class B_Factory implements Factory<B> {",
- "",
- " private final MembersInjector<B> membersInjector;",
- "",
- " public B_Factory(MembersInjector<B> membersInjector) {",
- " assert membersInjector != null;",
- " this.membersInjector = membersInjector;",
- " }",
- "",
- " @Override public B get() {",
- " B instance = new B();",
- " membersInjector.injectMembers(instance);",
- " return instance;",
- " }",
- "",
- " public static Factory<B> create(MembersInjector<B> membersInjector) {",
- " return new B_Factory(membersInjector);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(aFile, bFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expectedFactory);
- }
-
- @Test
- public void wildcardDependency() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
- "package test;",
- "",
- "import java.util.List;",
- "import javax.inject.Inject;",
- "",
- "class InjectConstructor {",
- " @Inject InjectConstructor(List<? extends Object> objects) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.InjectConstructor_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import java.util.List;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class InjectConstructor_Factory ",
- " implements Factory<InjectConstructor> {",
- "",
- " private final Provider<List<? extends Object>> objectsProvider;",
- "",
- " public InjectConstructor_Factory(Provider<List<? extends Object>> objectsProvider) {",
- " assert objectsProvider != null;",
- " this.objectsProvider = objectsProvider;",
- " }",
- "",
- " @Override public InjectConstructor get() {",
- " return new InjectConstructor(objectsProvider.get());",
- " }",
- "",
- " public static Factory<InjectConstructor> create(",
- " Provider<List<? extends Object>> objectsProvider) {",
- " return new InjectConstructor_Factory(objectsProvider);",
- " }",
- "}");
- assertAbout(javaSource()).that(file).processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test
- public void basicNameCollision() {
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines("other.pkg.Factory",
- "package other.pkg;",
- "",
- "public class Factory {}");
- JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import other.pkg.Factory;",
- "",
- "class InjectConstructor {",
- " @Inject InjectConstructor(Factory factory) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.InjectConstructor_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class InjectConstructor_Factory ",
- " implements Factory<InjectConstructor> {",
- "",
- " private final Provider<other.pkg.Factory> factoryProvider;",
- "",
- " public InjectConstructor_Factory(Provider<other.pkg.Factory> factoryProvider) {",
- " assert factoryProvider != null;",
- " this.factoryProvider = factoryProvider;",
- " }",
- "",
- " @Override public InjectConstructor get() {",
- " return new InjectConstructor(factoryProvider.get());",
- " }",
- "",
- " public static Factory<InjectConstructor> create(",
- " Provider<other.pkg.Factory> factoryProvider) {",
- " return new InjectConstructor_Factory(factoryProvider);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(factoryFile, file))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test
- public void nestedNameCollision() {
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines("other.pkg.Outer",
- "package other.pkg;",
- "",
- "public class Outer {",
- " public class Factory {}",
- "}");
- JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import other.pkg.Outer;",
- "",
- "class InjectConstructor {",
- " @Inject InjectConstructor(Outer.Factory factory) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.InjectConstructor_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import other.pkg.Outer;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class InjectConstructor_Factory ",
- " implements Factory<InjectConstructor> {",
- "",
- " private final Provider<Outer.Factory> factoryProvider;",
- "",
- " public InjectConstructor_Factory(Provider<Outer.Factory> factoryProvider) {",
- " assert factoryProvider != null;",
- " this.factoryProvider = factoryProvider;",
- " }",
- "",
- " @Override public InjectConstructor get() {",
- " return new InjectConstructor(factoryProvider.get());",
- " }",
- "",
- " public static Factory<InjectConstructor> create(",
- " Provider<Outer.Factory> factoryProvider) {",
- " return new InjectConstructor_Factory(factoryProvider);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(factoryFile, file))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test
- public void samePackageNameCollision() {
- JavaFileObject samePackageInterface = JavaFileObjects.forSourceLines("test.CommonName",
- "package test;",
- "",
- "public interface CommonName {}");
- JavaFileObject differentPackageInterface = JavaFileObjects.forSourceLines(
- "other.pkg.CommonName",
- "package other.pkg;",
- "",
- "public interface CommonName {}");
- JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class InjectConstructor implements CommonName {",
- " @Inject InjectConstructor(other.pkg.CommonName otherPackage, CommonName samePackage) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.InjectConstructor_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import other.pkg.CommonName;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class InjectConstructor_Factory ",
- " implements Factory<InjectConstructor> {",
- "",
- " private final Provider<CommonName> otherPackageProvider;",
- " private final Provider<test.CommonName> samePackageProvider;",
- "",
- " public InjectConstructor_Factory(Provider<CommonName> otherPackageProvider,",
- " Provider<test.CommonName> samePackageProvider) {",
- " assert otherPackageProvider != null;",
- " this.otherPackageProvider = otherPackageProvider;",
- " assert samePackageProvider != null;",
- " this.samePackageProvider = samePackageProvider;",
- " }",
- "",
- " @Override public InjectConstructor get() {",
- " return new InjectConstructor(otherPackageProvider.get(), samePackageProvider.get());",
- " }",
- "",
- " public static Factory<InjectConstructor> create(",
- " Provider<CommonName> otherPackageProvider,",
- " Provider<test.CommonName> samePackageProvider) {",
- " return new InjectConstructor_Factory(otherPackageProvider, samePackageProvider);",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(samePackageInterface, differentPackageInterface, file))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expected);
- }
-
- @Test
- public void noDeps() {
- JavaFileObject simpleType = JavaFileObjects.forSourceLines("test.SimpleType",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class SimpleType {",
- " @Inject SimpleType() {}",
- "}");
- JavaFileObject factory = JavaFileObjects.forSourceLines("test.SimpleType_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public enum SimpleType_Factory implements Factory<SimpleType> {",
- " INSTANCE;",
- "",
- " @Override public SimpleType get() {",
- " return new SimpleType();",
- " }",
- "",
- " public static Factory<SimpleType> create() {",
- " return INSTANCE;",
- " }",
- "}");
- assertAbout(javaSource())
- .that(simpleType)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(factory);
- }
-
- @Test public void simpleComponentWithNesting() {
- JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines("test.OuterType",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Inject;",
- "",
- "final class OuterType {",
- " static class A {",
- " @Inject A() {}",
- " }",
- " static class B {",
- " @Inject A a;",
- " }",
- " @Component interface SimpleComponent {",
- " A a();",
- " void inject(B b);",
- " }",
- "}");
- JavaFileObject aFactory = JavaFileObjects.forSourceLines(
- "test.OuterType$A_Factory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "import test.OuterType.A;",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public enum OuterType$A_Factory implements Factory<A> {",
- " INSTANCE;",
- "",
- " @Override public A get() {",
- " return new A();",
- " }",
- "",
- " public static Factory<A> create() {",
- " return INSTANCE;",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(nestedTypesFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(aFactory);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/KeyTest.java b/compiler/src/test/java/dagger/internal/codegen/KeyTest.java
deleted file mode 100644
index c1d622d..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/KeyTest.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Equivalence;
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.testing.compile.CompilationRule;
-import dagger.Module;
-import dagger.Provides;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Qualifier;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static dagger.Provides.Type.SET;
-import static dagger.Provides.Type.SET_VALUES;
-
-/**
- * Tests {@link Key}.
- */
-@RunWith(JUnit4.class)
-public class KeyTest {
- @Rule public CompilationRule compilationRule = new CompilationRule();
-
- private Elements elements;
- private Types types;
- private Key.Factory keyFactory;
-
- @Before public void setUp() {
- this.types = compilationRule.getTypes();
- this.elements = compilationRule.getElements();
- this.keyFactory = new Key.Factory(types, elements);
- }
-
- @Test public void forInjectConstructorWithResolvedType() {
- TypeElement typeElement =
- compilationRule.getElements().getTypeElement(InjectedClass.class.getCanonicalName());
- ExecutableElement constructor =
- Iterables.getOnlyElement(ElementFilter.constructorsIn(typeElement.getEnclosedElements()));
- assertThat(
- keyFactory.forInjectConstructorWithResolvedType(constructor.getEnclosingElement().asType()))
- .isEqualTo(new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(typeElement.asType())));
- }
-
- static final class InjectedClass {
- @SuppressWarnings("unused")
- @Inject InjectedClass(String s, int i) {}
- }
-
- @Test public void forProvidesMethod() {
- TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
- TypeElement moduleElement =
- elements.getTypeElement(ProvidesMethodModule.class.getCanonicalName());
- ExecutableElement providesMethod =
- Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
- assertThat(
- keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod))
- .isEqualTo(new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(stringType)));
- }
-
- @Module
- static final class ProvidesMethodModule {
- @Provides String provideString() {
- return null;
- }
- }
-
- @Test public void forProvidesMethod_qualified() {
- TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
- TypeElement qualifierElement =
- elements.getTypeElement(TestQualifier.class.getCanonicalName());
- TypeElement moduleElement =
- elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
- ExecutableElement providesMethod =
- Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
- Key key =
- keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod);
- assertThat(MoreTypes.equivalence().wrap(key.qualifier().get().getAnnotationType()))
- .isEqualTo(MoreTypes.equivalence().wrap(qualifierElement.asType()));
- assertThat(key.wrappedType()).isEqualTo(MoreTypes.equivalence().wrap(stringType));
- }
-
- @Test public void qualifiedKeyEquivalents() {
- TypeElement moduleElement =
- elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
- ExecutableElement providesMethod =
- Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
- Key provisionKey =
- keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod);
-
- TypeMirror type = elements.getTypeElement(String.class.getCanonicalName()).asType();
- TypeElement injectableElement =
- elements.getTypeElement(QualifiedFieldHolder.class.getCanonicalName());
- Element injectionField =
- Iterables.getOnlyElement(ElementFilter.fieldsIn(injectableElement.getEnclosedElements()));
- AnnotationMirror qualifier = Iterables.getOnlyElement(injectionField.getAnnotationMirrors());
- Key injectionKey = keyFactory.forQualifiedType(Optional.<AnnotationMirror>of(qualifier), type);
-
- assertThat(provisionKey).isEqualTo(injectionKey);
- }
-
- @Module
- static final class QualifiedProvidesMethodModule {
- @Provides
- @TestQualifier(@InnerAnnotation)
- String provideQualifiedString() {
- return null;
- }
- }
-
- static final class QualifiedFieldHolder {
- @TestQualifier(@InnerAnnotation) String aString;
- }
-
- @Qualifier
- @interface TestQualifier {
- InnerAnnotation[] value();
- }
-
- @interface InnerAnnotation {}
-
- @Test public void forProvidesMethod_sets() {
- TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName());
- TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
- TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType);
- TypeElement moduleElement =
- elements.getTypeElement(SetProvidesMethodsModule.class.getCanonicalName());
- for (ExecutableElement providesMethod
- : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
- assertThat(
- keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod))
- .isEqualTo(new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(setOfStringsType)));
- }
- }
-
- @Module
- static final class SetProvidesMethodsModule {
- @Provides(type = SET) String provideString() {
- return null;
- }
-
- @Provides(type = SET_VALUES) Set<String> provideStrings() {
- return null;
- }
- }
-
- @Module
- static final class PrimitiveTypes {
- @Provides int foo() {
- return 0;
- }
- }
-
- @Module
- static final class BoxedPrimitiveTypes {
- @Provides Integer foo() {
- return 0;
- }
- }
-
- @Test public void primitiveKeysMatchBoxedKeys() {
- TypeElement primitiveHolder = elements.getTypeElement(PrimitiveTypes.class.getCanonicalName());
- ExecutableElement intMethod =
- Iterables.getOnlyElement(ElementFilter.methodsIn(primitiveHolder.getEnclosedElements()));
- TypeElement boxedPrimitiveHolder =
- elements.getTypeElement(BoxedPrimitiveTypes.class.getCanonicalName());
- ExecutableElement integerMethod = Iterables.getOnlyElement(
- ElementFilter.methodsIn(boxedPrimitiveHolder.getEnclosedElements()));
-
- // TODO(cgruber): Truth subject for TypeMirror and TypeElement
- TypeMirror intType = intMethod.getReturnType();
- assertThat(intType.getKind().isPrimitive()).isTrue();
- TypeMirror integerType = integerMethod.getReturnType();
- assertThat(integerType.getKind().isPrimitive()).isFalse();
- assertThat(types.isSameType(intType, integerType)).named("type equality").isFalse();
-
- Key intKey = keyFactory.forProvidesMethod((ExecutableType) intMethod.asType(), intMethod);
- Key integerKey =
- keyFactory.forProvidesMethod((ExecutableType) integerMethod.asType(), integerMethod);
- assertThat(intKey).isEqualTo(integerKey);
- }
-
- @Test public void forProducesMethod() {
- TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
- TypeElement moduleElement =
- elements.getTypeElement(ProducesMethodsModule.class.getCanonicalName());
- for (ExecutableElement producesMethod
- : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
- assertThat(keyFactory.forProducesMethod(
- (ExecutableType) producesMethod.asType(), producesMethod))
- .isEqualTo(new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(stringType)));
- }
- }
-
- @ProducerModule
- static final class ProducesMethodsModule {
- @Produces String produceString() {
- return null;
- }
-
- @Produces ListenableFuture<String> produceFutureString() {
- return null;
- }
- }
-
- @Test public void forProducesMethod_sets() {
- TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName());
- TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
- TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType);
- TypeElement moduleElement =
- elements.getTypeElement(SetProducesMethodsModule.class.getCanonicalName());
- for (ExecutableElement producesMethod
- : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
- assertThat(keyFactory.forProducesMethod(
- (ExecutableType) producesMethod.asType(), producesMethod))
- .isEqualTo(new AutoValue_Key(
- Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
- MoreTypes.equivalence().wrap(setOfStringsType)));
- }
- }
-
- @ProducerModule
- static final class SetProducesMethodsModule {
- @Produces(type = Produces.Type.SET) String produceString() {
- return null;
- }
-
- @Produces(type = Produces.Type.SET) ListenableFuture<String> produceFutureString() {
- return null;
- }
-
- @Produces(type = Produces.Type.SET_VALUES) Set<String> produceStrings() {
- return null;
- }
-
- @Produces(type = Produces.Type.SET_VALUES)
- ListenableFuture<Set<String>> produceFutureStrings() {
- return null;
- }
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java
deleted file mode 100644
index 9e1b6dc..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java
+++ /dev/null
@@ -1,906 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.value.processor.AutoAnnotationProcessor;
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-@RunWith(JUnit4.class)
-public class MapBindingComponentProcessorTest {
-
- @Test
- public void mapBindingsWithEnumKey() {
- JavaFileObject mapModuleOneFile =
- JavaFileObjects
- .forSourceLines("test.MapModuleOne",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleOne {",
- " @Provides(type = MAP) @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {",
- " return new AdminHandler();",
- " }",
- "}");
- JavaFileObject mapModuleTwoFile =
- JavaFileObjects
- .forSourceLines("test.MapModuleTwo",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleTwo {",
- " @Provides(type = MAP) @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {",
- " return new LoginHandler();",
- " }",
- "}");
- JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
- "package test;",
- "import dagger.MapKey;",
- "import java.lang.annotation.Retention;",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "",
- "@MapKey(unwrapValue = true)",
- "@Retention(RUNTIME)",
- "public @interface PathKey {",
- " PathEnum value();",
- "}");
- JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
- "package test;",
- "",
- "public enum PathEnum {",
- " ADMIN,",
- " LOGIN;",
- "}");
-
- JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
- "package test;",
- "",
- "interface Handler {}");
- JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
- "package test;",
- "",
- "class LoginHandler implements Handler {",
- " public LoginHandler() {}",
- "}");
- JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
- "package test;",
- "",
- "class AdminHandler implements Handler {",
- " public AdminHandler() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
- "interface TestComponent {",
- " Map<PathEnum, Provider<Handler>> dispatcher();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines("test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.internal.MapProviderFactory;",
- "import java.util.Map;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Handler> mapOfPathEnumAndProviderOfHandlerContribution1;",
- " private Provider<Handler> mapOfPathEnumAndProviderOfHandlerContribution2;",
- " private Provider<Map<PathEnum, Provider<Handler>>>",
- " mapOfPathEnumAndProviderOfHandlerProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.mapOfPathEnumAndProviderOfHandlerContribution1 =",
- " MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
- " this.mapOfPathEnumAndProviderOfHandlerContribution2 =",
- " MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
- " this.mapOfPathEnumAndProviderOfHandlerProvider =",
- " MapProviderFactory.<PathEnum, Handler>builder(2)",
- " .put(PathEnum.ADMIN,",
- " mapOfPathEnumAndProviderOfHandlerContribution1)",
- " .put(PathEnum.LOGIN,",
- " mapOfPathEnumAndProviderOfHandlerContribution2)",
- " .build();",
- " }",
- "",
- " @Override",
- " public Map<PathEnum, Provider<Handler>> dispatcher() {",
- " return mapOfPathEnumAndProviderOfHandlerProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private MapModuleOne mapModuleOne;",
- " private MapModuleTwo mapModuleTwo;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (mapModuleOne == null) {",
- " this.mapModuleOne = new MapModuleOne();",
- " }",
- " if (mapModuleTwo == null) {",
- " this.mapModuleTwo = new MapModuleTwo();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder mapModuleOne(MapModuleOne mapModuleOne) {",
- " if (mapModuleOne == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleOne = mapModuleOne;",
- " return this;",
- " }",
- "",
- " public Builder mapModuleTwo(MapModuleTwo mapModuleTwo) {",
- " if (mapModuleTwo == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleTwo = mapModuleTwo;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(mapModuleOneFile,
- mapModuleTwoFile,
- enumKeyFile,
- pathEnumFile,
- HandlerFile,
- LoginHandlerFile,
- AdminHandlerFile,
- componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedComponent);
- }
-
- @Test
- public void mapBindingsWithStringKey() {
- JavaFileObject mapModuleOneFile =
- JavaFileObjects
- .forSourceLines("test.MapModuleOne",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.mapkeys.StringKey;",
- "",
- "@Module",
- "final class MapModuleOne {",
- " @Provides(type = MAP) @StringKey(\"Admin\") Handler provideAdminHandler() {",
- " return new AdminHandler();",
- " }",
- "}");
- JavaFileObject mapModuleTwoFile =
- JavaFileObjects
- .forSourceLines("test.MapModuleTwo",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.mapkeys.StringKey;",
- "",
- "@Module",
- "final class MapModuleTwo {",
- " @Provides(type = MAP) @StringKey(\"Login\") Handler provideLoginHandler() {",
- " return new LoginHandler();",
- " }",
- "}");
- JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
- "package test;",
- "",
- "interface Handler {}");
- JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
- "package test;",
- "",
- "class LoginHandler implements Handler {",
- " public LoginHandler() {}",
- "}");
- JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
- "package test;",
- "",
- "class AdminHandler implements Handler {",
- " public AdminHandler() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
- "interface TestComponent {",
- " Map<String, Provider<Handler>> dispatcher();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines("test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.internal.MapProviderFactory;",
- "import java.util.Map;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Handler> mapOfStringAndProviderOfHandlerContribution1;",
- " private Provider<Handler> mapOfStringAndProviderOfHandlerContribution2;",
- " private Provider<Map<String, Provider<Handler>>>",
- " mapOfStringAndProviderOfHandlerProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.mapOfStringAndProviderOfHandlerContribution1 =",
- " MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
- " this.mapOfStringAndProviderOfHandlerContribution2 =",
- " MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
- " this.mapOfStringAndProviderOfHandlerProvider =",
- " MapProviderFactory.<String, Handler>builder(2)",
- " .put(\"Admin\", mapOfStringAndProviderOfHandlerContribution1)",
- " .put(\"Login\", mapOfStringAndProviderOfHandlerContribution2)",
- " .build();",
- " }",
- "",
- " @Override",
- " public Map<String, Provider<Handler>> dispatcher() {",
- " return mapOfStringAndProviderOfHandlerProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private MapModuleOne mapModuleOne;",
- " private MapModuleTwo mapModuleTwo;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (mapModuleOne == null) {",
- " this.mapModuleOne = new MapModuleOne();",
- " }",
- " if (mapModuleTwo == null) {",
- " this.mapModuleTwo = new MapModuleTwo();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder mapModuleOne(MapModuleOne mapModuleOne) {",
- " if (mapModuleOne == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleOne = mapModuleOne;",
- " return this;",
- " }",
- "",
- " public Builder mapModuleTwo(MapModuleTwo mapModuleTwo) {",
- " if (mapModuleTwo == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleTwo = mapModuleTwo;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(mapModuleOneFile,
- mapModuleTwoFile,
- HandlerFile,
- LoginHandlerFile,
- AdminHandlerFile,
- componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedComponent);
- }
-
- @Test
- public void mapBindingsWithWrappedKey() {
- JavaFileObject mapModuleOneFile =
- JavaFileObjects
- .forSourceLines("test.MapModuleOne",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleOne {",
- " @Provides(type = MAP)",
- " @WrappedClassKey(Integer.class) Handler provideAdminHandler() {",
- " return new AdminHandler();",
- " }",
- "}");
- JavaFileObject mapModuleTwoFile =
- JavaFileObjects
- .forSourceLines("test.MapModuleTwo",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleTwo {",
- " @Provides(type = MAP)",
- " @WrappedClassKey(Long.class) Handler provideLoginHandler() {",
- " return new LoginHandler();",
- " }",
- "}");
- JavaFileObject wrappedClassKeyFile = JavaFileObjects.forSourceLines("test.WrappedClassKey",
- "package test;",
- "import dagger.MapKey;",
- "import java.lang.annotation.Retention;",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "",
- "@MapKey(unwrapValue = false)",
- "@Retention(RUNTIME)",
- "public @interface WrappedClassKey {",
- " Class<?> value();",
- "}");
- JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
- "package test;",
- "",
- "interface Handler {}");
- JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
- "package test;",
- "",
- "class LoginHandler implements Handler {",
- " public LoginHandler() {}",
- "}");
- JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
- "package test;",
- "",
- "class AdminHandler implements Handler {",
- " public AdminHandler() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
- "interface TestComponent {",
- " Map<WrappedClassKey, Provider<Handler>> dispatcher();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines("test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.internal.MapProviderFactory;",
- "import java.util.Map;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Handler> mapOfWrappedClassKeyAndProviderOfHandlerContribution1;",
- " private Provider<Handler> mapOfWrappedClassKeyAndProviderOfHandlerContribution2;",
- " private Provider<Map<WrappedClassKey, Provider<Handler>>>",
- " mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.mapOfWrappedClassKeyAndProviderOfHandlerContribution1 =",
- " MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
- " this.mapOfWrappedClassKeyAndProviderOfHandlerContribution2 =",
- " MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
- " this.mapOfWrappedClassKeyAndProviderOfHandlerProvider =",
- " MapProviderFactory.<WrappedClassKey, Handler>builder(2)",
- " .put(WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
- " mapOfWrappedClassKeyAndProviderOfHandlerContribution1)",
- " .put(WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
- " mapOfWrappedClassKeyAndProviderOfHandlerContribution2)",
- " .build();",
- " }",
- "",
- " @Override",
- " public Map<WrappedClassKey, Provider<Handler>> dispatcher() {",
- " return mapOfWrappedClassKeyAndProviderOfHandlerProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private MapModuleOne mapModuleOne;",
- " private MapModuleTwo mapModuleTwo;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (mapModuleOne == null) {",
- " this.mapModuleOne = new MapModuleOne();",
- " }",
- " if (mapModuleTwo == null) {",
- " this.mapModuleTwo = new MapModuleTwo();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder mapModuleOne(MapModuleOne mapModuleOne) {",
- " if (mapModuleOne == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleOne = mapModuleOne;",
- " return this;",
- " }",
- "",
- " public Builder mapModuleTwo(MapModuleTwo mapModuleTwo) {",
- " if (mapModuleTwo == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleTwo = mapModuleTwo;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(mapModuleOneFile,
- mapModuleTwoFile,
- wrappedClassKeyFile,
- HandlerFile,
- LoginHandlerFile,
- AdminHandlerFile,
- componentFile))
- .processedWith(new ComponentProcessor(), new AutoAnnotationProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedComponent);
- }
-
- @Test
- public void mapBindingsWithNonProviderValue() {
- JavaFileObject mapModuleOneFile = JavaFileObjects.forSourceLines("test.MapModuleOne",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleOne {",
- " @Provides(type = MAP) @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {",
- " return new AdminHandler();",
- " }",
- "}");
- JavaFileObject mapModuleTwoFile = JavaFileObjects.forSourceLines("test.MapModuleTwo",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleTwo {",
- " @Provides(type = MAP) @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {",
- " return new LoginHandler();",
- " }",
- "}");
- JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
- "package test;",
- "import dagger.MapKey;",
- "import java.lang.annotation.Retention;",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "",
- "@MapKey(unwrapValue = true)",
- "@Retention(RUNTIME)",
- "public @interface PathKey {",
- " PathEnum value();",
- "}");
- JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
- "package test;",
- "",
- "public enum PathEnum {",
- " ADMIN,",
- " LOGIN;",
- "}");
- JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
- "package test;",
- "",
- "interface Handler {}");
- JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
- "package test;",
- "",
- "class LoginHandler implements Handler {",
- " public LoginHandler() {}",
- "}");
- JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
- "package test;",
- "",
- "class AdminHandler implements Handler {",
- " public AdminHandler() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
- "interface TestComponent {",
- " Map<PathEnum, Handler> dispatcher();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines("test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.internal.MapFactory;",
- "import dagger.internal.MapProviderFactory;",
- "import java.util.Map;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Handler> mapOfPathEnumAndProviderOfHandlerContribution1;",
- " private Provider<Handler> mapOfPathEnumAndProviderOfHandlerContribution2;",
- " private Provider<Map<PathEnum, Provider<Handler>>>",
- " mapOfPathEnumAndProviderOfHandlerProvider;",
- " private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.mapOfPathEnumAndProviderOfHandlerContribution1 =",
- " MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
- " this.mapOfPathEnumAndProviderOfHandlerContribution2 =",
- " MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
- " this.mapOfPathEnumAndProviderOfHandlerProvider =",
- " MapProviderFactory.<PathEnum, Handler>builder(2)",
- " .put(PathEnum.ADMIN,",
- " mapOfPathEnumAndProviderOfHandlerContribution1)",
- " .put(PathEnum.LOGIN,",
- " mapOfPathEnumAndProviderOfHandlerContribution2)",
- " .build();",
- " this.mapOfPathEnumAndHandlerProvider =",
- " MapFactory.create(mapOfPathEnumAndProviderOfHandlerProvider);",
- " }",
- "",
- " @Override",
- " public Map<PathEnum, Handler> dispatcher() {",
- " return mapOfPathEnumAndHandlerProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private MapModuleOne mapModuleOne;",
- " private MapModuleTwo mapModuleTwo;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (mapModuleOne == null) {",
- " this.mapModuleOne = new MapModuleOne();",
- " }",
- " if (mapModuleTwo == null) {",
- " this.mapModuleTwo = new MapModuleTwo();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder mapModuleOne(MapModuleOne mapModuleOne) {",
- " if (mapModuleOne == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleOne = mapModuleOne;",
- " return this;",
- " }",
- "",
- " public Builder mapModuleTwo(MapModuleTwo mapModuleTwo) {",
- " if (mapModuleTwo == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleTwo = mapModuleTwo;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(mapModuleOneFile,
- mapModuleTwoFile,
- enumKeyFile,
- pathEnumFile,
- HandlerFile,
- LoginHandlerFile,
- AdminHandlerFile,
- componentFile)).
- processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test
- public void injectMapWithoutMapBinding() {
- JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("test.MapModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.HashMap;",
- "import java.util.Map;",
- "",
- "@Module",
- "final class MapModule {",
- " @Provides Map<String, String> provideAMap() {",
- " Map<String, String> map = new HashMap<String, String>();",
- " map.put(\"Hello\", \"World\");",
- " return map;",
- " }",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "",
- "@Component(modules = {MapModule.class})",
- "interface TestComponent {",
- " Map<String, String> dispatcher();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines("test.DaggerTestComponent",
- "package test;",
- "",
- "import java.util.Map;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Map<String, String>> provideAMapProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.provideAMapProvider = MapModule_ProvideAMapFactory.create(builder.mapModule);",
- " }",
- "",
- " @Override",
- " public Map<String, String> dispatcher() {",
- " return provideAMapProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private MapModule mapModule;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (mapModule == null) {",
- " this.mapModule = new MapModule();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder mapModule(MapModule mapModule) {",
- " if (mapModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModule = mapModule;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(mapModuleFile,componentFile))
- .processedWith(new ComponentProcessor()).compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test
- public void mapBindingsWithDuplicateKeys() {
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.MapModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.mapkeys.StringKey;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "@Module",
- "final class MapModule {",
- " @Provides(type = MAP) @StringKey(\"AKey\") Object provideObjectForAKey() {",
- " return \"one\";",
- " }",
- "",
- " @Provides(type = MAP) @StringKey(\"AKey\") Object provideObjectForAKeyAgain() {",
- " return \"one again\";",
- " }",
- "}");
- JavaFileObject componentFile =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {MapModule.class})",
- "interface TestComponent {",
- " Map<String, Object> objects();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(module, componentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("The same map key is bound more than once")
- .and()
- .withErrorContaining("provideObjectForAKey()")
- .and()
- .withErrorContaining("provideObjectForAKeyAgain()")
- .and()
- .withErrorCount(1);
- }
-
- @Test
- public void mapBindingsWithInconsistentKeyAnnotations() {
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.MapModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.mapkeys.StringKey;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "@Module",
- "final class MapModule {",
- " @Provides(type = MAP) @StringKey(\"AKey\") Object provideObjectForAKey() {",
- " return \"one\";",
- " }",
- "",
- " @Provides(type = MAP) @StringKeyTwo(\"BKey\") Object provideObjectForBKey() {",
- " return \"two\";",
- " }",
- "}");
- JavaFileObject stringKeyTwoFile =
- JavaFileObjects.forSourceLines(
- "test.StringKeyTwo",
- "package test;",
- "",
- "import dagger.MapKey;",
- "",
- "@MapKey(unwrapValue = true)",
- "public @interface StringKeyTwo {",
- " String value();",
- "}");
- JavaFileObject componentFile =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "",
- "@Component(modules = {MapModule.class})",
- "interface TestComponent {",
- " Map<String, Object> objects();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(module, stringKeyTwoFile, componentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("uses more than one @MapKey annotation type")
- .and()
- .withErrorContaining("provideObjectForAKey()")
- .and()
- .withErrorContaining("provideObjectForBKey()")
- .and()
- .withErrorCount(1);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java
deleted file mode 100644
index 191ee6c..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.auto.value.processor.AutoAnnotationProcessor;
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-@RunWith(JUnit4.class)
-public class MapKeyProcessorTest {
- @Test
- public void mapKeyCreatorFile() {
- JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
- "package test;",
- "import dagger.MapKey;",
- "import java.lang.annotation.Retention;",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "",
- "@MapKey(unwrapValue = false)",
- "@Retention(RUNTIME)",
- "public @interface PathKey {",
- " PathEnum value();",
- " String relativePath() default \"Defaultpath\";",
- "}");
- JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
- "package test;",
- "",
- "public enum PathEnum {",
- " ADMIN,",
- " LOGIN;",
- "}");
- JavaFileObject generatedKeyCreator =
- JavaFileObjects.forSourceLines(
- "test.PathKeyCreator",
- "package test;",
- "",
- "import com.google.auto.value.AutoAnnotation;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class PathKeyCreator {",
- " @AutoAnnotation",
- " public static PathKey createPathKey(PathEnum value, String relativePath) {",
- " return new AutoAnnotation_PathKeyCreator_createPathKey(value, relativePath);",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(enumKeyFile, pathEnumFile))
- .processedWith(new ComponentProcessor(), new AutoAnnotationProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedKeyCreator);
- }
-
- @Test
- public void nestedMapKeyCreatorFile() {
- JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.Container",
- "package test;",
- "import dagger.MapKey;",
- "import java.lang.annotation.Retention;",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "",
- "public interface Container {",
- "@MapKey(unwrapValue = false)",
- "@Retention(RUNTIME)",
- "public @interface PathKey {",
- " PathEnum value();",
- " String relativePath() default \"Defaultpath\";",
- "}",
- "}");
- JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
- "package test;",
- "",
- "public enum PathEnum {",
- " ADMIN,",
- " LOGIN;",
- "}");
- JavaFileObject generatedKeyCreator =
- JavaFileObjects.forSourceLines(
- "test.Container$PathKeyCreator",
- "package test;",
- "",
- "import com.google.auto.value.AutoAnnotation;",
- "import javax.annotation.Generated;",
- "import test.Container.PathKey",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class Container$PathKeyCreator {",
- " @AutoAnnotation",
- " public static PathKey createPathKey(PathEnum value, String relativePath) {",
- " return new AutoAnnotation_Container$PathKeyCreator_createPathKey(",
- " value, relativePath);",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(enumKeyFile, pathEnumFile))
- .processedWith(new ComponentProcessor(), new AutoAnnotationProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedKeyCreator);
- }
-
- @Test
- public void mapKeyComponentFileWithDisorderedKeyField() {
- JavaFileObject mapModuleOneFile = JavaFileObjects.forSourceLines("test.MapModuleOne",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleOne {",
- " @Provides(type = MAP) @PathKey(relativePath = \"AdminPath\", value = PathEnum.ADMIN)",
- " Handler provideAdminHandler() {",
- " return new AdminHandler();",
- " }",
- "}");
- JavaFileObject mapModuleTwoFile =JavaFileObjects.forSourceLines("test.MapModuleTwo",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleTwo {",
- " @Provides(type = MAP) @PathKey(value = PathEnum.LOGIN, relativePath = \"LoginPath\")",
- " Handler provideLoginHandler() {",
- " return new LoginHandler();",
- " }",
- "}");
- JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
- "package test;",
- "import dagger.MapKey;",
- "import java.lang.annotation.Retention;",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "",
- "@MapKey(unwrapValue = false)",
- "@Retention(RUNTIME)",
- "public @interface PathKey {",
- " PathEnum value();",
- " String relativePath() default \"DefaultPath\";",
- "}");
- JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
- "package test;",
- "",
- "public enum PathEnum {",
- " ADMIN,",
- " LOGIN;",
- "}");
- JavaFileObject handlerFile = JavaFileObjects.forSourceLines("test.Handler",
- "package test;",
- "",
- "interface Handler {}");
- JavaFileObject loginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
- "package test;",
- "",
- "class LoginHandler implements Handler {",
- " public LoginHandler() {}",
- "}");
- JavaFileObject adminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
- "package test;",
- "",
- "class AdminHandler implements Handler {",
- " public AdminHandler() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
- "interface TestComponent {",
- " Map<PathKey, Provider<Handler>> dispatcher();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines("test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.internal.MapProviderFactory;",
- "import java.util.Map;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Handler> mapOfPathKeyAndProviderOfHandlerContribution1;",
- " private Provider<Handler> mapOfPathKeyAndProviderOfHandlerContribution2;",
- " private Provider<Map<PathKey, Provider<Handler>>>",
- " mapOfPathKeyAndProviderOfHandlerProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.mapOfPathKeyAndProviderOfHandlerContribution1 =",
- " MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
- " this.mapOfPathKeyAndProviderOfHandlerContribution2 =",
- " MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
- " this.mapOfPathKeyAndProviderOfHandlerProvider =",
- " MapProviderFactory.<PathKey, Handler>builder(2)",
- " .put(PathKeyCreator.createPathKey(PathEnum.ADMIN, \"AdminPath\"),",
- " mapOfPathKeyAndProviderOfHandlerContribution1)",
- " .put(PathKeyCreator.createPathKey(PathEnum.LOGIN, \"LoginPath\"),",
- " mapOfPathKeyAndProviderOfHandlerContribution2)",
- " .build();",
- " }",
- "",
- " @Override",
- " public Map<PathKey, Provider<Handler>> dispatcher() {",
- " return mapOfPathKeyAndProviderOfHandlerProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private MapModuleOne mapModuleOne;",
- " private MapModuleTwo mapModuleTwo;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (mapModuleOne == null) {",
- " this.mapModuleOne = new MapModuleOne();",
- " }",
- " if (mapModuleTwo == null) {",
- " this.mapModuleTwo = new MapModuleTwo();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder mapModuleOne(MapModuleOne mapModuleOne) {",
- " if (mapModuleOne == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleOne = mapModuleOne;",
- " return this;",
- " }",
- "",
- " public Builder mapModuleTwo(MapModuleTwo mapModuleTwo) {",
- " if (mapModuleTwo == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleTwo = mapModuleTwo;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(
- ImmutableList.of(
- mapModuleOneFile,
- mapModuleTwoFile,
- enumKeyFile,
- pathEnumFile,
- handlerFile,
- loginHandlerFile,
- adminHandlerFile,
- componentFile))
- .processedWith(new ComponentProcessor(), new AutoAnnotationProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedComponent);
- }
-
- @Test
- public void mapKeyComponentFileWithDefaultField() {
- JavaFileObject mapModuleOneFile = JavaFileObjects.forSourceLines("test.MapModuleOne",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleOne {",
- " @Provides(type = MAP) @PathKey(value = PathEnum.ADMIN) Handler provideAdminHandler() {",
- " return new AdminHandler();",
- " }",
- "}");
- JavaFileObject mapModuleTwoFile =JavaFileObjects.forSourceLines("test.MapModuleTwo",
- "package test;",
- "",
- "import static dagger.Provides.Type.MAP;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class MapModuleTwo {",
- " @Provides(type = MAP) @PathKey(value = PathEnum.LOGIN, relativePath = \"LoginPath\")",
- " Handler provideLoginHandler() {",
- " return new LoginHandler();",
- " }",
- "}");
- JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
- "package test;",
- "import dagger.MapKey;",
- "import java.lang.annotation.Retention;",
- "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
- "",
- "@MapKey(unwrapValue = false)",
- "@Retention(RUNTIME)",
- "public @interface PathKey {",
- " PathEnum value();",
- " String relativePath() default \"DefaultPath\";",
- "}");
- JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
- "package test;",
- "",
- "public enum PathEnum {",
- " ADMIN,",
- " LOGIN;",
- "}");
- JavaFileObject handlerFile = JavaFileObjects.forSourceLines("test.Handler",
- "package test;",
- "",
- "interface Handler {}");
- JavaFileObject loginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
- "package test;",
- "",
- "class LoginHandler implements Handler {",
- " public LoginHandler() {}",
- "}");
- JavaFileObject adminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
- "package test;",
- "",
- "class AdminHandler implements Handler {",
- " public AdminHandler() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
- "interface TestComponent {",
- " Map<PathKey, Provider<Handler>> dispatcher();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines("test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.internal.MapProviderFactory;",
- "import java.util.Map;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Handler> mapOfPathKeyAndProviderOfHandlerContribution1;",
- " private Provider<Handler> mapOfPathKeyAndProviderOfHandlerContribution2;",
- " private Provider<Map<PathKey, Provider<Handler>>>",
- " mapOfPathKeyAndProviderOfHandlerProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.mapOfPathKeyAndProviderOfHandlerContribution1 =",
- " MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
- " this.mapOfPathKeyAndProviderOfHandlerContribution2 =",
- " MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
- " this.mapOfPathKeyAndProviderOfHandlerProvider =",
- " MapProviderFactory.<PathKey, Handler>builder(2)",
- " .put(PathKeyCreator.createPathKey(PathEnum.ADMIN, \"DefaultPath\"),",
- " mapOfPathKeyAndProviderOfHandlerContribution1)",
- " .put(PathKeyCreator.createPathKey(PathEnum.LOGIN, \"LoginPath\"),",
- " mapOfPathKeyAndProviderOfHandlerContribution2)",
- " .build();",
- " }",
- "",
- " @Override",
- " public Map<PathKey, Provider<Handler>> dispatcher() {",
- " return mapOfPathKeyAndProviderOfHandlerProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private MapModuleOne mapModuleOne;",
- " private MapModuleTwo mapModuleTwo;",
- "",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " if (mapModuleOne == null) {",
- " this.mapModuleOne = new MapModuleOne();",
- " }",
- " if (mapModuleTwo == null) {",
- " this.mapModuleTwo = new MapModuleTwo();",
- " }",
- " return new DaggerTestComponent(this);",
- " }",
- "",
- " public Builder mapModuleOne(MapModuleOne mapModuleOne) {",
- " if (mapModuleOne == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleOne = mapModuleOne;",
- " return this;",
- " }",
- "",
- " public Builder mapModuleTwo(MapModuleTwo mapModuleTwo) {",
- " if (mapModuleTwo == null) {",
- " throw new NullPointerException();",
- " }",
- " this.mapModuleTwo = mapModuleTwo;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(
- ImmutableList.of(
- mapModuleOneFile,
- mapModuleTwoFile,
- enumKeyFile,
- pathEnumFile,
- handlerFile,
- loginHandlerFile,
- adminHandlerFile,
- componentFile))
- .processedWith(new ComponentProcessor(), new AutoAnnotationProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedComponent);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java b/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java
deleted file mode 100644
index 52be72a..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java
+++ /dev/null
@@ -1,921 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.testing.compile.JavaFileObjects;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Set;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.TypeElement;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static javax.tools.StandardLocation.CLASS_OUTPUT;
-
-@RunWith(JUnit4.class)
-public class MembersInjectionTest {
- @Test
- public void parentClass_noInjectedMembers() {
- JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "public final class Child extends Parent {",
- " @Inject Child() {}",
- "}");
- JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "public abstract class Parent {}");
-
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "",
- "@Component",
- "interface TestComponent {",
- " Child child();",
- "}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.MembersInjectors;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private Provider<Child> childProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.childProvider =",
- " Child_Factory.create((MembersInjector) MembersInjectors.noOp());",
- " }",
- "",
- " @Override",
- " public Child child() {",
- " return childProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {",
- " }",
- "",
- " public TestComponent build() {",
- " return new DaggerTestComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(childFile, parentFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-
- @Test
- public void parentClass_injectedMembersInSupertype() {
- JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "public final class Child extends Parent {",
- " @Inject Child() {}",
- "}");
- JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "public abstract class Parent {",
- " @Inject Dep dep;",
- "}");
- JavaFileObject depFile = JavaFileObjects.forSourceLines("test.Dep",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class Dep {",
- " @Inject Dep() {}",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "",
- "@Component",
- "interface TestComponent {",
- " Child child();",
- "}");
- JavaFileObject generatedComponent =
- JavaFileObjects.forSourceLines(
- "test.DaggerTestComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestComponent implements TestComponent {",
- " private MembersInjector<Child> childMembersInjector;",
- " private Provider<Child> childProvider;",
- "",
- " private DaggerTestComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static TestComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.childMembersInjector = Child_MembersInjector.create(Dep_Factory.create());",
- " this.childProvider = Child_Factory.create(childMembersInjector);",
- " }",
- "",
- " @Override",
- " public Child child() {",
- " return childProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() {}",
- "",
- " public TestComponent build() {",
- " return new DaggerTestComponent(this);",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(childFile, parentFile, depFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(generatedComponent);
- }
-
- @Test public void fieldAndMethodGenerics() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class GenericClass<A, B> {",
- " @Inject A a;",
- "",
- " @Inject GenericClass() {}",
- "",
- " @Inject void register(B b) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.GenericClass_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class GenericClass_MembersInjector<A, B>",
- " implements MembersInjector<GenericClass<A, B>> {",
- " private final Provider<A> aProvider;",
- " private final Provider<B> bProvider;",
- "",
- " public GenericClass_MembersInjector(Provider<A> aProvider, Provider<B> bProvider) {",
- " assert aProvider != null;",
- " this.aProvider = aProvider;",
- " assert bProvider != null;",
- " this.bProvider = bProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(GenericClass<A, B> instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.a = aProvider.get();",
- " instance.register(bProvider.get());",
- " }",
- "",
- " public static <A, B> MembersInjector<GenericClass<A, B>> create(",
- " Provider<A> aProvider, Provider<B> bProvider) {",
- " return new GenericClass_MembersInjector<A, B>(aProvider, bProvider);",
- " }",
- "",
- " public static <A, B> void injectA(GenericClass<A, B> instance, Provider<A> aProvider) {",
- " instance.a = aProvider.get();",
- " }",
- "",
- " public static <A, B> void injectRegister(",
- " GenericClass<A, B> instance, Provider<B> bProvider) {",
- " instance.register(bProvider.get());",
- " }",
- "",
- "}");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expected);
- }
-
- @Test public void subclassedGenericMembersInjectors() {
- JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A() {}",
- "}");
- JavaFileObject a2 = JavaFileObjects.forSourceLines("test.A2",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A2 {",
- " @Inject A2() {}",
- "}");
- JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class Parent<X, Y> {",
- " @Inject X x;",
- " @Inject Y y;",
- " @Inject A2 a2;",
- "",
- " @Inject Parent() {}",
- "}");
- JavaFileObject child = JavaFileObjects.forSourceLines("test.Child",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class Child<T> extends Parent<T, A> {",
- " @Inject A a;",
- " @Inject T t;",
- "",
- " @Inject Child() {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.Child_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class Child_MembersInjector<T>",
- " implements MembersInjector<Child<T>> {",
- " private final Provider<T> tAndXProvider;",
- " private final Provider<A> aAndYProvider;",
- " private final Provider<A2> a2Provider;",
- "",
- " public Child_MembersInjector(",
- " Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
- " assert tAndXProvider != null;",
- " this.tAndXProvider = tAndXProvider;",
- " assert aAndYProvider != null;",
- " this.aAndYProvider = aAndYProvider;",
- " assert a2Provider != null;",
- " this.a2Provider = a2Provider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(Child<T> instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " ((test.Parent) instance).x = tAndXProvider.get();",
- " ((test.Parent) instance).y = aAndYProvider.get();",
- " ((test.Parent) instance).a2 = a2Provider.get();",
- " instance.a = aAndYProvider.get();",
- " instance.t = tAndXProvider.get();",
- " }",
- "",
- " public static <T> MembersInjector<Child<T>> create(",
- " Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
- " return new Child_MembersInjector<T>(tAndXProvider, aAndYProvider, a2Provider);",
- " }",
- "",
- " public static <T> void injectA(Child<T> instance, Provider<A> aProvider) {",
- " instance.a = aProvider.get();",
- " }",
- "",
- " public static <T> void injectT(Child<T> instance, Provider<T> tProvider) {",
- " instance.t = tProvider.get();",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(a, a2, parent, child))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expected);
- }
-
- @Test public void fieldInjection() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.FieldInjection",
- "package test;",
- "",
- "import dagger.Lazy;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "class FieldInjection {",
- " @Inject String string;",
- " @Inject Lazy<String> lazyString;",
- " @Inject Provider<String> stringProvider;",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.FieldInjection_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.DoubleCheckLazy;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class FieldInjection_MembersInjector",
- " implements MembersInjector<FieldInjection> {",
- " private final Provider<String> stringProvider;",
- "",
- " public FieldInjection_MembersInjector(Provider<String> stringProvider) {",
- " assert stringProvider != null;",
- " this.stringProvider = stringProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(FieldInjection instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.string = stringProvider.get();",
- " instance.lazyString = DoubleCheckLazy.create(stringProvider);",
- " instance.stringProvider = stringProvider;",
- " }",
- "",
- " public static MembersInjector<FieldInjection> create(Provider<String> stringProvider) {",
- " return new FieldInjection_MembersInjector(stringProvider);",
- " }",
- "",
- " public static void injectString(",
- " FieldInjection instance, Provider<String> stringProvider) {",
- " instance.string = stringProvider.get();",
- " }",
- "",
- " public static void injectLazyString(",
- " FieldInjection instance, Provider<String> lazyStringProvider) {",
- " instance.lazyString = DoubleCheckLazy.create(lazyStringProvider);",
- " }",
- "",
- " public static void injectStringProvider(",
- " FieldInjection instance, Provider<String> stringProvider) {",
- " instance.stringProvider = stringProvider;",
- " }",
- "}");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expected);
- }
-
- @Test public void methodInjection() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.MethodInjection",
- "package test;",
- "",
- "import dagger.Lazy;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "class MethodInjection {",
- " @Inject void noArgs() {}",
- " @Inject void oneArg(String string) {}",
- " @Inject void manyArgs(",
- " String string, Lazy<String> lazyString, Provider<String> stringProvider) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.MethodInjection_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.DoubleCheckLazy;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class MethodInjection_MembersInjector",
- " implements MembersInjector<MethodInjection> {",
- "",
- " private final Provider<String> stringProvider;",
- "",
- " public MethodInjection_MembersInjector(Provider<String> stringProvider) {",
- " assert stringProvider != null;",
- " this.stringProvider = stringProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(MethodInjection instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.noArgs();",
- " instance.oneArg(stringProvider.get());",
- " instance.manyArgs(stringProvider.get(), DoubleCheckLazy.create(stringProvider),",
- " stringProvider);",
- " }",
- "",
- " public static MembersInjector<MethodInjection> create(",
- " Provider<String> stringProvider) {",
- " return new MethodInjection_MembersInjector(stringProvider);",
- " }",
- "",
- " public static void injectNoArgs(MethodInjection instance) {",
- " instance.noArgs();",
- " }",
- "",
- " public static void injectOneArg(",
- " MethodInjection instance, Provider<String> stringProvider) {",
- " instance.oneArg(stringProvider.get());",
- " }",
- "",
- " public static void injectManyArgs(",
- " MethodInjection instance,",
- " Provider<String> stringProvider,",
- " Provider<String> lazyStringProvider,",
- " Provider<String> stringProvider2) {",
- " instance.manyArgs(",
- " stringProvider.get(),",
- " DoubleCheckLazy.create(lazyStringProvider),",
- " stringProvider2);",
- " }",
- "}");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expected);
- }
-
- @Test
- public void mixedMemberInjection() {
- JavaFileObject file = JavaFileObjects.forSourceLines(
- "test.MixedMemberInjection",
- "package test;",
- "",
- "import dagger.Lazy;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "class MixedMemberInjection {",
- " @Inject String string;",
- " @Inject void setString(String s) {}",
- " @Inject Object object;",
- " @Inject void setObject(Object o) {}",
- "}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.MixedMemberInjection_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class MixedMemberInjection_MembersInjector",
- " implements MembersInjector<MixedMemberInjection> {",
- "",
- " private final Provider<String> stringAndSProvider;",
- " private final Provider<Object> objectAndOProvider;",
- "",
- " public MixedMemberInjection_MembersInjector(",
- " Provider<String> stringAndSProvider,",
- " Provider<Object> objectAndOProvider) {",
- " assert stringAndSProvider != null;",
- " this.stringAndSProvider = stringAndSProvider;",
- " assert objectAndOProvider != null;",
- " this.objectAndOProvider = objectAndOProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(MixedMemberInjection instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.string = stringAndSProvider.get();",
- " instance.object = objectAndOProvider.get();",
- " instance.setString(stringAndSProvider.get());",
- " instance.setObject(objectAndOProvider.get());",
- " }",
- "",
- " public static MembersInjector<MixedMemberInjection> create(",
- " Provider<String> stringAndSProvider,",
- " Provider<Object> objectAndOProvider) {",
- " return new MixedMemberInjection_MembersInjector(",
- " stringAndSProvider, objectAndOProvider);",
- " }",
- " public static void injectString(",
- " MixedMemberInjection instance, Provider<String> stringProvider) {",
- " instance.string = stringProvider.get();",
- " }",
- "",
- " public static void injectObject(",
- " MixedMemberInjection instance, Provider<Object> objectProvider) {",
- " instance.object = objectProvider.get();",
- " }",
- "",
- " public static void injectSetString(",
- " MixedMemberInjection instance, Provider<String> sProvider) {",
- " instance.setString(sProvider.get());",
- " }",
- "",
- " public static void injectSetObject(",
- " MixedMemberInjection instance, Provider<Object> oProvider) {",
- " instance.setObject(oProvider.get());",
- " }",
- "}");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expected);
- }
-
- @Test public void injectConstructorAndMembersInjection() {
- JavaFileObject file = JavaFileObjects.forSourceLines("test.AllInjections",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class AllInjections {",
- " @Inject String s;",
- " @Inject AllInjections(String s) {}",
- " @Inject void s(String s) {}",
- "}");
- JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
- "test.AllInjections_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class AllInjections_MembersInjector ",
- " implements MembersInjector<AllInjections> {",
- "",
- " private final Provider<String> sProvider;",
- "",
- " public AllInjections_MembersInjector(Provider<String> sProvider) {",
- " assert sProvider != null;",
- " this.sProvider = sProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(AllInjections instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.s = sProvider.get();",
- " instance.s(sProvider.get());",
- " }",
- "",
- " public static MembersInjector<AllInjections> create(Provider<String> sProvider) {",
- " return new AllInjections_MembersInjector(sProvider);",
- " }",
- "",
- " public static void injectS(AllInjections instance, Provider<String> sProvider) {",
- " instance.s = sProvider.get();",
- " }",
- "}");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expectedMembersInjector);
- }
-
- @Test public void supertypeMembersInjection() {
- JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "class A {}");
- JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class B extends A {",
- " @Inject String s;",
- "}");
- JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
- "test.AllInjections_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class B_MembersInjector implements MembersInjector<B> {",
- " private final Provider<String> sProvider;",
- "",
- " public B_MembersInjector(Provider<String> sProvider) {",
- " assert sProvider != null;",
- " this.sProvider = sProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(B instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.s = sProvider.get();",
- " }",
- "",
- " public static MembersInjector<B> create(Provider<String> sProvider) {",
- " return new B_MembersInjector(sProvider);",
- " }",
- " public static void injectS(B instance, Provider<String> sProvider) {",
- " instance.s = sProvider.get();",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(aFile, bFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expectedMembersInjector);
- }
-
- @Test
- public void simpleComponentWithNesting() {
- JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines(
- "test.OuterType",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Inject;",
- "",
- "final class OuterType {",
- " static class A {",
- " @Inject A() {}",
- " }",
- " static class B {",
- " @Inject A a;",
- " }",
- " @Component interface SimpleComponent {",
- " A a();",
- " void inject(B b);",
- " }",
- "}");
- JavaFileObject bMembersInjector = JavaFileObjects.forSourceLines(
- "test.OuterType$B_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import test.OuterType.A;",
- "import test.OuterType.B;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class OuterType$B_MembersInjector implements MembersInjector<B> {",
- " private final Provider<A> aProvider;",
- "",
- " public OuterType$B_MembersInjector(Provider<A> aProvider) {",
- " assert aProvider != null;",
- " this.aProvider = aProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(B instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.a = aProvider.get();",
- " }",
- "",
- " public static MembersInjector<B> create(Provider<A> aProvider) {",
- " return new OuterType$B_MembersInjector(aProvider);",
- " }",
- "",
- " public static void injectA(B instance, Provider<A> aProvider) {",
- " instance.a = aProvider.get();",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(nestedTypesFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(bMembersInjector);
- }
-
- @Test
- public void componentWithNestingAndGeneratedType() {
- JavaFileObject nestedTypesFile =
- JavaFileObjects.forSourceLines(
- "test.OuterType",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Inject;",
- "",
- "final class OuterType {",
- " @Inject GeneratedType generated;",
- " static class A {",
- " @Inject A() {}",
- " }",
- " static class B {",
- " @Inject A a;",
- " }",
- " @Component interface SimpleComponent {",
- " A a();",
- " void inject(B b);",
- " }",
- "}");
- JavaFileObject bMembersInjector =
- JavaFileObjects.forSourceLines(
- "test.OuterType$B_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import test.OuterType.A;",
- "import test.OuterType.B;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class OuterType$B_MembersInjector implements MembersInjector<B> {",
- " private final Provider<A> aProvider;",
- "",
- " public OuterType$B_MembersInjector(Provider<A> aProvider) {",
- " assert aProvider != null;",
- " this.aProvider = aProvider;",
- " }",
- "",
- " @Override",
- " public void injectMembers(B instance) {",
- " if (instance == null) {",
- " throw new NullPointerException(\"Cannot inject members into a null reference\");",
- " }",
- " instance.a = aProvider.get();",
- " }",
- "",
- " public static MembersInjector<B> create(Provider<A> aProvider) {",
- " return new OuterType$B_MembersInjector(aProvider);",
- " }",
- "",
- " public static void injectA(B instance, Provider<A> aProvider) {",
- " instance.a = aProvider.get();",
- " }",
- "}");
- assertAbout(javaSource())
- .that(nestedTypesFile)
- .processedWith(
- new ComponentProcessor(),
- new AbstractProcessor() {
- private boolean done;
-
- @Override
- public Set<String> getSupportedAnnotationTypes() {
- return ImmutableSet.of("*");
- }
-
- @Override
- public boolean process(
- Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- if (!done) {
- done = true;
- try (Writer writer =
- processingEnv
- .getFiler()
- .createSourceFile("test.GeneratedType")
- .openWriter()) {
- writer.write(
- Joiner.on('\n')
- .join(
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class GeneratedType {",
- " @Inject GeneratedType() {}",
- "}"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- return false;
- }
- })
- .compilesWithoutError()
- .and()
- .generatesSources(bMembersInjector);
- }
-
- @Test
- public void lowerCaseNamedMembersInjector_forLowerCaseType() {
- JavaFileObject foo =
- JavaFileObjects.forSourceLines(
- "test.foo",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class foo {",
- " @Inject String string;",
- "}");
- JavaFileObject fooModule =
- JavaFileObjects.forSourceLines(
- "test.fooModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class fooModule {",
- " @Provides String string() { return \"foo\"; }",
- "}");
- JavaFileObject fooComponent =
- JavaFileObjects.forSourceLines(
- "test.fooComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = fooModule.class)",
- "interface fooComponent {",
- " void inject(foo target);",
- "}");
-
- assertAbout(javaSources())
- .that(ImmutableList.of(foo, fooModule, fooComponent))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesFileNamed(CLASS_OUTPUT, "test", "foo_MembersInjector.class");
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/MethodSignatureFormatterTest.java b/compiler/src/test/java/dagger/internal/codegen/MethodSignatureFormatterTest.java
deleted file mode 100644
index 45791c7..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/MethodSignatureFormatterTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.testing.compile.CompilationRule;
-import dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.InnerClass;
-import javax.inject.Singleton;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-@RunWith(JUnit4.class)
-public class MethodSignatureFormatterTest {
- @Rule public CompilationRule compilationRule = new CompilationRule();
-
- static class OuterClass {
- @interface Foo {
- Class<?> bar();
- }
-
- static class InnerClass {
- @Foo(bar = String.class)
- @Singleton
- String foo(@SuppressWarnings("unused") int a, ImmutableList<Boolean> blah) { return "foo"; }
- }
- }
-
- @Test public void methodSignatureTest() {
- Elements elements = compilationRule.getElements();
- TypeElement inner = elements.getTypeElement(InnerClass.class.getCanonicalName());
- ExecutableElement method = Iterables.getOnlyElement(methodsIn(inner.getEnclosedElements()));
- String formatted = new MethodSignatureFormatter(compilationRule.getTypes()).format(method);
- // This is gross, but it turns out that annotation order is not guaranteed when getting
- // all the AnnotationMirrors from an Element, so I have to test this chopped-up to make it
- // less brittle.
- assertThat(formatted).contains("@Singleton");
- assertThat(formatted).doesNotContain("@javax.inject.Singleton"); // maybe more importantly
- assertThat(formatted)
- .contains("@dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.Foo"
- + "(bar=String.class)");
- assertThat(formatted).contains(" String "); // return type compressed
- assertThat(formatted).contains("int, ImmutableList<Boolean>)"); // parameters compressed.
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java b/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java
deleted file mode 100644
index 03ec35d..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-@RunWith(JUnit4.class)
-public class MissingBindingSuggestionsTest {
- private static JavaFileObject injectable(String className, String constructorParams) {
- return JavaFileObjects.forSourceLines("test." + className,
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class " + className +" {",
- " @Inject " + className + "(" + constructorParams + ") {}",
- "}");
- }
-
- private static JavaFileObject emptyInterface(String interfaceName) {
- return JavaFileObjects.forSourceLines("test." + interfaceName,
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "interface " + interfaceName +" {}");
- }
-
- @Test public void suggestsBindingInSeparateComponent() {
- JavaFileObject fooComponent = JavaFileObjects.forSourceLines("test.FooComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface FooComponent {",
- " Foo getFoo();",
- "}");
- JavaFileObject barModule = JavaFileObjects.forSourceLines("test.BarModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class BarModule {",
- " @Provides Bar provideBar() {return null;}",
- "}");
- JavaFileObject barComponent = JavaFileObjects.forSourceLines("test.BarComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = {BarModule.class})",
- "interface BarComponent {",
- " Bar getBar();",
- "}");
- JavaFileObject foo = injectable("Foo", "Bar bar");
- JavaFileObject bar = emptyInterface("Bar");
-
- JavaFileObject topComponent = JavaFileObjects.forSourceLines("test.TopComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TopComponent {",
- " FooComponent getFoo();",
- " BarComponent getBar(BarModule barModule);",
- "}");
-
- assertAbout(javaSources())
- .that(ImmutableList.of(
- fooComponent, barComponent, topComponent, foo, bar, barModule))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("A binding with matching key exists in component: test.BarComponent");
- }
-
- @Test public void suggestsBindingInNestedSubcomponent() {
- JavaFileObject fooComponent = JavaFileObjects.forSourceLines("test.FooComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface FooComponent {",
- " Foo getFoo();",
- "}");
- JavaFileObject barComponent = JavaFileObjects.forSourceLines("test.BarComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent()",
- "interface BarComponent {",
- " BazComponent getBaz();",
- "}");
- JavaFileObject bazModule = JavaFileObjects.forSourceLines("test.BazModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class BazModule {",
- " @Provides Baz provideBaz() {return null;}",
- "}");
- JavaFileObject bazComponent = JavaFileObjects.forSourceLines("test.BazComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = {BazModule.class})",
- "interface BazComponent {",
- " Baz getBaz();",
- "}");
- JavaFileObject foo = injectable("Foo", "Baz baz");
- JavaFileObject baz = emptyInterface("Baz");
-
- JavaFileObject topComponent = JavaFileObjects.forSourceLines("test.TopComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TopComponent {",
- " FooComponent getFoo();",
- " BarComponent getBar();",
- "}");
-
- assertAbout(javaSources())
- .that(ImmutableList.of(
- fooComponent, barComponent, bazComponent, topComponent, foo, baz, bazModule))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("A binding with matching key exists in component: test.BazComponent");
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ModuleFactoryGeneratorTest.java b/compiler/src/test/java/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
deleted file mode 100644
index 72248e0..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
+++ /dev/null
@@ -1,1070 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import dagger.internal.codegen.writer.StringLiteral;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_ABSTRACT;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_MUST_RETURN_A_VALUE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_IN_MODULE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_PRIVATE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_SET_VALUES_RAW_SET;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_TYPE_PARAMETER;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_SAME_NAME;
-import static dagger.internal.codegen.ErrorMessages.MODULES_WITH_TYPE_PARAMS_MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_METHOD_RETURN_TYPE;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_METHOD_SET_VALUES_RETURN_SET;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS;
-
-@RunWith(JUnit4.class)
-public class ModuleFactoryGeneratorTest {
-
- private final JavaFileObject NULLABLE = JavaFileObjects.forSourceLines("test.Nullable",
- "package test;",
- "public @interface Nullable {}");
-
- private static final StringLiteral NPE_LITERAL =
- StringLiteral.forValue(ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD);
-
- // TODO(gak): add tests for invalid combinations of scope and qualifier annotations like we have
- // for @Inject
-
- private String formatErrorMessage(String msg) {
- return String.format(msg, "Provides");
- }
-
- private String formatModuleErrorMessage(String msg) {
- return String.format(msg, "Provides", "Module");
- }
-
- @Test public void providesMethodNotInModule() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "",
- "final class TestModule {",
- " @Provides String provideString() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatModuleErrorMessage(BINDING_METHOD_NOT_IN_MODULE));
- }
-
- @Test public void providesMethodAbstract() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "abstract class TestModule {",
- " @Provides abstract String provideString();",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_ABSTRACT));
- }
-
- @Test public void providesMethodPrivate() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides private String provideString() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_PRIVATE));
- }
-
- @Test public void providesMethodReturnVoid() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides void provideNothing() {}",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_MUST_RETURN_A_VALUE));
- }
-
- @Test public void providesMethodWithTypeParameter() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides <T> String provideString() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_TYPE_PARAMETER));
- }
-
- @Test public void providesMethodSetValuesWildcard() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET_VALUES;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "import java.util.Set;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides(type = SET_VALUES) Set<?> provideWildcard() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PROVIDES_METHOD_RETURN_TYPE);
- }
-
- @Test public void providesMethodSetValuesRawSet() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET_VALUES;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "import java.util.Set;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides(type = SET_VALUES) Set provideSomething() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_SET_VALUES_RAW_SET));
- }
-
- @Test public void providesMethodSetValuesNotASet() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET_VALUES;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "import java.util.List;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides(type = SET_VALUES) List<String> provideStrings() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PROVIDES_METHOD_SET_VALUES_RETURN_SET);
- }
-
- @Test public void modulesWithTypeParamsMustBeAbstract() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule<A> {}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MODULES_WITH_TYPE_PARAMS_MUST_BE_ABSTRACT);
- }
-
- @Test public void provideOverriddenByNoProvide() {
- JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class Parent {",
- " @Provides String foo() { return null; }",
- "}");
- JavaFileObject child = JavaFileObjects.forSourceLines("test.Child",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "class Child extends Parent{",
- " String foo() { return null; }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(parent, child))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(ErrorMessages.METHOD_OVERRIDES_PROVIDES_METHOD,
- "Provides", "@Provides String test.Parent.foo()"));
- }
-
- @Test public void provideOverriddenByProvide() {
- JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class Parent {",
- " @Provides String foo() { return null; }",
- "}");
- JavaFileObject child = JavaFileObjects.forSourceLines("test.Child",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class Child extends Parent{",
- " @Provides String foo() { return null; }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(parent, child))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(ErrorMessages.PROVIDES_METHOD_OVERRIDES_ANOTHER,
- "Provides", "@Provides String test.Parent.foo()"));
- }
-
- @Test public void providesOverridesNonProvides() {
- JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "class Parent {",
- " String foo() { return null; }",
- "}");
- JavaFileObject child = JavaFileObjects.forSourceLines("test.Child",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class Child extends Parent{",
- " @Provides String foo() { return null; }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(parent, child))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(ErrorMessages.PROVIDES_METHOD_OVERRIDES_ANOTHER,
- "Provides", "String test.Parent.foo()"));
- }
-
- @Test public void validatesIncludedModules() {
- JavaFileObject module = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = Void.class)",
- "class TestModule {}");
- assertAbout(javaSources())
- .that(ImmutableList.of(module))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(
- ErrorMessages.REFERENCED_MODULE_NOT_ANNOTATED, "java.lang.Void", "@Module"));
- }
-
- @Test public void referencedModulesMustNotBeAbstract() {
- JavaFileObject module = JavaFileObjects.forSourceLines("test.Parent",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = AbstractModule.class)",
- "class TestModule {}");
- JavaFileObject abstractModule = JavaFileObjects.forSourceLines("test.AbstractModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "abstract class AbstractModule {}");
- assertAbout(javaSources()).that(ImmutableList.of(module, abstractModule))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(ErrorMessages.REFERENCED_MODULES_MUST_NOT_BE_ABSTRACT,
- "test.AbstractModule"));
- }
-
- @Test public void singleProvidesMethodNoArgs() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides String provideString() {",
- " return \"\";",
- " }",
- "}");
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines("TestModule_ProvideStringFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProvideStringFactory implements Factory<String> {",
- " private final TestModule module;",
- "",
- " public TestModule_ProvideStringFactory(TestModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override public String get() {",
- " String provided = module.provideString();",
- " if (provided == null) {",
- " throw new NullPointerException(" + NPE_LITERAL + ");",
- " }",
- " return provided;",
- " }",
- "",
- " public static Factory<String> create(TestModule module) {",
- " return new TestModule_ProvideStringFactory(module);",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(factoryFile);
- }
-
- @Test public void singleProvidesMethodNoArgs_disableNullable() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides String provideString() {",
- " return \"\";",
- " }",
- "}");
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines("TestModule_ProvideStringFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProvideStringFactory implements Factory<String> {",
- " private final TestModule module;",
- "",
- " public TestModule_ProvideStringFactory(TestModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override public String get() {",
- " return module.provideString();",
- " }",
- "",
- " public static Factory<String> create(TestModule module) {",
- " return new TestModule_ProvideStringFactory(module);",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .withCompilerOptions("-Adagger.nullableValidation=WARNING")
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(factoryFile);
- }
-
- @Test public void nullableProvides() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides @Nullable String provideString() { return null; }",
- "}");
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines("TestModule_ProvideStringFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProvideStringFactory implements Factory<String> {",
- " private final TestModule module;",
- "",
- " public TestModule_ProvideStringFactory(TestModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override",
- " @Nullable",
- " public String get() {",
- " return module.provideString();",
- " }",
- "",
- " public static Factory<String> create(TestModule module) {",
- " return new TestModule_ProvideStringFactory(module);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(moduleFile, NULLABLE))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(factoryFile);
- }
-
- private static final JavaFileObject QUALIFIER_A =
- JavaFileObjects.forSourceLines("test.QualifierA",
- "package test;",
- "",
- "import javax.inject.Qualifier;",
- "",
- "@Qualifier @interface QualifierA {}");
- private static final JavaFileObject QUALIFIER_B =
- JavaFileObjects.forSourceLines("test.QualifierB",
- "package test;",
- "",
- "import javax.inject.Qualifier;",
- "",
- "@Qualifier @interface QualifierB {}");
-
- @Test public void multipleProvidesMethods() {
- JavaFileObject classXFile = JavaFileObjects.forSourceLines("test.X",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class X {",
- " @Inject public String s;",
- "}");
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "import java.util.Arrays;",
- "import java.util.List;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides List<Object> provideObjects(",
- " @QualifierA Object a, @QualifierB Object b, MembersInjector<X> x) {",
- " return Arrays.asList(a, b);",
- " }",
- "",
- " @Provides @QualifierA Object provideAObject() {",
- " return new Object();",
- " }",
- "",
- " @Provides @QualifierB Object provideBObject() {",
- " return new Object();",
- " }",
- "}");
- JavaFileObject listFactoryFile = JavaFileObjects.forSourceLines(
- "TestModule_ProvideObjectsFactory",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import dagger.internal.Factory;",
- "import java.util.List;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProvideObjectsFactory implements Factory<List<Object>> {",
- " private final TestModule module;",
- " private final Provider<Object> aProvider;",
- " private final Provider<Object> bProvider;",
- " private final MembersInjector<X> xMembersInjector;",
- "",
- " public TestModule_ProvideObjectsFactory(",
- " TestModule module,",
- " Provider<Object> aProvider,",
- " Provider<Object> bProvider,",
- " MembersInjector<X> xMembersInjector) {",
- " assert module != null;",
- " this.module = module;",
- " assert aProvider != null;",
- " this.aProvider = aProvider;",
- " assert bProvider != null;",
- " this.bProvider = bProvider;",
- " assert xMembersInjector != null;",
- " this.xMembersInjector = xMembersInjector;",
- " }",
- "",
- " @Override public List<Object> get() {",
- " List<Object> provided =",
- " module.provideObjects(aProvider.get(), bProvider.get(), xMembersInjector);",
- " if (provided == null) {",
- " throw new NullPointerException(" + NPE_LITERAL + ");",
- " }",
- " return provided;",
- " }",
- "",
- " public static Factory<List<Object>> create(",
- " TestModule module,",
- " Provider<Object> aProvider,",
- " Provider<Object> bProvider,",
- " MembersInjector<X> xMembersInjector) {",
- " return new TestModule_ProvideObjectsFactory(",
- " module, aProvider, bProvider, xMembersInjector);",
- " }",
- "}");
- assertAbout(javaSources()).that(
- ImmutableList.of(classXFile, moduleFile, QUALIFIER_A, QUALIFIER_B))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(listFactoryFile);
- }
-
- @Test public void providesSetElement() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET;",
- "",
- "import java.util.logging.Logger;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides(type = SET) String provideString() {",
- " return \"\";",
- " }",
- "}");
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines("TestModule_ProvideStringFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import java.util.Collections;",
- "import java.util.Set;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProvideStringFactory implements Factory<Set<String>> {",
- " private final TestModule module;",
- "",
- " public TestModule_ProvideStringFactory(TestModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override public Set<String> get() {",
- " return Collections.<String>singleton(module.provideString());",
- " }",
- "",
- " public static Factory<Set<String>> create(TestModule module) {",
- " return new TestModule_ProvideStringFactory(module);",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(factoryFile);
- }
-
- @Test public void providesSetElementWildcard() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET;",
- "",
- "import java.util.logging.Logger;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.ArrayList;",
- "import java.util.List;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides(type = SET) List<List<?>> provideWildcardList() {",
- " return new ArrayList<>();",
- " }",
- "}");
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines(
- "TestModule_ProvideWildcardListFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import java.util.Collections;",
- "import java.util.List;",
- "import java.util.Set;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProvideWildcardListFactory implements "
- + "Factory<Set<List<List<?>>>> {",
- " private final TestModule module;",
- "",
- " public TestModule_ProvideWildcardListFactory(TestModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override public Set<List<List<?>>> get() {",
- " return Collections.<List<List<?>>>singleton(module.provideWildcardList());",
- " }",
- "",
- " public static Factory<Set<List<List<?>>>> create(TestModule module) {",
- " return new TestModule_ProvideWildcardListFactory(module);",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(factoryFile);
- }
-
- @Test public void providesSetValues() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.Provides.Type.SET_VALUES;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.Set;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides(type = SET_VALUES) Set<String> provideStrings() {",
- " return null;",
- " }",
- "}");
- JavaFileObject factoryFile = JavaFileObjects.forSourceLines("TestModule_ProvideStringsFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import java.util.Set;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProvideStringsFactory implements Factory<Set<String>> {",
- " private final TestModule module;",
- "",
- " public TestModule_ProvideStringsFactory(TestModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override public Set<String> get() {",
- " Set<String> provided = module.provideStrings();",
- " if (provided == null) {",
- " throw new NullPointerException(" + NPE_LITERAL + ");",
- " }",
- " return provided;",
- " }",
- "",
- " public static Factory<Set<String>> create(TestModule module) {",
- " return new TestModule_ProvideStringsFactory(module);",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(factoryFile);
- }
-
- @Test public void multipleProvidesMethodsWithSameName() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides Object provide(int i) {",
- " return i;",
- " }",
- "",
- " @Provides String provide() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_WITH_SAME_NAME)).in(moduleFile).onLine(8)
- .and().withErrorContaining(formatErrorMessage(BINDING_METHOD_WITH_SAME_NAME))
- .in(moduleFile).onLine(12);
- }
-
- @Test
- public void providedTypes() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.io.Closeable;",
- "import java.util.Set;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides String string() {",
- " return null;",
- " }",
- "",
- " @Provides Set<String> strings() {",
- " return null;",
- " }",
- "",
- " @Provides Set<? extends Closeable> closeables() {",
- " return null;",
- " }",
- "",
- " @Provides String[] stringArray() {",
- " return null;",
- " }",
- "",
- " @Provides int integer() {",
- " return 0;",
- " }",
- "",
- " @Provides int[] integers() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test
- public void privateModule() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "final class Enclosing {",
- " @Module private static final class PrivateModule {",
- " }",
- "}");
- assertAbout(javaSource())
- .that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("Modules cannot be private.")
- .in(moduleFile).onLine(6);
- }
-
- @Test
- public void enclosedInPrivateModule() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "final class Enclosing {",
- " private static final class PrivateEnclosing {",
- " @Module static final class TestModule {",
- " }",
- " }",
- "}");
- assertAbout(javaSource())
- .that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("Modules cannot be enclosed in private types.")
- .in(moduleFile).onLine(7);
- }
-
- @Test
- public void publicModuleNonPublicIncludes() {
- JavaFileObject publicModuleFile = JavaFileObjects.forSourceLines("test.PublicModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(includes = {",
- " NonPublicModule1.class, OtherPublicModule.class, NonPublicModule2.class",
- "})",
- "public final class PublicModule {",
- "}");
- JavaFileObject nonPublicModule1File = JavaFileObjects.forSourceLines("test.NonPublicModule1",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class NonPublicModule1 {",
- "}");
- JavaFileObject nonPublicModule2File = JavaFileObjects.forSourceLines("test.NonPublicModule2",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class NonPublicModule2 {",
- "}");
- JavaFileObject otherPublicModuleFile = JavaFileObjects.forSourceLines("test.OtherPublicModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "public final class OtherPublicModule {",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(
- publicModuleFile, nonPublicModule1File, nonPublicModule2File, otherPublicModuleFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("This module is public, but it includes non-public "
- + "(or effectively non-public) modules. "
- + "Either reduce the visibility of this module or make "
- + "test.NonPublicModule1 and test.NonPublicModule2 public.")
- .in(publicModuleFile).onLine(8);
- }
-
- @Test
- public void genericSubclassedModule() {
- JavaFileObject parent = JavaFileObjects.forSourceLines("test.ParentModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.List;",
- "import java.util.ArrayList;",
- "",
- "@Module",
- "abstract class ParentModule<A extends CharSequence,",
- " B,",
- " C extends Number & Comparable<C>> {",
- " @Provides List<B> provideListB(B b) {",
- " List<B> list = new ArrayList<B>();",
- " list.add(b);",
- " return list;",
- " }",
- "}");
- JavaFileObject numberChild = JavaFileObjects.forSourceLines("test.ChildNumberModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class ChildNumberModule extends ParentModule<String, Number, Double> {",
- " @Provides Number provideNumber() { return 1; }",
- "}");
- JavaFileObject integerChild = JavaFileObjects.forSourceLines("test.ChildIntegerModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class ChildIntegerModule extends ParentModule<StringBuilder, Integer, Float> {",
- " @Provides Integer provideInteger() { return 2; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.C",
- "package test;",
- "",
- "import dagger.Component;",
- "import java.util.List;",
- "",
- "@Component(modules={ChildNumberModule.class, ChildIntegerModule.class})",
- "interface C {",
- " List<Number> numberList();",
- " List<Integer> integerList();",
- "}");
- JavaFileObject listBFactory = JavaFileObjects.forSourceLines(
- "test.ParentModule_ProvidesListBFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import java.util.List;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class ParentModule_ProvideListBFactory<A extends CharSequence,",
- " B, C extends Number & Comparable<C>> implements Factory<List<B>> {",
- " private final ParentModule<A, B, C> module;",
- " private final Provider<B> bProvider;",
- "",
- " public ParentModule_ProvideListBFactory(",
- " ParentModule<A, B, C> module, Provider<B> bProvider) {",
- " assert module != null;",
- " this.module = module;",
- " assert bProvider != null;",
- " this.bProvider = bProvider;",
- " }",
- "",
- " @Override",
- " public List<B> get() { ",
- " List<B> provided = module.provideListB(bProvider.get());",
- " if (provided == null) {",
- " throw new NullPointerException(" + NPE_LITERAL + ");",
- " }",
- " return provided;",
- " }",
- "",
- " public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
- " Factory<List<B>> create(ParentModule<A, B, C> module, Provider<B> bProvider) {",
- " return new ParentModule_ProvideListBFactory<A, B, C>(module, bProvider);",
- " }",
- "}");
- JavaFileObject numberFactory = JavaFileObjects.forSourceLines(
- "test.ChildNumberModule_ProvideNumberFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class ChildNumberModule_ProvideNumberFactory implements Factory<Number> {",
- " private final ChildNumberModule module;",
- "",
- " public ChildNumberModule_ProvideNumberFactory(ChildNumberModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override",
- " public Number get() { ",
- " Number provided = module.provideNumber();",
- " if (provided == null) {",
- " throw new NullPointerException(" + NPE_LITERAL + ");",
- " }",
- " return provided;",
- " }",
- "",
- " public static Factory<Number> create(ChildNumberModule module) {",
- " return new ChildNumberModule_ProvideNumberFactory(module);",
- " }",
- "}");
- JavaFileObject integerFactory = JavaFileObjects.forSourceLines(
- "test.ChildIntegerModule_ProvideIntegerFactory",
- "package test;",
- "",
- "import dagger.internal.Factory;",
- "import javax.annotation.Generated;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class ChildIntegerModule_ProvideIntegerFactory",
- " implements Factory<Integer> {",
- " private final ChildIntegerModule module;",
- "",
- " public ChildIntegerModule_ProvideIntegerFactory(ChildIntegerModule module) {",
- " assert module != null;",
- " this.module = module;",
- " }",
- "",
- " @Override",
- " public Integer get() { ",
- " Integer provided = module.provideInteger();",
- " if (provided == null) {",
- " throw new NullPointerException(" + NPE_LITERAL + ");",
- " }",
- " return provided;",
- " }",
- "",
- " public static Factory<Integer> create(ChildIntegerModule module) {",
- " return new ChildIntegerModule_ProvideIntegerFactory(module);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(parent, numberChild, integerChild, component))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(listBFactory, numberFactory, integerFactory);
- }
-
- @Test public void providesMethodMultipleQualifiers() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "import javax.annotation.Nullable;",
- "import javax.inject.Singleton;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides @QualifierA @QualifierB String provideString() {",
- " return \"foo\";",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(moduleFile, QUALIFIER_A, QUALIFIER_B))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/MultipleRequestTest.java b/compiler/src/test/java/dagger/internal/codegen/MultipleRequestTest.java
deleted file mode 100644
index 27e720d..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/MultipleRequestTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assert_;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-@RunWith(JUnit4.class)
-public class MultipleRequestTest {
- private static final JavaFileObject DEP_FILE = JavaFileObjects.forSourceLines("test.Dep",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class Dep {",
- " @Inject Dep() {}",
- "}");
-
- @Test public void multipleRequests_constructor() {
- assert_().about(javaSources())
- .that(ImmutableList.of(
- DEP_FILE,
- JavaFileObjects.forSourceLines("test.ConstructorInjectsMultiple",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class ConstructorInjectsMultiple {",
- " @Inject ConstructorInjectsMultiple(Dep d1, Dep d2) {}",
- "}"),
- JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " ConstructorInjectsMultiple get();",
- "}")))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test public void multipleRequests_field() {
- assert_().about(javaSources())
- .that(ImmutableList.of(
- DEP_FILE,
- JavaFileObjects.forSourceLines("test.FieldInjectsMultiple",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class FieldInjectsMultiple {",
- " @Inject Dep d1;",
- " @Inject Dep d2;",
- " @Inject FieldInjectsMultiple() {}",
- "}"),
- JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface SimpleComponent {",
- " FieldInjectsMultiple get();",
- "}")))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-
- @Test public void multipleRequests_providesMethod() {
- assert_().about(javaSources())
- .that(ImmutableList.of(
- DEP_FILE,
- JavaFileObjects.forSourceLines("test.FieldInjectsMultiple",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class SimpleModule {",
- " @Provides Object provide(Dep d1, Dep d2) {",
- " return null;",
- " }",
- "}"),
- JavaFileObjects.forSourceLines("test.SimpleComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = SimpleModule.class)",
- "interface SimpleComponent {",
- " Object get();",
- "}")))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java b/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
deleted file mode 100644
index f9c2878..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// TODO(beder): Merge the error-handling tests with the ModuleFactoryGeneratorTest.
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_ABSTRACT;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_MUST_RETURN_A_VALUE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_IN_MODULE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_PRIVATE;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_SET_VALUES_RAW_SET;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_TYPE_PARAMETER;
-import static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_SAME_NAME;
-import static dagger.internal.codegen.ErrorMessages.PRODUCES_METHOD_RAW_FUTURE;
-import static dagger.internal.codegen.ErrorMessages.PRODUCES_METHOD_RETURN_TYPE;
-import static dagger.internal.codegen.ErrorMessages.PRODUCES_METHOD_SET_VALUES_RETURN_SET;
-import static dagger.internal.codegen.ErrorMessages.PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS;
-
-@RunWith(JUnit4.class)
-public class ProducerModuleFactoryGeneratorTest {
- private String formatErrorMessage(String msg) {
- return String.format(msg, "Produces");
- }
-
- private String formatModuleErrorMessage(String msg) {
- return String.format(msg, "Produces", "ProducerModule");
- }
-
- @Test public void producesMethodNotInModule() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.producers.Produces;",
- "",
- "final class TestModule {",
- " @Produces String produceString() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatModuleErrorMessage(BINDING_METHOD_NOT_IN_MODULE));
- }
-
- @Test public void producesMethodAbstract() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "abstract class TestModule {",
- " @Produces abstract String produceString();",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_ABSTRACT));
- }
-
- @Test public void producesMethodPrivate() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces private String produceString() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_PRIVATE));
- }
-
- @Test public void producesMethodReturnVoid() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces void produceNothing() {}",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_MUST_RETURN_A_VALUE));
- }
-
- @Test public void producesMethodReturnRawFuture() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces ListenableFuture produceRaw() {}",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRODUCES_METHOD_RAW_FUTURE);
- }
-
- @Test public void producesMethodReturnWildcardFuture() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces ListenableFuture<?> produceRaw() {}",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRODUCES_METHOD_RETURN_TYPE);
- }
-
- @Test public void producesMethodWithTypeParameter() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces <T> String produceString() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_TYPE_PARAMETER));
- }
-
- @Test public void producesMethodSetValuesWildcard() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.producers.Produces.Type.SET_VALUES;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "import java.util.Set;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces(type = SET_VALUES) Set<?> produceWildcard() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRODUCES_METHOD_RETURN_TYPE);
- }
-
- @Test public void producesMethodSetValuesRawSet() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.producers.Produces.Type.SET_VALUES;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "import java.util.Set;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces(type = SET_VALUES) Set produceSomething() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_SET_VALUES_RAW_SET));
- }
-
- @Test public void producesMethodSetValuesNotASet() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.producers.Produces.Type.SET_VALUES;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "import java.util.List;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces(type = SET_VALUES) List<String> produceStrings() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRODUCES_METHOD_SET_VALUES_RETURN_SET);
- }
-
- @Test public void producesMethodSetValuesWildcardInFuture() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.producers.Produces.Type.SET_VALUES;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "import java.util.Set;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces(type = SET_VALUES) ListenableFuture<Set<?>> produceWildcard() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRODUCES_METHOD_RETURN_TYPE);
- }
-
- @Test public void producesMethodSetValuesFutureRawSet() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.producers.Produces.Type.SET_VALUES;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "import java.util.Set;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces(type = SET_VALUES) ListenableFuture<Set> produceSomething() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(formatErrorMessage(BINDING_METHOD_SET_VALUES_RAW_SET));
- }
-
- @Test public void producesMethodSetValuesFutureNotASet() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import static dagger.producers.Produces.Type.SET_VALUES;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "import java.util.List;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces(type = SET_VALUES) ListenableFuture<List<String>> produceStrings() {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PRODUCES_METHOD_SET_VALUES_RETURN_SET);
- }
-
- @Test public void multipleProducesMethodsWithSameName() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces Object produce(int i) {",
- " return i;",
- " }",
- "",
- " @Produces String produce() {",
- " return \"\";",
- " }",
- "}");
- String errorMessage = String.format(BINDING_METHOD_WITH_SAME_NAME, "Produces");
- assertAbout(javaSource()).that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(errorMessage).in(moduleFile).onLine(8)
- .and().withErrorContaining(errorMessage).in(moduleFile).onLine(12);
- }
-
- @Test
- public void privateModule() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "",
- "final class Enclosing {",
- " @ProducerModule private static final class PrivateModule {",
- " }",
- "}");
- assertAbout(javaSource())
- .that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("Modules cannot be private.")
- .in(moduleFile).onLine(6);
- }
-
- @Test
- public void enclosedInPrivateModule() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "",
- "final class Enclosing {",
- " private static final class PrivateEnclosing {",
- " @ProducerModule static final class TestModule {",
- " }",
- " }",
- "}");
- assertAbout(javaSource())
- .that(moduleFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("Modules cannot be enclosed in private types.")
- .in(moduleFile).onLine(7);
- }
-
- @Test
- public void includesNonModule() {
- JavaFileObject xFile =
- JavaFileObjects.forSourceLines("test.X", "package test;", "", "public final class X {}");
- JavaFileObject moduleFile =
- JavaFileObjects.forSourceLines(
- "test.FooModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "",
- "@ProducerModule(includes = X.class)",
- "public final class FooModule {",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(xFile, moduleFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(
- ErrorMessages.REFERENCED_MODULE_NOT_ANNOTATED,
- "X",
- "one of @Module, @ProducerModule"));
- }
-
- @Test
- public void publicModuleNonPublicIncludes() {
- JavaFileObject publicModuleFile = JavaFileObjects.forSourceLines("test.PublicModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "",
- "@ProducerModule(includes = {",
- " NonPublicModule1.class, OtherPublicModule.class, NonPublicModule2.class",
- "})",
- "public final class PublicModule {",
- "}");
- JavaFileObject nonPublicModule1File = JavaFileObjects.forSourceLines("test.NonPublicModule1",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "",
- "@ProducerModule",
- "final class NonPublicModule1 {",
- "}");
- JavaFileObject nonPublicModule2File = JavaFileObjects.forSourceLines("test.NonPublicModule2",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "",
- "@ProducerModule",
- "final class NonPublicModule2 {",
- "}");
- JavaFileObject otherPublicModuleFile = JavaFileObjects.forSourceLines("test.OtherPublicModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "",
- "@ProducerModule",
- "public final class OtherPublicModule {",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(
- publicModuleFile, nonPublicModule1File, nonPublicModule2File, otherPublicModuleFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("This module is public, but it includes non-public "
- + "(or effectively non-public) modules. "
- + "Either reduce the visibility of this module or make "
- + "test.NonPublicModule1 and test.NonPublicModule2 public.")
- .in(publicModuleFile).onLine(8);
- }
-
- @Test public void singleProducesMethodNoArgsFuture() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces ListenableFuture<String> produceString() {",
- " return null;",
- " }",
- "}");
- JavaFileObject factoryFile =
- JavaFileObjects.forSourceLines(
- "TestModule_ProduceStringFactory",
- "package test;",
- "",
- "import com.google.common.util.concurrent.AsyncFunction;",
- "import com.google.common.util.concurrent.Futures;",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.internal.AbstractProducer;",
- "import dagger.producers.monitoring.ProducerMonitor;",
- "import dagger.producers.monitoring.ProducerToken;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "import java.util.concurrent.Executor;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class TestModule_ProduceStringFactory extends AbstractProducer<String> {",
- " private final TestModule module;",
- " private final Executor executor;",
- " private final Provider<ProductionComponentMonitor> monitorProvider;",
- "",
- " public TestModule_ProduceStringFactory(",
- " TestModule module,",
- " Executor executor,",
- " Provider<ProductionComponentMonitor> monitorProvider) {",
- " super(",
- " monitorProvider,",
- " ProducerToken.create(TestModule_ProduceStringFactory.class));",
- " assert module != null;",
- " this.module = module;",
- " assert executor != null;",
- " this.executor = executor;",
- " assert monitorProvider != null;",
- " this.monitorProvider = monitorProvider;",
- " }",
- "",
- " @Override protected ListenableFuture<String> compute(",
- " final ProducerMonitor monitor) {",
- " return Futures.transform(",
- " Futures.<Void>immediateFuture(null),",
- " new AsyncFunction<Void, String>() {",
- " @Override public ListenableFuture<String> apply(Void ignoredVoidArg) {",
- " monitor.methodStarting();",
- " try {",
- " return module.produceString();",
- " } finally {",
- " monitor.methodFinished();",
- " }",
- " }",
- " }, executor);",
- " }",
- "}");
- assertAbout(javaSource())
- .that(moduleFile)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(factoryFile);
- }
-
- private static final JavaFileObject QUALIFIER_A =
- JavaFileObjects.forSourceLines("test.QualifierA",
- "package test;",
- "",
- "import javax.inject.Qualifier;",
- "",
- "@Qualifier @interface QualifierA {}");
- private static final JavaFileObject QUALIFIER_B =
- JavaFileObjects.forSourceLines("test.QualifierB",
- "package test;",
- "",
- "import javax.inject.Qualifier;",
- "",
- "@Qualifier @interface QualifierB {}");
-
- @Test public void producesMethodMultipleQualifiers() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class TestModule {",
- " @Produces @QualifierA @QualifierB abstract String produceString() {",
- " return \"\";",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(moduleFile, QUALIFIER_A, QUALIFIER_B))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java
deleted file mode 100644
index a8e39b2..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-
-@RunWith(JUnit4.class)
-public class ProductionComponentProcessorTest {
- @Test public void componentOnConcreteClass() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.producers.ProductionComponent;",
- "",
- "@ProductionComponent",
- "final class NotAComponent {}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("interface");
- }
-
- @Test public void componentOnEnum() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.producers.ProductionComponent;",
- "",
- "@ProductionComponent",
- "enum NotAComponent {",
- " INSTANCE",
- "}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("interface");
- }
-
- @Test public void componentOnAnnotation() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.producers.ProductionComponent;",
- "",
- "@ProductionComponent",
- "@interface NotAComponent {}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("interface");
- }
-
- @Test public void nonModuleModule() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
- "package test;",
- "",
- "import dagger.producers.ProductionComponent;",
- "",
- "@ProductionComponent(modules = Object.class)",
- "interface NotAComponent {}");
- assertAbout(javaSource()).that(componentFile)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("is not annotated with @Module or @ProducerModule");
- }
-
- @Test public void simpleComponent() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "import dagger.producers.ProductionComponent;",
- "import javax.inject.Inject;",
- "",
- "final class TestClass {",
- " static final class C {",
- " @Inject C() {}",
- " }",
- "",
- " interface A {}",
- " interface B {}",
- "",
- " @Module",
- " static final class BModule {",
- " @Provides B b(C c) {",
- " return null;",
- " }",
- " }",
- "",
- " @ProducerModule",
- " static final class AModule {",
- " @Produces ListenableFuture<A> a(B b) {",
- " return null;",
- " }",
- " }",
- "",
- " @ProductionComponent(modules = {AModule.class, BModule.class})",
- " interface SimpleComponent {",
- " ListenableFuture<A> a();",
- " }",
- "}");
- JavaFileObject generatedComponent =
- JavaFileObjects.forSourceLines(
- "test.DaggerTestClass_SimpleComponent",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.internal.InstanceFactory;",
- "import dagger.internal.SetFactory;",
- "import dagger.producers.Producer;",
- "import dagger.producers.internal.Producers;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "import dagger.producers.monitoring.ProductionComponentMonitor.Factory;",
- "import java.util.Set;",
- "import java.util.concurrent.Executor;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import test.TestClass.A;",
- "import test.TestClass.AModule;",
- "import test.TestClass.B;",
- "import test.TestClass.BModule;",
- "import test.TestClass.SimpleComponent;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestClass_SimpleComponent implements SimpleComponent {",
- " private Provider<SimpleComponent> simpleComponentProvider;",
- " private Provider<Set<Factory>> setOfFactoryContribution1Provider;",
- " private Provider<Set<Factory>> setOfFactoryProvider;",
- " private Provider<ProductionComponentMonitor> monitorProvider;",
- " private Provider<B> bProvider;",
- " private Producer<A> aProducer;",
- "",
- " private DaggerTestClass_SimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.simpleComponentProvider = InstanceFactory.<SimpleComponent>create(this);",
- " this.setOfFactoryContribution1Provider =",
- " TestClass$SimpleComponent_MonitoringModule_DefaultSetOfFactoriesFactory",
- " .create();",
- " this.setOfFactoryProvider = SetFactory.create(setOfFactoryContribution1Provider);",
- " this.monitorProvider =",
- " TestClass$SimpleComponent_MonitoringModule_MonitorFactory.create(",
- " builder.testClass$SimpleComponent_MonitoringModule,",
- " simpleComponentProvider,",
- " setOfFactoryProvider);",
- " this.bProvider = TestClass$BModule_BFactory.create(",
- " builder.bModule, TestClass$C_Factory.create());",
- " this.aProducer = new TestClass$AModule_AFactory(",
- " builder.aModule,",
- " builder.executor,",
- " monitorProvider,",
- " Producers.producerFromProvider(bProvider));",
- " }",
- "",
- " @Override",
- " public ListenableFuture<A> a() {",
- " return aProducer.get();",
- " }",
- "",
- " public static final class Builder {",
- " private TestClass$SimpleComponent_MonitoringModule",
- " testClass$SimpleComponent_MonitoringModule;",
- " private BModule bModule;",
- " private AModule aModule;",
- " private Executor executor;",
- "",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " if (testClass$SimpleComponent_MonitoringModule == null) {",
- " this.testClass$SimpleComponent_MonitoringModule =",
- " new TestClass$SimpleComponent_MonitoringModule();",
- " }",
- " if (bModule == null) {",
- " this.bModule = new BModule();",
- " }",
- " if (aModule == null) {",
- " this.aModule = new AModule();",
- " }",
- " if (executor == null) {",
- " throw new IllegalStateException(Executor.class.getCanonicalName()",
- " + \" must be set\");",
- " }",
- " return new DaggerTestClass_SimpleComponent(this);",
- " }",
- "",
- " public Builder aModule(AModule aModule) {",
- " if (aModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.aModule = aModule;",
- " return this;",
- " }",
- "",
- " public Builder bModule(BModule bModule) {",
- " if (bModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.bModule = bModule;",
- " return this;",
- " }",
- "",
- " public Builder testClass$SimpleComponent_MonitoringModule(",
- " TestClass$SimpleComponent_MonitoringModule",
- " testClass$SimpleComponent_MonitoringModule) {",
- " if (testClass$SimpleComponent_MonitoringModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.testClass$SimpleComponent_MonitoringModule =",
- " testClass$SimpleComponent_MonitoringModule;",
- " return this;",
- " }",
- "",
- " public Builder executor(Executor executor) {",
- " if (executor == null) {",
- " throw new NullPointerException();",
- " }",
- " this.executor = executor;",
- " return this;",
- " }",
- " }",
- "}");
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(generatedComponent);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java
deleted file mode 100644
index 6fc8716..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.testing.compile.JavaFileObjects;
-import java.util.Arrays;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-/**
- * Unit tests for {@link BindingGraphValidator} that exercise producer-specific logic.
- */
-@RunWith(JUnit4.class)
-public class ProductionGraphValidationTest {
- @Test public void componentWithUnprovidedInput() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.MyComponent",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProductionComponent;",
- "",
- "@ProductionComponent(modules = FooModule.class)",
- "interface MyComponent {",
- " ListenableFuture<Foo> getFoo();",
- "}");
- JavaFileObject module = JavaFileObjects.forSourceLines("test.FooModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "class Foo {}",
- "class Bar {}",
- "",
- "@ProducerModule",
- "class FooModule {",
- " @Produces Foo foo(Bar bar) {",
- " return null;",
- " }",
- "}");
- assertAbout(javaSources()).that(Arrays.asList(module, component))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("test.Bar cannot be provided without an @Inject constructor or from "
- + "an @Provides- or @Produces-annotated method.")
- .in(component).onLine(8);
- }
-
- @Test public void componentProductionWithNoDependencyChain() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProductionComponent;",
- "",
- "final class TestClass {",
- " interface A {}",
- "",
- " @ProductionComponent()",
- " interface AComponent {",
- " ListenableFuture<A> getA();",
- " }",
- "}");
- String expectedError =
- "test.TestClass.A cannot be provided without an @Provides- or @Produces-annotated method.";
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(11);
- }
-
- @Test public void provisionDependsOnProduction() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "import dagger.producers.ProductionComponent;",
- "",
- "final class TestClass {",
- " interface A {}",
- " interface B {}",
- "",
- " @Module",
- " final class AModule {",
- " @Provides A a(B b) {",
- " return null;",
- " }",
- " }",
- "",
- " @ProducerModule",
- " final class BModule {",
- " @Produces ListenableFuture<B> b() {",
- " return null;",
- " }",
- " }",
- "",
- " @ProductionComponent(modules = {AModule.class, BModule.class})",
- " interface AComponent {",
- " ListenableFuture<A> getA();",
- " }",
- "}");
- String expectedError =
- "test.TestClass.A is a provision, which cannot depend on a production.";
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(30);
- }
-
- @Test public void provisionEntryPointDependsOnProduction() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "import dagger.producers.ProductionComponent;",
- "",
- "final class TestClass {",
- " interface A {}",
- "",
- " @ProducerModule",
- " final class AModule {",
- " @Produces ListenableFuture<A> a() {",
- " return null;",
- " }",
- " }",
- "",
- " @ProductionComponent(modules = AModule.class)",
- " interface AComponent {",
- " A getA();",
- " }",
- "}");
- String expectedError =
- "test.TestClass.A is a provision entry-point, which cannot depend on a production.";
- assertAbout(javaSource()).that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(20);
- }
-
- @Test
- public void monitoringDependsOnUnboundType() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "import dagger.producers.ProductionComponent;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "",
- "import static dagger.Provides.Type.SET;",
- "",
- "final class TestClass {",
- " interface A {}",
- "",
- " @Module",
- " final class MonitoringModule {",
- " @Provides(type = SET)",
- " ProductionComponentMonitor.Factory monitorFactory(A unbound) {",
- " return null;",
- " }",
- " }",
- "",
- " @ProducerModule",
- " final class StringModule {",
- " @Produces ListenableFuture<String> str() {",
- " return null;",
- " }",
- " }",
- "",
- " @ProductionComponent(modules = {MonitoringModule.class, StringModule.class})",
- " interface StringComponent {",
- " ListenableFuture<String> getString();",
- " }",
- "}");
- String expectedError =
- "test.TestClass.A cannot be provided without an @Provides-annotated method.";
- assertAbout(javaSource())
- .that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError)
- .in(component)
- .onLine(33);
- }
-
- @Test
- public void monitoringDependsOnProduction() {
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "import dagger.producers.ProductionComponent;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "",
- "import static dagger.Provides.Type.SET;",
- "",
- "final class TestClass {",
- " interface A {}",
- "",
- " @Module",
- " final class MonitoringModule {",
- " @Provides(type = SET) ProductionComponentMonitor.Factory monitorFactory(A a) {",
- " return null;",
- " }",
- " }",
- "",
- " @ProducerModule",
- " final class StringModule {",
- " @Produces A a() {",
- " return null;",
- " }",
- "",
- " @Produces ListenableFuture<String> str() {",
- " return null;",
- " }",
- " }",
- "",
- " @ProductionComponent(modules = {MonitoringModule.class, StringModule.class})",
- " interface StringComponent {",
- " ListenableFuture<String> getString();",
- " }",
- "}");
- String expectedError =
- "java.util.Set<dagger.producers.monitoring.ProductionComponentMonitor.Factory> is a"
- + " provision, which cannot depend on a production.";
- assertAbout(javaSource())
- .that(component)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(expectedError)
- .in(component)
- .onLine(36);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/RepeatedModuleValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/RepeatedModuleValidationTest.java
deleted file mode 100644
index 6bf0c9e..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/RepeatedModuleValidationTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-@RunWith(JUnit4.class)
-public class RepeatedModuleValidationTest {
- private static final JavaFileObject MODULE_FILE =
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class TestModule {}");
-
- @Test
- public void moduleRepeatedInSubcomponentFactoryMethod() {
- JavaFileObject subcomponentFile =
- JavaFileObjects.forSourceLines(
- "test.TestSubcomponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = TestModule.class)",
- "interface TestSubcomponent {",
- "}");
- JavaFileObject componentFile =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " TestSubcomponent newTestSubcomponent(TestModule module);",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(MODULE_FILE, subcomponentFile, componentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("This module is present in test.TestComponent.")
- .in(componentFile)
- .onLine(7)
- .atColumn(51);
- }
-
- @Test
- public void moduleRepeatedInSubcomponentBuilderMethod() {
- JavaFileObject subcomponentFile =
- JavaFileObjects.forSourceLines(
- "test.TestSubcomponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = TestModule.class)",
- "interface TestSubcomponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " Builder testModule(TestModule testModule);",
- " TestSubcomponent build();",
- " }",
- "}");
- JavaFileObject componentFile =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " TestSubcomponent.Builder newTestSubcomponentBuilder();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(MODULE_FILE, subcomponentFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- // TODO(gak): assert about the warning when we have that ability
- }
-
- @Test
- public void moduleRepeatedButNotPassed() {
- JavaFileObject subcomponentFile =
- JavaFileObjects.forSourceLines(
- "test.TestSubcomponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = TestModule.class)",
- "interface TestSubcomponent {",
- "}");
- JavaFileObject componentFile =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " TestSubcomponent newTestSubcomponent();",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(MODULE_FILE, subcomponentFile, componentFile))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError();
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/SubcomponentBuilderValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
deleted file mode 100644
index 6311a90..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-/** Tests for {@link dagger.Subcomponent.Builder} validation. */
-@RunWith(JUnit4.class)
-public class SubcomponentBuilderValidationTest {
-
- private static final ErrorMessages.SubcomponentBuilderMessages MSGS =
- new ErrorMessages.SubcomponentBuilderMessages();
-
- @Test
- public void testRefSubcomponentAndSubBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent child();",
- " ChildComponent.Builder builder();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {",
- " @Subcomponent.Builder",
- " static interface Builder {",
- " ChildComponent build();",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(MSGS.moreThanOneRefToSubcomponent(),
- "test.ChildComponent", "[child(), builder()]"))
- .in(componentFile);
- }
-
- @Test
- public void testRefSubBuilderTwiceFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent.Builder builder1();",
- " ChildComponent.Builder builder2();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {",
- " @Subcomponent.Builder",
- " static interface Builder {",
- " ChildComponent build();",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(MSGS.moreThanOneRefToSubcomponent(),
- "test.ChildComponent", "[builder1(), builder2()]"))
- .in(componentFile);
- }
-
- @Test
- public void testMoreThanOneBuilderFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent.Builder1 build();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {",
- " @Subcomponent.Builder",
- " static interface Builder1 {",
- " ChildComponent build();",
- " }",
- "",
- " @Subcomponent.Builder",
- " static interface Builder2 {",
- " ChildComponent build();",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(MSGS.moreThanOne(),
- "[test.ChildComponent.Builder1, test.ChildComponent.Builder2]"))
- .in(childComponentFile);
- }
-
- @Test
- public void testBuilderGenericsFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent.Builder1 build();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder<T> {",
- " ChildComponent build();",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.generics())
- .in(childComponentFile);
- }
-
- @Test
- public void testBuilderNotInComponentFails() {
- JavaFileObject builder = JavaFileObjects.forSourceLines("test.Builder",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent.Builder",
- "interface Builder {}");
- assertAbout(javaSource()).that(builder)
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeInComponent())
- .in(builder);
- }
-
- @Test
- public void testBuilderMissingBuildMethodFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent.Builder1 build();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.missingBuildMethod())
- .in(childComponentFile);
- }
-
- @Test
- public void testPrivateBuilderFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " private interface Builder {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.isPrivate())
- .in(childComponentFile);
- }
-
- @Test
- public void testNonStaticBuilderFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " abstract class Builder {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeStatic())
- .in(childComponentFile);
- }
-
- @Test
- public void testNonAbstractBuilderFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " static class Builder {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeAbstract())
- .in(childComponentFile);
- }
-
- @Test
- public void testBuilderOneCxtorWithArgsFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " static abstract class Builder {",
- " Builder(String unused) {}",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.cxtorOnlyOneAndNoArgs())
- .in(childComponentFile);
- }
-
- @Test
- public void testBuilderMoreThanOneCxtorFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " static abstract class Builder {",
- " Builder() {}",
- " Builder(String unused) {}",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.cxtorOnlyOneAndNoArgs())
- .in(childComponentFile);
- }
-
- @Test
- public void testBuilderEnumFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " enum Builder {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.mustBeClassOrInterface())
- .in(childComponentFile);
- }
-
- @Test
- public void testBuilderBuildReturnsWrongTypeFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " String build();",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.buildMustReturnComponentType())
- .in(childComponentFile).onLine(9);
- }
-
- @Test
- public void testInheritedBuilderBuildReturnsWrongTypeFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " interface Parent {",
- " String build();",
- " }",
- "",
- " @Subcomponent.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedBuildMustReturnComponentType(), "build"))
- .in(childComponentFile).onLine(12);
- }
-
- @Test
- public void testTwoBuildMethodsFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " ChildComponent build();",
- " ChildComponent create();",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(String.format(MSGS.twoBuildMethods(), "build()"))
- .in(childComponentFile).onLine(10);
- }
-
- @Test
- public void testInheritedTwoBuildMethodsFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " interface Parent {",
- " ChildComponent build();",
- " ChildComponent create();",
- " }",
- "",
- " @Subcomponent.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedTwoBuildMethods(), "create()", "build()"))
- .in(childComponentFile).onLine(13);
- }
-
- @Test
- public void testMoreThanOneArgFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " ChildComponent build();",
- " Builder set(String s, Integer i);",
- " Builder set(Number n, Double d);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.methodsMustTakeOneArg())
- .in(childComponentFile).onLine(10)
- .and().withErrorContaining(MSGS.methodsMustTakeOneArg())
- .in(childComponentFile).onLine(11);
- }
-
- @Test
- public void testInheritedMoreThanOneArgFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " interface Parent {",
- " ChildComponent build();",
- " Builder set1(String s, Integer i);",
- " }",
- "",
- " @Subcomponent.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedMethodsMustTakeOneArg(),
- "set1(java.lang.String,java.lang.Integer)"))
- .in(childComponentFile).onLine(13);
- }
-
- @Test
- public void testSetterReturningNonVoidOrBuilderFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " ChildComponent build();",
- " String set(Integer i);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.methodsMustReturnVoidOrBuilder())
- .in(childComponentFile).onLine(10);
- }
-
- @Test
- public void testInheritedSetterReturningNonVoidOrBuilderFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " interface Parent {",
- " ChildComponent build();",
- " String set(Integer i);",
- " }",
- "",
- " @Subcomponent.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedMethodsMustReturnVoidOrBuilder(),
- "set(java.lang.Integer)"))
- .in(childComponentFile).onLine(13);
- }
-
- @Test
- public void testGenericsOnSetterMethodFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " ChildComponent build();",
- " <T> Builder set(T t);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(MSGS.methodsMayNotHaveTypeParameters())
- .in(childComponentFile).onLine(10);
- }
-
- @Test
- public void testGenericsOnInheritedSetterMethodFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " interface Parent {",
- " ChildComponent build();",
- " <T> Builder set(T t);",
- " }",
- "",
- " @Subcomponent.Builder",
- " interface Builder extends Parent {}",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.inheritedMethodsMayNotHaveTypeParameters(), "<T>set(T)"))
- .in(childComponentFile).onLine(13);
- }
-
- @Test
- public void testMultipleSettersPerTypeFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " ChildComponent build();",
- " void set1(String s);",
- " void set2(String s);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.manyMethodsForType(),
- "java.lang.String", "[set1(java.lang.String), set2(java.lang.String)]"))
- .in(childComponentFile).onLine(8);
- }
-
- @Test
- public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "abstract class ChildComponent {",
- " interface Parent<T> {",
- " void set1(T t);",
- " }",
- "",
- " @Subcomponent.Builder",
- " interface Builder extends Parent<String> {",
- " ChildComponent build();",
- " void set2(String s);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.manyMethodsForType(),
- "java.lang.String", "[set1(T), set2(java.lang.String)]"))
- .in(childComponentFile).onLine(12);
- }
-
- @Test
- public void testExtraSettersFails() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent.Builder build();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " ChildComponent build();",
- " void set1(String s);",
- " void set2(Integer s);",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- String.format(MSGS.extraSetters(),
- "[void test.ChildComponent.Builder.set1(String),"
- + " void test.ChildComponent.Builder.set2(Integer)]"))
- .in(childComponentFile).onLine(8);
-
- }
-
- @Test
- public void testMissingSettersFail() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " TestModule(String unused) {}",
- " @Provides String s() { return null; }",
- "}");
- JavaFileObject module2File = JavaFileObjects.forSourceLines("test.Test2Module",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class Test2Module {",
- " @Provides Integer i() { return null; }",
- "}");
- JavaFileObject module3File = JavaFileObjects.forSourceLines("test.Test3Module",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class Test3Module {",
- " Test3Module(String unused) {}",
- " @Provides Double d() { return null; }",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent.Builder build();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = {TestModule.class, Test2Module.class, Test3Module.class})",
- "interface ChildComponent {",
- " String string();",
- " Integer integer();",
- "",
- " @Subcomponent.Builder",
- " interface Builder {",
- " ChildComponent create();",
- " }",
- "}");
- assertAbout(javaSources())
- .that(ImmutableList.of(moduleFile,
- module2File,
- module3File,
- componentFile,
- childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- // Ignores Test2Module because we can construct it ourselves.
- // TODO(sameb): Ignore Test3Module because it's not used within transitive dependencies.
- String.format(MSGS.missingSetters(), "[test.TestModule, test.Test3Module]"))
- .in(childComponentFile).onLine(11);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java
deleted file mode 100644
index d47c328..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-
-@RunWith(JUnit4.class)
-public final class SubcomponentValidationTest {
- @Test public void factoryMethod_missingModulesWithParameters() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " ChildComponent newChildComponent();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = ModuleWithParameters.class)",
- "interface ChildComponent {",
- " Object object();",
- "}");
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class ModuleWithParameters {",
- " private final Object object;",
- "",
- " ModuleWithParameters(Object object) {",
- " this.object = object;",
- " }",
- "",
- " @Provides Object object() {",
- " return object;",
- " }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile, moduleFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- "test.ChildComponent requires modules which have no visible default constructors. "
- + "Add the following modules as parameters to this method: "
- + "test.ModuleWithParameters")
- .in(componentFile).onLine(7);
- }
-
- @Test public void factoryMethod_nonModuleParameter() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " ChildComponent newChildComponent(String someRandomString);",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- "Subcomponent factory methods may only accept modules, but java.lang.String is not.")
- .in(componentFile).onLine(7).atColumn(43);
- }
-
- @Test public void factoryMethod_duplicateParameter() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class TestModule {}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " ChildComponent newChildComponent(TestModule testModule1, TestModule testModule2);",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = TestModule.class)",
- "interface ChildComponent {}");
- assertAbout(javaSources()).that(ImmutableList.of(moduleFile, componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- "A module may only occur once an an argument in a Subcomponent factory method, "
- + "but test.TestModule was already passed.")
- .in(componentFile).onLine(7).atColumn(71);
- }
-
- @Test public void factoryMethod_superflouousModule() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module",
- "final class TestModule {}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " ChildComponent newChildComponent(TestModule testModule);",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface ChildComponent {}");
- assertAbout(javaSources()).that(ImmutableList.of(moduleFile, componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- "test.TestModule is present as an argument to the test.ChildComponent factory method, but "
- + "is not one of the modules used to implement the subcomponent.")
- .in(componentFile).onLine(7);
- }
-
- @Test public void missingBinding() {
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " @Provides String provideString(int i) {",
- " return Integer.toString(i);",
- " }",
- "}");
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface TestComponent {",
- " ChildComponent newChildComponent();",
- "}");
- JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = TestModule.class)",
- "interface ChildComponent {",
- " String getString();",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(moduleFile, componentFile, childComponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining(
- "java.lang.Integer cannot be provided without an @Inject constructor or from an "
- + "@Provides-annotated method");
- }
-
- @Test public void subcomponentOnConcreteType() {
- JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.NotASubcomponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "final class NotASubcomponent {}");
- assertAbout(javaSources()).that(ImmutableList.of(subcomponentFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("interface");
- }
-
- @Test public void scopeMismatch() {
- JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "import javax.inject.Singleton;",
- "",
- "@Component",
- "@Singleton",
- "interface ParentComponent {",
- " ChildComponent childComponent();",
- "}");
- JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = ChildModule.class)",
- "interface ChildComponent {",
- " Object getObject();",
- "}");
- JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Singleton;",
- "",
- "@Module",
- "final class ChildModule {",
- " @Provides @Singleton Object provideObject() { return null; }",
- "}");
- assertAbout(javaSources()).that(ImmutableList.of(componentFile, subcomponentFile, moduleFile))
- .processedWith(new ComponentProcessor())
- .failsToCompile()
- .withErrorContaining("@Singleton");
- }
-
- @Test
- public void delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent() {
- JavaFileObject parentComponentFile =
- JavaFileObjects.forSourceLines(
- "test.ParentComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface ParentComponent {",
- " ChildComponent childComponent();",
- " Dep1 getDep1();",
- " Dep2 getDep2();",
- "}");
- JavaFileObject childComponentFile =
- JavaFileObjects.forSourceLines(
- "test.ChildComponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = ChildModule.class)",
- "interface ChildComponent {",
- " Object getObject();",
- "}");
- JavaFileObject childModuleFile =
- JavaFileObjects.forSourceLines(
- "test.ChildModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class ChildModule {",
- " @Provides Object provideObject(A a) { return null; }",
- "}");
- JavaFileObject aFile =
- JavaFileObjects.forSourceLines(
- "test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject public A(NeedsDep1 a, Dep1 b, Dep2 c) { }",
- " @Inject public void methodA() { }",
- "}");
- JavaFileObject needsDep1File =
- JavaFileObjects.forSourceLines(
- "test.NeedsDep1",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class NeedsDep1 {",
- " @Inject public NeedsDep1(Dep1 d) { }",
- "}");
- JavaFileObject dep1File =
- JavaFileObjects.forSourceLines(
- "test.Dep1",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class Dep1 {",
- " @Inject public Dep1() { }",
- " @Inject public void dep1Method() { }",
- "}");
- JavaFileObject dep2File =
- JavaFileObjects.forSourceLines(
- "test.Dep2",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class Dep2 {",
- " @Inject public Dep2() { }",
- " @Inject public void dep2Method() { }",
- "}");
-
- JavaFileObject componentGeneratedFile =
- JavaFileObjects.forSourceLines(
- "DaggerParentComponent",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerParentComponent implements ParentComponent {",
- " private MembersInjector<Dep1> dep1MembersInjector;",
- " private Provider<Dep1> dep1Provider;",
- " private MembersInjector<Dep2> dep2MembersInjector;",
- " private Provider<Dep2> dep2Provider;",
- "",
- " private DaggerParentComponent(Builder builder) { ",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() { ",
- " return new Builder();",
- " }",
- "",
- " public static ParentComponent create() { ",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) { ",
- " this.dep1MembersInjector = Dep1_MembersInjector.create();",
- " this.dep1Provider = Dep1_Factory.create(dep1MembersInjector);",
- " this.dep2MembersInjector = Dep2_MembersInjector.create();",
- " this.dep2Provider = Dep2_Factory.create(dep2MembersInjector);",
- " }",
- "",
- " @Override",
- " public Dep1 getDep1() { ",
- " return dep1Provider.get();",
- " }",
- "",
- " @Override",
- " public Dep2 getDep2() { ",
- " return dep2Provider.get();",
- " }",
- "",
- " @Override",
- " public ChildComponent childComponent() { ",
- " return new ChildComponentImpl();",
- " }",
- "",
- " public static final class Builder {",
- " private Builder() { ",
- " }",
- " ",
- " public ParentComponent build() { ",
- " return new DaggerParentComponent(this);",
- " }",
- " }",
- "",
- " private final class ChildComponentImpl implements ChildComponent {",
- " private final ChildModule childModule;",
- " private MembersInjector<A> aMembersInjector;",
- " private Provider<NeedsDep1> needsDep1Provider;",
- " private Provider<A> aProvider;",
- " private Provider<Object> provideObjectProvider;",
- " ",
- " private ChildComponentImpl() { ",
- " this.childModule = new ChildModule();",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() { ",
- " this.aMembersInjector = A_MembersInjector.create();",
- " this.needsDep1Provider = NeedsDep1_Factory.create(",
- " DaggerParentComponent.this.dep1Provider);",
- " this.aProvider = A_Factory.create(",
- " aMembersInjector,",
- " needsDep1Provider,",
- " DaggerParentComponent.this.dep1Provider,",
- " DaggerParentComponent.this.dep2Provider);",
- " this.provideObjectProvider = ChildModule_ProvideObjectFactory.create(",
- " childModule, aProvider);",
- " }",
- " ",
- " @Override",
- " public Object getObject() { ",
- " return provideObjectProvider.get();",
- " }",
- " }",
- "}");
- assertAbout(javaSources())
- .that(
- ImmutableList.of(
- parentComponentFile,
- childComponentFile,
- childModuleFile,
- aFile,
- needsDep1File,
- dep1File,
- dep2File))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(componentGeneratedFile);
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ValidationReportTest.java b/compiler/src/test/java/dagger/internal/codegen/ValidationReportTest.java
deleted file mode 100644
index d7f4451..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/ValidationReportTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.testing.compile.JavaFileObjects;
-import dagger.internal.codegen.ValidationReport.Builder;
-import java.util.Set;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.TypeElement;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-
-@RunWith(JUnit4.class)
-public class ValidationReportTest {
- private static final JavaFileObject TEST_CLASS_FILE =
- JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "final class TestClass {}");
-
- @Test
- public void basicReport() {
- assertAbout(javaSource())
- .that(TEST_CLASS_FILE)
- .processedWith(
- new SimpleTestProcessor() {
- @Override
- void test() {
- Builder<TypeElement> reportBuilder =
- ValidationReport.about(getTypeElement("test.TestClass"));
- reportBuilder.addError("simple error");
- reportBuilder.build().printMessagesTo(processingEnv.getMessager());
- }
- })
- .failsToCompile()
- .withErrorContaining("simple error")
- .in(TEST_CLASS_FILE)
- .onLine(3);
- }
-
- @Test
- public void messageOnDifferentElement() {
- assertAbout(javaSource())
- .that(TEST_CLASS_FILE)
- .processedWith(
- new SimpleTestProcessor() {
- @Override
- void test() {
- Builder<TypeElement> reportBuilder =
- ValidationReport.about(getTypeElement("test.TestClass"));
- reportBuilder.addError("simple error", getTypeElement(String.class));
- reportBuilder.build().printMessagesTo(processingEnv.getMessager());
- }
- })
- .failsToCompile()
- .withErrorContaining("[java.lang.String] simple error")
- .in(TEST_CLASS_FILE)
- .onLine(3);
- }
-
- @Test
- public void subreport() {
- assertAbout(javaSource())
- .that(TEST_CLASS_FILE)
- .processedWith(
- new SimpleTestProcessor() {
- @Override
- void test() {
- Builder<TypeElement> reportBuilder =
- ValidationReport.about(getTypeElement("test.TestClass"));
- reportBuilder.addError("simple error");
- ValidationReport<TypeElement> parentReport =
- ValidationReport.about(getTypeElement(String.class))
- .addSubreport(reportBuilder.build())
- .build();
- assertThat(parentReport.isClean()).isFalse();
- parentReport.printMessagesTo(processingEnv.getMessager());
- }
- })
- .failsToCompile()
- .withErrorContaining("simple error")
- .in(TEST_CLASS_FILE)
- .onLine(3);
- }
-
- private static abstract class SimpleTestProcessor extends AbstractProcessor {
- @Override
- public Set<String> getSupportedAnnotationTypes() {
- return ImmutableSet.of("*");
- }
-
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- test();
- return false;
- }
-
- protected final TypeElement getTypeElement(Class<?> clazz) {
- return getTypeElement(clazz.getCanonicalName());
- }
-
- protected final TypeElement getTypeElement(String canonicalName) {
- return processingEnv.getElementUtils().getTypeElement(canonicalName);
- }
-
- abstract void test();
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/writer/ClassNameTest.java b/compiler/src/test/java/dagger/internal/codegen/writer/ClassNameTest.java
deleted file mode 100644
index eff01b8..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/writer/ClassNameTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.CompilationRule;
-import dagger.internal.codegen.writer.ClassNameTest.OuterClass.InnerClass;
-import java.util.Map;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-@RunWith(JUnit4.class)
-public class ClassNameTest {
- @Rule public CompilationRule compilationRule = new CompilationRule();
-
- @Test public void bestGuessForString_simpleClass() {
- assertThat(ClassName.bestGuessFromString(String.class.getName()))
- .isEqualTo(ClassName.create("java.lang", "String"));
- }
-
- static class OuterClass {
- static class InnerClass {}
- }
-
- @Test public void bestGuessForString_nestedClass() {
- assertThat(ClassName.bestGuessFromString(Map.Entry.class.getCanonicalName()))
- .isEqualTo(ClassName.create("java.util", ImmutableList.of("Map"), "Entry"));
- assertThat(ClassName.bestGuessFromString(OuterClass.InnerClass.class.getCanonicalName()))
- .isEqualTo(
- ClassName.create("dagger.internal.codegen.writer",
- ImmutableList.of("ClassNameTest", "OuterClass"), "InnerClass"));
- }
-
- @Test public void bestGuessForString_defaultPackage() {
- assertThat(ClassName.bestGuessFromString("SomeClass"))
- .isEqualTo(ClassName.create("", "SomeClass"));
- assertThat(ClassName.bestGuessFromString("SomeClass.Nested"))
- .isEqualTo(ClassName.create("", ImmutableList.of("SomeClass"), "Nested"));
- assertThat(ClassName.bestGuessFromString("SomeClass.Nested.EvenMore"))
- .isEqualTo(ClassName.create("", ImmutableList.of("SomeClass", "Nested"), "EvenMore"));
- }
-
- @Test public void bestGuessForString_confusingInput() {
- try {
- ClassName.bestGuessFromString("com.test.$");
- fail();
- } catch (IllegalArgumentException expected) {}
- try {
- ClassName.bestGuessFromString("com.test.LooksLikeAClass.pkg");
- fail();
- } catch (IllegalArgumentException expected) {}
- try {
- ClassName.bestGuessFromString("!@#$gibberish%^&*");
- fail();
- } catch (IllegalArgumentException expected) {}
- }
-
- @Test public void classNameFromTypeElement() {
- Elements elements = compilationRule.getElements();
- TypeElement element = elements.getTypeElement(Object.class.getCanonicalName());
- assertThat(ClassName.fromTypeElement(element).canonicalName())
- .isEqualTo("java.lang.Object");
- }
-
- @Test public void peerNamed_topLevelClass() {
- Elements elements = compilationRule.getElements();
- TypeElement element = elements.getTypeElement(ClassNameTest.class.getCanonicalName());
- ClassName className = ClassName.fromTypeElement(element);
- ClassName peerName = className.peerNamed("Foo");
- assertThat(peerName.canonicalName())
- .isEqualTo("dagger.internal.codegen.writer.Foo");
- }
-
- @Test public void peerNamed_nestedClass() {
- Elements elements = compilationRule.getElements();
- TypeElement element = elements.getTypeElement(OuterClass.class.getCanonicalName());
- ClassName className = ClassName.fromTypeElement(element);
- ClassName peerName = className.peerNamed("Foo");
- assertThat(peerName.canonicalName())
- .isEqualTo("dagger.internal.codegen.writer.ClassNameTest.Foo");
- }
-
- @Test public void peerNamed_deeplyNestedClass() {
- Elements elements = compilationRule.getElements();
- TypeElement element = elements.getTypeElement(InnerClass.class.getCanonicalName());
- ClassName className = ClassName.fromTypeElement(element);
- ClassName peerName = className.peerNamed("Foo");
- assertThat(peerName.canonicalName())
- .isEqualTo("dagger.internal.codegen.writer.ClassNameTest.OuterClass.Foo");
- }
-
- @Test public void fromClass_NonNestedClass() {
- ClassName className = ClassName.fromClass(ClassNameTest.class);
- assertThat(className.canonicalName()).isEqualTo(
- "dagger.internal.codegen.writer.ClassNameTest");
- }
-
- @Test public void fromClass_NestedClass() {
- ClassName className = ClassName.fromClass(InnerClass.class);
- assertThat(className.canonicalName()).isEqualTo(
- "dagger.internal.codegen.writer.ClassNameTest.OuterClass.InnerClass");
- }
-
- @Test public void fromClass_classFileName() {
- ClassName className = ClassName.fromClass(InnerClass.class);
- assertThat(className.classFileName('_')).isEqualTo("ClassNameTest_OuterClass_InnerClass");
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/writer/JavaWriterTest.java b/compiler/src/test/java/dagger/internal/codegen/writer/JavaWriterTest.java
deleted file mode 100644
index e775f74..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/writer/JavaWriterTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class JavaWriterTest {
- @Test public void referencedAndDeclaredSimpleName() {
- JavaWriter javaWriter = JavaWriter.inPackage("test");
- ClassWriter topClass = javaWriter.addClass("Top");
- topClass.addNestedClass("Middle").addNestedClass("Bottom");
- topClass.addField(ClassName.create("some.other.pkg", "Bottom"), "field");
- assertThat(topClass.toString()).doesNotContain("import some.other.pkg.Bottom;");
- }
-}
diff --git a/compiler/src/test/java/dagger/internal/codegen/writer/TypeNamesTest.java b/compiler/src/test/java/dagger/internal/codegen/writer/TypeNamesTest.java
deleted file mode 100644
index ec82e96..0000000
--- a/compiler/src/test/java/dagger/internal/codegen/writer/TypeNamesTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen.writer;
-
-import com.google.testing.compile.CompilationRule;
-import java.nio.charset.Charset;
-import java.util.Set;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class TypeNamesTest {
- @Rule public final CompilationRule compilation = new CompilationRule();
-
- private TypeElement getElement(Class<?> clazz) {
- return compilation.getElements().getTypeElement(clazz.getCanonicalName());
- }
-
- private TypeMirror getType(Class<?> clazz) {
- return getElement(clazz).asType();
- }
-
- @Test
- public void forTypeMirror_basicTypes() {
- assertThat(TypeNames.forTypeMirror(getType(Object.class)))
- .isEqualTo(ClassName.fromClass(Object.class));
- assertThat(TypeNames.forTypeMirror(getType(Charset.class)))
- .isEqualTo(ClassName.fromClass(Charset.class));
- assertThat(TypeNames.forTypeMirror(getType(TypeNamesTest.class)))
- .isEqualTo(ClassName.fromClass(TypeNamesTest.class));
- }
-
- @Test
- public void forTypeMirror_parameterizedType() {
- DeclaredType setType =
- compilation.getTypes().getDeclaredType(getElement(Set.class), getType(Object.class));
- assertThat(TypeNames.forTypeMirror(setType))
- .isEqualTo(ParameterizedTypeName.create(Set.class, ClassName.fromClass(Object.class)));
- }
-
- @Test
- public void forTypeMirror_typeVariables() {
- TypeMirror setType = getType(Set.class);
- assertThat(TypeNames.forTypeMirror(setType))
- .isEqualTo(ParameterizedTypeName.create(Set.class, TypeVariableName.named("E")));
- }
-
- @Test
- public void forTypeMirror_primitive() {
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.BOOLEAN)))
- .isEqualTo(PrimitiveName.BOOLEAN);
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.BYTE)))
- .isEqualTo(PrimitiveName.BYTE);
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.SHORT)))
- .isEqualTo(PrimitiveName.SHORT);
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.INT)))
- .isEqualTo(PrimitiveName.INT);
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.LONG)))
- .isEqualTo(PrimitiveName.LONG);
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.CHAR)))
- .isEqualTo(PrimitiveName.CHAR);
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.FLOAT)))
- .isEqualTo(PrimitiveName.FLOAT);
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getPrimitiveType(TypeKind.DOUBLE)))
- .isEqualTo(PrimitiveName.DOUBLE);
- }
-
- @Test
- public void forTypeMirror_arrays() {
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getArrayType(getType(Object.class))))
- .isEqualTo(new ArrayTypeName(ClassName.fromClass(Object.class)));
- }
-
- @Test
- public void forTypeMirror_void() {
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getNoType(TypeKind.VOID)))
- .isEqualTo(VoidName.VOID);
- }
-
- @Test
- public void forTypeMirror_null() {
- assertThat(TypeNames.forTypeMirror(compilation.getTypes().getNullType()))
- .isEqualTo(NullName.NULL);
- }
-}
diff --git a/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java b/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java
deleted file mode 100644
index 58fa263..0000000
--- a/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
- * Copyright (C) 2013 Google, Inc.
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.tests.integration.operation;
-
-import com.google.testing.compile.JavaFileObjects;
-import dagger.internal.codegen.ComponentProcessor;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assert_;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static java.util.Arrays.asList;
-
-@RunWith(JUnit4.class)
-public final class PrimitiveInjectionTest {
-
- // TODO(cgruber): Use @test.ForTest to qualify primitives once qualifier equivalence is working.
- /*
- JavaFileObject annotation = JavaFileObjects.forSourceLines("test.ForTest",
- "package test;",
- "import javax.inject.Qualifier;",
- "@Qualifier",
- "public @interface ForTest {",
- "}");
- */
-
- // TODO(cgruber): Expand test to support more primitive types when b/15512877 is fixed.
- JavaFileObject primitiveInjectable = JavaFileObjects.forSourceLines("test.PrimitiveInjectable",
- "package test;",
- "import javax.inject.Inject;",
- "class PrimitiveInjectable {",
- " @Inject PrimitiveInjectable(int ignored) {}",
- "}");
-
- JavaFileObject primitiveModule = JavaFileObjects.forSourceLines("test.PrimitiveModule",
- "package test;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "@Module",
- "class PrimitiveModule {",
- " @Provides int primitiveInt() { return Integer.MAX_VALUE; }",
- "}");
-
- JavaFileObject component = JavaFileObjects.forSourceLines("test.PrimitiveComponent",
- "package test;",
- "import dagger.Component;",
- "import dagger.Provides;",
- "@Component(modules = PrimitiveModule.class)",
- "interface PrimitiveComponent {",
- " int primitiveInt();",
- " PrimitiveInjectable primitiveInjectable();",
- "}");
-
- JavaFileObject expectedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerPrimitiveComponent",
- "package test;",
- "",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerPrimitiveComponent implements PrimitiveComponent {",
- " private Provider<Integer> primitiveIntProvider;",
- " private Provider<PrimitiveInjectable> primitiveInjectableProvider;",
- "",
- " private DaggerPrimitiveComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static PrimitiveComponent create() {",
- " return builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final Builder builder) {",
- " this.primitiveIntProvider =",
- " PrimitiveModule_PrimitiveIntFactory.create(builder.primitiveModule);",
- " this.primitiveInjectableProvider =",
- " PrimitiveInjectable_Factory.create(primitiveIntProvider);",
- " }",
- "",
- " @Override",
- " public int primitiveInt() {",
- " return primitiveIntProvider.get();",
- " }",
- "",
- " @Override",
- " public PrimitiveInjectable primitiveInjectable() {",
- " return primitiveInjectableProvider.get();",
- " }",
- "",
- " public static final class Builder {",
- " private PrimitiveModule primitiveModule;",
- "",
- " private Builder() {",
- " }",
- "",
- " public PrimitiveComponent build() {",
- " if (primitiveModule == null) {",
- " this.primitiveModule = new PrimitiveModule();",
- " }",
- " return new DaggerPrimitiveComponent(this);",
- " }",
- "",
- " public Builder primitiveModule(PrimitiveModule primitiveModule) {",
- " if (primitiveModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.primitiveModule = primitiveModule;",
- " return this;",
- " }",
- " }",
- "}");
-
- @Test public void primitiveArrayTypesAllInjected() {
- assert_().about(javaSources())
- .that(asList(component, primitiveInjectable, primitiveModule))
- .processedWith(new ComponentProcessor())
- .compilesWithoutError()
- .and().generatesSources(expectedComponent);
- }
-}
diff --git a/core/pom.xml b/core/pom.xml
deleted file mode 100644
index eab9dd8..0000000
--- a/core/pom.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2012 Square, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>dagger</artifactId>
- <name>Dagger</name>
-
- <properties>
- <!-- Runtime must remain Java6 to support android. -->
- <java.version>1.6</java.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject</artifactId>
- </dependency>
-
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.truth</groupId>
- <artifactId>truth</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>animal-sniffer-maven-plugin</artifactId>
- <version>1.8</version><!-- 1.9+ requires JDK7 on the build machine -->
- <executions>
- <execution>
- <id>sniff-api</id>
- <goals><goal>check</goal></goals>
- </execution>
- </executions>
- <configuration>
- <signature>
- <groupId>org.codehaus.mojo.signature</groupId>
- <artifactId>java16</artifactId>
- <version>1.0</version>
- </signature>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-javadoc-plugin</artifactId>
- <configuration>
- <excludePackageNames>dagger.internal:dagger.internal.*</excludePackageNames>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-jar-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>test-jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/core/src/main/java/dagger/Component.java b/core/src/main/java/dagger/Component.java
deleted file mode 100644
index 7d72401..0000000
--- a/core/src/main/java/dagger/Component.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Qualifier;
-import javax.inject.Scope;
-import javax.inject.Singleton;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * Annotates an interface or abstract class for which a fully-formed, dependency-injected
- * implementation is to be generated from a set of {@linkplain #modules}. The generated class will
- * have the name of the type annotated with {@code @Component} prepended with {@code Dagger}. For
- * example, {@code @Component interface MyComponent {...}} will produce an implementation named
- * {@code DaggerMyComponent}.
- *
- * <a name="component-methods">
- * <h2>Component methods</h2>
- * </a>
- *
- * <p>Every type annotated with {@code @Component} must contain at least one abstract component
- * method. Component methods may have any name, but must have signatures that conform to either
- * {@linkplain Provider provision} or {@linkplain MembersInjector members-injection} contracts.
- *
- * <a name="provision-methods">
- * <h3>Provision methods</h3>
- * </a>
- *
- * <p>Provision methods have no parameters and return an {@link Inject injected} or
- * {@link Provides provided} type. Each method may have a {@link Qualifier} annotation as well. The
- * following are all valid provision method declarations: <pre><code>
- * SomeType getSomeType();
- * {@literal Set<SomeType>} getSomeTypes();
- * {@literal @PortNumber} int getPortNumber();
- * </code></pre>
- *
- * <p>Provision methods, like typical {@link Inject injection} sites, may use {@link Provider} or
- * {@link Lazy} to more explicitly control provision requests. A {@link Provider} allows the user
- * of the component to request provision any number of times by calling {@link Provider#get}. A
- * {@link Lazy} will only ever request a single provision, but will defer it until the first call to
- * {@link Lazy#get}. The following provision methods all request provision of the same type, but
- * each implies different semantics: <pre><code>
- * SomeType getSomeType();
- * {@literal Provider<SomeType>} getSomeTypeProvider();
- * {@literal Lazy<SomeType>} getLazySomeType();
- * </code></pre>
- *
- * <a name="members-injection-methods">
- * <h3>Members-injection methods</h3>
- * </a>
- *
- * <p>Members-injection methods have a single parameter and inject dependencies into each of the
- * {@link Inject}-annotated fields and methods of the passed instance. A members-injection method
- * may be void or return its single parameter as a convenience for chaining. The following are all
- * valid members-injection method declarations: <pre><code>
- * void injectSomeType(SomeType someType);
- * SomeType injectAndReturnSomeType(SomeType someType);
- * </code></pre>
- *
- * <p>A method with no parameters that returns a {@link MembersInjector} is equivalent to a members
- * injection method. Calling {@link MembersInjector#injectMembers} on the returned object will
- * perform the same work as a members injection method. For example: <pre><code>
- * {@literal MembersInjector<SomeType>} getSomeTypeMembersInjector();
- * </code></pre>
- *
- * <h4>A note about covariance</h4>
- *
- * <p>While a members-injection method for a type will accept instances of its subtypes, only
- * {@link Inject}-annotated members of the parameter type and its supertypes will be injected;
- * members of subtypes will not. For example, given the following types, only {@code a} and
- * {@code b} will be injected into an instance of {@code Child} when it is passed to the
- * members-injection method {@code injectSelf(Self instance)}: <pre><code>
- * class Parent {
- * {@literal @}Inject A a;
- * }
- *
- * class Self extends Parent {
- * {@literal @}Inject B b;
- * }
- *
- * class Child extends Self {
- * {@literal @}Inject C c;
- * }
- * </code></pre>
- *
- * <a name="instantiation">
- * <h2>Instantiation</h2>
- * </a>
- *
- * <p>Component implementations are primarily instantiated via a generated
- * <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a>. An instance of the builder
- * is obtained using the {@code builder()} method on the component implementation.
- * If a nested {@code @Component.Builder} type exists in the component, the {@code builder()}
- * method will return a generated implementation of that type. If no nested
- * {@code @Component.Builder} exists, the returned builder has a method to set each of the
- * {@linkplain #modules} and component {@linkplain #dependencies} named with the
- * <a href="http://en.wikipedia.org/wiki/CamelCase">lower camel case</a> version of the module
- * or dependency type. Each component dependency and module without a visible default constructor
- * must be set explicitly, but any module with a default or no-args constructor accessible to the
- * component implementation may be elided. This is an example usage of a component builder:
- * <pre><code>
- * public static void main(String[] args) {
- * OtherComponent otherComponent = ...;
- * MyComponent component = DaggerMyComponent.builder()
- * // required because component dependencies must be set
- * .otherComponent(otherComponent)
- * // required because FlagsModule has constructor parameters
- * .flagsModule(new FlagsModule(args))
- * // may be elided because a no-args constructor is visible
- * .myApplicationModule(new MyApplicationModule())
- * .build();
- * }
- * </code></pre>
- *
- * <p>In the case that a component has no component dependencies and only no-arg modules, the
- * generated component will also have a factory method {@code create()}.
- * {@code SomeComponent.create()} and {@code SomeComponent.builder().build()} are both valid and
- * equivalent.
- *
- * <a name="scope">
- * <h2>Scope</h2>
- * </a>
- *
- * <p>Each Dagger component can be associated with a scope by annotating it with the
- * {@linkplain Scope scope annotation}. The component implementation ensures that there is only one
- * provision of each scoped binding per instance of the component. If the component declares a
- * scope, it may only contain unscoped bindings or bindings of that scope anywhere in the graph. For
- * example: <pre><code>
- * {@literal @}Singleton {@literal @}Component
- * interface MyApplicationComponent {
- * // this component can only inject types using unscoped or {@literal @}Singleton bindings
- * }
- * </code></pre>
- *
- * <p>In order to get the proper behavior associated with a scope annotation, it is the caller's
- * responsibility to instantiate new component instances when appropriate. A {@link Singleton}
- * component, for instance, should only be instantiated once per application, while a
- * {@code RequestScoped} component should be instantiated once per request. Because components are
- * self-contained implementations, exiting a scope is as simple as dropping all references to the
- * component instance.
- *
- * <a name="component-relationships">
- * <h2>Component relationships</h2>
- * </a>
- *
- * <p>While there is much utility in isolated components with purely unscoped bindings, many
- * applications will call for multiple components with multiple scopes to interact. Dagger provides
- * two mechanisms for relating components.
- *
- * <a name="subcomponents">
- * <h3>Subcomponents</h3>
- * </a>
- *
- * <p>The simplest way to relate two components is by declaring a {@link Subcomponent}. A
- * subcomponent behaves exactly like a component, but has its implementation generated within
- * a parent component or subcomponent. That relationship allows the subcomponent implementation to
- * inherit the <em>entire</em> binding graph from its parent when it is declared. For that reason,
- * a subcomponent isn't evaluated for completeness until it is associated with a parent.
- *
- * <p>Subcomponents are declared via a factory method on a parent component or subcomponent. The
- * method may have any name, but must return the subcomponent. The factory method's parameters may
- * be any number of the subcomponent's modules, but must at least include those without visible
- * no-arg constructors. The following is an example of a factory method that creates a
- * request-scoped subcomponent from a singleton-scoped parent: <pre><code>
- * {@literal @}Singleton {@literal @}Component
- * interface ApplicationComponent {
- * // component methods...
- *
- * RequestComponent newRequestComponent(RequestModule requestModule);
- * }
- * </code></pre>
- *
- * <a name="component-dependencies">
- * <h3>Component dependencies</h3>
- * </a>
- *
- * <p>While subcomponents are the simplest way to compose subgraphs of bindings, subcomponents are
- * tightly coupled with the parents; they may use any binding defined by their ancestor component
- * and subcomponents. As an alternative, components can use bindings only from another
- * <em>component interface</em> by declaring a {@linkplain #dependencies component dependency}. When
- * a type is used as a component dependency, each <a href="#provision-methods">provision method</a>
- * on the dependency is bound as a provider. Note that <em>only</em> the bindings exposed as
- * provision methods are available through component dependencies.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
-@Target(TYPE)
-@Documented
-public @interface Component {
- /**
- * A list of classes annotated with {@link Module} whose bindings are used to generate the
- * component implementation. Note that through the use of {@link Module#includes} the full set of
- * modules used to implement the component may include more modules that just those listed here.
- */
- Class<?>[] modules() default {};
-
- /**
- * A list of types that are to be used as <a href="#component-dependencies">component
- * dependencies</a>.
- */
- Class<?>[] dependencies() default {};
-
- /**
- * A builder for a component. Components may have a single nested static abstract class or
- * interface annotated with {@code @Component.Builder}. If they do, then the component's
- * generated builder will match the API in the type. Builders must follow some rules:
- * <ul>
- * <li> A single abstract method with no arguments must exist, and must return the component.
- * (This is typically the {@code build()} method.)
- * <li> All other abstract methods must take a single argument and must return void,
- * the Builder type, or a supertype of the builder.
- * <li> Each component dependency <b>must</b> have an abstract setter method.
- * <li> Each module dependency that Dagger can't instantiate itself (e.g, the module
- * doesn't have a visible no-args constructor) <b>must</b> have an abstract setter method.
- * Other module dependencies (ones that Dagger can instantiate) are allowed, but not required.
- * <li> Non-abstract methods are allowed, but ignored as far as validation and builder generation
- * are concerned.
- * </ul>
- *
- * For example, this could be a valid Component with a Builder: <pre><code>
- * {@literal @}Component(modules = {BackendModule.class, FrontendModule.class})
- * interface MyComponent {
- * MyWidget myWidget();
- *
- * {@literal @}Component.Builder
- * interface Builder {
- * MyComponent build();
- * Builder backendModule(BackendModule bm);
- * Builder frontendModule(FrontendModule fm);
- * }
- * }</code></pre>
- */
- @Target(TYPE)
- @Documented
- @interface Builder {}
-}
diff --git a/core/src/main/java/dagger/Lazy.java b/core/src/main/java/dagger/Lazy.java
deleted file mode 100644
index e04cc03..0000000
--- a/core/src/main/java/dagger/Lazy.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2012 Google, Inc.
- * Copyright (C) 2012 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger;
-
-/**
- * A handle to a lazily-computed value. Each {@code Lazy} computes its value on
- * the first call to {@code get()} and remembers that same value for all
- * subsequent calls to {@code get()}.
- *
- * <p>{@code null} is not a supported value. Implementations of {@code Lazy}
- * are expected to throw {@link NullPointerException} if the computed value is
- * {@code null}.
- *
- * <h2>Example</h2>
- * The differences between <strong>direct injection</strong>, <strong>provider
- * injection</strong> and <strong>lazy injection</strong> are best demonstrated
- * with an example. Start with a module that computes a different integer for
- * each use:<pre><code>
- * {@literal @Module}
- * final class CounterModule {
- * int next = 100;
- *
- * {@literal @Provides} Integer provideInteger() {
- * System.out.println("computing...");
- * return next++;
- * }
- * }
- * </code></pre>
- *
- * <h3>Direct Injection</h3>
- * This class injects that integer and prints it 3 times:<pre><code>
- * final class DirectCounter {
- * {@literal @Inject} Integer value;
- *
- * void print() {
- * System.out.println("printing...");
- * System.out.println(value);
- * System.out.println(value);
- * System.out.println(value);
- * }
- * }
- * </code></pre>
- * Injecting a {@code DirectCounter} and invoking {@code print()} reveals that
- * the value is computed <i>before</i> it is required:<pre><code>
- * computing...
- * printing...
- * 100
- * 100
- * 100
- * </code></pre>
- *
- * <h3>Provider Injection</h3>
- * This class injects a {@linkplain javax.inject.Provider provider} for the
- * integer. It calls {@code Provider.get()} 3 times and prints each result:
- * <pre><code>
- * final class ProviderCounter {
- * {@literal @Inject Provider<Integer> provider;}
- *
- * void print() {
- * System.out.println("printing...");
- * System.out.println(provider.get());
- * System.out.println(provider.get());
- * System.out.println(provider.get());
- * }
- * }
- * </code></pre>
- * Injecting a {@code ProviderCounter} and invoking {@code print()} shows that
- * a new value is computed each time {@code Provider.get()} is used:<pre><code>
- * printing...
- * computing...
- * 100
- * computing...
- * 101
- * computing...
- * 102
- * </code></pre>
- *
- * <h3>Lazy Injection</h3>
- * This class injects a {@code Lazy} for the integer. Like the provider above,
- * it calls {@code Lazy.get()} 3 times and prints each result:<pre><code>
- * final class LazyCounter {
- * {@literal @Inject Lazy<Integer> lazy;}
- *
- * void print() {
- * System.out.println("printing...");
- * System.out.println(lazy.get());
- * System.out.println(lazy.get());
- * System.out.println(lazy.get());
- * }
- * }
- * </code></pre>
- * Injecting a {@code LazyCounter} and invoking {@code print()} shows that a new
- * value is computed immediately before it is needed. The same value is returned
- * for all subsequent uses:<pre><code>
- * printing...
- * computing...
- * 100
- * 100
- * 100
- * </code></pre>
- *
- * <h3>Lazy != Singleton</h3>
- * Note that each injected {@code Lazy} is independent, and remembers its value
- * in isolation of other {@code Lazy} instances. In this example, two {@code
- * LazyCounter} objects are created and {@code print()} is called on each:
- * <pre><code>
- * final class LazyCounters {
- * {@literal @Inject} LazyCounter counter1;
- * {@literal @Inject} LazyCounter counter2;
- *
- * void print() {
- * counter1.print();
- * counter2.print();
- * }
- * }
- * </code></pre>
- * The output demonstrates that each {@code Lazy} works independently:
- * <pre><code>
- * printing...
- * computing...
- * 100
- * 100
- * 100
- * printing...
- * computing...
- * 101
- * 101
- * 101
- * </code></pre>
- * Use {@link javax.inject.Singleton @Singleton} to share one instance among all
- * clients, and {@code Lazy} for lazy computation in a single client.
- */
-public interface Lazy<T> {
- /**
- * Return the underlying value, computing the value if necessary. All calls to
- * the same {@code Lazy} instance will return the same result.
- *
- * @throws NullPointerException if the computed value is {@code null}.
- */
- T get();
-}
diff --git a/core/src/main/java/dagger/MapKey.java b/core/src/main/java/dagger/MapKey.java
deleted file mode 100644
index 106c001..0000000
--- a/core/src/main/java/dagger/MapKey.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger;
-
-import dagger.internal.Beta;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-import java.util.Map;
-
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * Identifies annotation types that are used to associate keys with values returned by
- * {@linkplain Provides provider methods} in order to compose a {@linkplain Provides.Type#MAP map}.
- *
- * <p>Every provider method annotated with {@code @Provides(type = MAP)} must also have an
- * annotation that identifies the key for that map entry. That annotation's type must be annotated
- * with {@code @MapKey}.
- *
- * <p>Typically, the key annotation has a single member, whose value is used as the map key.
- *
- * <p>For example, to add an entry to a {@code Map<SomeEnum, Integer>} with key
- * {@code SomeEnum.FOO}, you could use an annotation called {@code @SomeEnumKey}:
- *
- * <pre><code>
- * {@literal @}MapKey
- * {@literal @}interface SomeEnumKey {
- * SomeEnum value();
- * }
- *
- * {@literal @}Module
- * class SomeModule {
- * {@literal @}Provides(type = MAP)
- * {@literal @}SomeEnumKey(SomeEnum.FOO)
- * Integer provideFooValue() {
- * return 2;
- * }
- * }
- *
- * class SomeInjectedType {
- * {@literal @}Inject
- * SomeInjectedType(Map<SomeEnum, Integer> map) {
- * assert map.get(SomeEnum.FOO) == 2;
- * }
- * }
- * </code></pre>
- *
- * <p>If {@code unwrapValue} is true, the annotation's single member can be any type except an
- * array.
- *
- * <p>See {@link dagger.mapkeys} for standard unwrapped map key annotations for keys that are boxed
- * primitives, strings, or classes.
- *
- * <h2>Annotations as keys</h2>
- *
- * <p>If {@link #unwrapValue} is false, then the annotation itself is used as the map key. For
- * example, to add an entry to a {@code Map<MyMapKey, Integer>} map:
- *
- * <pre><code>
- * {@literal @}MapKey(unwrapValue = false)
- * {@literal @}interface MyMapKey {
- * String someString();
- * MyEnum someEnum();
- * }
- *
- * {@literal @}Module
- * class SomeModule {
- * {@literal @}Provides(type = MAP)
- * {@literal @}MyMapKey(someString = "foo", someEnum = BAR)
- * Integer provideFooBarValue() {
- * return 2;
- * }
- * }
- *
- * class SomeInjectedType {
- * {@literal @}Inject
- * SomeInjectedType(Map<MyMapKey, Integer> map) {
- * assert map.get(new MyMapKeyImpl("foo", MyEnum.BAR)) == 2;
- * }
- * }
- * </code></pre>
- *
- * <p>(Note that there must be a class {@code MyMapKeyImpl} that implements {@code MyMapKey} in
- * order to call {@link Map#get(Object)} on the provided map.)
- *
- */
-@Documented
-@Target(ANNOTATION_TYPE)
-@Retention(RUNTIME)
-@Beta
-public @interface MapKey {
- /**
- * True to use the value of the single member of the annotated annotation as the map key; false
- * to use the annotation instance as the map key.
- *
- * <p>If true, the single member must not be an array.
- */
- boolean unwrapValue() default true;
-}
diff --git a/core/src/main/java/dagger/MembersInjector.java b/core/src/main/java/dagger/MembersInjector.java
deleted file mode 100644
index d0de7f3..0000000
--- a/core/src/main/java/dagger/MembersInjector.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2012 Square, Inc.
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger;
-
-/**
- * Injects dependencies into the fields and methods on instances of type {@code T}. Ignores the
- * presence or absence of an injectable constructor.
- *
- * @param <T> type to inject members of
- *
- * @author Bob Lee
- * @author Jesse Wilson
- * @since 2.0 (since 1.0 without the provision that {@link #injectMembers} cannot accept
- * {@code null})
- */
-public interface MembersInjector<T> {
-
- /**
- * Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
- * absence of an injectable constructor.
- *
- * <p>Whenever the object graph creates an instance, it performs this injection automatically
- * (after first performing constructor injection), so if you're able to let the object graph
- * create all your objects for you, you'll never need to use this method.
- *
- * @param instance into which members are to be injected
- * @throws NullPointerException if {@code instance} is {@code null}
- */
- void injectMembers(T instance);
-}
diff --git a/core/src/main/java/dagger/Module.java b/core/src/main/java/dagger/Module.java
deleted file mode 100644
index 05f0f3a..0000000
--- a/core/src/main/java/dagger/Module.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2012 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotates a class that contributes to the object graph.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface Module {
- /**
- * Additional {@code @Module}-annotated classes from which this module is
- * composed. The de-duplicated contributions of the modules in
- * {@code includes}, and of their inclusions recursively, are all contributed
- * to the object graph.
- */
- Class<?>[] includes() default {};
-}
diff --git a/core/src/main/java/dagger/Provides.java b/core/src/main/java/dagger/Provides.java
deleted file mode 100644
index 741a54a..0000000
--- a/core/src/main/java/dagger/Provides.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2007 Google Inc.
- * Copyright (C) 2012 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger;
-
-import dagger.internal.Beta;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * Annotates methods of a module to create a provider method binding. The
- * method's return type is bound to its returned value. The object graph will
- * pass dependencies to the method as parameters.
- *
- * @author Bob Lee
- */
-@Documented @Target(METHOD) @Retention(RUNTIME)
-public @interface Provides {
- /** The type of binding into which the return type of the annotated method contributes. */
- enum Type {
- /**
- * The method is the only one which can produce the value for the specified return type. This
- * is the default behavior.
- */
- UNIQUE,
-
- /**
- * The method's return type forms the generic type argument of a {@code Set<T>}, and the
- * returned value is contributed to the set. The object graph will pass dependencies to the
- * method as parameters. The {@code Set<T>} produced from the accumulation of values will be
- * immutable.
- *
- */
- SET,
-
- /**
- * Like {@link #SET}, except the method's return type is {@code Set<T>}, where any values are
- * contributed to the set. An example use is to provide a default empty set binding, which is
- * otherwise not possible using {@link #SET}.
- *
- */
- SET_VALUES,
-
- /**
- * The method's return type forms the type argument for the value of a
- * {@code Map<K, Provider<V>>}, and the combination of the annotated key and the returned value
- * is contributed to the map as a key/value pair. The {@code Map<K, Provider<V>>} produced from
- * the accumulation of values will be immutable.
- *
- */
- @Beta
- MAP;
- }
-
- Type type() default Type.UNIQUE;
-}
diff --git a/core/src/main/java/dagger/Subcomponent.java b/core/src/main/java/dagger/Subcomponent.java
deleted file mode 100644
index 988f17b..0000000
--- a/core/src/main/java/dagger/Subcomponent.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * A subcomponent that inherits the bindings from a parent {@link Component} or
- * {@link Subcomponent}. The details of how to associate a subcomponent with a parent are described
- * in the documentation for {@link Component}.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
-@Target(TYPE)
-@Documented
-public @interface Subcomponent {
- /**
- * A list of classes annotated with {@link Module} whose bindings are used to generate the
- * subcomponent implementation. Note that through the use of {@link Module#includes} the full set
- * of modules used to implement the subcomponent may include more modules that just those listed
- * here.
- */
- Class<?>[] modules() default {};
-
- /**
- * A builder for a subcomponent. This follows all the rules of {@link Component.Builder}, except
- * it must appear in classes annotated with {@link Subcomponent} instead of {@code Component}.
- * Components can have methods that return a {@link Subcomponent.Builder}-annotated type,
- * allowing the user to set modules on the subcomponent using their defined API.
- */
- @Target(TYPE)
- @Documented
- @interface Builder {}
-}
diff --git a/core/src/main/java/dagger/internal/Beta.java b/core/src/main/java/dagger/internal/Beta.java
deleted file mode 100644
index a0a82c6..0000000
--- a/core/src/main/java/dagger/internal/Beta.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-/**
- * Signifies that a public API (public class, method or field) is subject to
- * incompatible changes, or even removal, in a future release. An API bearing
- * this annotation is exempt from any compatibility guarantees made by its
- * containing library. Note that the presence of this annotation implies nothing
- * about the quality or performance of the API in question, only the fact that
- * it is not "API-frozen."
- */
-@Documented
-@Retention(SOURCE)
-public @interface Beta {}
diff --git a/core/src/main/java/dagger/internal/Collections.java b/core/src/main/java/dagger/internal/Collections.java
deleted file mode 100644
index 55f26eb..0000000
--- a/core/src/main/java/dagger/internal/Collections.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-
-final class Collections {
- /**
- * The maximum value for a signed 32-bit integer that is equal to a power of 2.
- */
- private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2);
-
- private Collections() {
- }
-
- /**
- * Creates a {@link LinkedHashSet} instance, with a high enough "initial capacity" that it
- * <em>should</em> hold {@code expectedSize} elements without growth.
- */
- static <E> LinkedHashSet<E> newLinkedHashSetWithExpectedSize(int expectedSize) {
- return new LinkedHashSet<E>(calculateInitialCapacity(expectedSize));
- }
-
- /**
- * Creates a {@link LinkedHashMap} instance, with a high enough "initial capacity" that it
- * <em>should</em> hold {@code expectedSize} elements without growth.
- */
- static <K, V> LinkedHashMap<K, V> newLinkedHashMapWithExpectedSize(int expectedSize) {
- return new LinkedHashMap<K, V>(calculateInitialCapacity(expectedSize));
- }
-
- private static int calculateInitialCapacity(int expectedSize) {
- if (expectedSize < 3) {
- return expectedSize + 1;
- }
- if (expectedSize < MAX_POWER_OF_TWO) {
- // This is the calculation used in JDK8 to resize when a putAll
- // happens; it seems to be the most conservative calculation we
- // can make. 0.75 is the default load factor.
- return (int) (expectedSize / 0.75F + 1.0F);
- }
- return Integer.MAX_VALUE; // any large value
- }
-}
diff --git a/core/src/main/java/dagger/internal/DelegateFactory.java b/core/src/main/java/dagger/internal/DelegateFactory.java
deleted file mode 100644
index d1e864d..0000000
--- a/core/src/main/java/dagger/internal/DelegateFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import javax.inject.Provider;
-
-/**
- * A DelegateFactory that is used to stitch Provider/Lazy indirection based dependency cycles.
- *
- * @since 2.0.1
- */
-public final class DelegateFactory<T> implements Factory<T> {
- private Provider<T> delegate;
-
- @Override
- public T get() {
- if (delegate == null) {
- throw new IllegalStateException();
- }
- return delegate.get();
- }
-
- public void setDelegatedProvider(Provider<T> delegate) {
- if (delegate == null) {
- throw new IllegalArgumentException();
- }
- if (this.delegate != null) {
- throw new IllegalStateException();
- }
- this.delegate = delegate;
- }
-}
-
diff --git a/core/src/main/java/dagger/internal/DoubleCheckLazy.java b/core/src/main/java/dagger/internal/DoubleCheckLazy.java
deleted file mode 100644
index d0f1028..0000000
--- a/core/src/main/java/dagger/internal/DoubleCheckLazy.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import dagger.Lazy;
-import javax.inject.Provider;
-
-/**
- * A basic {@link Lazy} implementation that memoizes the value returned from a {@link Provider}
- * using the double-check idiom described in Effective Java 2: Item 71.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-// TODO(gak): Unify the duplicated code between this and ScopedProvider.
-public final class DoubleCheckLazy<T> implements Lazy<T> {
- private static final Object UNINITIALIZED = new Object();
-
- private final Provider<T> provider;
- private volatile Object instance = UNINITIALIZED;
-
- private DoubleCheckLazy(Provider<T> provider) {
- assert provider != null;
- this.provider = provider;
- }
-
- @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
- @Override
- public T get() {
- // to suppress it.
- Object result = instance;
- if (result == UNINITIALIZED) {
- synchronized (this) {
- result = instance;
- if (result == UNINITIALIZED) {
- instance = result = provider.get();
- }
- }
- }
- return (T) result;
- }
-
- public static <T> Lazy<T> create(Provider<T> provider) {
- if (provider == null) {
- throw new NullPointerException();
- }
- if (provider instanceof Lazy) {
- @SuppressWarnings("unchecked")
- final Lazy<T> lazy = (Lazy<T>) provider;
- // Avoids memoizing a value that is already memoized.
- // NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
- // are different types using covariant return on get(). Right now this is used with
- // ScopedProvider<T> exclusively, which is implemented such that P and L are always the same
- // so it will be fine for that case.
- return lazy;
- }
- return new DoubleCheckLazy<T>(provider);
- }
-}
diff --git a/core/src/main/java/dagger/internal/Factory.java b/core/src/main/java/dagger/internal/Factory.java
deleted file mode 100644
index 3e2774c..0000000
--- a/core/src/main/java/dagger/internal/Factory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Scope;
-
-/**
- * An {@linkplain Scope unscoped} {@link Provider}. While a {@link Provider} <i>may<i> apply
- * scoping semantics while providing an instance, a factory implementation is guaranteed to exercise
- * the binding logic ({@link Inject} constructors, {@link Provides} methods) upon each call to
- * {@link #get}.
- *
- * <p>Note that while subsequent calls to {@link #get} will create new instances for bindings such
- * as those created by {@link Inject} constructors, a new instance is not guaranteed by all
- * bindings. For example, {@link Provides} methods may be implemented in ways that return the same
- * instance for each call.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-public interface Factory<T> extends Provider<T> {
-}
diff --git a/core/src/main/java/dagger/internal/InstanceFactory.java b/core/src/main/java/dagger/internal/InstanceFactory.java
deleted file mode 100644
index 59b1fcb..0000000
--- a/core/src/main/java/dagger/internal/InstanceFactory.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-/**
- * A {@link Factory} implementation that returns a single instance for all invocations of
- * {@link #get}.
- *
- * <p>Note that while this is a {@link Factory} implementation, and thus unscoped, each call to
- * {@link #get} will always return the same instance. As such, any scoping applied to this factory
- * is redundant and unnecessary. However, using this with the {@link ScopedProvider} is valid and
- * may be desired for testing or contractual guarantees.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-public final class InstanceFactory<T> implements Factory<T> {
- public static <T> Factory<T> create(T instance) {
- if (instance == null) {
- throw new NullPointerException();
- }
- return new InstanceFactory<T>(instance);
- }
-
- private final T instance;
-
- private InstanceFactory(T instance) {
- this.instance = instance;
- }
-
- @Override
- public T get() {
- return instance;
- }
-}
diff --git a/core/src/main/java/dagger/internal/MapFactory.java b/core/src/main/java/dagger/internal/MapFactory.java
deleted file mode 100644
index 4dac126..0000000
--- a/core/src/main/java/dagger/internal/MapFactory.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import java.util.Map;
-import java.util.Map.Entry;
-import javax.inject.Provider;
-
-import static dagger.internal.Collections.newLinkedHashMapWithExpectedSize;
-import static java.util.Collections.unmodifiableMap;
-
-/**
- * A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
- * {@code Map<K, V>} when calling {@link #get} (as specified by {@link Factory}).
- *
- * @author Chenying Hou
- * @since 2.0
- *
- */
-public final class MapFactory<K, V> implements Factory<Map<K, V>> {
- private final Map<K, Provider<V>> contributingMap;
-
- private MapFactory(Map<K, Provider<V>> map) {
- this.contributingMap = unmodifiableMap(map);
- }
-
- /**
- * Returns a new MapFactory.
- */
- public static <K, V> MapFactory<K, V> create(Provider<Map<K, Provider<V>>> mapProviderFactory) {
- Map<K, Provider<V>> map = mapProviderFactory.get();
- return new MapFactory<K, V>(map);
- }
-
- /**
- * Returns a {@code Map<K, V>} whose iteration order is that of the elements
- * given by each of the providers, which are invoked in the order given at creation.
- */
- @Override
- public Map<K, V> get() {
- Map<K, V> result = newLinkedHashMapWithExpectedSize(contributingMap.size());
- for (Entry<K, Provider<V>> entry: contributingMap.entrySet()) {
- result.put(entry.getKey(), entry.getValue().get());
- }
- return unmodifiableMap(result);
- }
-}
diff --git a/core/src/main/java/dagger/internal/MapProviderFactory.java b/core/src/main/java/dagger/internal/MapProviderFactory.java
deleted file mode 100644
index 00c0fd3..0000000
--- a/core/src/main/java/dagger/internal/MapProviderFactory.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import javax.inject.Provider;
-
-import static dagger.internal.Collections.newLinkedHashMapWithExpectedSize;
-import static java.util.Collections.unmodifiableMap;
-
-/**
- * A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
- * {@code Map<K, Provider<V>>} when calling {@link #get} (as specified by {@link Factory}).
- *
- * @author Chenying Hou
- * @since 2.0
- *
- */
-public final class MapProviderFactory<K, V> implements Factory<Map<K, Provider<V>>> {
- private final Map<K, Provider<V>> contributingMap;
-
- /**
- * Returns a new {@link Builder}
- */
- public static <K, V> Builder<K, V> builder(int size) {
- return new Builder<K, V>(size);
- }
-
- private MapProviderFactory(LinkedHashMap<K, Provider<V>> contributingMap) {
- this.contributingMap = unmodifiableMap(contributingMap);
- }
-
- /**
- * Returns a {@code Map<K, Provider<V>>} whose iteration order is that of the elements
- * given by each of the providers, which are invoked in the order given at creation.
- *
- */
- @Override
- public Map<K, Provider<V>> get() {
- return this.contributingMap;
- }
-
- /**
- * A builder to help build the {@link MapProviderFactory}
- */
- public static final class Builder<K, V> {
- private final LinkedHashMap<K, Provider<V>> mapBuilder;
-
- private Builder(int size) {
- // TODO(user): consider which way to initialize mapBuilder is better
- this.mapBuilder = newLinkedHashMapWithExpectedSize(size);
- }
-
- /**
- * Returns a new {@link MapProviderFactory}
- */
- public MapProviderFactory<K, V> build() {
- return new MapProviderFactory<K, V>(this.mapBuilder);
- }
-
- /**
- * Associate k with providerOfValue in {@code Builder}
- */
- public Builder<K, V> put(K key, Provider<V> providerOfValue) {
- if (key == null) {
- throw new NullPointerException("The key is null");
- }
- if (providerOfValue == null) {
- throw new NullPointerException("The provider of the value is null");
- }
-
- this.mapBuilder.put(key, providerOfValue);
- return this;
- }
- }
-}
diff --git a/core/src/main/java/dagger/internal/MembersInjectors.java b/core/src/main/java/dagger/internal/MembersInjectors.java
deleted file mode 100644
index ee4c7b4..0000000
--- a/core/src/main/java/dagger/internal/MembersInjectors.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import dagger.MembersInjector;
-import javax.inject.Inject;
-
-/**
- * Basic {@link MembersInjector} implementations used by the framework.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-public final class MembersInjectors {
- /**
- * Returns a {@link MembersInjector} implementation that injects no members
- *
- * <p>Note that there is no verification that the type being injected does not have {@link Inject}
- * members, so care should be taken to ensure appropriate use.
- */
- @SuppressWarnings("unchecked")
- public static <T> MembersInjector<T> noOp() {
- return (MembersInjector<T>) NoOpMembersInjector.INSTANCE;
- }
-
- private static enum NoOpMembersInjector implements MembersInjector<Object> {
- INSTANCE;
-
- @Override public void injectMembers(Object instance) {
- if (instance == null) {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * Returns a {@link MembersInjector} that delegates to the {@link MembersInjector} of its
- * supertype. This is useful for cases where a type is known not to have its own {@link Inject}
- * members, but must still inject members on its supertype(s).
- *
- * <p>Note that there is no verification that the type being injected does not have {@link Inject}
- * members, so care should be taken to ensure appropriate use.
- */
- @SuppressWarnings("unchecked")
- public static <T> MembersInjector<T> delegatingTo(MembersInjector<? super T> delegate) {
- return (MembersInjector<T>) delegate;
- }
-
- private MembersInjectors() {}
-}
diff --git a/core/src/main/java/dagger/internal/ScopedProvider.java b/core/src/main/java/dagger/internal/ScopedProvider.java
deleted file mode 100644
index b25db38..0000000
--- a/core/src/main/java/dagger/internal/ScopedProvider.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import dagger.Lazy;
-import javax.inject.Provider;
-
-/**
- * A {@link Provider} implementation that memoizes the result of a {@link Factory} instance.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-public final class ScopedProvider<T> implements Provider<T>, Lazy<T> {
- private static final Object UNINITIALIZED = new Object();
-
- private final Factory<T> factory;
- private volatile Object instance = UNINITIALIZED;
-
- private ScopedProvider(Factory<T> factory) {
- assert factory != null;
- this.factory = factory;
- }
-
- @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
- @Override
- public T get() {
- // double-check idiom from EJ2: Item 71
- Object result = instance;
- if (result == UNINITIALIZED) {
- synchronized (this) {
- result = instance;
- if (result == UNINITIALIZED) {
- instance = result = factory.get();
- }
- }
- }
- return (T) result;
- }
-
- /** Returns a new scoped provider for the given factory. */
- public static <T> Provider<T> create(Factory<T> factory) {
- if (factory == null) {
- throw new NullPointerException();
- }
- return new ScopedProvider<T>(factory);
- }
-}
diff --git a/core/src/main/java/dagger/internal/SetFactory.java b/core/src/main/java/dagger/internal/SetFactory.java
deleted file mode 100644
index 9b73e79..0000000
--- a/core/src/main/java/dagger/internal/SetFactory.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import javax.inject.Provider;
-
-import static dagger.internal.Collections.newLinkedHashSetWithExpectedSize;
-import static java.util.Collections.unmodifiableSet;
-
-/**
- * A {@link Factory} implementation used to implement {@link Set} bindings. This factory always
- * returns a new {@link Set} instance for each call to {@link #get} (as required by {@link Factory})
- * whose elements are populated by subsequent calls to their {@link Provider#get} methods.
- *
- * @author Gregory Kick
- * @since 2.0
- */
-public final class SetFactory<T> implements Factory<Set<T>> {
- /**
- * A message for NPEs that trigger on bad argument lists.
- */
- private static final String ARGUMENTS_MUST_BE_NON_NULL =
- "SetFactory.create() requires its arguments to be non-null";
-
- /**
- * Returns the supplied factory. If there's just one factory, there's no need to wrap it or its
- * result.
- */
- public static <T> Factory<Set<T>> create(Factory<Set<T>> factory) {
- assert factory != null : ARGUMENTS_MUST_BE_NON_NULL;
- return factory;
- }
-
- /**
- * Returns a new factory that creates {@link Set} instances that form the union of the given
- * {@link Provider} instances. Callers must not modify the providers array after invoking this
- * method; no copy is made.
- */
- public static <T> Factory<Set<T>> create(
- @SuppressWarnings("unchecked") Provider<Set<T>>... providers) {
- assert providers != null : ARGUMENTS_MUST_BE_NON_NULL;
-
- List<Provider<Set<T>>> contributingProviders = Arrays.asList(providers);
-
- assert !contributingProviders.contains(null)
- : "Codegen error? Null within provider list.";
- assert !hasDuplicates(contributingProviders)
- : "Codegen error? Duplicates in the provider list";
-
- return new SetFactory<T>(contributingProviders);
- }
-
- /**
- * Returns true if at least one pair of items in (@code original) are equals.
- */
- private static boolean hasDuplicates(List<? extends Object> original) {
- Set<Object> asSet = new HashSet<Object>(original);
- return original.size() != asSet.size();
- }
-
- private final List<Provider<Set<T>>> contributingProviders;
-
- private SetFactory(List<Provider<Set<T>>> contributingProviders) {
- this.contributingProviders = contributingProviders;
- }
-
- /**
- * Returns a {@link Set} whose iteration order is that of the elements given by each of the
- * providers, which are invoked in the order given at creation.
- *
- * @throws NullPointerException if any of the delegate {@link Set} instances or elements therein
- * are {@code null}
- */
- @Override
- public Set<T> get() {
- int size = 0;
-
- // Profiling revealed that this method was a CPU-consuming hotspot in some applications, so
- // these loops were changed to use c-style for. Versus enhanced for-each loops, C-style for is
- // faster for ArrayLists, at least through Java 8.
-
- List<Set<T>> providedSets = new ArrayList<Set<T>>(contributingProviders.size());
- for (int i = 0, c = contributingProviders.size(); i < c; i++) {
- Provider<Set<T>> provider = contributingProviders.get(i);
- Set<T> providedSet = provider.get();
- if (providedSet == null) {
- throw new NullPointerException(provider + " returned null");
- }
- providedSets.add(providedSet);
- size += providedSet.size();
- }
-
- Set<T> result = newLinkedHashSetWithExpectedSize(size);
- for (int i = 0, c = providedSets.size(); i < c; i++) {
- for (T element : providedSets.get(i)) {
- if (element == null) {
- throw new NullPointerException("a null element was provided");
- }
- result.add(element);
- }
- }
- return unmodifiableSet(result);
- }
-}
diff --git a/core/src/main/java/dagger/mapkeys/ClassKey.java b/core/src/main/java/dagger/mapkeys/ClassKey.java
deleted file mode 100644
index 21497c6..0000000
--- a/core/src/main/java/dagger/mapkeys/ClassKey.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.mapkeys;
-
-import dagger.MapKey;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-/**
- * A {@link MapKey} annotation for maps with {@code Class<?>} keys.
- *
- * <p>If your map's keys can be constrained, consider using a custom annotation instead, with a
- * member whose type is {@code Class<? extends Something>}.
- */
-@Documented
-@Target(METHOD)
-@MapKey
-public @interface ClassKey {
- Class<?> value();
-}
\ No newline at end of file
diff --git a/core/src/main/java/dagger/mapkeys/IntKey.java b/core/src/main/java/dagger/mapkeys/IntKey.java
deleted file mode 100644
index 011b49f..0000000
--- a/core/src/main/java/dagger/mapkeys/IntKey.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.mapkeys;
-
-import dagger.MapKey;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-/** A {@link MapKey} annotation for maps with {@code int} keys. */
-@Documented
-@Target(METHOD)
-@MapKey
-public @interface IntKey {
- int value();
-}
\ No newline at end of file
diff --git a/core/src/main/java/dagger/mapkeys/LongKey.java b/core/src/main/java/dagger/mapkeys/LongKey.java
deleted file mode 100644
index 183b74d..0000000
--- a/core/src/main/java/dagger/mapkeys/LongKey.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.mapkeys;
-
-import dagger.MapKey;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-/** A {@link MapKey} annotation for maps with {@code long} keys. */
-@Documented
-@Target(METHOD)
-@MapKey
-public @interface LongKey {
- long value();
-}
\ No newline at end of file
diff --git a/core/src/main/java/dagger/mapkeys/StringKey.java b/core/src/main/java/dagger/mapkeys/StringKey.java
deleted file mode 100644
index 7455a9b..0000000
--- a/core/src/main/java/dagger/mapkeys/StringKey.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.mapkeys;
-
-import dagger.MapKey;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-/** A {@link MapKey} annotation for maps with {@link String} keys. */
-@Documented
-@Target(METHOD)
-@MapKey
-public @interface StringKey {
- String value();
-}
\ No newline at end of file
diff --git a/core/src/main/java/dagger/package-info.java b/core/src/main/java/dagger/package-info.java
deleted file mode 100644
index e5cc67f..0000000
--- a/core/src/main/java/dagger/package-info.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This package contains the public API for the <a href="http://google.github.io/dagger/">Dagger
- * 2</a> dependency injection framework. By building upon
- * <a href="https://jcp.org/en/jsr/detail?id=330">JSR 330</a>, Dagger 2 provides an
- * annotation-driven API for dependency injection whose implementation is entirely generated at
- * compile time by <a href="http://en.wikipedia.org/wiki/Java_annotation#Processing">annotation
- * processors</a>.
- *
- * <p>The entry point into the API is the {@link Component}, which annotates abstract types for
- * Dagger 2 to implement. The dependency graph is configured using using annotations such as
- * {@link Module}, {@link Provides} and {@link javax.inject.Inject}.
- *
- * <p>{@code dagger.internal.codegen.ComponentProcessor} is the processor responsible for generating
- * the implementation. Dagger uses the annotation procesor
- * {@linkplain java.util.ServiceLoader service loader} to automatically configure the processor, so
- * explict build configuration shouldn't be necessary.
- */
-package dagger;
diff --git a/core/src/test/java/dagger/internal/DoubleCheckLazyTest.java b/core/src/test/java/dagger/internal/DoubleCheckLazyTest.java
deleted file mode 100644
index 579e040..0000000
--- a/core/src/test/java/dagger/internal/DoubleCheckLazyTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.Uninterruptibles;
-import dagger.Lazy;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.inject.Provider;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assert_;
-import static org.junit.Assert.fail;
-
-@RunWith(JUnit4.class)
-public class DoubleCheckLazyTest {
- @Test public void get() throws Exception {
- int numThreads = 10;
- ExecutorService executor = Executors.newFixedThreadPool(numThreads);
-
- final CountDownLatch latch = new CountDownLatch(numThreads);
- LatchedProvider provider = new LatchedProvider(latch);
- final Lazy<Object> lazy = DoubleCheckLazy.create(provider);
-
- List<Callable<Object>> tasks = Lists.newArrayListWithCapacity(numThreads);
- for (int i = 0; i < numThreads; i++) {
- tasks.add(new Callable<Object>() {
- @Override public Object call() throws Exception {
- latch.countDown();
- return lazy.get();
- }
- });
- }
-
- List<Future<Object>> futures = executor.invokeAll(tasks);
-
- assert_().that(provider.provisions.get()).isEqualTo(1);
- Set<Object> results = Sets.newIdentityHashSet();
- for (Future<Object> future : futures) {
- results.add(future.get());
- }
- assert_().that(results.size()).isEqualTo(1);
- }
-
- // TODO(gak): reenable this test once we can ensure that factories are no longer providing null
- @Ignore @Test public void get_null() {
- Lazy<Object> lazy = DoubleCheckLazy.create(new Provider<Object> () {
- @Override public Object get() {
- return null;
- }
- });
- try {
- lazy.get();
- fail();
- } catch (NullPointerException expected) {}
- }
-
- private static class LatchedProvider implements Provider<Object> {
- final AtomicInteger provisions;
- final CountDownLatch latch;
-
- LatchedProvider(CountDownLatch latch) {
- this.latch = latch;
- this.provisions = new AtomicInteger();
- }
-
- @Override
- public Object get() {
- if (latch != null) {
- Uninterruptibles.awaitUninterruptibly(latch);
- }
- provisions.incrementAndGet();
- return new Object();
- }
- }
-}
diff --git a/core/src/test/java/dagger/internal/InstanceFactoryTest.java b/core/src/test/java/dagger/internal/InstanceFactoryTest.java
deleted file mode 100644
index acaf20d..0000000
--- a/core/src/test/java/dagger/internal/InstanceFactoryTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assert_;
-
-@RunWith(JUnit4.class)
-public final class InstanceFactoryTest {
- @Rule public final ExpectedException thrown = ExpectedException.none();
-
- @Test public void instanceFactory() {
- Object instance = new Object();
- Factory<Object> factory = InstanceFactory.create(instance);
- assert_().that(factory.get()).isEqualTo(instance);
- assert_().that(factory.get()).isEqualTo(instance);
- assert_().that(factory.get()).isEqualTo(instance);
- }
-
- @Test public void create_throwsNullPointerException() {
- thrown.expect(NullPointerException.class);
- InstanceFactory.create(null);
- }
-}
diff --git a/core/src/test/java/dagger/internal/MapProviderFactoryTest.java b/core/src/test/java/dagger/internal/MapProviderFactoryTest.java
deleted file mode 100644
index b4496e9..0000000
--- a/core/src/test/java/dagger/internal/MapProviderFactoryTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.inject.Provider;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assert_;
-
-@RunWith(JUnit4.class)
-@SuppressWarnings("unchecked")
-public class MapProviderFactoryTest {
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @Test
- public void nullKey() {
- thrown.expect(NullPointerException.class);
- MapProviderFactory.<String, Integer>builder(1).put(null, incrementingIntegerProvider(1));
- }
-
- @Test
- public void nullValue() {
- thrown.expect(NullPointerException.class);
- MapProviderFactory.<String, Integer>builder(1).put("Hello", null);
- }
-
- @Test
- public void iterationOrder() {
- Provider<Integer> p1 = incrementingIntegerProvider(10);
- Provider<Integer> p2 = incrementingIntegerProvider(20);
- Provider<Integer> p3 = incrementingIntegerProvider(30);
- Provider<Integer> p4 = incrementingIntegerProvider(40);
- Provider<Integer> p5 = incrementingIntegerProvider(50);
-
- Factory<Map<String, Provider<Integer>>> factory = MapProviderFactory
- .<String, Integer>builder(4)
- .put("two", p2)
- .put("one", p1)
- .put("three", p3)
- .put("one", p5)
- .put("four", p4)
- .build();
-
- Map<String, Provider<Integer>> expectedMap = new LinkedHashMap<String, Provider<Integer>>();
- expectedMap.put("two", p2);
- expectedMap.put("one", p1);
- expectedMap.put("three", p3);
- expectedMap.put("one", p5);
- expectedMap.put("four", p4);
- assert_()
- .that(factory.get().entrySet())
- .containsExactlyElementsIn(expectedMap.entrySet())
- .inOrder();
- }
-
- private static Provider<Integer> incrementingIntegerProvider(int seed) {
- final AtomicInteger value = new AtomicInteger(seed);
- return new Provider<Integer>() {
- @Override
- public Integer get() {
- return value.getAndIncrement();
- }
- };
- }
-}
diff --git a/core/src/test/java/dagger/internal/ScopedProviderTest.java b/core/src/test/java/dagger/internal/ScopedProviderTest.java
deleted file mode 100644
index 84b02c5..0000000
--- a/core/src/test/java/dagger/internal/ScopedProviderTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import javax.inject.Provider;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assert_;
-import static org.junit.Assert.fail;
-
-/**
- * Tests {@link ScopedProvider}.
- */
-@RunWith(JUnit4.class)
-public class ScopedProviderTest {
- @Test public void create_nullPointerException() {
- try {
- ScopedProvider.create(null);
- fail();
- } catch (NullPointerException expected) { }
- }
-
- // TODO(gak): reenable this test once we can ensure that factories are no longer providing null
- @Ignore @Test public void get_nullPointerException() {
- Provider<Object> scopedProvider = ScopedProvider.create(new Factory<Object>() {
- @Override public Object get() {
- return null;
- }
- });
- try {
- scopedProvider.get();
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- @Test public void get() {
- Provider<Integer> scopedProvider = ScopedProvider.create(new Factory<Integer>() {
- int i = 0;
-
- @Override public Integer get() {
- return i++;
- }
- });
- assert_().that(scopedProvider.get()).isEqualTo(0);
- assert_().that(scopedProvider.get()).isEqualTo(0);
- assert_().that(scopedProvider.get()).isEqualTo(0);
- }
-}
diff --git a/core/src/test/java/dagger/internal/SetFactoryTest.java b/core/src/test/java/dagger/internal/SetFactoryTest.java
deleted file mode 100644
index 04b9822..0000000
--- a/core/src/test/java/dagger/internal/SetFactoryTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal;
-
-import com.google.common.collect.ContiguousSet;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Range;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.inject.Provider;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.collect.DiscreteDomain.integers;
-import static com.google.common.truth.Truth.assert_;
-
-@RunWith(JUnit4.class)
-@SuppressWarnings("unchecked")
-public class SetFactoryTest {
- @Rule public ExpectedException thrown = ExpectedException.none();
-
- @Test
- public void providerReturnsNullSet() {
- Factory<Set<Integer>> factory = SetFactory.create(new Provider<Set<Integer>>() {
- @Override
- public Set<Integer> get() {
- return null;
- }
- }, incrementingIntegerProvider(0));
- thrown.expect(NullPointerException.class);
- factory.get();
- }
-
- @Test
- public void providerReturnsNullSet_single() {
- Factory<Set<Integer>> factory = SetFactory.create(new Provider<Set<Integer>>() {
- @Override
- public Set<Integer> get() {
- return null;
- }
- });
- thrown.expect(NullPointerException.class);
- factory.get();
- }
-
- @Test
- public void providerReturnsSetWithNullElement() {
- Factory<Set<Integer>> factory = SetFactory.create(new Provider<Set<Integer>>() {
- @Override
- public Set<Integer> get() {
- LinkedHashSet<Integer> result = new LinkedHashSet<Integer>();
- result.add(1);
- result.add(null);
- result.add(3);
- return result;
- }
- });
- thrown.expect(NullPointerException.class);
- factory.get();
- }
-
- @Test
- public void providerReturnsSetWithNullElement_single() {
- Factory<Set<Integer>> factory = SetFactory.create(new Provider<Set<Integer>>() {
- @Override
- public Set<Integer> get() {
- LinkedHashSet<Integer> result = new LinkedHashSet<Integer>();
- result.add(1);
- result.add(null);
- result.add(3);
- return result;
- }
- }, incrementingIntegerProvider(0));
- thrown.expect(NullPointerException.class);
- factory.get();
- }
-
- @Test
- public void invokesProvidersEverytTime() {
- Factory<Set<Integer>> factory = SetFactory.create(
- incrementingIntegerProvider(0),
- incrementingIntegerProvider(10),
- incrementingIntegerProvider(20));
- assert_().that(factory.get()).containsExactly(0, 10, 20);
- assert_().that(factory.get()).containsExactly(1, 11, 21);
- assert_().that(factory.get()).containsExactly(2, 12, 22);
- }
-
- @Test
- public void iterationOrder() {
- Factory<Set<Integer>> factory = SetFactory.create(
- integerSetProvider(Range.closed(5, 9)),
- integerSetProvider(Range.closed(3, 6)),
- integerSetProvider(Range.closed(0, 5)));
- assert_().that(factory.get()).containsExactly(5, 6, 7, 8, 9, 3, 4, 0, 1, 2).inOrder();
- }
-
- private static Provider<Set<Integer>> incrementingIntegerProvider(int seed) {
- final AtomicInteger value = new AtomicInteger(seed);
- return new Provider<Set<Integer>>() {
- @Override
- public Set<Integer> get() {
- return ImmutableSet.of(value.getAndIncrement());
- }
- };
- }
-
- private static Provider<Set<Integer>> integerSetProvider(Range<Integer> range) {
- final ContiguousSet<Integer> set = ContiguousSet.create(range, integers());
- return new Provider<Set<Integer>>() {
- @Override
- public Set<Integer> get() {
- return set;
- }
- };
- }
-}
diff --git a/deploy_website.sh b/deploy_website.sh
deleted file mode 100755
index 1fde1bd..0000000
--- a/deploy_website.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash
-#
-# Deploys the current Dagger website to the gh-pages branch of the GitHub
-# repository. To test the site locally before deploying run `jekyll --server`
-# in the website/ directory.
-
-set -ex
-
-REPO="git@github.com:square/dagger.git"
-GROUP_ID="com.squareup.dagger"
-ARTIFACT_ID="dagger"
-
-DIR=temp-dagger-clone
-
-# Delete any existing temporary website clone
-rm -rf $DIR
-
-# Clone the current repo into temp folder
-git clone $REPO $DIR
-
-# Move working directory into temp folder
-cd $DIR
-
-# Checkout and track the gh-pages branch
-git checkout -t origin/gh-pages
-
-# Delete everything
-rm -rf *
-
-# Copy website files from real repo
-cp -R ../website/* .
-
-# Download the latest javadoc
-curl -L "http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=$GROUP_ID&a=$ARTIFACT_ID&v=LATEST&c=javadoc" > javadoc.zip
-mkdir javadoc
-unzip javadoc.zip -d javadoc
-rm javadoc.zip
-
-# Stage all files in git and create a commit
-git add .
-git add -u
-git commit -m "Website at $(date)"
-
-# Push the new files up to GitHub
-git push origin gh-pages
-
-# Delete our temp folder
-cd ..
-rm -rf $DIR
diff --git a/examples/android-activity-graphs/README.md b/examples/android-activity-graphs/README.md
deleted file mode 100644
index ac3680b..0000000
--- a/examples/android-activity-graphs/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-Example: Android Activity Graphs
-================================
-
-Building on top of the simple Android example, this example demonstrates how it is possible to
-create child graphs for each activity which extend from the global graph.
-
-Some of the advantages of the activity scope:
-
- * Provides the ability to inject objects which require the activity to be constructed.
- * Allows for the use of singletons on a per-activity basis. This is a great way to manage a
- resource that is shared by a bunch of fragments in an activity.
- * Keeps the global object graph clear of things that can be used only by activities.
-
-While this example only shows the presence of an activity scope, you should be able to see the
-potential for other useful scopes that can be used. For example, having a dedicated object graph
-for the current user session is a great way to manage data that is tied to the currently logged-in
-user.
-
-_Note: The app does not actually do anything when it is run. It is only to show how you can
- structure Dagger within an Android app_
-
-_Note: The app is in transition to Dagger 2 and may not reflect recommended patterns. Before
- we release Dagger 2.0 it will, but until this note is removed, please do not rely on this
- example as a strong recommendation._
diff --git a/examples/android-activity-graphs/pom.xml b/examples/android-activity-graphs/pom.xml
deleted file mode 100644
index 340c59c..0000000
--- a/examples/android-activity-graphs/pom.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2013 Square, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>com.google.dagger.example</groupId>
- <artifactId>dagger-example-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>android-activity-graphs</artifactId>
- <name>Examples: Android - Activity Graphs</name>
- <packaging>apk</packaging>
-
- <dependencies>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-compiler</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
-
- <dependency>
- <groupId>com.google.android</groupId>
- <artifactId>android</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.google.android</groupId>
- <artifactId>support-v4</artifactId>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>com.simpligility.maven.plugins</groupId>
- <artifactId>android-maven-plugin</artifactId>
- <extensions>true</extensions>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/examples/android-activity-graphs/src/main/AndroidManifest.xml b/examples/android-activity-graphs/src/main/AndroidManifest.xml
deleted file mode 100644
index 234406d..0000000
--- a/examples/android-activity-graphs/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- android:versionCode="1"
- android:versionName="1.0.0"
- package="com.example.dagger.activitygraphs">
-
- <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17"/>
-
- <application
- android:label="app_name"
- android:name=".DemoApplication">
- <activity
- android:label="app_name"
- android:name=".ui.HomeActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/AbstractActivityComponent.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/AbstractActivityComponent.java
deleted file mode 100644
index 430838e..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/AbstractActivityComponent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs;
-
-import android.app.Activity;
-import dagger.Component;
-
-/**
- * A base component upon which fragment's components may depend. Activity-level components
- * should extend this component.
- */
-@PerActivity // Subtypes of AbstractActivityComponent should be decorated with @PerActivity.
-@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
-public interface AbstractActivityComponent {
- Activity activity(); // Expose the activity to sub-graphs.
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ActivityModule.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ActivityModule.java
deleted file mode 100644
index cf5462e..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ActivityModule.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs;
-
-import android.app.Activity;
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * A module to wrap the Activity state and expose it to the graph.
- */
-@Module
-public class ActivityModule {
- private final Activity activity;
-
- public ActivityModule(Activity activity) {
- this.activity = activity;
- }
-
- /**
- * Expose the activity to dependents in the graph.
- */
- @Provides @PerActivity Activity activity() {
- return activity;
- }
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ApplicationComponent.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ApplicationComponent.java
deleted file mode 100644
index 04c2062..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ApplicationComponent.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs;
-
-import android.app.Application;
-import android.location.LocationManager;
-import dagger.Component;
-import javax.inject.Singleton;
-
-/**
- * A component whose lifetime is the life of the application.
- */
-@Singleton // Constraints this component to one-per-application or unscoped bindings.
-@Component(modules = DemoApplicationModule.class)
-public interface ApplicationComponent {
- // Field injections of any dependencies of the DemoApplication
- void inject(DemoApplication application);
-
- // Exported for child-components.
- Application application();
- LocationManager locationManager();
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplication.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplication.java
deleted file mode 100644
index 7205733..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplication.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs;
-
-import android.app.Application;
-import android.location.LocationManager;
-import javax.inject.Singleton;
-import javax.inject.Inject;
-
-public class DemoApplication extends Application {
- private ApplicationComponent applicationComponent;
-
- // TODO(cgruber): Figure out a better example of something one might inject into the app.
- @Inject LocationManager locationManager; // to illustrate injecting something into the app.
-
- @Override public void onCreate() {
- super.onCreate();
- applicationComponent = DaggerApplicationComponent.builder()
- .demoApplicationModule(new DemoApplicationModule(this))
- .build();
- }
-
- public ApplicationComponent component() {
- return applicationComponent;
- }
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplicationModule.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplicationModule.java
deleted file mode 100644
index 070d2c7..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplicationModule.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs;
-
-import android.app.Application;
-import android.location.LocationManager;
-import dagger.Module;
-import dagger.Provides;
-import javax.inject.Singleton;
-
-import static android.content.Context.LOCATION_SERVICE;
-
-/**
- * A module for Android-specific dependencies which require a {@link Context} or
- * {@link android.app.Application} to create.
- */
-@Module
-public class DemoApplicationModule {
- private final Application application;
-
- public DemoApplicationModule(Application application) {
- this.application = application;
- }
-
- /**
- * Expose the application to the graph.
- */
- @Provides @Singleton Application application() {
- return application;
- }
-
- @Provides @Singleton LocationManager provideLocationManager() {
- return (LocationManager) application.getSystemService(LOCATION_SERVICE);
- }
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/PerActivity.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/PerActivity.java
deleted file mode 100644
index d54b193..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/PerActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs;
-
-import java.lang.annotation.Retention;
-import javax.inject.Scope;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * A scoping annotation to permit objects whose lifetime should
- * conform to the life of the activity to be memoized in the
- * correct component.
- */
-@Scope
-@Retention(RUNTIME)
-public @interface PerActivity {
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/ActivityTitleController.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/ActivityTitleController.java
deleted file mode 100644
index c416c75..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/ActivityTitleController.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs.ui;
-
-import android.app.Activity;
-import com.example.dagger.activitygraphs.PerActivity;
-import javax.inject.Inject;
-
-/**
- * A simple abstraction which provides the ability to set the title on an activity.
- * <p>
- * Fragments should not directly modify any part of an activity outside of the view or dialog that
- * it creates. This class provides a way for fragments to inject a controller that will allow for
- * control of the activity title. While not exceedingly useful in practice, this concept could be
- * expanded to things like facilitating control over the action bar, dialogs, notifications, etc.
- */
-@PerActivity
-public class ActivityTitleController {
- private final Activity activity;
-
- @Inject public ActivityTitleController(Activity activity) {
- this.activity = activity;
- }
-
- public void setTitle(CharSequence title) {
- activity.setTitle(title);
- }
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeActivity.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeActivity.java
deleted file mode 100644
index 1f3bb70..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeActivity.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs.ui;
-
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-import com.example.dagger.activitygraphs.ActivityModule;
-import com.example.dagger.activitygraphs.DemoApplication;
-import javax.inject.Inject;
-
-public class HomeActivity extends FragmentActivity {
- @Inject LocationManager locationManager;
- private HomeComponent component;
-
- HomeComponent component() {
- if (component == null) {
- component = DaggerHomeComponent.builder()
- .applicationComponent(((DemoApplication) getApplication()).component())
- .activityModule(new ActivityModule(this))
- .build();
- }
- return component;
- }
-
- @Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- component().inject(this);
-
- if (savedInstanceState == null) {
- getSupportFragmentManager().beginTransaction()
- .add(android.R.id.content, new HomeFragment())
- .commit();
- }
-
- // TODO do something with the injected dependencies here!
- }
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeComponent.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeComponent.java
deleted file mode 100644
index 84d2a42..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeComponent.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs.ui;
-
-import com.example.dagger.activitygraphs.AbstractActivityComponent;
-import com.example.dagger.activitygraphs.ActivityModule;
-import com.example.dagger.activitygraphs.ApplicationComponent;
-import com.example.dagger.activitygraphs.PerActivity;
-import dagger.Component;
-
-@PerActivity
-@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
-public interface HomeComponent extends AbstractActivityComponent {
- void inject(HomeActivity homeActivity);
- void inject(HomeFragment homeFragment);
-}
diff --git a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeFragment.java b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeFragment.java
deleted file mode 100644
index 1df2724..0000000
--- a/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeFragment.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.activitygraphs.ui;
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import javax.inject.Inject;
-
-import static android.view.Gravity.CENTER;
-
-public class HomeFragment extends Fragment {
- @Inject ActivityTitleController titleController;
-
- @Override public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- ((HomeActivity) getActivity()).component().inject(this);
- }
-
- @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- TextView tv = new TextView(getActivity());
- tv.setGravity(CENTER);
- tv.setText("Hello, World");
- return tv;
- }
-
- @Override public void onResume() {
- super.onResume();
-
- // Fragments should not modify things outside of their own view. Use an external controller to
- // ask the activity to change its title.
- titleController.setTitle("Home Fragment");
- }
-}
diff --git a/examples/android-simple/README.md b/examples/android-simple/README.md
deleted file mode 100644
index 944d015..0000000
--- a/examples/android-simple/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-Example: Android Simple
-=======================
-
-This example demonstrates how to structure an Android application with Dagger.
-
-A custom `Application` class is used to manage a global object graph of objects. Modules are
-assembled with a `getModules` method on the application that can be overridden to add additional
-modules in development versions of your applications or in tests.
-
-Injection of activities is done automatically in a base activity.
-
-_Note: The app does not actually do anything when it is run. It is only to show how you can
- structure Dagger within an Android app_
-
-_Note: The app is in transition to Dagger 2 and may not reflect recommended patterns. Before
- we release Dagger 2.0 it will, but until this note is removed, please do not rely on this
- example as a strong recommendation._
diff --git a/examples/android-simple/pom.xml b/examples/android-simple/pom.xml
deleted file mode 100644
index fb934e6..0000000
--- a/examples/android-simple/pom.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2013 Square, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>com.google.dagger.example</groupId>
- <artifactId>dagger-example-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>android-simple</artifactId>
- <name>Examples: Android - Simple</name>
- <packaging>apk</packaging>
-
- <dependencies>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-compiler</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
-
- <dependency>
- <groupId>com.google.android</groupId>
- <artifactId>android</artifactId>
- <scope>provided</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>com.simpligility.maven.plugins</groupId>
- <artifactId>android-maven-plugin</artifactId>
- <extensions>true</extensions>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/examples/android-simple/src/main/AndroidManifest.xml b/examples/android-simple/src/main/AndroidManifest.xml
deleted file mode 100644
index 53c83bf..0000000
--- a/examples/android-simple/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- android:versionCode="1"
- android:versionName="1.0.0"
- package="com.example.dagger.simple">
-
- <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17"/>
-
- <application
- android:label="app_name"
- android:name=".DemoApplication">
- <activity
- android:label="app_name"
- android:name=".ui.HomeActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/examples/android-simple/src/main/java/com/example/dagger/simple/AndroidModule.java b/examples/android-simple/src/main/java/com/example/dagger/simple/AndroidModule.java
deleted file mode 100644
index 18184d1..0000000
--- a/examples/android-simple/src/main/java/com/example/dagger/simple/AndroidModule.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.simple;
-
-import android.content.Context;
-import android.location.LocationManager;
-import dagger.Module;
-import dagger.Provides;
-import javax.inject.Singleton;
-
-import static android.content.Context.LOCATION_SERVICE;
-
-/**
- * A module for Android-specific dependencies which require a {@link Context} or
- * {@link android.app.Application} to create.
- */
-@Module
-public class AndroidModule {
- private final DemoApplication application;
-
- public AndroidModule(DemoApplication application) {
- this.application = application;
- }
-
- /**
- * Allow the application context to be injected but require that it be annotated with
- * {@link ForApplication @Annotation} to explicitly differentiate it from an activity context.
- */
- @Provides @Singleton @ForApplication Context provideApplicationContext() {
- return application;
- }
-
- @Provides @Singleton LocationManager provideLocationManager() {
- return (LocationManager) application.getSystemService(LOCATION_SERVICE);
- }
-}
diff --git a/examples/android-simple/src/main/java/com/example/dagger/simple/DemoActivity.java b/examples/android-simple/src/main/java/com/example/dagger/simple/DemoActivity.java
deleted file mode 100644
index aa09f2d..0000000
--- a/examples/android-simple/src/main/java/com/example/dagger/simple/DemoActivity.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.simple;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public abstract class DemoActivity extends Activity {
- @Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Perform injection so that when this call returns all dependencies will be available for use.
- ((DemoApplication) getApplication()).component().inject(this);
- }
-}
diff --git a/examples/android-simple/src/main/java/com/example/dagger/simple/DemoApplication.java b/examples/android-simple/src/main/java/com/example/dagger/simple/DemoApplication.java
deleted file mode 100644
index 55402c6..0000000
--- a/examples/android-simple/src/main/java/com/example/dagger/simple/DemoApplication.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.simple;
-
-import android.app.Application;
-import android.location.LocationManager;
-import com.example.dagger.simple.ui.HomeActivity;
-import dagger.Component;
-import java.util.Arrays;
-import java.util.List;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-public class DemoApplication extends Application {
-
- @Singleton
- @Component(modules = AndroidModule.class)
- public interface ApplicationComponent {
- void inject(DemoApplication application);
- void inject(HomeActivity homeActivity);
- void inject(DemoActivity demoActivity);
- }
-
- @Inject LocationManager locationManager; // for some reason.
-
- private ApplicationComponent component;
-
- @Override public void onCreate() {
- super.onCreate();
- component = DaggerDemoApplication_ApplicationComponent.builder()
- .androidModule(new AndroidModule(this))
- .build();
- component().inject(this); // As of now, LocationManager should be injected into this.
- }
-
- public ApplicationComponent component() {
- return component;
- }
-}
diff --git a/examples/android-simple/src/main/java/com/example/dagger/simple/ForApplication.java b/examples/android-simple/src/main/java/com/example/dagger/simple/ForApplication.java
deleted file mode 100644
index 84d2247..0000000
--- a/examples/android-simple/src/main/java/com/example/dagger/simple/ForApplication.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.simple;
-
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Qualifier @Retention(RUNTIME)
-public @interface ForApplication {
-}
diff --git a/examples/android-simple/src/main/java/com/example/dagger/simple/ui/HomeActivity.java b/examples/android-simple/src/main/java/com/example/dagger/simple/ui/HomeActivity.java
deleted file mode 100644
index 7e33b8e..0000000
--- a/examples/android-simple/src/main/java/com/example/dagger/simple/ui/HomeActivity.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.dagger.simple.ui;
-
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.util.Log;
-import com.example.dagger.simple.DemoActivity;
-import com.example.dagger.simple.DemoApplication;
-import javax.inject.Inject;
-
-public class HomeActivity extends DemoActivity {
- @Inject LocationManager locationManager;
-
- @Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ((DemoApplication) getApplication()).component().inject(this);
-
- // TODO do something with the injected dependencies here!
- Log.d("HomeActivity", locationManager.toString());
- }
-}
diff --git a/examples/pom.xml b/examples/pom.xml
index eb4685a..c12050d 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (C) 2013 Google, Inc.
- Copyright (C) 2013 Square, Inc.
+ Copyright (C) 2013 The Dagger Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -19,34 +18,38 @@
<modelVersion>4.0.0</modelVersion>
<parent>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
+ <groupId>org.sonatype.oss</groupId>
+ <artifactId>oss-parent</artifactId>
+ <version>9</version>
</parent>
<groupId>com.google.dagger.example</groupId>
<artifactId>dagger-example-parent</artifactId>
<packaging>pom</packaging>
<name>Examples</name>
+ <version>2.17</version>
<modules>
<module>simple</module>
- <module>android-simple</module>
- <module>android-activity-graphs</module>
</modules>
<!-- Example-only dependencies. -->
<dependencyManagement>
<dependencies>
<dependency>
- <groupId>com.google.android</groupId>
- <artifactId>android</artifactId>
- <version>4.1.1.4</version>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
- <groupId>com.google.android</groupId>
- <artifactId>support-v4</artifactId>
- <version>r7</version>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger-compiler</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>26.0-jre</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -61,17 +64,6 @@
<target>1.6</target>
</configuration>
</plugin>
- <plugin>
- <groupId>com.simpligility.maven.plugins</groupId>
- <artifactId>android-maven-plugin</artifactId>
- <version>4.3.0</version>
- <configuration>
- <sdk>
- <platform>23</platform>
- <path>${env.ANDROID_HOME}</path>
- </sdk>
- </configuration>
- </plugin>
</plugins>
</pluginManagement>
</build>
diff --git a/examples/simple/pom.xml b/examples/simple/pom.xml
index 0be10b8..ffc99dc 100644
--- a/examples/simple/pom.xml
+++ b/examples/simple/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (C) 2012 Square, Inc.
+ Copyright (C) 2012 The Dagger Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
<parent>
<groupId>com.google.dagger.example</groupId>
<artifactId>dagger-example-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
+ <version>2.17</version>
</parent>
<artifactId>simple</artifactId>
@@ -28,15 +28,32 @@
<dependencies>
<dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
+ <!-- Force the correct version of Guava to be on the classpath. -->
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
- <artifactId>dagger-compiler</artifactId>
- <version>${project.version}</version>
- <optional>true</optional>
+ <artifactId>dagger</artifactId>
</dependency>
</dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.6.1</version>
+ <configuration>
+ <annotationProcessorPaths>
+ <path>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger-compiler</artifactId>
+ <version>${project.version}</version>
+ </path>
+ </annotationProcessorPaths>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
diff --git a/examples/simple/src/main/java/coffee/CoffeeApp.java b/examples/simple/src/main/java/coffee/CoffeeApp.java
index b0a93ec..7a2b9a6 100644
--- a/examples/simple/src/main/java/coffee/CoffeeApp.java
+++ b/examples/simple/src/main/java/coffee/CoffeeApp.java
@@ -6,12 +6,12 @@
public class CoffeeApp {
@Singleton
@Component(modules = { DripCoffeeModule.class })
- public interface Coffee {
+ public interface CoffeeShop {
CoffeeMaker maker();
}
public static void main(String[] args) {
- Coffee coffee = DaggerCoffeeApp_Coffee.builder().build();
- coffee.maker().brew();
+ CoffeeShop coffeeShop = DaggerCoffeeApp_CoffeeShop.builder().build();
+ coffeeShop.maker().brew();
}
}
diff --git a/examples/simple/src/main/java/coffee/PumpModule.java b/examples/simple/src/main/java/coffee/PumpModule.java
index 338ad33..df00b86 100644
--- a/examples/simple/src/main/java/coffee/PumpModule.java
+++ b/examples/simple/src/main/java/coffee/PumpModule.java
@@ -1,11 +1,10 @@
package coffee;
+import dagger.Binds;
import dagger.Module;
-import dagger.Provides;
@Module
-class PumpModule {
- @Provides Pump providePump(Thermosiphon pump) {
- return pump;
- }
+abstract class PumpModule {
+ @Binds
+ abstract Pump providePump(Thermosiphon pump);
}
diff --git a/gwt/BUILD b/gwt/BUILD
new file mode 100644
index 0000000..da73013
--- /dev/null
+++ b/gwt/BUILD
@@ -0,0 +1,50 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# GWT-specific files for Dagger
+
+package(default_visibility = ["//:src"])
+
+load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+
+java_library(
+ name = "gwt",
+ resource_strip_prefix = "gwt/",
+ resources = glob(["**/*.gwt.xml"]),
+ tags = ["maven_coordinates=com.google.dagger:dagger-gwt:" + POM_VERSION],
+ exports = [
+ ":manual_deps",
+ "//java/dagger:core",
+ ],
+)
+
+java_library(
+ name = "manual_deps",
+ tags = [
+ "maven_coordinates=com.google.dagger:dagger:%s:jar:sources" % POM_VERSION,
+ "maven_coordinates=javax.inject:javax.inject:1:jar:sources",
+ ],
+ visibility = ["//visibility:private"],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-gwt",
+ artifact_name = "Dagger GWT",
+ targets = [
+ ":gwt",
+ ":manual_deps",
+ ],
+)
diff --git a/gwt/dagger/Dagger.gwt.xml b/gwt/dagger/Dagger.gwt.xml
new file mode 100644
index 0000000..ad106fd
--- /dev/null
+++ b/gwt/dagger/Dagger.gwt.xml
@@ -0,0 +1,20 @@
+<!--
+ Copyright (C) 2015 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<module>
+ <inherits name="javax.inject.Inject" />
+
+ <source path=""/>
+</module>
diff --git a/gwt/javax/inject/Inject.gwt.xml b/gwt/javax/inject/Inject.gwt.xml
new file mode 100644
index 0000000..b634926
--- /dev/null
+++ b/gwt/javax/inject/Inject.gwt.xml
@@ -0,0 +1,18 @@
+<!--
+ Copyright (C) 2017 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<module>
+ <source path=""/>
+</module>
diff --git a/jarjar-rules.txt b/jarjar-rules.txt
new file mode 100644
index 0000000..57941b3
--- /dev/null
+++ b/jarjar-rules.txt
@@ -0,0 +1,3 @@
+# shade guava to avoid conflicts with guava embedded in Error Prone.
+rule com.google.common.** com.google.dagger.common.@1
+rule com.google.auto.** com.google.dagger.auto.@1
diff --git a/java/dagger/BUILD b/java/dagger/BUILD
new file mode 100644
index 0000000..3485431
--- /dev/null
+++ b/java/dagger/BUILD
@@ -0,0 +1,58 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A JSR-330 compliant dependency injection system for android and java
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "SOURCE_7_TARGET_7",
+)
+load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+
+java_library(
+ name = "core",
+ srcs = glob(["**/*.java"]),
+ javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX,
+ tags = ["maven_coordinates=com.google.dagger:dagger:" + POM_VERSION],
+ exports = ["@google_bazel_common//third_party/java/jsr330_inject"],
+ deps = [
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger",
+ artifact_name = "Dagger",
+ targets = [":core"],
+)
+
+filegroup(
+ name = "javadoc-srcs",
+ srcs = glob(["**/*"]),
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "core-javadoc",
+ srcs = [":javadoc-srcs"],
+ exclude_packages = ["dagger.internal"],
+ root_packages = ["dagger"],
+ deps = ["@google_bazel_common//third_party/java/jsr330_inject"],
+)
diff --git a/java/dagger/Binds.java b/java/dagger/Binds.java
new file mode 100644
index 0000000..99b65e2
--- /dev/null
+++ b/java/dagger/Binds.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates <em>abstract</em> methods of a {@link Module} that delegate bindings. For example, to
+ * bind {@link java.util.Random} to {@link java.security.SecureRandom} a module could declare the
+ * following: {@code @Binds abstract Random bindRandom(SecureRandom secureRandom);}
+ *
+ * <p>{@code @Binds} methods are a drop-in replacement for {@link Provides} methods that simply
+ * return an injected parameter. Prefer {@code @Binds} because the generated implementation is
+ * likely to be more efficient.
+ *
+ * <p>A {@code @Binds} method:
+ *
+ * <ul>
+ * <li>Must be {@code abstract}.
+ * <li>May be {@linkplain javax.inject.Scope scoped}.
+ * <li>May be {@linkplain javax.inject.Qualifier qualified}.
+ * <li>Must have a single parameter whose type is assignable to the return type. The return type
+ * declares the bound type (just as it would for a {@literal @}{@link dagger.Provides} method)
+ * and the parameter is the type to which it is bound.
+ * <p>For {@linkplain dagger.multibindings multibindings}, assignability is checked in similar
+ * ways:
+ * <dl>
+ * <dt>{@link dagger.multibindings.IntoSet}
+ * <dd>The parameter must be assignable to the only parameter of {@link java.util.Set#add}
+ * when viewed as a member of the return type — the parameter must be assignable to the
+ * return type.
+ * <dt>{@link dagger.multibindings.ElementsIntoSet}
+ * <dd>The parameter must be assignable to the only parameter of {@link
+ * java.util.Set#addAll} when viewed as a member of the return type — if the return type
+ * is {@code Set<E>}, the parameter must be assignable to {@code Collection<? extends
+ * E>}.
+ * <dt>{@link dagger.multibindings.IntoMap}
+ * <dd>The parameter must be assignable to the {@code value} parameter of {@link
+ * java.util.Map#put} when viewed as a member of a {@link java.util.Map} in which {@code
+ * V} is bound to the return type — the parameter must be assignable to the return type
+ * </dl>
+ * </ul>
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface Binds {}
diff --git a/java/dagger/BindsInstance.java b/java/dagger/BindsInstance.java
new file mode 100644
index 0000000..eab8796
--- /dev/null
+++ b/java/dagger/BindsInstance.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a method on a {@linkplain Component.Builder component builder} or a parameter on a
+ * {@linkplain Component.Factory component factory} as binding an instance to some key within the
+ * component.
+ *
+ * <p>For example:
+ *
+ * <pre>
+ * {@literal @Component.Builder}
+ * interface Builder {
+ * {@literal @BindsInstance} Builder foo(Foo foo);
+ * {@literal @BindsInstance} Builder bar({@literal @Blue} Bar bar);
+ * ...
+ * }
+ *
+ * // or
+ *
+ * {@literal @Component.Factory}
+ * interface Factory {
+ * MyComponent newMyComponent(
+ * {@literal @BindsInstance} Foo foo,
+ * {@literal @BindsInstance @Blue} Bar bar);
+ * }
+ * </pre>
+ *
+ * <p>will allow clients of the builder or factory to pass their own instances of {@code Foo} and
+ * {@code Bar}, and those instances can be injected within the component as {@code Foo} or
+ * {@code @Blue Bar}, respectively.
+ *
+ * <p>{@code @BindsInstance} arguments may not be {@code null} unless the parameter is annotated
+ * with {@code @Nullable}.
+ *
+ * <p>For builders, {@code @BindsInstance} methods must be called before building the component,
+ * unless their parameter is marked {@code @Nullable}, in which case the component will act as
+ * though it was called with a {@code null} argument. Primitives, of course, may not be marked
+ * {@code @Nullable}.
+ *
+ * <p>Binding an instance is equivalent to passing an instance to a module constructor and providing
+ * that instance, but is often more efficient. When possible, binding object instances should be
+ * preferred to using module instances.
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({METHOD, PARAMETER})
+@Beta
+public @interface BindsInstance {}
diff --git a/java/dagger/BindsOptionalOf.java b/java/dagger/BindsOptionalOf.java
new file mode 100644
index 0000000..9a356ff
--- /dev/null
+++ b/java/dagger/BindsOptionalOf.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+
+/**
+ * Annotates methods that declare bindings for {@code Optional} containers of values from bindings
+ * that may or may not be present in the component.
+ *
+ * <p>If a module contains a method declaration like this:
+ *
+ * <pre>
+ * {@literal @BindsOptionalOf} abstract Foo optionalFoo();</pre>
+ *
+ * then any binding in the component can depend on an {@code Optional} of {@code Foo}. If there is
+ * no binding for {@code Foo} in the component, the {@code Optional} will be absent. If there is a
+ * binding for {@code Foo} in the component, the {@code Optional} will be present, and its value
+ * will be the value given by the binding for {@code Foo}.
+ *
+ * <p>A {@code @BindsOptionalOf} method:
+ *
+ * <ul>
+ * <li>must be {@code abstract}
+ * <li>may have a {@linkplain Qualifier qualifier} annotation
+ * <li>must not return {@code void}
+ * <li>must not have parameters
+ * <li>must not throw exceptions
+ * <li>must not return an unqualified type with an {@link Inject @Inject}-annotated constructor,
+ * since such a type is always present
+ * </ul>
+ *
+ * <p>Other bindings may inject any of:
+ *
+ * <ul>
+ * <li>{@code Optional<Foo>} (unless there is a {@code @Nullable} binding for {@code Foo}; see
+ * below)
+ * <li>{@code Optional<Provider<Foo>>}
+ * <li>{@code Optional<Lazy<Foo>>}
+ * <li>{@code Optional<Provider<Lazy<Foo>>>}
+ * </ul>
+ *
+ * <p>If there is a binding for {@code Foo}, and that binding is {@code @Nullable}, then it is a
+ * compile-time error to inject {@code Optional<Foo>}, because {@code Optional} cannot contain
+ * {@code null}. You can always inject the other forms, because {@link Provider} and {@link Lazy}
+ * can always return {@code null} from their {@code get()} methods.
+ *
+ * <p>Explicit bindings for any of the above will conflict with a {@code @BindsOptionalOf} binding.
+ *
+ * <p>If the binding for {@code Foo} is a {@code @Produces} binding, then another {@code @Produces}
+ * binding can depend on any of:
+ *
+ * <ul>
+ * <li>{@code Optional<Foo>}
+ * <!-- TODO(dpb): Update this once producers support nullability checks -->
+ * <li>{@code Optional<Producer<Foo>>}
+ * <li>{@code Optional<Produced<Foo>>}
+ * </ul>
+ *
+ * <p>You can inject either {@code com.google.common.base.Optional} or {@code java.util.Optional}.
+ */
+@Documented
+@Beta
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface BindsOptionalOf {}
diff --git a/java/dagger/Component.java b/java/dagger/Component.java
new file mode 100644
index 0000000..709b0e6
--- /dev/null
+++ b/java/dagger/Component.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+import javax.inject.Scope;
+import javax.inject.Singleton;
+
+/**
+ * Annotates an interface or abstract class for which a fully-formed, dependency-injected
+ * implementation is to be generated from a set of {@linkplain #modules}. The generated class will
+ * have the name of the type annotated with {@code @Component} prepended with {@code Dagger}. For
+ * example, {@code @Component interface MyComponent {...}} will produce an implementation named
+ * {@code DaggerMyComponent}.
+ *
+ * <a name="component-methods"></a>
+ * <h2>Component methods</h2>
+ *
+ * <p>Every type annotated with {@code @Component} must contain at least one abstract component
+ * method. Component methods may have any name, but must have signatures that conform to either
+ * {@linkplain Provider provision} or {@linkplain MembersInjector members-injection} contracts.
+ *
+ * <a name="provision-methods"></a>
+ * <h3>Provision methods</h3>
+ *
+ * <p>Provision methods have no parameters and return an {@link Inject injected} or {@link Provides
+ * provided} type. Each method may have a {@link Qualifier} annotation as well. The following are
+ * all valid provision method declarations:
+ *
+ * <pre><code>
+ * SomeType getSomeType();
+ * {@literal Set<SomeType>} getSomeTypes();
+ * {@literal @PortNumber} int getPortNumber();
+ * </code></pre>
+ *
+ * <p>Provision methods, like typical {@link Inject injection} sites, may use {@link Provider} or
+ * {@link Lazy} to more explicitly control provision requests. A {@link Provider} allows the user of
+ * the component to request provision any number of times by calling {@link Provider#get}. A {@link
+ * Lazy} will only ever request a single provision, but will defer it until the first call to {@link
+ * Lazy#get}. The following provision methods all request provision of the same type, but each
+ * implies different semantics:
+ *
+ * <pre><code>
+ * SomeType getSomeType();
+ * {@literal Provider<SomeType>} getSomeTypeProvider();
+ * {@literal Lazy<SomeType>} getLazySomeType();
+ * </code></pre>
+ *
+ * <a name="members-injection-methods"></a>
+ * <h3>Members-injection methods</h3>
+ *
+ * <p>Members-injection methods have a single parameter and inject dependencies into each of the
+ * {@link Inject}-annotated fields and methods of the passed instance. A members-injection method
+ * may be void or return its single parameter as a convenience for chaining. The following are all
+ * valid members-injection method declarations:
+ *
+ * <pre><code>
+ * void injectSomeType(SomeType someType);
+ * SomeType injectAndReturnSomeType(SomeType someType);
+ * </code></pre>
+ *
+ * <p>A method with no parameters that returns a {@link MembersInjector} is equivalent to a members
+ * injection method. Calling {@link MembersInjector#injectMembers} on the returned object will
+ * perform the same work as a members injection method. For example:
+ *
+ * <pre><code>
+ * {@literal MembersInjector<SomeType>} getSomeTypeMembersInjector();
+ * </code></pre>
+ *
+ * <h4>A note about covariance</h4>
+ *
+ * <p>While a members-injection method for a type will accept instances of its subtypes, only {@link
+ * Inject}-annotated members of the parameter type and its supertypes will be injected; members of
+ * subtypes will not. For example, given the following types, only {@code a} and {@code b} will be
+ * injected into an instance of {@code Child} when it is passed to the members-injection method
+ * {@code injectSelf(Self instance)}:
+ *
+ * <pre><code>
+ * class Parent {
+ * {@literal @}Inject A a;
+ * }
+ *
+ * class Self extends Parent {
+ * {@literal @}Inject B b;
+ * }
+ *
+ * class Child extends Self {
+ * {@literal @}Inject C c;
+ * }
+ * </code></pre>
+ *
+ * <a name="instantiation"></a>
+ * <h2>Instantiation</h2>
+ *
+ * <p>Component implementations are primarily instantiated via a generated <a
+ * href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a> or <a
+ * href="https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)">factory</a>.
+ *
+ * <p>If a nested {@link Builder @Component.Builder} or {@link Factory @Component.Factory} type
+ * exists in the component, Dagger will generate an implementation of that type. If neither exists,
+ * Dagger will generate a builder type that has a method to set each of the {@linkplain #modules}
+ * and component {@linkplain #dependencies} named with the <a
+ * href="http://en.wikipedia.org/wiki/CamelCase">lower camel case</a> version of the module or
+ * dependency type.
+ *
+ * <p>In either case, the Dagger-generated component type will have a static method, named either
+ * {@code builder()} or {@code factory()}, that returns a builder or factory instance.
+ *
+ * <p>Example of using a builder:
+ *
+ * <pre>{@code
+ * public static void main(String[] args) {
+ * OtherComponent otherComponent = ...;
+ * MyComponent component = DaggerMyComponent.builder()
+ * // required because component dependencies must be set
+ * .otherComponent(otherComponent)
+ * // required because FlagsModule has constructor parameters
+ * .flagsModule(new FlagsModule(args))
+ * // may be elided because a no-args constructor is visible
+ * .myApplicationModule(new MyApplicationModule())
+ * .build();
+ * }
+ * }</pre>
+ *
+ * <p>Example of using a factory:
+ *
+ * <pre>{@code
+ * public static void main(String[] args) {
+ * OtherComponent otherComponent = ...;
+ * MyComponent component = DaggerMyComponent.factory()
+ * .create(otherComponent, new FlagsModule(args), new MyApplicationModule());
+ * // Note that all parameters to a factory method are required, even if one is for a module
+ * // that Dagger could instantiate. The only case where null is legal is for a
+ * // @BindsInstance @Nullable parameter.
+ * }
+ * }</pre>
+ *
+ * <p>In the case that a component has no component dependencies and only no-arg modules, the
+ * generated component will also have a factory method {@code create()}. {@code
+ * SomeComponent.create()} and {@code SomeComponent.builder().build()} are both valid and
+ * equivalent.
+ *
+ * <a name="scope"></a>
+ * <h2>Scope</h2>
+ *
+ * <p>Each Dagger component can be associated with a scope by annotating it with the {@linkplain
+ * Scope scope annotation}. The component implementation ensures that there is only one provision of
+ * each scoped binding per instance of the component. If the component declares a scope, it may only
+ * contain unscoped bindings or bindings of that scope anywhere in the graph. For example:
+ *
+ * <pre><code>
+ * {@literal @}Singleton {@literal @}Component
+ * interface MyApplicationComponent {
+ * // this component can only inject types using unscoped or {@literal @}Singleton bindings
+ * }
+ * </code></pre>
+ *
+ * <p>In order to get the proper behavior associated with a scope annotation, it is the caller's
+ * responsibility to instantiate new component instances when appropriate. A {@link Singleton}
+ * component, for instance, should only be instantiated once per application, while a {@code
+ * RequestScoped} component should be instantiated once per request. Because components are
+ * self-contained implementations, exiting a scope is as simple as dropping all references to the
+ * component instance.
+ *
+ * <a name="component-relationships"></a>
+ * <h2>Component relationships</h2>
+ *
+ * <p>While there is much utility in isolated components with purely unscoped bindings, many
+ * applications will call for multiple components with multiple scopes to interact. Dagger provides
+ * two mechanisms for relating components.
+ *
+ * <a name="subcomponents"></a>
+ * <h3>Subcomponents</h3>
+ *
+ * <p>The simplest way to relate two components is by declaring a {@link Subcomponent}. A
+ * subcomponent behaves exactly like a component, but has its implementation generated within a
+ * parent component or subcomponent. That relationship allows the subcomponent implementation to
+ * inherit the <em>entire</em> binding graph from its parent when it is declared. For that reason, a
+ * subcomponent isn't evaluated for completeness until it is associated with a parent.
+ *
+ * <p>Subcomponents are declared by listing the class in the {@link Module#subcomponents()}
+ * attribute of one of the parent component's modules. This binds the {@link Subcomponent.Builder}
+ * or {@link Subcomponent.Factory} for that subcomponent within the parent component.
+ *
+ * <p>Subcomponents may also be declared via a factory method on a parent component or subcomponent.
+ * The method may have any name, but must return the subcomponent. The factory method's parameters
+ * may be any number of the subcomponent's modules, but must at least include those without visible
+ * no-arg constructors. The following is an example of a factory method that creates a
+ * request-scoped subcomponent from a singleton-scoped parent:
+ *
+ * <pre><code>
+ * {@literal @}Singleton {@literal @}Component
+ * interface ApplicationComponent {
+ * // component methods...
+ *
+ * RequestComponent newRequestComponent(RequestModule requestModule);
+ * }
+ * </code></pre>
+ *
+ * <a name="component-dependencies"></a>
+ * <h3>Component dependencies</h3>
+ *
+ * <p>While subcomponents are the simplest way to compose subgraphs of bindings, subcomponents are
+ * tightly coupled with the parents; they may use any binding defined by their ancestor component
+ * and subcomponents. As an alternative, components can use bindings only from another <em>component
+ * interface</em> by declaring a {@linkplain #dependencies component dependency}. When a type is
+ * used as a component dependency, each <a href="#provision-methods">provision method</a> on the
+ * dependency is bound as a provider. Note that <em>only</em> the bindings exposed as provision
+ * methods are available through component dependencies.
+ *
+ * @since 2.0
+ */
+@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+@Target(TYPE)
+@Documented
+public @interface Component {
+ /**
+ * A list of classes annotated with {@link Module} whose bindings are used to generate the
+ * component implementation. Note that through the use of {@link Module#includes} the full set of
+ * modules used to implement the component may include more modules that just those listed here.
+ */
+ Class<?>[] modules() default {};
+
+ /**
+ * A list of types that are to be used as <a href="#component-dependencies">component
+ * dependencies</a>.
+ */
+ Class<?>[] dependencies() default {};
+
+ /**
+ * A builder for a component.
+ *
+ * <p>A builder is a type with setter methods for the {@linkplain Component#modules modules},
+ * {@linkplain Component#dependencies dependencies} and {@linkplain BindsInstance bound instances}
+ * required by the component and a single no-argument build method that creates a new component
+ * instance.
+ *
+ * <p>Components may have a single nested {@code static abstract class} or {@code interface}
+ * annotated with {@code @Component.Builder}. If they do, then Dagger will generate a builder
+ * class that implements that type. Note that a component with a {@code @Component.Builder} may
+ * not also have a {@code @Component.Factory}.
+ *
+ * <p>Builder types must follow some rules:
+ *
+ * <ul>
+ * <li>There <i>must</i> be exactly one abstract no-argument method that returns the component
+ * type or one of its supertypes, called the "build method".
+ * <li>There <i>may</i> be other other abstract methods, called "setter methods".
+ * <li>Setter methods <i>must</i> take a single argument and return {@code void}, the builder
+ * type or a supertype of the builder type.
+ * <li>There <i>must</i> be a setter method for each {@linkplain Component#dependencies
+ * component dependency}.
+ * <li>There <i>must</i> be a setter method for each non-{@code abstract} {@linkplain
+ * Component#modules module} that has non-{@code static} binding methods, unless Dagger can
+ * instantiate that module with a visible no-argument constructor.
+ * <li>There <i>may</i> be setter methods for modules that Dagger can instantiate or does not
+ * need to instantiate.
+ * <li>There <i>may</i> be setter methods annotated with {@code @BindsInstance}. These methods
+ * bind the instance passed to them within the component. See {@link
+ * BindsInstance @BindsInstance} for more information.
+ * <li>There <i>may</i> be non-{@code abstract} methods, but they are ignored as far as
+ * validation and builder generation are concerned.
+ * </ul>
+ *
+ * For example, this could be a valid {@code Component} with a {@code Builder}:
+ *
+ * <pre><code>
+ * {@literal @}Component(modules = {BackendModule.class, FrontendModule.class})
+ * interface MyComponent {
+ * MyWidget myWidget();
+ *
+ * {@literal @}Component.Builder
+ * interface Builder {
+ * Builder backendModule(BackendModule bm);
+ * Builder frontendModule(FrontendModule fm);
+ * {@literal @}BindsInstance
+ * Builder foo(Foo foo);
+ * MyComponent build();
+ * }
+ * }</code></pre>
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Builder {}
+
+ /**
+ * A factory for a component.
+ *
+ * <p>A factory is a type with a single method that returns a new component instance each time it
+ * is called. The parameters of that method allow the caller to provide the {@linkplain
+ * Component#modules modules}, {@linkplain Component#dependencies dependencies} and {@linkplain
+ * BindsInstance bound instances} required by the component.
+ *
+ * <p>Components may have a single nested {@code static abstract class} or {@code interface}
+ * annotated with {@code @Component.Factory}. If they do, then Dagger will generate a factory
+ * class that will implement that type. Note that a component with a {@code @Component.Factory}
+ * may not also have a {@code @Component.Builder}.
+ *
+ * <p>Factory types must follow some rules:
+ *
+ * <ul>
+ * <li>There <i>must</i> be exactly one abstract method, which must return the component type or
+ * one of its supertypes.
+ * <li>The method <i>must</i> have a parameter for each {@linkplain Component#dependencies
+ * component dependency}.
+ * <li>The method <i>must</i> have a parameter for each non-{@code abstract} {@linkplain
+ * Component#modules module} that has non-{@code static} binding methods, unless Dagger can
+ * instantiate that module with a visible no-argument constructor.
+ * <li>The method <i>may</i> have parameters for modules that Dagger can instantiate or does not
+ * need to instantiate.
+ * <li>The method <i>may</i> have parameters annotated with {@code @BindsInstance}. These
+ * parameters bind the instance passed for that parameter within the component. See {@link
+ * BindsInstance @BindsInstance} for more information.
+ * <li>There <i>may</i> be non-{@code abstract} methods, but they are ignored as far as
+ * validation and factory generation are concerned.
+ * </ul>
+ *
+ * For example, this could be a valid {@code Component} with a {@code Factory}:
+ *
+ * <pre><code>
+ * {@literal @}Component(modules = {BackendModule.class, FrontendModule.class})
+ * interface MyComponent {
+ * MyWidget myWidget();
+ *
+ * {@literal @}Component.Factory
+ * interface Factory {
+ * MyComponent newMyComponent(
+ * BackendModule bm, FrontendModule fm, {@literal @}BindsInstance Foo foo);
+ * }
+ * }</code></pre>
+ *
+ * <p>For a root component, if a {@code @Component.Factory} is defined, the generated component
+ * type will have a {@code static} method named {@code factory()} that returns an instance of that
+ * factory.
+ *
+ * @since 2.22
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Factory {}
+}
diff --git a/java/dagger/Lazy.java b/java/dagger/Lazy.java
new file mode 100644
index 0000000..d4408fa
--- /dev/null
+++ b/java/dagger/Lazy.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2012 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+/**
+ * A handle to a lazily-computed value. Each {@code Lazy} computes its value on
+ * the first call to {@link #get()} and remembers that same value for all
+ * subsequent calls to {@code get()}.
+ *
+ * <p>All implementations are expected to be thread-safe and compute their value at most once.
+ *
+ * <h2>Example</h2>
+ * The differences between <strong>direct injection</strong>, <strong>provider
+ * injection</strong> and <strong>lazy injection</strong> are best demonstrated
+ * with an example. Start with a module that computes a different integer for
+ * each use:<pre><code>
+ * {@literal @Module}
+ * final class CounterModule {
+ * int next = 100;
+ *
+ * {@literal @Provides} Integer provideInteger() {
+ * System.out.println("computing...");
+ * return next++;
+ * }
+ * }
+ * </code></pre>
+ *
+ * <h3>Direct Injection</h3>
+ * This class injects that integer and prints it 3 times:<pre><code>
+ * final class DirectCounter {
+ * {@literal @Inject} Integer value;
+ *
+ * void print() {
+ * System.out.println("printing...");
+ * System.out.println(value);
+ * System.out.println(value);
+ * System.out.println(value);
+ * }
+ * }
+ * </code></pre>
+ * Injecting a {@code DirectCounter} and invoking {@code print()} reveals that
+ * the value is computed <i>before</i> it is required:<pre><code>
+ * computing...
+ * printing...
+ * 100
+ * 100
+ * 100
+ * </code></pre>
+ *
+ * <h3>Provider Injection</h3>
+ * This class injects a {@linkplain javax.inject.Provider provider} for the
+ * integer. It calls {@code Provider.get()} 3 times and prints each result:
+ * <pre><code>
+ * final class ProviderCounter {
+ * {@literal @Inject Provider<Integer> provider;}
+ *
+ * void print() {
+ * System.out.println("printing...");
+ * System.out.println(provider.get());
+ * System.out.println(provider.get());
+ * System.out.println(provider.get());
+ * }
+ * }
+ * </code></pre>
+ * Injecting a {@code ProviderCounter} and invoking {@code print()} shows that
+ * a new value is computed each time {@code Provider.get()} is used:<pre><code>
+ * printing...
+ * computing...
+ * 100
+ * computing...
+ * 101
+ * computing...
+ * 102
+ * </code></pre>
+ *
+ * <h3>Lazy Injection</h3>
+ * This class injects a {@code Lazy} for the integer. Like the provider above,
+ * it calls {@code Lazy.get()} 3 times and prints each result:<pre><code>
+ * final class LazyCounter {
+ * {@literal @Inject Lazy<Integer> lazy;}
+ *
+ * void print() {
+ * System.out.println("printing...");
+ * System.out.println(lazy.get());
+ * System.out.println(lazy.get());
+ * System.out.println(lazy.get());
+ * }
+ * }
+ * </code></pre>
+ * Injecting a {@code LazyCounter} and invoking {@code print()} shows that a new
+ * value is computed immediately before it is needed. The same value is returned
+ * for all subsequent uses:<pre><code>
+ * printing...
+ * computing...
+ * 100
+ * 100
+ * 100
+ * </code></pre>
+ *
+ * <h3>Lazy != Singleton</h3>
+ * Note that each injected {@code Lazy} is independent, and remembers its value
+ * in isolation of other {@code Lazy} instances. In this example, two {@code
+ * LazyCounter} objects are created and {@code print()} is called on each:
+ * <pre><code>
+ * final class LazyCounters {
+ * {@literal @Inject} LazyCounter counter1;
+ * {@literal @Inject} LazyCounter counter2;
+ *
+ * void print() {
+ * counter1.print();
+ * counter2.print();
+ * }
+ * }
+ * </code></pre>
+ * The output demonstrates that each {@code Lazy} works independently:
+ * <pre><code>
+ * printing...
+ * computing...
+ * 100
+ * 100
+ * 100
+ * printing...
+ * computing...
+ * 101
+ * 101
+ * 101
+ * </code></pre>
+ * Use {@link javax.inject.Singleton @Singleton} to share one instance among all
+ * clients, and {@code Lazy} for lazy computation in a single client.
+ */
+public interface Lazy<T> {
+ /**
+ * Return the underlying value, computing the value if necessary. All calls to
+ * the same {@code Lazy} instance will return the same result.
+ */
+ T get();
+}
diff --git a/java/dagger/MapKey.java b/java/dagger/MapKey.java
new file mode 100644
index 0000000..46dbf93
--- /dev/null
+++ b/java/dagger/MapKey.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Map;
+
+/**
+ * Identifies annotation types that are used to associate keys with values returned by {@linkplain
+ * Provides provider methods} in order to compose a {@linkplain dagger.multibindings.IntoMap map}.
+ *
+ * <p>Every provider method annotated with {@code @Provides} and {@code @IntoMap} must also have an
+ * annotation that identifies the key for that map entry. That annotation's type must be annotated
+ * with {@code @MapKey}.
+ *
+ * <p>Typically, the key annotation has a single member, whose value is used as the map key.
+ *
+ * <p>For example, to add an entry to a {@code Map<SomeEnum, Integer>} with key {@code
+ * SomeEnum.FOO}, you could use an annotation called {@code @SomeEnumKey}:
+ *
+ * <pre><code>
+ * {@literal @}MapKey
+ * {@literal @}interface SomeEnumKey {
+ * SomeEnum value();
+ * }
+ *
+ * {@literal @}Module
+ * class SomeModule {
+ * {@literal @}Provides
+ * {@literal @}IntoMap
+ * {@literal @}SomeEnumKey(SomeEnum.FOO)
+ * Integer provideFooValue() {
+ * return 2;
+ * }
+ * }
+ *
+ * class SomeInjectedType {
+ * {@literal @}Inject
+ * SomeInjectedType({@literal Map<SomeEnum, Integer>} map) {
+ * assert map.get(SomeEnum.FOO) == 2;
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>If {@code unwrapValue} is true, the annotation's single member can be any type except an
+ * array.
+ *
+ * <p>See {@link dagger.multibindings} for standard unwrapped map key annotations for keys that are
+ * boxed primitives, strings, or classes.
+ *
+ * <h2>Annotations as keys</h2>
+ *
+ * <p>If {@link #unwrapValue} is false, then the annotation itself is used as the map key. For
+ * example, to add an entry to a {@code Map<MyMapKey, Integer>} map:
+ *
+ * <pre><code>
+ * {@literal @}MapKey(unwrapValue = false)
+ * {@literal @}interface MyMapKey {
+ * String someString();
+ * MyEnum someEnum();
+ * }
+ *
+ * {@literal @}Module
+ * class SomeModule {
+ * {@literal @}Provides
+ * {@literal @}IntoMap
+ * {@literal @}MyMapKey(someString = "foo", someEnum = BAR)
+ * Integer provideFooBarValue() {
+ * return 2;
+ * }
+ * }
+ *
+ * class SomeInjectedType {
+ * {@literal @}Inject
+ * SomeInjectedType({@literal Map<MyMapKey, Integer>} map) {
+ * assert map.get(new MyMapKeyImpl("foo", MyEnum.BAR)) == 2;
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>(Note that there must be a class {@code MyMapKeyImpl} that implements {@code MyMapKey} in
+ * order to call {@link Map#get(Object)} on the provided map.)
+ *
+ * @see <a href="https://dagger.dev/multibindings#map-multibindings">Map multibinding</a>
+ */
+@Documented
+@Target(ANNOTATION_TYPE)
+@Retention(RUNTIME)
+public @interface MapKey {
+ /**
+ * True to use the value of the single member of the annotated annotation as the map key; false
+ * to use the annotation instance as the map key.
+ *
+ * <p>If true, the single member must not be an array.
+ */
+ boolean unwrapValue() default true;
+}
diff --git a/java/dagger/MembersInjector.java b/java/dagger/MembersInjector.java
new file mode 100644
index 0000000..5e83889
--- /dev/null
+++ b/java/dagger/MembersInjector.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+/**
+ * Injects dependencies into the fields and methods on instances of type {@code T}. Ignores the
+ * presence or absence of an injectable constructor.
+ *
+ * @param <T> type to inject members of
+ *
+ * @since 2.0 (since 1.0 without the provision that {@link #injectMembers} cannot accept
+ * {@code null})
+ */
+public interface MembersInjector<T> {
+
+ /**
+ * Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
+ * absence of an injectable constructor.
+ *
+ * <p>Whenever a {@link Component} creates an instance, it performs this injection automatically
+ * (after first performing constructor injection), so if you're able to let the component create
+ * all your objects for you, you'll never need to use this method.
+ *
+ * @param instance into which members are to be injected
+ * @throws NullPointerException if {@code instance} is {@code null}
+ */
+ void injectMembers(T instance);
+}
diff --git a/java/dagger/Module.java b/java/dagger/Module.java
new file mode 100644
index 0000000..bd32a1f
--- /dev/null
+++ b/java/dagger/Module.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a class that contributes to the object graph.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Module {
+ /**
+ * Additional {@code @Module}-annotated classes from which this module is
+ * composed. The de-duplicated contributions of the modules in
+ * {@code includes}, and of their inclusions recursively, are all contributed
+ * to the object graph.
+ */
+ Class<?>[] includes() default {};
+
+ /**
+ * Any {@link Subcomponent}- or {@code @ProductionSubcomponent}-annotated classes which should be
+ * children of the component in which this module is installed. A subcomponent may be listed in
+ * more than one module in a component.
+ *
+ * @since 2.7
+ */
+ @Beta
+ Class<?>[] subcomponents() default {};
+}
diff --git a/java/dagger/Provides.java b/java/dagger/Provides.java
new file mode 100644
index 0000000..a60be3b
--- /dev/null
+++ b/java/dagger/Provides.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates methods of a {@linkplain Module module} to create a provider method binding. The
+ * method's return type is bound to its returned value. The {@linkplain Component component}
+ * implementation will pass dependencies to the method as parameters.
+ *
+ * <h3>Nullability</h3>
+ *
+ * <p>Dagger forbids injecting {@code null} by default. Component implemenations that invoke
+ * {@code @Provides} methods that return {@code null} will throw a {@link NullPointerException}
+ * immediately thereafter. {@code @Provides} methods may opt into allowing {@code null} by
+ * annotating the method with any {@code @Nullable} annotation like
+ * {@code javax.annotation.Nullable} or {@code android.support.annotation.Nullable}.
+ *
+ * <p>If a {@code @Provides} method is marked {@code @Nullable}, Dagger will <em>only</em>
+ * allow injection into sites that are marked {@code @Nullable} as well. A component that
+ * attempts to pair a {@code @Nullable} provision with a non-{@code @Nullable} injection site
+ * will fail to compile.
+ */
+@Documented @Target(METHOD) @Retention(RUNTIME)
+public @interface Provides {
+}
diff --git a/java/dagger/Reusable.java b/java/dagger/Reusable.java
new file mode 100644
index 0000000..2cb68aa
--- /dev/null
+++ b/java/dagger/Reusable.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * A scope that indicates that the object returned by a binding may be (but might not be) reused.
+ *
+ * <p>{@code @Reusable} is useful when you want to limit the number of provisions of a type, but
+ * there is no specific lifetime over which there must be only one instance.
+ *
+ * @see <a href="https://dagger.dev/users-guide#reusable-scope">Reusable Scope</a>
+ */
+@Documented
+@Beta
+@Retention(RUNTIME)
+@Scope
+public @interface Reusable {}
diff --git a/java/dagger/Subcomponent.java b/java/dagger/Subcomponent.java
new file mode 100644
index 0000000..2bf087f
--- /dev/null
+++ b/java/dagger/Subcomponent.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A subcomponent that inherits the bindings from a parent {@link Component} or
+ * {@link Subcomponent}. The details of how to associate a subcomponent with a parent are described
+ * in the documentation for {@link Component}.
+ *
+ * @since 2.0
+ */
+@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+@Target(TYPE)
+@Documented
+public @interface Subcomponent {
+ /**
+ * A list of classes annotated with {@link Module} whose bindings are used to generate the
+ * subcomponent implementation. Note that through the use of {@link Module#includes} the full set
+ * of modules used to implement the subcomponent may include more modules that just those listed
+ * here.
+ */
+ Class<?>[] modules() default {};
+
+ /**
+ * A builder for a subcomponent.
+ *
+ * <p>This follows all the rules of {@link Component.Builder}, except it must appear in classes
+ * annotated with {@link Subcomponent} instead of {@code Component}.
+ *
+ * <p>If a subcomponent defines a builder, its parent component(s) will have a binding for that
+ * builder type, allowing an instance or {@code Provider} of that builder to be injected or
+ * returned from a method on that component like any other binding.
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Builder {}
+
+ /**
+ * A factory for a subcomponent.
+ *
+ * <p>This follows all the rules of {@link Component.Factory}, except it must appear in classes
+ * annotated with {@link Subcomponent} instead of {@code Component}.
+ *
+ * <p>If a subcomponent defines a factory, its parent component(s) will have a binding for that
+ * factory type, allowing an instance of that factory to be injected or returned from a method on
+ * that component like any other binding.
+ *
+ * @since 2.22
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Factory {}
+}
diff --git a/java/dagger/android/AndroidInjection.java b/java/dagger/android/AndroidInjection.java
new file mode 100644
index 0000000..2f4fb4c
--- /dev/null
+++ b/java/dagger/android/AndroidInjection.java
@@ -0,0 +1,233 @@
+ /*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import static android.util.Log.DEBUG;
+import static dagger.internal.Preconditions.checkNotNull;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Fragment;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.util.Log;
+import dagger.internal.Beta;
+
+/** Injects core Android types. */
+@Beta
+public final class AndroidInjection {
+ private static final String TAG = "dagger.android";
+
+ /**
+ * Injects {@code activity} if an associated {@link AndroidInjector} implementation can be found,
+ * otherwise throws an {@link IllegalArgumentException}.
+ *
+ * @throws RuntimeException if the {@link Application} doesn't implement {@link
+ * HasAndroidInjector} or {@link HasActivityInjector}.
+ */
+ public static void inject(Activity activity) {
+ checkNotNull(activity, "activity");
+ Application application = activity.getApplication();
+ AndroidInjector<? super Activity> injector;
+ if (application instanceof HasAndroidInjector) {
+ injector = ((HasAndroidInjector) application).androidInjector();
+ checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+ } else if (application instanceof HasActivityInjector) {
+ injector = ((HasActivityInjector) application).activityInjector();
+ checkNotNull(injector, "%s.activityInjector() returned null", application.getClass());
+ } else {
+ throw new RuntimeException(
+ String.format(
+ "%s does not implement %s or %s",
+ application.getClass().getCanonicalName(),
+ HasAndroidInjector.class.getCanonicalName(),
+ HasActivityInjector.class.getCanonicalName()));
+ }
+
+ injector.inject(activity);
+ }
+
+ /**
+ * Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found,
+ * otherwise throws an {@link IllegalArgumentException}.
+ *
+ * <p>Uses the following algorithm to find the appropriate {@link AndroidInjector} to use to
+ * inject {@code fragment}:
+ *
+ * <ol>
+ * <li>Walks the parent-fragment hierarchy to find a fragment that implements {@link
+ * HasAndroidInjector} or {@link HasFragmentInjector}, and if none do
+ * <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements
+ * {@link HasAndroidInjector} or {@link HasFragmentInjector}, and if not
+ * <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}
+ * {@link HasFragmentInjector}.
+ * </ol>
+ *
+ * If none of them implement {@link HasAndroidInjector} or {@link HasFragmentInjector}, a {@link
+ * IllegalArgumentException} is thrown.
+ *
+ * @throws IllegalArgumentException if no parent fragment, activity, or application implements
+ * {@link HasAndroidInjector} or {@link HasFragmentInjector}.
+ */
+ public static void inject(Fragment fragment) {
+ checkNotNull(fragment, "fragment");
+
+ Object hasInjector = findHasFragmentInjector(fragment);
+ AndroidInjector<? super Fragment> injector;
+ if (hasInjector instanceof HasAndroidInjector) {
+ injector = ((HasAndroidInjector) hasInjector).androidInjector();
+ checkNotNull(injector, "%s.androidInjector() returned null", hasInjector.getClass());
+ } else if (hasInjector instanceof HasFragmentInjector) {
+ injector = ((HasFragmentInjector) hasInjector).fragmentInjector();
+ checkNotNull(injector, "%s.fragmentInjector() returned null", hasInjector.getClass());
+ } else {
+ throw new RuntimeException(
+ String.format(
+ "%s does not implement %s or %s",
+ hasInjector.getClass().getCanonicalName(),
+ HasAndroidInjector.class.getCanonicalName(),
+ HasFragmentInjector.class.getCanonicalName()));
+ }
+
+ if (Log.isLoggable(TAG, DEBUG)) {
+ Log.d(
+ TAG,
+ String.format(
+ "An injector for %s was found in %s",
+ fragment.getClass().getCanonicalName(),
+ hasInjector.getClass().getCanonicalName()));
+ }
+
+ injector.inject(fragment);
+ }
+
+ private static Object findHasFragmentInjector(Fragment fragment) {
+ Fragment parentFragment = fragment;
+ while ((parentFragment = parentFragment.getParentFragment()) != null) {
+ if (parentFragment instanceof HasAndroidInjector
+ || parentFragment instanceof HasFragmentInjector) {
+ return parentFragment;
+ }
+ }
+ Activity activity = fragment.getActivity();
+ if (activity instanceof HasAndroidInjector || activity instanceof HasFragmentInjector) {
+ return activity;
+ }
+ Application application = activity.getApplication();
+ if (application instanceof HasAndroidInjector || application instanceof HasFragmentInjector) {
+ return application;
+ }
+ throw new IllegalArgumentException(
+ String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
+ }
+
+ /**
+ * Injects {@code service} if an associated {@link AndroidInjector} implementation can be found,
+ * otherwise throws an {@link IllegalArgumentException}.
+ *
+ * @throws RuntimeException if the {@link Application} doesn't implement {@link
+ * HasAndroidInjector} or {@link HasServiceInjector}.
+ */
+ public static void inject(Service service) {
+ checkNotNull(service, "service");
+ Application application = service.getApplication();
+ AndroidInjector<? super Service> injector;
+ if (application instanceof HasAndroidInjector) {
+ injector = ((HasAndroidInjector) application).androidInjector();
+ checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+ } else if (application instanceof HasServiceInjector) {
+ injector = ((HasServiceInjector) application).serviceInjector();
+ checkNotNull(injector, "%s.serviceInjector() returned null", application.getClass());
+ } else {
+ throw new RuntimeException(
+ String.format(
+ "%s does not implement %s or %s",
+ application.getClass().getCanonicalName(),
+ HasAndroidInjector.class.getCanonicalName(),
+ HasServiceInjector.class.getCanonicalName()));
+ }
+
+ injector.inject(service);
+ }
+
+ /**
+ * Injects {@code broadcastReceiver} if an associated {@link AndroidInjector} implementation can
+ * be found, otherwise throws an {@link IllegalArgumentException}.
+ *
+ * @throws RuntimeException if the {@link Application} from {@link
+ * Context#getApplicationContext()} doesn't implement {@link HasAndroidInjector} or {@link
+ * HasBroadcastReceiverInjector}.
+ */
+ public static void inject(BroadcastReceiver broadcastReceiver, Context context) {
+ checkNotNull(broadcastReceiver, "broadcastReceiver");
+ checkNotNull(context, "context");
+
+ Application application = (Application) context.getApplicationContext();
+ AndroidInjector<? super BroadcastReceiver> injector;
+ if (application instanceof HasAndroidInjector) {
+ injector = ((HasAndroidInjector) application).androidInjector();
+ checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+ } else if (application instanceof HasBroadcastReceiverInjector) {
+ injector = ((HasBroadcastReceiverInjector) application).broadcastReceiverInjector();
+ checkNotNull(
+ injector, "%s.broadcastReceiverInjector() returned null", application.getClass());
+ } else {
+ throw new RuntimeException(
+ String.format(
+ "%s does not implement %s or %s",
+ application.getClass().getCanonicalName(),
+ HasAndroidInjector.class.getCanonicalName(),
+ HasBroadcastReceiverInjector.class.getCanonicalName()));
+ }
+
+ injector.inject(broadcastReceiver);
+ }
+
+ /**
+ * Injects {@code contentProvider} if an associated {@link AndroidInjector} implementation can be
+ * found, otherwise throws an {@link IllegalArgumentException}.
+ *
+ * @throws RuntimeException if the {@link Application} doesn't implement {@link
+ * HasAndroidInjector} or {@link HasContentProviderInjector}.
+ */
+ public static void inject(ContentProvider contentProvider) {
+ checkNotNull(contentProvider, "contentProvider");
+ Application application = (Application) contentProvider.getContext().getApplicationContext();
+
+ AndroidInjector<? super ContentProvider> injector;
+ if (application instanceof HasAndroidInjector) {
+ injector = ((HasAndroidInjector) application).androidInjector();
+ checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+ } else if (application instanceof HasContentProviderInjector) {
+ injector = ((HasContentProviderInjector) application).contentProviderInjector();
+ checkNotNull(injector, "%s.contentProviderInjector() returned null", application.getClass());
+ } else {
+ throw new RuntimeException(
+ String.format(
+ "%s does not implement %s or %s",
+ application.getClass().getCanonicalName(),
+ HasAndroidInjector.class.getCanonicalName(),
+ HasBroadcastReceiverInjector.class.getCanonicalName()));
+ }
+
+ injector.inject(contentProvider);
+ }
+
+ private AndroidInjection() {}
+}
diff --git a/java/dagger/android/AndroidInjectionKey.java b/java/dagger/android/AndroidInjectionKey.java
new file mode 100644
index 0000000..d4a5d72
--- /dev/null
+++ b/java/dagger/android/AndroidInjectionKey.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import dagger.MapKey;
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Target;
+
+/**
+ * {@link MapKey} annotation to key {@link AndroidInjector.Factory} bindings. The {@linkplain
+ * #value() value} of the annotation is the canonical name of the class that will be passed to
+ * {@link AndroidInjector#inject(Object)}.
+ *
+ * <p>All key strings will be obfuscated by ProGuard/R8/AppReduce if the named class is obfuscated.
+ *
+ * <p>
+ * You should only use this annotation if you are using a version of ProGuard/R8/AppReduce that
+ * supports the {@code -identifiernamestring} flag.
+ */
+@Beta
+@MapKey
+@Target(METHOD)
+@Documented
+public @interface AndroidInjectionKey {
+ /** The fully qualified class name of the type to be injected. */
+ String value();
+}
diff --git a/java/dagger/android/AndroidInjectionModule.java b/java/dagger/android/AndroidInjectionModule.java
new file mode 100644
index 0000000..4e496f8
--- /dev/null
+++ b/java/dagger/android/AndroidInjectionModule.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import dagger.Module;
+import dagger.internal.Beta;
+import dagger.multibindings.Multibinds;
+import java.util.Map;
+
+/**
+ * Contains bindings to ensure the usability of {@code dagger.android} framework classes. This
+ * module should be installed in the component that is used to inject the {@link
+ * android.app.Application} class.
+ */
+@Beta
+@Module
+public abstract class AndroidInjectionModule {
+ @Multibinds
+ abstract Map<Class<?>, AndroidInjector.Factory<?>> classKeyedInjectorFactories();
+
+ @Multibinds
+ abstract Map<String, AndroidInjector.Factory<?>> stringKeyedInjectorFactories();
+
+ private AndroidInjectionModule() {}
+}
diff --git a/java/dagger/android/AndroidInjector.java b/java/dagger/android/AndroidInjector.java
new file mode 100644
index 0000000..d2b9dec
--- /dev/null
+++ b/java/dagger/android/AndroidInjector.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import dagger.BindsInstance;
+import dagger.internal.Beta;
+
+/**
+ * Performs members-injection for a concrete subtype of a <a
+ * href="https://developer.android.com/guide/components/">core Android type</a> (e.g., {@link
+ * android.app.Activity} or {@link android.app.Fragment}).
+ *
+ * <p>Commonly implemented by {@link dagger.Subcomponent}-annotated types whose {@link
+ * dagger.Subcomponent.Factory} extends {@link Factory}.
+ *
+ * @param <T> a concrete subtype of a core Android type
+ * @see AndroidInjection
+ * @see DispatchingAndroidInjector
+ * @see ContributesAndroidInjector
+ */
+@Beta
+public interface AndroidInjector<T> {
+
+ /** Injects the members of {@code instance}. */
+ void inject(T instance);
+
+ /**
+ * Creates {@link AndroidInjector}s for a concrete subtype of a core Android type.
+ *
+ * @param <T> the concrete type to be injected
+ */
+ interface Factory<T> {
+ /**
+ * Creates an {@link AndroidInjector} for {@code instance}. This should be the same instance
+ * that will be passed to {@link #inject(Object)}.
+ */
+ AndroidInjector<T> create(@BindsInstance T instance);
+ }
+
+ /**
+ * An adapter that lets the common {@link dagger.Subcomponent.Builder} pattern implement {@link
+ * Factory}.
+ *
+ * @param <T> the concrete type to be injected
+ * @deprecated Prefer {@link Factory} now that components can have {@link dagger.Component.Factory
+ * factories} instead of builders
+ */
+ @Deprecated
+ abstract class Builder<T> implements AndroidInjector.Factory<T> {
+ @Override
+ public final AndroidInjector<T> create(T instance) {
+ seedInstance(instance);
+ return build();
+ }
+
+ /**
+ * Provides {@code instance} to be used in the binding graph of the built {@link
+ * AndroidInjector}. By default, this is used as a {@link BindsInstance} method, but it may be
+ * overridden to provide any modules which need a reference to the activity.
+ *
+ * <p>This should be the same instance that will be passed to {@link #inject(Object)}.
+ */
+ @BindsInstance
+ public abstract void seedInstance(T instance);
+
+ /** Returns a newly-constructed {@link AndroidInjector}. */
+ public abstract AndroidInjector<T> build();
+ }
+}
diff --git a/java/dagger/android/AndroidManifest.xml b/java/dagger/android/AndroidManifest.xml
new file mode 100644
index 0000000..f071b60
--- /dev/null
+++ b/java/dagger/android/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.android">
+ <uses-sdk android:minSdkVersion="14" />
+</manifest>
diff --git a/java/dagger/android/BUILD b/java/dagger/android/BUILD
new file mode 100644
index 0000000..a88d16e
--- /dev/null
+++ b/java/dagger/android/BUILD
@@ -0,0 +1,91 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Public Dagger API for Android
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+ "SOURCE_7_TARGET_7",
+)
+load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+
+# Work around b/70476182 which prevents Kythe from connecting :producers to the .java files it
+# contains.
+SRCS = glob([
+ "*.java",
+ "internal/*.java",
+])
+
+filegroup(
+ name = "android-srcs",
+ srcs = SRCS,
+)
+
+android_library(
+ name = "android",
+ srcs = SRCS,
+ javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ manifest = "AndroidManifest.xml",
+ proguard_specs = ["proguard.cfg"],
+ tags = ["maven_coordinates=com.google.dagger:dagger-android:" + POM_VERSION],
+ deps = [
+ ":manual-maven-deps",
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ ],
+)
+
+# Our pom.xml generator does not have a way to add manual maven deps. This target exports the
+# targets that don't have the necessary maven_coordinates tags.
+android_library(
+ name = "manual-maven-deps",
+ tags = ["maven_coordinates=com.android.support:support-annotations:25.0.0"],
+ visibility = ["//visibility:private"],
+ exports = [
+ "@androidsdk//com.android.support:support-annotations-25.0.0",
+ ],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-android",
+ artifact_name = "Dagger Android",
+ packaging = "aar",
+ targets = [":android"],
+)
+
+# b/37741866 and https://github.com/google/dagger/issues/715
+pom_file(
+ name = "jarimpl-pom",
+ artifact_id = "dagger-android-jarimpl",
+ artifact_name = "Dagger Android",
+ targets = [":android"],
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "android-javadoc",
+ srcs = [":android-srcs"],
+ android_api_level = 26,
+ exclude_packages = ["dagger.android.internal"],
+ root_packages = ["dagger.android"],
+ deps = [":android"],
+)
diff --git a/java/dagger/android/ContributesAndroidInjector.java b/java/dagger/android/ContributesAndroidInjector.java
new file mode 100644
index 0000000..5aa9312
--- /dev/null
+++ b/java/dagger/android/ContributesAndroidInjector.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Generates an {@link AndroidInjector} for the return type of this method. The injector is
+ * implemented with a {@link dagger.Subcomponent} and will be a child of the {@link dagger.Module}'s
+ * component.
+ *
+ * <p>This annotation must be applied to an abstract method in a {@link dagger.Module} that returns
+ * a concrete Android framework type (e.g. {@code FooActivity}, {@code BarFragment}, {@code
+ * MyService}, etc). The method should have no parameters.
+ *
+ * <p>For more information, see <a href="https://dagger.dev/android">the docs</a>
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(METHOD)
+public @interface ContributesAndroidInjector {
+ /** Modules to be installed in the generated {@link dagger.Subcomponent}. */
+ Class<?>[] modules() default {};
+}
diff --git a/java/dagger/android/DaggerActivity.java b/java/dagger/android/DaggerActivity.java
new file mode 100644
index 0000000..43708f3
--- /dev/null
+++ b/java/dagger/android/DaggerActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * An {@link Activity} that injects its members in {@link #onCreate(Bundle)} and can be used to
+ * inject {@link Fragment}s attached to it.
+ */
+@Beta
+public abstract class DaggerActivity extends Activity implements HasAndroidInjector {
+
+ @Inject DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ AndroidInjection.inject(this);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/DaggerApplication.java b/java/dagger/android/DaggerApplication.java
new file mode 100644
index 0000000..d09050b
--- /dev/null
+++ b/java/dagger/android/DaggerApplication.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.Application;
+import android.content.ContentProvider;
+import com.google.errorprone.annotations.ForOverride;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * An {@link Application} that injects its members and can be used to inject objects that the
+ * Android framework instantiates, such as Activitys, Fragments, or Services. Injection is performed
+ * in {@link #onCreate()} or the first call to {@link AndroidInjection#inject(ContentProvider)},
+ * whichever happens first.
+ */
+@Beta
+public abstract class DaggerApplication extends Application implements HasAndroidInjector {
+ @Inject volatile DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ injectIfNecessary();
+ }
+
+ /**
+ * Implementations should return an {@link AndroidInjector} for the concrete {@link
+ * DaggerApplication}. Typically, that injector is a {@link dagger.Component}.
+ */
+ @ForOverride
+ protected abstract AndroidInjector<? extends DaggerApplication> applicationInjector();
+
+ /**
+ * Lazily injects the {@link DaggerApplication}'s members. Injection cannot be performed in {@link
+ * Application#onCreate()} since {@link android.content.ContentProvider}s' {@link
+ * android.content.ContentProvider#onCreate() onCreate()} method will be called first and might
+ * need injected members on the application. Injection is not performed in the constructor, as
+ * that may result in members-injection methods being called before the constructor has completed,
+ * allowing for a partially-constructed instance to escape.
+ */
+ private void injectIfNecessary() {
+ if (androidInjector == null) {
+ synchronized (this) {
+ if (androidInjector == null) {
+ @SuppressWarnings("unchecked")
+ AndroidInjector<DaggerApplication> applicationInjector =
+ (AndroidInjector<DaggerApplication>) applicationInjector();
+ applicationInjector.inject(this);
+ if (androidInjector == null) {
+ throw new IllegalStateException(
+ "The AndroidInjector returned from applicationInjector() did not inject the "
+ + "DaggerApplication");
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ // injectIfNecessary should already be called unless we are about to inject a ContentProvider,
+ // which can happen before Application.onCreate()
+ injectIfNecessary();
+
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/DaggerBroadcastReceiver.java b/java/dagger/android/DaggerBroadcastReceiver.java
new file mode 100644
index 0000000..d39aa86
--- /dev/null
+++ b/java/dagger/android/DaggerBroadcastReceiver.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.CallSuper;
+import dagger.internal.Beta;
+
+/**
+ * A {@link BroadcastReceiver} that injects its members in every call to {@link #onReceive(Context,
+ * Intent)}.
+ *
+ * <p>This class should only be used for {@link BroadcastReceiver}s that are declared in an {@code
+ * AndroidManifest.xml}. If, instead, the {@link BroadcastReceiver} is created in code, prefer
+ * constructor injection.
+ *
+ * <p>Note: this class is <em>not thread safe</em> and should not be used with multiple {@link
+ * android.os.Handler}s in calls to {@link Context#registerReceiver(BroadcastReceiver,
+ * android.content.IntentFilter, String, android.os.Handler)}. Injection is performed on each
+ * invocation to {@link #onReceive(Context, Intent)} which could result in inconsistent views of
+ * injected dependencies across threads.
+ *
+ * <p>Subclasses should override {@link #onReceive(Context, Intent)} and call {@code
+ * super.onReceive(context, intent)} immediately to ensure injection is performed immediately.
+ */
+@Beta
+public abstract class DaggerBroadcastReceiver extends BroadcastReceiver {
+ @CallSuper
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ AndroidInjection.inject(this, context);
+ }
+}
diff --git a/java/dagger/android/DaggerContentProvider.java b/java/dagger/android/DaggerContentProvider.java
new file mode 100644
index 0000000..4aad485
--- /dev/null
+++ b/java/dagger/android/DaggerContentProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.content.ContentProvider;
+import android.support.annotation.CallSuper;
+import dagger.internal.Beta;
+
+/** A {@link ContentProvider} that injects its members in {@link #onCreate()}. */
+@Beta
+public abstract class DaggerContentProvider extends ContentProvider {
+ @CallSuper
+ @Override
+ public boolean onCreate() {
+ AndroidInjection.inject(this);
+ return true;
+ }
+}
diff --git a/java/dagger/android/DaggerDialogFragment.java b/java/dagger/android/DaggerDialogFragment.java
new file mode 100644
index 0000000..3cbc0f1
--- /dev/null
+++ b/java/dagger/android/DaggerDialogFragment.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.Context;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * A {@link DialogFragment} that injects its members in {@link #onAttach(Context)} and can be used
+ * to inject child {@link Fragment}s attached to it. Note that when this fragment gets reattached,
+ * its members will be injected again.
+ *
+ * @deprecated Framework fragments are deprecated in Android P; prefer {@code
+ * dagger.android.support.DaggerDialogFragment} to use a support-library-friendly {@code
+ * dagger.android} dialog fragment implementation.
+ */
+@Deprecated
+@Beta
+public abstract class DaggerDialogFragment extends DialogFragment implements HasAndroidInjector {
+
+ @Inject DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ public void onAttach(Context context) {
+ AndroidInjection.inject(this);
+ super.onAttach(context);
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/DaggerFragment.java b/java/dagger/android/DaggerFragment.java
new file mode 100644
index 0000000..187820b
--- /dev/null
+++ b/java/dagger/android/DaggerFragment.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.Fragment;
+import android.content.Context;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * A {@link Fragment} that injects its members in {@link #onAttach(Context)} and can be used to
+ * inject child {@link Fragment}s attached to it. Note that when this fragment gets reattached, its
+ * members will be injected again.
+ *
+ * @deprecated Framework fragments are deprecated in Android P; prefer {@code
+ * dagger.android.support.DaggerFragment} to use a support-library-friendly {@code
+ * dagger.android} fragment implementation.
+ */
+@Beta
+@Deprecated
+public abstract class DaggerFragment extends Fragment implements HasAndroidInjector {
+
+ @Inject DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ public void onAttach(Context context) {
+ AndroidInjection.inject(this);
+ super.onAttach(context);
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/DaggerIntentService.java b/java/dagger/android/DaggerIntentService.java
new file mode 100644
index 0000000..7d9dabb
--- /dev/null
+++ b/java/dagger/android/DaggerIntentService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.IntentService;
+import dagger.internal.Beta;
+
+/** An {@link IntentService} that injects its members in {@link #onCreate()}. */
+@Beta
+public abstract class DaggerIntentService extends IntentService {
+ public DaggerIntentService(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onCreate() {
+ AndroidInjection.inject(this);
+ super.onCreate();
+ }
+}
diff --git a/java/dagger/android/DaggerService.java b/java/dagger/android/DaggerService.java
new file mode 100644
index 0000000..cfc6d6b
--- /dev/null
+++ b/java/dagger/android/DaggerService.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.Service;
+import dagger.internal.Beta;
+
+/** A {@link Service} that injects its members in {@link #onCreate()}. */
+@Beta
+public abstract class DaggerService extends Service {
+ @Override
+ public void onCreate() {
+ AndroidInjection.inject(this);
+ super.onCreate();
+ }
+}
diff --git a/java/dagger/android/DispatchingAndroidInjector.java b/java/dagger/android/DispatchingAndroidInjector.java
new file mode 100644
index 0000000..9b65180
--- /dev/null
+++ b/java/dagger/android/DispatchingAndroidInjector.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+import static dagger.internal.Preconditions.checkNotNull;
+
+import android.app.Activity;
+import android.app.Fragment;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.internal.Beta;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * Performs members-injection on instances of core Android types (e.g. {@link Activity}, {@link
+ * Fragment}) that are constructed by the Android framework and not by Dagger. This class relies on
+ * an injected mapping from each concrete class to an {@link AndroidInjector.Factory} for an {@link
+ * AndroidInjector} of that class. Each concrete class must have its own entry in the map, even if
+ * it extends another class which is already present in the map. Calls {@link Object#getClass()} on
+ * the instance in order to find the appropriate {@link AndroidInjector.Factory}.
+ *
+ * @param <T> the core Android type to be injected
+ */
+@Beta
+public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
+ private static final String NO_SUPERTYPES_BOUND_FORMAT =
+ "No injector factory bound for Class<%s>";
+ private static final String SUPERTYPES_BOUND_FORMAT =
+ "No injector factory bound for Class<%1$s>. Injector factories were bound for supertypes "
+ + "of %1$s: %2$s. Did you mean to bind an injector factory for the subtype?";
+
+ private final Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactories;
+
+ @Inject
+ DispatchingAndroidInjector(
+ Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys,
+ Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys) {
+ this.injectorFactories = merge(injectorFactoriesWithClassKeys, injectorFactoriesWithStringKeys);
+ }
+
+ /**
+ * Merges the two maps into one by transforming the values of the {@code classKeyedMap} with
+ * {@link Class#getName()}.
+ *
+ * <p>An SPI plugin verifies the logical uniqueness of the keysets of these two maps so we're
+ * assured there's no overlap.
+ *
+ * <p>Ideally we could achieve this with a generic {@code @Provides} method, but we'd need to have
+ * <i>N</i> modules that each extend one base module.
+ */
+ private static <C, V> Map<String, Provider<AndroidInjector.Factory<?>>> merge(
+ Map<Class<? extends C>, V> classKeyedMap, Map<String, V> stringKeyedMap) {
+ if (classKeyedMap.isEmpty()) {
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ Map<String, Provider<AndroidInjector.Factory<?>>> safeCast = (Map) stringKeyedMap;
+ return safeCast;
+ }
+
+ Map<String, V> merged =
+ newLinkedHashMapWithExpectedSize(classKeyedMap.size() + stringKeyedMap.size());
+ merged.putAll(stringKeyedMap);
+ for (Entry<Class<? extends C>, V> entry : classKeyedMap.entrySet()) {
+ merged.put(entry.getKey().getName(), entry.getValue());
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ Map<String, Provider<AndroidInjector.Factory<?>>> safeCast = (Map) merged;
+ return Collections.unmodifiableMap(safeCast);
+ }
+
+ /**
+ * Attempts to perform members-injection on {@code instance}, returning {@code true} if
+ * successful, {@code false} otherwise.
+ *
+ * @throws InvalidInjectorBindingException if the injector factory bound for a class does not
+ * inject instances of that class
+ */
+ @CanIgnoreReturnValue
+ public boolean maybeInject(T instance) {
+ Provider<AndroidInjector.Factory<?>> factoryProvider =
+ injectorFactories.get(instance.getClass().getName());
+ if (factoryProvider == null) {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
+ try {
+ AndroidInjector<T> injector =
+ checkNotNull(
+ factory.create(instance), "%s.create(I) should not return null.", factory.getClass());
+
+ injector.inject(instance);
+ return true;
+ } catch (ClassCastException e) {
+ throw new InvalidInjectorBindingException(
+ String.format(
+ "%s does not implement AndroidInjector.Factory<%s>",
+ factory.getClass().getCanonicalName(), instance.getClass().getCanonicalName()),
+ e);
+ }
+ }
+
+ /**
+ * Performs members-injection on {@code instance}.
+ *
+ * @throws InvalidInjectorBindingException if the injector factory bound for a class does not
+ * inject instances of that class
+ * @throws IllegalArgumentException if no {@link AndroidInjector.Factory} is bound for {@code
+ * instance}
+ */
+ @Override
+ public void inject(T instance) {
+ boolean wasInjected = maybeInject(instance);
+ if (!wasInjected) {
+ throw new IllegalArgumentException(errorMessageSuggestions(instance));
+ }
+ }
+
+ /**
+ * Exception thrown if an incorrect binding is made for a {@link AndroidInjector.Factory}. If you
+ * see this exception, make sure the value in your {@code @ActivityKey(YourActivity.class)} or
+ * {@code @FragmentKey(YourFragment.class)} matches the type argument of the injector factory.
+ */
+ @Beta
+ public static final class InvalidInjectorBindingException extends RuntimeException {
+ InvalidInjectorBindingException(String message, ClassCastException cause) {
+ super(message, cause);
+ }
+ }
+
+ /** Returns an error message with the class names that are supertypes of {@code instance}. */
+ private String errorMessageSuggestions(T instance) {
+ List<String> suggestions = new ArrayList<>();
+ for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
+ if (injectorFactories.containsKey(clazz.getCanonicalName())) {
+ suggestions.add(clazz.getCanonicalName());
+ }
+ }
+
+ return suggestions.isEmpty()
+ ? String.format(NO_SUPERTYPES_BOUND_FORMAT, instance.getClass().getCanonicalName())
+ : String.format(
+ SUPERTYPES_BOUND_FORMAT, instance.getClass().getCanonicalName(), suggestions);
+ }
+}
diff --git a/java/dagger/android/HasActivityInjector.java b/java/dagger/android/HasActivityInjector.java
new file mode 100644
index 0000000..136bbad
--- /dev/null
+++ b/java/dagger/android/HasActivityInjector.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.Activity;
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector} of {@link Activity}s. */
+@Beta
+public interface HasActivityInjector {
+
+ /** Returns an {@link AndroidInjector} of {@link Activity}s. */
+ AndroidInjector<Activity> activityInjector();
+}
diff --git a/java/dagger/android/HasAndroidInjector.java b/java/dagger/android/HasAndroidInjector.java
new file mode 100644
index 0000000..3b49718
--- /dev/null
+++ b/java/dagger/android/HasAndroidInjector.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector}. */
+@Beta
+public interface HasAndroidInjector {
+ /** Returns an {@link AndroidInjector}. */
+ AndroidInjector<Object> androidInjector();
+}
diff --git a/java/dagger/android/HasBroadcastReceiverInjector.java b/java/dagger/android/HasBroadcastReceiverInjector.java
new file mode 100644
index 0000000..b2aa992
--- /dev/null
+++ b/java/dagger/android/HasBroadcastReceiverInjector.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.content.BroadcastReceiver;
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector} of {@link BroadcastReceiver}s. */
+@Beta
+public interface HasBroadcastReceiverInjector {
+
+ /** Returns an {@link AndroidInjector} of {@link BroadcastReceiver}s. */
+ AndroidInjector<BroadcastReceiver> broadcastReceiverInjector();
+}
diff --git a/java/dagger/android/HasContentProviderInjector.java b/java/dagger/android/HasContentProviderInjector.java
new file mode 100644
index 0000000..997ddb8
--- /dev/null
+++ b/java/dagger/android/HasContentProviderInjector.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.content.ContentProvider;
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector} of {@link ContentProvider}s. */
+@Beta
+public interface HasContentProviderInjector {
+
+ /** Returns an {@link AndroidInjector} of {@link ContentProvider}s. */
+ AndroidInjector<ContentProvider> contentProviderInjector();
+}
diff --git a/java/dagger/android/HasFragmentInjector.java b/java/dagger/android/HasFragmentInjector.java
new file mode 100644
index 0000000..564f32d
--- /dev/null
+++ b/java/dagger/android/HasFragmentInjector.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.Fragment;
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector} of {@link Fragment}s. */
+@Beta
+public interface HasFragmentInjector {
+
+ /** Returns an {@link AndroidInjector} of {@link Fragment}s. */
+ AndroidInjector<Fragment> fragmentInjector();
+}
diff --git a/java/dagger/android/HasServiceInjector.java b/java/dagger/android/HasServiceInjector.java
new file mode 100644
index 0000000..d1c6a6c
--- /dev/null
+++ b/java/dagger/android/HasServiceInjector.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import android.app.Service;
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector} of {@link Service}s. */
+@Beta
+public interface HasServiceInjector {
+
+ /** Returns an {@link AndroidInjector} of {@link Service}s. */
+ AndroidInjector<Service> serviceInjector();
+}
diff --git a/java/dagger/android/internal/AndroidInjectionKeys.java b/java/dagger/android/internal/AndroidInjectionKeys.java
new file mode 100644
index 0000000..f30b92c
--- /dev/null
+++ b/java/dagger/android/internal/AndroidInjectionKeys.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.internal;
+
+/**
+ * An internal implementation detail of Dagger's generated code. This is not guaranteed to remain
+ * consistent from version to version.
+ */
+public final class AndroidInjectionKeys {
+ /**
+ * Accepts the fully qualified name of a class that is injected with {@code dagger.android}.
+ *
+ * <p>From a runtime perspective, this method does nothing except return its single argument. It
+ * is used as a signal to bytecode shrinking tools that its argument should be rewritten if it
+ * corresponds to a class that has been obfuscated/relocated. Once it is done so, it is expected
+ * that the argument will be inlined and this method will go away.
+ */
+ public static String of(String mapKey) {
+ return mapKey;
+ }
+
+ private AndroidInjectionKeys() {}
+}
diff --git a/java/dagger/android/package-info.java b/java/dagger/android/package-info.java
new file mode 100644
index 0000000..f59ef48
--- /dev/null
+++ b/java/dagger/android/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** APIs to assist with performing injection on Android. */
+@CheckReturnValue
+package dagger.android;
+
+import com.google.errorprone.annotations.CheckReturnValue;
diff --git a/java/dagger/android/processor/AndroidInjectorDescriptor.java b/java/dagger/android/processor/AndroidInjectorDescriptor.java
new file mode 100644
index 0000000..3ec6613
--- /dev/null
+++ b/java/dagger/android/processor/AndroidInjectorDescriptor.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreElements.getAnnotationMirror;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static java.util.stream.Collectors.toList;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.Module;
+import dagger.android.ContributesAndroidInjector;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.inject.Qualifier;
+import javax.inject.Scope;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * A descriptor of a generated {@link Module} and {@link dagger.Subcomponent} to be generated from a
+ * {@link ContributesAndroidInjector} method.
+ */
+@AutoValue
+abstract class AndroidInjectorDescriptor {
+ /** The type to be injected; the return type of the {@link ContributesAndroidInjector} method. */
+ abstract ClassName injectedType();
+
+ /** Scopes to apply to the generated {@link dagger.Subcomponent}. */
+ abstract ImmutableSet<AnnotationSpec> scopes();
+
+ /** @see ContributesAndroidInjector#modules() */
+ abstract ImmutableSet<ClassName> modules();
+
+ /** The {@link Module} that contains the {@link ContributesAndroidInjector} method. */
+ abstract ClassName enclosingModule();
+
+ /** The method annotated with {@link ContributesAndroidInjector}. */
+ abstract ExecutableElement method();
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder injectedType(ClassName injectedType);
+
+ abstract ImmutableSet.Builder<AnnotationSpec> scopesBuilder();
+
+ abstract ImmutableSet.Builder<ClassName> modulesBuilder();
+
+ abstract Builder enclosingModule(ClassName enclosingModule);
+
+ abstract Builder method(ExecutableElement method);
+
+ abstract AndroidInjectorDescriptor build();
+ }
+
+ static final class Validator {
+ private final Messager messager;
+
+ Validator(Messager messager) {
+ this.messager = messager;
+ }
+
+ /**
+ * Validates a {@link ContributesAndroidInjector} method, returning an {@link
+ * AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise.
+ */
+ Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) {
+ ErrorReporter reporter = new ErrorReporter(method, messager);
+
+ if (!method.getModifiers().contains(ABSTRACT)) {
+ reporter.reportError("@ContributesAndroidInjector methods must be abstract");
+ }
+
+ if (!method.getParameters().isEmpty()) {
+ reporter.reportError("@ContributesAndroidInjector methods cannot have parameters");
+ }
+
+ AndroidInjectorDescriptor.Builder builder =
+ new AutoValue_AndroidInjectorDescriptor.Builder().method(method);
+ TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement());
+ if (!isAnnotationPresent(enclosingElement, Module.class)) {
+ reporter.reportError("@ContributesAndroidInjector methods must be in a @Module");
+ }
+ builder.enclosingModule(ClassName.get(enclosingElement));
+
+ TypeMirror injectedType = method.getReturnType();
+ if (MoreTypes.asDeclared(injectedType).getTypeArguments().isEmpty()) {
+ builder.injectedType(ClassName.get(MoreTypes.asTypeElement(injectedType)));
+ } else {
+ reporter.reportError(
+ "@ContributesAndroidInjector methods cannot return parameterized types");
+ }
+
+ AnnotationMirror annotation =
+ getAnnotationMirror(method, ContributesAndroidInjector.class).get();
+ for (TypeMirror module :
+ getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) {
+ if (isAnnotationPresent(MoreTypes.asElement(module), Module.class)) {
+ builder.modulesBuilder().add((ClassName) TypeName.get(module));
+ } else {
+ reporter.reportError(String.format("%s is not a @Module", module), annotation);
+ }
+ }
+
+ for (AnnotationMirror scope : getAnnotatedAnnotations(method, Scope.class)) {
+ builder.scopesBuilder().add(AnnotationSpec.get(scope));
+ }
+
+ for (AnnotationMirror qualifier : getAnnotatedAnnotations(method, Qualifier.class)) {
+ reporter.reportError(
+ "@ContributesAndroidInjector methods cannot have qualifiers", qualifier);
+ }
+
+ return reporter.hasError ? Optional.empty() : Optional.of(builder.build());
+ }
+
+ // TODO(ronshapiro): use ValidationReport once it is moved out of the compiler
+ private static class ErrorReporter {
+ private final Element subject;
+ private final Messager messager;
+ private boolean hasError;
+
+ ErrorReporter(Element subject, Messager messager) {
+ this.subject = subject;
+ this.messager = messager;
+ }
+
+ void reportError(String error) {
+ hasError = true;
+ messager.printMessage(Kind.ERROR, error, subject);
+ }
+
+ void reportError(String error, AnnotationMirror annotation) {
+ hasError = true;
+ messager.printMessage(Kind.ERROR, error, subject, annotation);
+ }
+ }
+ }
+
+ private static final class AllTypesVisitor
+ extends SimpleAnnotationValueVisitor8<ImmutableSet<TypeMirror>, Void> {
+ @Override
+ public ImmutableSet<TypeMirror> visitArray(List<? extends AnnotationValue> values, Void aVoid) {
+ return ImmutableSet.copyOf(
+ values.stream().flatMap(v -> v.accept(this, null).stream()).collect(toList()));
+ }
+
+ @Override
+ public ImmutableSet<TypeMirror> visitType(TypeMirror a, Void aVoid) {
+ return ImmutableSet.of(a);
+ }
+
+ @Override
+ protected ImmutableSet<TypeMirror> defaultAction(Object o, Void aVoid) {
+ throw new AssertionError(o);
+ }
+ }
+}
diff --git a/java/dagger/android/processor/AndroidMapKeyValidator.java b/java/dagger/android/processor/AndroidMapKeyValidator.java
new file mode 100644
index 0000000..f6e808a
--- /dev/null
+++ b/java/dagger/android/processor/AndroidMapKeyValidator.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.auto.common.MoreElements.getAnnotationMirror;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
+import dagger.Binds;
+import dagger.MapKey;
+import dagger.android.AndroidInjectionKey;
+import dagger.android.AndroidInjector;
+import dagger.multibindings.ClassKey;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Qualifier;
+import javax.inject.Scope;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic.Kind;
+
+/** Validates the correctness of {@link MapKey}s used with {@code dagger.android}. */
+final class AndroidMapKeyValidator implements ProcessingStep {
+ private final Elements elements;
+ private final Types types;
+ private final Messager messager;
+
+ AndroidMapKeyValidator(Elements elements, Types types, Messager messager) {
+ this.elements = elements;
+ this.types = types;
+ this.messager = messager;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.<Class<? extends Annotation>>builder()
+ .add(AndroidInjectionKey.class)
+ .add(ClassKey.class)
+ .build();
+ }
+
+ @Override
+ public Set<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
+ elementsByAnnotation
+ .entries()
+ .forEach(
+ entry -> {
+ try {
+ validateMethod(entry.getKey(), MoreElements.asExecutable(entry.getValue()));
+ } catch (TypeNotPresentException e) {
+ deferredElements.add(entry.getValue());
+ }
+ });
+ return deferredElements.build();
+ }
+
+ private void validateMethod(Class<? extends Annotation> annotation, ExecutableElement method) {
+ if (!getAnnotatedAnnotations(method, Qualifier.class).isEmpty()) {
+ return;
+ }
+
+ TypeMirror returnType = method.getReturnType();
+ if (!types.isAssignable(types.erasure(returnType), factoryElement().asType())) {
+ // if returnType is not related to AndroidInjector.Factory, ignore the method
+ return;
+ }
+
+ if (!getAnnotatedAnnotations(method, Scope.class).isEmpty()) {
+ SuppressWarnings suppressedWarnings = method.getAnnotation(SuppressWarnings.class);
+ if (suppressedWarnings == null
+ || !ImmutableSet.copyOf(suppressedWarnings.value())
+ .contains("dagger.android.ScopedInjectorFactory")) {
+ AnnotationMirror mapKeyAnnotation =
+ getOnlyElement(getAnnotatedAnnotations(method, MapKey.class));
+ TypeElement mapKeyValueElement =
+ elements.getTypeElement(injectedTypeFromMapKey(mapKeyAnnotation).get());
+ messager.printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s bindings should not be scoped. Scoping this method may leak instances of %s.",
+ AndroidInjector.Factory.class.getCanonicalName(),
+ mapKeyValueElement.getQualifiedName()),
+ method);
+ }
+ }
+
+ validateReturnType(method);
+
+ // @Binds methods should only have one parameter, but we can't guarantee the order of Processors
+ // in javac, so do a basic check for valid form
+ if (isAnnotationPresent(method, Binds.class) && method.getParameters().size() == 1) {
+ validateMapKeyMatchesBindsParameter(annotation, method);
+ }
+ }
+
+ /** Report an error if the method's return type is not {@code AndroidInjector.Factory<?>}. */
+ private void validateReturnType(ExecutableElement method) {
+ TypeMirror returnType = method.getReturnType();
+ DeclaredType requiredReturnType = injectorFactoryOf(types.getWildcardType(null, null));
+
+ if (!types.isSameType(returnType, requiredReturnType)) {
+ messager.printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s should bind %s, not %s. See https://dagger.dev/android",
+ method, requiredReturnType, returnType),
+ method);
+ }
+ }
+
+ /**
+ * A valid @Binds method could bind an {@link AndroidInjector.Factory} for one type, while giving
+ * it a map key of a different type. The return type and parameter type would pass typical @Binds
+ * validation, but the map lookup in {@link dagger.android.DispatchingAndroidInjector} would
+ * retrieve the wrong injector factory.
+ *
+ * <pre>{@code
+ * {@literal @Binds}
+ * {@literal @IntoMap}
+ * {@literal @ClassKey(GreenActivity.class)}
+ * abstract AndroidInjector.Factory<?> bindBlueActivity(
+ * BlueActivityComponent.Builder builder);
+ * }</pre>
+ */
+ private void validateMapKeyMatchesBindsParameter(
+ Class<? extends Annotation> annotation, ExecutableElement method) {
+ TypeMirror parameterType = getOnlyElement(method.getParameters()).asType();
+ AnnotationMirror annotationMirror = getAnnotationMirror(method, annotation).get();
+ TypeMirror mapKeyType =
+ elements.getTypeElement(injectedTypeFromMapKey(annotationMirror).get()).asType();
+ if (!types.isAssignable(parameterType, injectorFactoryOf(mapKeyType))) {
+ messager.printMessage(
+ Kind.ERROR,
+ String.format("%s does not implement AndroidInjector<%s>", parameterType, mapKeyType),
+ method,
+ annotationMirror);
+ }
+ }
+
+ /** Returns a {@link DeclaredType} for {@code AndroidInjector.Factory<implementationType>}. */
+ private DeclaredType injectorFactoryOf(TypeMirror implementationType) {
+ return types.getDeclaredType(factoryElement(), implementationType);
+ }
+
+ private TypeElement factoryElement() {
+ return elements.getTypeElement(AndroidInjector.Factory.class.getCanonicalName());
+ }
+}
diff --git a/java/dagger/android/processor/AndroidMapKeys.java b/java/dagger/android/processor/AndroidMapKeys.java
new file mode 100644
index 0000000..fb1fc38
--- /dev/null
+++ b/java/dagger/android/processor/AndroidMapKeys.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+
+import com.google.auto.common.MoreTypes;
+import dagger.android.AndroidInjectionKey;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+final class AndroidMapKeys {
+ /**
+ * If {@code mapKey} is {@link AndroidInjectionKey}, returns the string value for the map key. If
+ * it's {@link dagger.multibindings.ClassKey}, returns the fully-qualified class name of the
+ * annotation value. Otherwise returns {@link Optional#empty()}.
+ */
+ static Optional<String> injectedTypeFromMapKey(AnnotationMirror mapKey) {
+ Object mapKeyClass = getAnnotationValue(mapKey, "value").getValue();
+ if (mapKeyClass instanceof String) {
+ return Optional.of((String) mapKeyClass);
+ } else if (mapKeyClass instanceof TypeMirror) {
+ TypeElement type = MoreTypes.asTypeElement((TypeMirror) mapKeyClass);
+ return Optional.of(type.getQualifiedName().toString());
+ } else {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/java/dagger/android/processor/AndroidProcessor.java b/java/dagger/android/processor/AndroidProcessor.java
new file mode 100644
index 0000000..5c17341
--- /dev/null
+++ b/java/dagger/android/processor/AndroidProcessor.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.common.BasicAnnotationProcessor;
+import com.google.auto.service.AutoService;
+import com.google.common.base.Ascii;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.googlejavaformat.java.filer.FormattingFiler;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/**
+ * An {@linkplain javax.annotation.processing.Processor annotation processor} to verify usage of
+ * {@code dagger.android} code.
+ *
+ * <p>Additionally, if {@code -Adagger.android.experimentalUseStringKeys} is passed to the
+ * compilation, a file will be generated to support obfuscated injected Android types used with
+ * {@code @AndroidInjectionKey}. The fact that this is generated is deliberate: not all versions of
+ * ProGuard/R8 support {@code -identifiernamestring}, so we can't include a ProGuard file in the
+ * dagger-android artifact Instead, we generate the file in {@code META-INF/proguard} only when
+ * users enable the flag. They should only be enabling it if their shrinker supports those files,
+ * and any version that does so will also support {@code -identifiernamestring}. This was added to
+ * R8 in <a href="https://r8.googlesource.com/r8/+/389123dfcc11e6dda0eec31ab62e1b7eb0da80d2">May
+ * 2018</a>.
+ */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class AndroidProcessor extends BasicAnnotationProcessor {
+ private static final String FLAG_EXPERIMENTAL_USE_STRING_KEYS =
+ "dagger.android.experimentalUseStringKeys";
+
+ @Override
+ protected Iterable<? extends ProcessingStep> initSteps() {
+ Filer filer = new FormattingFiler(processingEnv.getFiler());
+ Messager messager = processingEnv.getMessager();
+ Elements elements = processingEnv.getElementUtils();
+ Types types = processingEnv.getTypeUtils();
+
+ return ImmutableList.of(
+ new AndroidMapKeyValidator(elements, types, messager),
+ new ContributesAndroidInjectorGenerator(
+ new AndroidInjectorDescriptor.Validator(messager),
+ useStringKeys(),
+ filer,
+ elements,
+ processingEnv.getSourceVersion()));
+ }
+
+ private boolean useStringKeys() {
+ if (!processingEnv.getOptions().containsKey(FLAG_EXPERIMENTAL_USE_STRING_KEYS)) {
+ return false;
+ }
+ String flagValue = processingEnv.getOptions().get(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
+ if (flagValue == null || Ascii.equalsIgnoreCase(flagValue, "true")) {
+ return true;
+ } else if (Ascii.equalsIgnoreCase(flagValue, "false")) {
+ return false;
+ } else {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ ERROR,
+ String.format(
+ "Unknown flag value: %s. %s must be set to either 'true' or 'false'.",
+ flagValue, FLAG_EXPERIMENTAL_USE_STRING_KEYS));
+ return false;
+ }
+ }
+
+ @Override
+ protected void postRound(RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver() && useStringKeys()) {
+ try (Writer writer = createProguardFile()){
+ writer.write(
+ Joiner.on("\n")
+ .join(
+ "-identifiernamestring class dagger.android.internal.AndroidInjectionKeys {",
+ " java.lang.String of(java.lang.String);",
+ "}"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private Writer createProguardFile() throws IOException {
+ return processingEnv
+ .getFiler()
+ .createResource(CLASS_OUTPUT, "", "META-INF/proguard/dagger.android.AndroidInjectionKeys")
+ .openWriter();
+ }
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ return ImmutableSet.of(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+}
diff --git a/java/dagger/android/processor/BUILD b/java/dagger/android/processor/BUILD
new file mode 100644
index 0000000..5754143
--- /dev/null
+++ b/java/dagger/android/processor/BUILD
@@ -0,0 +1,88 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Public Dagger API for Android
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+)
+load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["*.java"]),
+)
+
+java_library(
+ name = "processor",
+ srcs = [":srcs"],
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ tags = ["maven_coordinates=com.google.dagger:dagger-android-processor:" + POM_VERSION],
+ deps = [
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/google_java_format",
+ "//java/dagger:core",
+ "//java/dagger/model",
+ "//java/dagger/spi",
+ # https://github.com/bazelbuild/bazel/issues/2517
+ ":dagger-android-jar",
+ ],
+)
+
+# https://github.com/bazelbuild/bazel/issues/2517
+# This target serves two (related) purposes:
+# 1. Bazel does not allow a java_library to depend on an android_library, even if that java_library
+# will be used in a java_plugin.
+# 2. It stores the metadata for the "jarimpl" target that we use to work-around Gradle not loading
+# aar artifacts that are declared as deps of an annotation processor. Our pom.xml generator reads
+# the tags and includes them apppropriately.
+java_import(
+ name = "dagger-android-jar",
+ jars = ["//java/dagger/android:libandroid.jar"],
+ tags = ["maven_coordinates=com.google.dagger:dagger-android-jarimpl:" + POM_VERSION],
+ visibility = ["//visibility:private"],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-android-processor",
+ artifact_name = "Dagger Android Processor",
+ targets = [":processor"],
+)
+
+java_plugin(
+ name = "plugin",
+ generates_api = 1,
+ processor_class = "dagger.android.processor.AndroidProcessor",
+ deps = [":processor"],
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "processor-javadoc",
+ srcs = [":srcs"],
+ root_packages = ["dagger.android.processor"],
+ deps = [":processor"],
+)
diff --git a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java b/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
new file mode 100644
index 0000000..5c99fd4
--- /dev/null
+++ b/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec;
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static com.squareup.javapoet.TypeSpec.interfaceBuilder;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.WildcardTypeName;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.android.AndroidInjectionKey;
+import dagger.android.AndroidInjector;
+import dagger.android.ContributesAndroidInjector;
+import dagger.android.processor.AndroidInjectorDescriptor.Validator;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.util.Elements;
+
+/** Generates the implementation specified in {@link ContributesAndroidInjector}. */
+final class ContributesAndroidInjectorGenerator implements ProcessingStep {
+
+ private final AndroidInjectorDescriptor.Validator validator;
+ private final Filer filer;
+ private final Elements elements;
+ private final boolean useStringKeys;
+ private final SourceVersion sourceVersion;
+
+ ContributesAndroidInjectorGenerator(
+ Validator validator,
+ boolean useStringKeys,
+ Filer filer,
+ Elements elements,
+ SourceVersion sourceVersion) {
+ this.validator = validator;
+ this.useStringKeys = useStringKeys;
+ this.filer = filer;
+ this.elements = elements;
+ this.sourceVersion = sourceVersion;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(ContributesAndroidInjector.class);
+ }
+
+ @Override
+ public Set<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
+ for (ExecutableElement method : methodsIn(elementsByAnnotation.values())) {
+ try {
+ validator.createIfValid(method).ifPresent(this::generate);
+ } catch (TypeNotPresentException e) {
+ deferredElements.add(method);
+ }
+ }
+ return deferredElements.build();
+ }
+
+ private void generate(AndroidInjectorDescriptor descriptor) {
+ ClassName moduleName =
+ descriptor
+ .enclosingModule()
+ .topLevelClassName()
+ .peerClass(
+ Joiner.on('_').join(descriptor.enclosingModule().simpleNames())
+ + "_"
+ + LOWER_CAMEL.to(UPPER_CAMEL, descriptor.method().getSimpleName().toString()));
+
+ String baseName = descriptor.injectedType().simpleName();
+ ClassName subcomponentName = moduleName.nestedClass(baseName + "Subcomponent");
+ ClassName subcomponentFactoryName = subcomponentName.nestedClass("Factory");
+
+ TypeSpec.Builder module =
+ classBuilder(moduleName)
+ .addOriginatingElement(descriptor.method())
+ .addAnnotation(
+ AnnotationSpec.builder(Module.class)
+ .addMember("subcomponents", "$T.class", subcomponentName)
+ .build())
+ .addModifiers(PUBLIC, ABSTRACT)
+ .addMethod(bindAndroidInjectorFactory(descriptor, subcomponentFactoryName))
+ .addType(subcomponent(descriptor, subcomponentName, subcomponentFactoryName))
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
+ generatedAnnotationSpec(elements, sourceVersion, AndroidProcessor.class)
+ .ifPresent(module::addAnnotation);
+
+ try {
+ JavaFile.builder(moduleName.packageName(), module.build())
+ .skipJavaLangImports(true)
+ .build()
+ .writeTo(filer);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private MethodSpec bindAndroidInjectorFactory(
+ AndroidInjectorDescriptor descriptor, ClassName subcomponentBuilderName) {
+ return methodBuilder("bindAndroidInjectorFactory")
+ .addAnnotation(Binds.class)
+ .addAnnotation(IntoMap.class)
+ .addAnnotation(androidInjectorMapKey(descriptor))
+ .addModifiers(ABSTRACT)
+ .returns(
+ parameterizedTypeName(
+ AndroidInjector.Factory.class,
+ WildcardTypeName.subtypeOf(TypeName.OBJECT)))
+ .addParameter(subcomponentBuilderName, "builder")
+ .build();
+ }
+
+ private AnnotationSpec androidInjectorMapKey(AndroidInjectorDescriptor descriptor) {
+ if (useStringKeys) {
+ return AnnotationSpec.builder(AndroidInjectionKey.class)
+ .addMember("value", "$S", descriptor.injectedType().toString())
+ .build();
+ }
+ return AnnotationSpec.builder(ClassKey.class)
+ .addMember("value", "$T.class", descriptor.injectedType())
+ .build();
+ }
+
+ private TypeSpec subcomponent(
+ AndroidInjectorDescriptor descriptor,
+ ClassName subcomponentName,
+ ClassName subcomponentFactoryName) {
+ AnnotationSpec.Builder subcomponentAnnotation = AnnotationSpec.builder(Subcomponent.class);
+ for (ClassName module : descriptor.modules()) {
+ subcomponentAnnotation.addMember("modules", "$T.class", module);
+ }
+
+ return interfaceBuilder(subcomponentName)
+ .addModifiers(PUBLIC)
+ .addAnnotation(subcomponentAnnotation.build())
+ .addAnnotations(descriptor.scopes())
+ .addSuperinterface(parameterizedTypeName(AndroidInjector.class, descriptor.injectedType()))
+ .addType(subcomponentFactory(descriptor, subcomponentFactoryName))
+ .build();
+ }
+
+ private TypeSpec subcomponentFactory(
+ AndroidInjectorDescriptor descriptor, ClassName subcomponentFactoryName) {
+ return interfaceBuilder(subcomponentFactoryName)
+ .addAnnotation(Subcomponent.Factory.class)
+ .addModifiers(PUBLIC, STATIC)
+ .addSuperinterface(
+ parameterizedTypeName(AndroidInjector.Factory.class, descriptor.injectedType()))
+ .build();
+ }
+
+ private static ParameterizedTypeName parameterizedTypeName(
+ Class<?> clazz, TypeName... typeArguments) {
+ return ParameterizedTypeName.get(ClassName.get(clazz), typeArguments);
+ }
+}
diff --git a/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
new file mode 100644
index 0000000..a19c5ef
--- /dev/null
+++ b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import dagger.MapKey;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.model.Binding;
+import dagger.model.BindingGraph;
+import dagger.model.BindingKind;
+import dagger.model.Key;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.inject.Provider;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Validates that the two maps that {@link DispatchingAndroidInjector} injects have logically
+ * different keys. If a contribution exists for the same {@code FooActivity} with
+ * {@code @ActivityKey(FooActivity.class)} and
+ * {@code @AndroidInjectionKey("com.example.FooActivity")}, report an error.
+ */
+@AutoService(BindingGraphPlugin.class)
+public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugin {
+ @Override
+ public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) {
+ for (Binding binding : graph.bindings()) {
+ if (isDispatchingAndroidInjector(binding)) {
+ validateMapKeyUniqueness(binding, graph, diagnosticReporter);
+ }
+ }
+ }
+
+ private boolean isDispatchingAndroidInjector(Binding binding) {
+ Key key = binding.key();
+ return MoreTypes.isTypeOf(DispatchingAndroidInjector.class, key.type())
+ && !key.qualifier().isPresent();
+ }
+
+ private void validateMapKeyUniqueness(
+ Binding dispatchingAndroidInjector,
+ BindingGraph graph,
+ DiagnosticReporter diagnosticReporter) {
+ ImmutableSet<Binding> injectorFactories =
+ injectorMapDependencies(dispatchingAndroidInjector, graph)
+ .flatMap(injectorFactoryMap -> graph.requestedBindings(injectorFactoryMap).stream())
+ .collect(collectingAndThen(toList(), ImmutableSet::copyOf));
+
+ ImmutableListMultimap.Builder<String, Binding> mapKeyIndex = ImmutableListMultimap.builder();
+ for (Binding injectorFactory : injectorFactories) {
+ AnnotationMirror mapKey = mapKey(injectorFactory).get();
+ Optional<String> injectedType = injectedTypeFromMapKey(mapKey);
+ if (injectedType.isPresent()) {
+ mapKeyIndex.put(injectedType.get(), injectorFactory);
+ } else {
+ diagnosticReporter.reportBinding(
+ ERROR, injectorFactory, "Unrecognized class: %s", mapKey);
+ }
+ }
+
+ Map<String, List<Binding>> duplicates =
+ Maps.filterValues(Multimaps.asMap(mapKeyIndex.build()), bindings -> bindings.size() > 1);
+ if (!duplicates.isEmpty()) {
+ StringBuilder errorMessage =
+ new StringBuilder("Multiple injector factories bound for the same type:\n");
+ Formatter formatter = new Formatter(errorMessage);
+ duplicates.forEach(
+ (injectedType, duplicateFactories) -> {
+ formatter.format(" %s:\n", injectedType);
+ duplicateFactories.forEach(duplicate -> formatter.format(" %s\n", duplicate));
+ });
+ diagnosticReporter.reportBinding(ERROR, dispatchingAndroidInjector, errorMessage.toString());
+ }
+ }
+
+ /**
+ * Returns a stream of the dependencies of {@code binding} that have a key type of {@code Map<K,
+ * Provider<AndroidInjector.Factory<?>>}.
+ */
+ private Stream<Binding> injectorMapDependencies(Binding binding, BindingGraph graph) {
+ return graph.requestedBindings(binding).stream()
+ .filter(requestedBinding -> requestedBinding.kind().equals(BindingKind.MULTIBOUND_MAP))
+ .filter(
+ requestedBinding -> {
+ TypeMirror valueType =
+ MoreTypes.asDeclared(requestedBinding.key().type()).getTypeArguments().get(1);
+ if (!MoreTypes.isTypeOf(Provider.class, valueType)
+ || !valueType.getKind().equals(TypeKind.DECLARED)) {
+ return false;
+ }
+ TypeMirror providedType = MoreTypes.asDeclared(valueType).getTypeArguments().get(0);
+ return MoreTypes.isTypeOf(AndroidInjector.Factory.class, providedType);
+ });
+ }
+
+ private Optional<AnnotationMirror> mapKey(Binding binding) {
+ return binding
+ .bindingElement()
+ .map(bindingElement -> getAnnotatedAnnotations(bindingElement, MapKey.class))
+ .flatMap(
+ annotations ->
+ annotations.isEmpty()
+ ? Optional.empty()
+ : Optional.of(getOnlyElement(annotations)));
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/Android/DuplicateAndroidInjectors";
+ }
+}
diff --git a/java/dagger/android/proguard.cfg b/java/dagger/android/proguard.cfg
new file mode 100644
index 0000000..bd8ffbf
--- /dev/null
+++ b/java/dagger/android/proguard.cfg
@@ -0,0 +1 @@
+-dontwarn com.google.errorprone.annotations.**
diff --git a/java/dagger/android/support/AndroidManifest.xml b/java/dagger/android/support/AndroidManifest.xml
new file mode 100644
index 0000000..d080e11
--- /dev/null
+++ b/java/dagger/android/support/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.android.support">
+ <uses-sdk android:minSdkVersion="14" />
+</manifest>
diff --git a/java/dagger/android/support/AndroidSupportInjection.java b/java/dagger/android/support/AndroidSupportInjection.java
new file mode 100644
index 0000000..1624345
--- /dev/null
+++ b/java/dagger/android/support/AndroidSupportInjection.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import static android.util.Log.DEBUG;
+import static dagger.internal.Preconditions.checkNotNull;
+
+import android.app.Activity;
+import android.app.Application;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import dagger.android.AndroidInjector;
+import dagger.android.HasAndroidInjector;
+import dagger.internal.Beta;
+
+/** Injects core Android types from support libraries. */
+@Beta
+public final class AndroidSupportInjection {
+ private static final String TAG = "dagger.android.support";
+
+ /**
+ * Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found,
+ * otherwise throws an {@link IllegalArgumentException}.
+ *
+ * <p>Uses the following algorithm to find the appropriate {@link AndroidInjector} to use to
+ * inject {@code fragment}:
+ *
+ * <ol>
+ * <li>Walks the parent-fragment hierarchy to find a fragment that implements {@link
+ * HasAndroidInjector} or {@link HasSupportFragmentInjector}, and if none do
+ * <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements
+ * {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}, and if not
+ * <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}
+ * {@link HasSupportFragmentInjector}.
+ * </ol>
+ *
+ * If none of them implement {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}, a
+ * {@link IllegalArgumentException} is thrown.
+ *
+ * @throws IllegalArgumentException if no parent fragment, activity, or application implements
+ * {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}.
+ */
+ public static void inject(Fragment fragment) {
+ checkNotNull(fragment, "fragment");
+
+ Object hasInjector = findHasSupportFragmentInjector(fragment);
+ AndroidInjector<? super Fragment> injector;
+ if (hasInjector instanceof HasAndroidInjector) {
+ injector = ((HasAndroidInjector) hasInjector).androidInjector();
+ checkNotNull(injector, "%s.androidInjector() returned null", hasInjector.getClass());
+ } else if (hasInjector instanceof HasSupportFragmentInjector) {
+ injector = ((HasSupportFragmentInjector) hasInjector).supportFragmentInjector();
+ checkNotNull(injector, "%s.supportFragmentInjector() returned null", hasInjector.getClass());
+ } else {
+ throw new RuntimeException(
+ String.format(
+ "%s does not implement %s or %s",
+ hasInjector.getClass().getCanonicalName(),
+ HasAndroidInjector.class.getCanonicalName(),
+ HasSupportFragmentInjector.class.getCanonicalName()));
+ }
+
+ if (Log.isLoggable(TAG, DEBUG)) {
+ Log.d(
+ TAG,
+ String.format(
+ "An injector for %s was found in %s",
+ fragment.getClass().getCanonicalName(),
+ hasInjector.getClass().getCanonicalName()));
+ }
+
+ injector.inject(fragment);
+ }
+
+ private static Object findHasSupportFragmentInjector(Fragment fragment) {
+ Fragment parentFragment = fragment;
+ while ((parentFragment = parentFragment.getParentFragment()) != null) {
+ if (parentFragment instanceof HasAndroidInjector
+ || parentFragment instanceof HasSupportFragmentInjector) {
+ return parentFragment;
+ }
+ }
+ Activity activity = fragment.getActivity();
+ if (activity instanceof HasAndroidInjector || activity instanceof HasSupportFragmentInjector) {
+ return activity;
+ }
+ Application application = activity.getApplication();
+ if (application instanceof HasAndroidInjector
+ || application instanceof HasSupportFragmentInjector) {
+ return application;
+ }
+ throw new IllegalArgumentException(
+ String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
+ }
+
+ private AndroidSupportInjection() {}
+}
diff --git a/java/dagger/android/support/AndroidSupportInjectionModule.java b/java/dagger/android/support/AndroidSupportInjectionModule.java
new file mode 100644
index 0000000..d78b0cb
--- /dev/null
+++ b/java/dagger/android/support/AndroidSupportInjectionModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import dagger.Module;
+import dagger.android.AndroidInjectionModule;
+import dagger.internal.Beta;
+
+/**
+ * This module no longer provides any value beyond what is provided in {@link
+ * AndroidInjectionModule} and is just an alias. It will be removed in a future release.
+ */
+@Beta
+@Module(includes = AndroidInjectionModule.class)
+public abstract class AndroidSupportInjectionModule {
+ private AndroidSupportInjectionModule() {}
+}
diff --git a/java/dagger/android/support/BUILD b/java/dagger/android/support/BUILD
new file mode 100644
index 0000000..749d541
--- /dev/null
+++ b/java/dagger/android/support/BUILD
@@ -0,0 +1,75 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Public Dagger API for Android that interacts with the Android support libraries
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "SOURCE_7_TARGET_7")
+load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+
+filegroup(
+ name = "support-srcs",
+ srcs = glob(["*.java"]),
+)
+
+android_library(
+ name = "support",
+ srcs = glob(["*.java"]),
+ javacopts = SOURCE_7_TARGET_7,
+ manifest = "AndroidManifest.xml",
+ tags = ["maven_coordinates=com.google.dagger:dagger-android-support:" + POM_VERSION],
+ deps = [
+ ":manual-maven-deps",
+ "//:dagger_with_compiler",
+ "//java/dagger/android",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ ],
+)
+
+# Our pom.xml generator does not have a way to add manual maven deps. This target exports the
+# targets that don't have the necessary maven_coordinates tags.
+android_library(
+ name = "manual-maven-deps",
+ tags = [
+ "maven_coordinates=com.android.support:appcompat-v7:25.0.0",
+ "maven_coordinates=com.android.support:support-annotations:25.0.0",
+ "maven_coordinates=com.android.support:support-fragment:25.0.0",
+ ],
+ visibility = ["//visibility:private"],
+ exports = [
+ "@androidsdk//com.android.support:appcompat-v7-25.0.0",
+ "@androidsdk//com.android.support:support-annotations-25.0.0",
+ "@androidsdk//com.android.support:support-fragment-25.0.0",
+ ],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-android-support",
+ artifact_name = "Dagger Android Support",
+ packaging = "aar",
+ targets = [":support"],
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "support-javadoc",
+ srcs = [":support-srcs"],
+ android_api_level = 26,
+ root_packages = ["dagger.android.support"],
+ deps = [":support"],
+)
diff --git a/java/dagger/android/support/DaggerAppCompatActivity.java b/java/dagger/android/support/DaggerAppCompatActivity.java
new file mode 100644
index 0000000..ccc4faa
--- /dev/null
+++ b/java/dagger/android/support/DaggerAppCompatActivity.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import dagger.android.AndroidInjection;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasAndroidInjector;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * An {@link AppCompatActivity} that injects its members in {@link #onCreate(Bundle)} and can be
+ * used to inject {@code Fragment}s attached to it.
+ */
+@Beta
+public abstract class DaggerAppCompatActivity extends AppCompatActivity
+ implements HasAndroidInjector {
+
+ @Inject DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ AndroidInjection.inject(this);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/support/DaggerAppCompatDialogFragment.java b/java/dagger/android/support/DaggerAppCompatDialogFragment.java
new file mode 100644
index 0000000..1efaeec
--- /dev/null
+++ b/java/dagger/android/support/DaggerAppCompatDialogFragment.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import android.content.Context;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatDialogFragment;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasAndroidInjector;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * An {@link AppCompatDialogFragment} that injects its members in {@link #onAttach(Context)} and can
+ * be used to inject child {@link Fragment}s attached to it. Note that when this fragment gets
+ * reattached, its members will be injected again.
+ */
+@Beta
+public abstract class DaggerAppCompatDialogFragment extends AppCompatDialogFragment
+ implements HasAndroidInjector {
+
+ @Inject DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ public void onAttach(Context context) {
+ AndroidSupportInjection.inject(this);
+ super.onAttach(context);
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/support/DaggerApplication.java b/java/dagger/android/support/DaggerApplication.java
new file mode 100644
index 0000000..1cb3bd8
--- /dev/null
+++ b/java/dagger/android/support/DaggerApplication.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import dagger.android.AndroidInjector;
+
+/**
+ * An {@link Application} that injects its members and can be used to inject classes that the
+ * Android framework instantiates. Injection is performed in {@link #onCreate()} or the first call
+ * to {@link AndroidInjection#inject(ContentProvider)}, whichever happens first.
+ */
+// TODO(ronshapiro): deprecate and remove this class
+public abstract class DaggerApplication extends dagger.android.DaggerApplication {
+ @Override
+ protected abstract AndroidInjector<? extends DaggerApplication> applicationInjector();
+}
diff --git a/java/dagger/android/support/DaggerDialogFragment.java b/java/dagger/android/support/DaggerDialogFragment.java
new file mode 100644
index 0000000..69b90bc
--- /dev/null
+++ b/java/dagger/android/support/DaggerDialogFragment.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import android.content.Context;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasAndroidInjector;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * A {@link DialogFragment} that injects its members in {@link #onAttach(Context)} and can be used
+ * to inject child {@link Fragment}s attached to it. Note that when this fragment gets reattached,
+ * its members will be injected again.
+ */
+@Beta
+public abstract class DaggerDialogFragment extends DialogFragment implements HasAndroidInjector {
+
+ @Inject DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ public void onAttach(Context context) {
+ AndroidSupportInjection.inject(this);
+ super.onAttach(context);
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/support/DaggerFragment.java b/java/dagger/android/support/DaggerFragment.java
new file mode 100644
index 0000000..332cdaa
--- /dev/null
+++ b/java/dagger/android/support/DaggerFragment.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import android.content.Context;
+import android.support.v4.app.Fragment;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasAndroidInjector;
+import dagger.internal.Beta;
+import javax.inject.Inject;
+
+/**
+ * A {@link Fragment} that injects its members in {@link #onAttach(Context)} and can be used to
+ * inject child {@link Fragment}s attached to it. Note that when this fragment gets reattached, its
+ * members will be injected again.
+ */
+@Beta
+public abstract class DaggerFragment extends Fragment implements HasAndroidInjector {
+
+ @Inject DispatchingAndroidInjector<Object> androidInjector;
+
+ @Override
+ public void onAttach(Context context) {
+ AndroidSupportInjection.inject(this);
+ super.onAttach(context);
+ }
+
+ @Override
+ public AndroidInjector<Object> androidInjector() {
+ return androidInjector;
+ }
+}
diff --git a/java/dagger/android/support/HasSupportFragmentInjector.java b/java/dagger/android/support/HasSupportFragmentInjector.java
new file mode 100644
index 0000000..e80609e
--- /dev/null
+++ b/java/dagger/android/support/HasSupportFragmentInjector.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import android.support.v4.app.Fragment;
+import dagger.android.AndroidInjector;
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector} of {@link Fragment}s. */
+@Beta
+public interface HasSupportFragmentInjector {
+
+ /** Returns an {@link AndroidInjector} of {@link Fragment}s. */
+ AndroidInjector<Fragment> supportFragmentInjector();
+}
diff --git a/java/dagger/android/support/package-info.java b/java/dagger/android/support/package-info.java
new file mode 100644
index 0000000..d49d44d
--- /dev/null
+++ b/java/dagger/android/support/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@CheckReturnValue
+package dagger.android.support;
+
+/**
+ * Additions to the APIs in {@link dagger.android} for use with the <a
+ * href="https://developer.android.com/topic/libraries/support-library">Android support
+ * libraries</a>.
+ */
+
+import com.google.errorprone.annotations.CheckReturnValue;
diff --git a/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java b/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
new file mode 100644
index 0000000..e98fe9b
--- /dev/null
+++ b/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.errorprone;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
+
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.BugPattern.ProvidesFix;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MemberSelectTreeMatcher;
+import com.google.errorprone.fixes.SuggestedFix;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.matchers.Matchers;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.tools.javac.code.Symbol;
+
+/** A refactoring to update AndroidInjector bindings to their new form. */
+@BugPattern(
+ name = "AndroidSupportInjectionModuleMigrator",
+ providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION,
+ summary = "Inlines usages of AndroidSupportInjectionModule to AndroidInjectionModule",
+ explanation =
+ "AndroidSupportInjectionModule is now an empty module and acts as an alias for "
+ + "AndroidInjectionModule. This migration rewrites usages of the former to the latter.",
+ severity = SUGGESTION)
+public final class AndroidSupportInjectionModuleMigrator extends BugChecker
+ implements MemberSelectTreeMatcher {
+ private static final Matcher<ExpressionTree> MODULE_CLASS_LITERAL =
+ Matchers.classLiteral(
+ (ExpressionTree expressionTree, VisitorState state) -> {
+ Symbol symbol = ASTHelpers.getSymbol(expressionTree);
+ if (symbol == null) {
+ return false;
+ }
+ return symbol
+ .getQualifiedName()
+ .contentEquals("dagger.android.support.AndroidSupportInjectionModule");
+ });
+
+ @Override
+ public Description matchMemberSelect(MemberSelectTree tree, VisitorState state) {
+ if (MODULE_CLASS_LITERAL.matches(tree, state)) {
+ return describeMatch(
+ tree,
+ SuggestedFix.builder()
+ .replace(tree, "AndroidInjectionModule.class")
+ .addImport("dagger.android.AndroidInjectionModule")
+ .build());
+ }
+ return Description.NO_MATCH;
+ }
+}
diff --git a/java/dagger/errorprone/BUILD b/java/dagger/errorprone/BUILD
new file mode 100644
index 0000000..408925a
--- /dev/null
+++ b/java/dagger/errorprone/BUILD
@@ -0,0 +1,15 @@
+# Description:
+# ErrorProne refactorings and static analysis for Dagger
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "errorprone",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/dagger:core",
+ "@bazel_tools//tools/jdk:langtools-neverlink",
+ "@google_bazel_common//third_party/java/error_prone:check_api",
+ "@google_bazel_common//third_party/java/guava",
+ ],
+)
diff --git a/java/dagger/example/android/simple/AndroidManifest.xml b/java/dagger/example/android/simple/AndroidManifest.xml
new file mode 100644
index 0000000..711fb1e
--- /dev/null
+++ b/java/dagger/example/android/simple/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.example.android.simple">
+
+ <uses-sdk
+ android:minSdkVersion="14"
+ android:targetSdkVersion="24"/>
+
+ <application android:name=".SimpleApplication" android:label="@string/appName">
+ <activity android:name=".MainActivity" android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/java/dagger/example/android/simple/BUILD b/java/dagger/example/android/simple/BUILD
new file mode 100644
index 0000000..7396744
--- /dev/null
+++ b/java/dagger/example/android/simple/BUILD
@@ -0,0 +1,42 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# A skeletal application that demonstates wiring for an injected Application and Actiity.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "simple_lib",
+ srcs = glob(["*.java"]),
+ manifest = "AndroidManifest.xml",
+ resource_files = glob(["res/**"]),
+ deps = [
+ "//:android",
+ "//:android-support",
+ "//:dagger_with_compiler",
+ "@androidsdk//com.android.support:appcompat-v7-25.0.0",
+ "@androidsdk//com.android.support:support-annotations-25.0.0",
+ "@androidsdk//com.android.support:support-fragment-25.0.0",
+ ],
+)
+
+android_binary(
+ name = "simple",
+ aapt_version = "aapt",
+ manifest = "AndroidManifest.xml",
+ deps = [
+ ":simple_lib",
+ ],
+)
diff --git a/java/dagger/example/android/simple/BuildModule.java b/java/dagger/example/android/simple/BuildModule.java
new file mode 100644
index 0000000..40ac8ee
--- /dev/null
+++ b/java/dagger/example/android/simple/BuildModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.android.simple;
+
+import static android.os.Build.MODEL;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class BuildModule {
+ @Provides
+ @Model
+ static String provideModel() {
+ return MODEL;
+ }
+}
diff --git a/java/dagger/example/android/simple/MainActivity.java b/java/dagger/example/android/simple/MainActivity.java
new file mode 100644
index 0000000..f2aab2d
--- /dev/null
+++ b/java/dagger/example/android/simple/MainActivity.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.android.simple;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+import dagger.Binds;
+import dagger.android.AndroidInjector;
+import dagger.android.support.DaggerAppCompatActivity;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import javax.inject.Inject;
+
+/**
+ * The main activity application. It can be injected with any binding from both {@link Component}
+ * and {@link dagger.example.android.simple.SimpleApplication.Component}.
+ */
+public class MainActivity extends DaggerAppCompatActivity {
+ @dagger.Subcomponent
+ interface Component extends AndroidInjector<MainActivity> {
+
+ @dagger.Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
+ }
+
+ @dagger.Module(subcomponents = Component.class)
+ abstract class Module {
+
+ @Binds
+ @IntoMap
+ @ClassKey(MainActivity.class)
+ abstract AndroidInjector.Factory<?> bind(Component.Builder builder);
+ }
+
+ private static final String TAG = MainActivity.class.getSimpleName();
+
+ @Inject @Model String model;
+
+ @Inject
+ void logInjection() {
+ Log.i(TAG, "Injecting " + MainActivity.class.getSimpleName());
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+
+ TextView greeting = (TextView) findViewById(R.id.greeting);
+ String text = getResources().getString(R.string.welcome, model);
+ greeting.setText(text);
+ }
+}
diff --git a/java/dagger/example/android/simple/Model.java b/java/dagger/example/android/simple/Model.java
new file mode 100644
index 0000000..c52bb98
--- /dev/null
+++ b/java/dagger/example/android/simple/Model.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.android.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to {@link android.os.Build#MODEL}. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface Model {}
diff --git a/java/dagger/example/android/simple/SimpleApplication.java b/java/dagger/example/android/simple/SimpleApplication.java
new file mode 100644
index 0000000..ae3d42d
--- /dev/null
+++ b/java/dagger/example/android/simple/SimpleApplication.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.android.simple;
+
+import android.util.Log;
+import dagger.android.AndroidInjectionModule;
+import dagger.android.AndroidInjector;
+import dagger.android.DaggerApplication;
+import javax.inject.Inject;
+
+/**
+ * A simple, skeletal application that demonstrates a dependency-injected application using the
+ * utilities in {@code dagger.android}.
+ */
+public class SimpleApplication extends DaggerApplication {
+ private static final String TAG = SimpleApplication.class.getSimpleName();
+
+ @dagger.Component(
+ modules = {AndroidInjectionModule.class, MainActivity.Module.class, BuildModule.class})
+ /* @ApplicationScoped and/or @Singleton */
+ interface Component extends AndroidInjector<SimpleApplication> {
+ @dagger.Component.Builder
+ abstract class Builder extends AndroidInjector.Builder<SimpleApplication> {}
+ }
+
+ @Inject
+ void logInjection() {
+ Log.i(TAG, "Injecting " + SimpleApplication.class.getSimpleName());
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ protected AndroidInjector<SimpleApplication> applicationInjector() {
+ return DaggerSimpleApplication_Component.builder().create(this);
+ }
+}
diff --git a/java/dagger/example/android/simple/res/layout/activity_main.xml b/java/dagger/example/android/simple/res/layout/activity_main.xml
new file mode 100644
index 0000000..37add1f
--- /dev/null
+++ b/java/dagger/example/android/simple/res/layout/activity_main.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ style="@style/TextAppearance.AppCompat.Display4"
+ />
+</RelativeLayout>
diff --git a/java/dagger/example/android/simple/res/values/strings.xml b/java/dagger/example/android/simple/res/values/strings.xml
new file mode 100644
index 0000000..c4ba1fd
--- /dev/null
+++ b/java/dagger/example/android/simple/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="appName">Simple Dagger</string>
+ <string name="welcome">Hello, %s!</string>
+</resources>
\ No newline at end of file
diff --git a/java/dagger/example/spi/BUILD b/java/dagger/example/spi/BUILD
new file mode 100644
index 0000000..84b4a87
--- /dev/null
+++ b/java/dagger/example/spi/BUILD
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# An example of the dagger.spi.BindingGraphPlugin usage
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "binding-graph-visualizer",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/dagger/model",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
diff --git a/java/dagger/example/spi/BindingGraphVisualizer.java b/java/dagger/example/spi/BindingGraphVisualizer.java
new file mode 100644
index 0000000..e83fa2e
--- /dev/null
+++ b/java/dagger/example/spi/BindingGraphVisualizer.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.spi;
+
+import static java.util.UUID.randomUUID;
+import static java.util.regex.Matcher.quoteReplacement;
+import static java.util.stream.Collectors.groupingBy;
+
+import com.google.auto.service.AutoService;
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import com.google.common.graph.EndpointPair;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
+import dagger.model.Binding;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.MaybeBinding;
+import dagger.model.BindingGraph.MissingBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.BindingGraph.SubcomponentCreatorBindingEdge;
+import dagger.model.BindingKind;
+import dagger.model.ComponentPath;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.TypeElement;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+/**
+ * Experimental visualizer used as a proof-of-concept for {@link BindingGraphPlugin}.
+ *
+ * <p>For each component, writes a <a href=http://www.graphviz.org/content/dot-language>DOT file</a>
+ * in the same package. The file name is the name of the component type (with enclosing type names,
+ * joined by underscores, preceding it), with a {@code .dot} extension.
+ *
+ * <p>For example, for a nested component type {@code Foo.Bar} this will generate a file {@code
+ * Foo_Bar.dot}.
+ */
+@AutoService(BindingGraphPlugin.class)
+public final class BindingGraphVisualizer implements BindingGraphPlugin {
+ private Filer filer;
+
+ @Override
+ public void initFiler(Filer filer) {
+ this.filer = filer;
+ }
+
+ /** Graphviz color names to use for binding nodes within each component. */
+ private static final ImmutableList<String> COMPONENT_COLORS =
+ ImmutableList.of(
+ "/set312/1",
+ "/set312/2",
+ "/set312/3",
+ "/set312/4",
+ "/set312/5",
+ "/set312/6",
+ "/set312/7",
+ "/set312/8",
+ "/set312/9",
+ "/set312/10",
+ "/set312/11",
+ "/set312/12");
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ TypeElement componentElement =
+ bindingGraph.rootComponentNode().componentPath().currentComponent();
+ DotGraph graph = new NodesGraph(bindingGraph).graph();
+ ClassName componentName = ClassName.get(componentElement);
+ try {
+ FileObject file =
+ filer
+ .createResource(
+ StandardLocation.CLASS_OUTPUT,
+ componentName.packageName(),
+ Joiner.on('_').join(componentName.simpleNames()) + ".dot",
+ componentElement);
+ try (PrintWriter writer = new PrintWriter(file.openWriter())) {
+ graph.write(0, writer);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private abstract static class Indented {
+
+ abstract void write(int level, PrintWriter writer);
+
+ @CanIgnoreReturnValue
+ PrintWriter indent(int level, PrintWriter writer) {
+ writer.print(Strings.repeat(" ", level * 2));
+ return writer;
+ }
+ }
+
+ static class DotGraph extends Indented {
+ private final String header;
+ private final List<Indented> elements = new ArrayList<>();
+
+ DotGraph(String header) {
+ this.header = header;
+ }
+
+ @CanIgnoreReturnValue
+ DotGraph add(Indented element) {
+ elements.add(element);
+ return this;
+ }
+
+ @Override
+ void write(int level, PrintWriter writer) {
+ indent(level, writer);
+ writer.println(header + " {");
+ for (Indented element : elements) {
+ element.write(level + 1, writer);
+ }
+ indent(level, writer);
+ writer.println("}");
+ }
+ }
+
+ static class DotStatement<S extends DotStatement<S>> extends Indented {
+ private final String base;
+ private final Map<String, Object> attributes = new LinkedHashMap<>();
+
+ DotStatement(String base) {
+ this.base = base;
+ }
+
+ @SuppressWarnings("unchecked")
+ @CanIgnoreReturnValue
+ S addAttribute(String name, Object value) {
+ attributes.put(name, value);
+ return (S) this;
+ }
+
+ @CanIgnoreReturnValue
+ S addAttributeFormat(String name, String format, Object... args) {
+ return addAttribute(name, String.format(format, args));
+ }
+
+ @Override
+ void write(int level, PrintWriter writer) {
+ indent(level, writer);
+ writer.print(base);
+ if (!attributes.isEmpty()) {
+ writer.print(
+ attributes
+ .entrySet()
+ .stream()
+ .map(
+ entry ->
+ String.format("%s=%s", entry.getKey(), quote(entry.getValue().toString())))
+ .collect(Collectors.joining(", ", " [", "]")));
+ }
+ writer.println();
+ }
+ }
+
+ private static String quote(String string) {
+ return '"' + string.replaceAll("\"", quoteReplacement("\\\"")) + '"';
+ }
+
+ static class DotNode extends DotStatement<DotNode> {
+ DotNode(Object nodeName) {
+ super(quote(nodeName.toString()));
+ }
+ }
+
+ static class DotEdge extends DotStatement<DotEdge> {
+ DotEdge(Object leftNode, Object rightNode) {
+ super(quote(leftNode.toString()) + " -> " + quote(rightNode.toString()));
+ }
+ }
+
+ static class NodesGraph {
+ private final DotGraph graph =
+ new DotGraph("digraph")
+ .add(
+ new DotStatement<>("graph")
+ .addAttribute("rankdir", "LR")
+ .addAttribute("labeljust", "l")
+ .addAttribute("compound", true));
+
+ private final BindingGraph bindingGraph;
+ private final Map<Node, UUID> nodeIds = new HashMap<>();
+
+ NodesGraph(BindingGraph bindingGraph) {
+ this.bindingGraph = bindingGraph;
+ }
+
+ DotGraph graph() {
+ if (nodeIds.isEmpty()) {
+ Iterator<String> colors = Iterators.cycle(COMPONENT_COLORS);
+ bindingGraph.network().nodes().stream()
+ .collect(groupingBy(Node::componentPath))
+ .forEach(
+ (component, networkNodes) -> {
+ DotGraph subgraph = subgraph(component);
+ subgraph.add(
+ new DotStatement<>("node")
+ .addAttribute("style", "filled")
+ .addAttribute("shape", "box")
+ .addAttribute("fillcolor", colors.next()));
+ subgraph.add(new DotStatement<>("graph").addAttribute("label", component));
+ for (Node node : networkNodes) {
+ subgraph.add(dotNode(node));
+ }
+ });
+ for (Edge edge : bindingGraph.network().edges()) {
+ dotEdge(edge).ifPresent(graph::add);
+ }
+ }
+ return graph;
+ }
+
+ DotGraph subgraph(ComponentPath component) {
+ DotGraph subgraph = new DotGraph("subgraph " + quote(clusterName(component)));
+ graph.add(subgraph);
+ return subgraph;
+ }
+
+ UUID nodeId(Node node) {
+ return nodeIds.computeIfAbsent(node, n -> randomUUID());
+ }
+
+ Optional<DotEdge> dotEdge(Edge edge) {
+ EndpointPair<Node> incidentNodes = bindingGraph.network().incidentNodes(edge);
+ DotEdge dotEdge = new DotEdge(nodeId(incidentNodes.source()), nodeId(incidentNodes.target()));
+ if (edge instanceof DependencyEdge) {
+ if (((DependencyEdge) edge).isEntryPoint()) {
+ return Optional.empty();
+ }
+ } else if (edge instanceof ChildFactoryMethodEdge) {
+ dotEdge.addAttribute("style", "dashed");
+ dotEdge.addAttribute("lhead", clusterName(incidentNodes.target().componentPath()));
+ dotEdge.addAttribute("ltail", clusterName(incidentNodes.source().componentPath()));
+ dotEdge.addAttribute("taillabel", ((ChildFactoryMethodEdge) edge).factoryMethod());
+ } else if (edge instanceof SubcomponentCreatorBindingEdge) {
+ dotEdge.addAttribute("style", "dashed");
+ dotEdge.addAttribute("lhead", clusterName(incidentNodes.target().componentPath()));
+ dotEdge.addAttribute("taillabel", "subcomponent");
+ }
+ return Optional.of(dotEdge);
+ }
+
+ DotNode dotNode(Node node) {
+ DotNode dotNode = new DotNode(nodeId(node));
+ if (node instanceof MaybeBinding) {
+ dotNode.addAttribute("tooltip", "");
+ if (bindingGraph.entryPointBindings().contains(node)) {
+ dotNode.addAttribute("penwidth", 3);
+ }
+ if (node instanceof Binding) {
+ dotNode.addAttribute("label", label((Binding) node));
+ }
+ if (node instanceof MissingBinding) {
+ dotNode.addAttributeFormat(
+ "label", "missing binding for %s", ((MissingBinding) node).key());
+ }
+ } else {
+ dotNode.addAttribute("style", "invis").addAttribute("shape", "point");
+ }
+ return dotNode;
+ }
+
+ private String label(Binding binding) {
+ if (binding.kind().equals(BindingKind.MEMBERS_INJECTION)) {
+ return String.format("inject(%s)", binding.key());
+ } else if (binding.isProduction()) {
+ return String.format("@Produces %s", binding.key());
+ } else {
+ return binding.key().toString();
+ }
+ }
+
+ private static String clusterName(ComponentPath owningComponentPath) {
+ return "cluster" + owningComponentPath;
+ }
+ }
+}
diff --git a/java/dagger/grpc/server/BUILD b/java/dagger/grpc/server/BUILD
new file mode 100644
index 0000000..1c57807
--- /dev/null
+++ b/java/dagger/grpc/server/BUILD
@@ -0,0 +1,77 @@
+# A framework supporting Dagger-injected gRPC servers.
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
+load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+
+ANNOTATIONS_SRCS = [
+ "CallScoped.java",
+ "ForGrpcService.java",
+ "GrpcService.java",
+]
+
+java_library(
+ name = "annotations",
+ srcs = ANNOTATIONS_SRCS,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ tags = ["maven_coordinates=com.google.dagger:dagger-grpc-server-annotations:" + POM_VERSION],
+ deps = [
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+# TODO(dpb): Split out the grpc:inprocess and grpc:netty deps into separate libraries.
+java_library(
+ name = "server",
+ srcs = glob(
+ ["*.java"],
+ exclude = ANNOTATIONS_SRCS,
+ ),
+ exported_plugins = ["//java/dagger/grpc/server/processor:plugin"],
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ tags = ["maven_coordinates=com.google.dagger:dagger-grpc-server:" + POM_VERSION],
+ exports = [":annotations"],
+ deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/grpc:context",
+ "@google_bazel_common//third_party/java/grpc:core",
+ "@google_bazel_common//third_party/java/grpc:netty",
+ "@google_bazel_common//third_party/java/grpc:protobuf",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@google_bazel_common//third_party/java/protobuf",
+ ],
+)
+
+pom_file(
+ name = "annotations-pom",
+ artifact_id = "dagger-grpc-server-annotations",
+ artifact_name = "Dagger gRPC Server Annotations",
+ targets = [":annotations"],
+)
+
+pom_file(
+ name = "server-pom",
+ artifact_id = "dagger-grpc-server",
+ artifact_name = "Dagger gRPC Server",
+ targets = [":server"],
+)
+
+filegroup(
+ name = "javadoc-srcs",
+ srcs = glob(["*.java"]),
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "javadoc",
+ srcs = [":javadoc-srcs"],
+ root_packages = ["dagger.grpc.server"],
+ deps = [
+ ":annotations",
+ ":server",
+ ],
+)
diff --git a/java/dagger/grpc/server/CallScoped.java b/java/dagger/grpc/server/CallScoped.java
new file mode 100644
index 0000000..4b9d14f
--- /dev/null
+++ b/java/dagger/grpc/server/CallScoped.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/** A scope that lasts as long as a single gRPC {@link io.grpc.ServerCall}. */
+@Retention(RUNTIME)
+@Scope
+@Documented
+public @interface CallScoped {}
diff --git a/java/dagger/grpc/server/CurrentContextModule.java b/java/dagger/grpc/server/CurrentContextModule.java
new file mode 100644
index 0000000..c117537
--- /dev/null
+++ b/java/dagger/grpc/server/CurrentContextModule.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import dagger.Module;
+import dagger.Provides;
+import io.grpc.Context;
+
+/**
+ * Provides the current {@link Context}.
+ */
+@Module
+public final class CurrentContextModule {
+
+ @Provides
+ static Context currentContext() {
+ return Context.current();
+ }
+}
diff --git a/java/dagger/grpc/server/ForGrpcService.java b/java/dagger/grpc/server/ForGrpcService.java
new file mode 100644
index 0000000..33a83ad
--- /dev/null
+++ b/java/dagger/grpc/server/ForGrpcService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import java.lang.annotation.Documented;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifies some per-service types provided by {@link dagger.Module}s generated by {@link
+ * GrpcService}.
+ */
+@Documented
+@Qualifier
+public @interface ForGrpcService {
+
+ /** The gRPC service class. */
+ Class<?> value();
+}
diff --git a/java/dagger/grpc/server/GrpcCallMetadataModule.java b/java/dagger/grpc/server/GrpcCallMetadataModule.java
new file mode 100644
index 0000000..8d474d9
--- /dev/null
+++ b/java/dagger/grpc/server/GrpcCallMetadataModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import dagger.Module;
+import dagger.Provides;
+import io.grpc.Metadata;
+
+/**
+ * Provides {@link Metadata} about a gRPC call.
+ */
+@Module
+public final class GrpcCallMetadataModule {
+ private final Metadata metadata;
+
+ public GrpcCallMetadataModule(Metadata metadata) {
+ this.metadata = checkNotNull(metadata);
+ }
+
+ @Provides
+ Metadata provideHeaders() {
+ return metadata;
+ }
+}
diff --git a/java/dagger/grpc/server/GrpcService.java b/java/dagger/grpc/server/GrpcService.java
new file mode 100644
index 0000000..a746195
--- /dev/null
+++ b/java/dagger/grpc/server/GrpcService.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a class that implements a gRPC service.
+ *
+ * <p>Generates several types when annotating a class {@code Foo}:
+ *
+ * <ul>
+ * <li>Interfaces {@code FooComponent} and {@code FooComponent.Factory}.
+ * <li>{@linkplain dagger.Module Modules} {@code FooGrpcProxyModule} and {@code
+ * FooGrpcServiceModule}.
+ * </ul>
+ *
+ * <p>To use these types to configure a server:
+ *
+ * <ol>
+ * <li>Create a {@linkplain dagger.Subcomponent subcomponent} that implements {@code FooComponent}
+ * and installs {@code FooGrpcServiceModule}.
+ * <li>Install {@link NettyServerModule} or another {@link ServerModule} subclass and {@code
+ * FooGrpcProxyModule} into your {@link javax.inject.Singleton @Singleton} {@linkplain
+ * dagger.Component component}.
+ * <li>Bind an implementation of {@code FooComponent.Factory} in your {@link
+ * javax.inject.Singleton @Singleton} {@linkplain dagger.Component component}. The
+ * implementation will typically inject the {@link javax.inject.Singleton @Singleton}
+ * {@linkplain dagger.Component component} and call subcomponent factory methods to instantiate
+ * the correct subcomponent.
+ * </ol>
+ */
+@Documented
+@Target(ElementType.TYPE)
+public @interface GrpcService {
+ /** The class that gRPC generates from the proto service definition. */
+ Class<?> grpcClass();
+}
diff --git a/java/dagger/grpc/server/InProcessServerModule.java b/java/dagger/grpc/server/InProcessServerModule.java
new file mode 100644
index 0000000..fd93382
--- /dev/null
+++ b/java/dagger/grpc/server/InProcessServerModule.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import dagger.Module;
+import dagger.Provides;
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import javax.inject.Singleton;
+
+/**
+ * Installing this module into a {@link Singleton @Singleton} component means the component can
+ * provide a {@link Server} that serves {@linkplain InProcessServerBuilder in-process} requests.
+ */
+@Module(includes = ServerModule.class)
+public final class InProcessServerModule {
+
+ private final String name;
+
+ private InProcessServerModule(String name) {
+ this.name = checkNotNull(name);
+ }
+
+ /**
+ * Creates a module that provides a server that binds to a given name
+ *
+ * @param name the identity of the server for clients to connect to
+ */
+ public static InProcessServerModule serverNamed(String name) {
+ return new InProcessServerModule(name);
+ }
+
+ @Provides
+ ServerBuilder<?> serverBuilder() {
+ return InProcessServerBuilder.forName(name);
+ }
+}
diff --git a/java/dagger/grpc/server/NettyServerModule.java b/java/dagger/grpc/server/NettyServerModule.java
new file mode 100644
index 0000000..4361d62
--- /dev/null
+++ b/java/dagger/grpc/server/NettyServerModule.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import dagger.Module;
+import dagger.Provides;
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.netty.NettyServerBuilder;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import javax.inject.Singleton;
+
+/**
+ * Installing this module into a {@link Singleton @Singleton} component means the component can
+ * provide a {@linkplain NettyServerBuilder Netty}-based {@link Server}.
+ */
+@Module(includes = ServerModule.class)
+public final class NettyServerModule {
+
+ private final SocketAddress socketAddress;
+
+ private NettyServerModule(SocketAddress socketAddress) {
+ this.socketAddress = socketAddress;
+ }
+
+ /**
+ * A module that binds to {@code port} on the wildcard address.
+ */
+ public static NettyServerModule bindingToPort(int port) {
+ return new NettyServerModule(new InetSocketAddress(port));
+ }
+
+ /**
+ * A module that binds to {@code socketAddress}.
+ */
+ public static NettyServerModule bindingTo(SocketAddress socketAddress) {
+ return new NettyServerModule(socketAddress);
+ }
+
+ @Provides
+ ServerBuilder<?> serverBuilder() {
+ return NettyServerBuilder.forAddress(socketAddress);
+ }
+}
diff --git a/java/dagger/grpc/server/ProxyServerCallHandler.java b/java/dagger/grpc/server/ProxyServerCallHandler.java
new file mode 100644
index 0000000..751d8ca
--- /dev/null
+++ b/java/dagger/grpc/server/ProxyServerCallHandler.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+import io.grpc.MethodDescriptor.Marshaller;
+import io.grpc.ServerCall;
+import io.grpc.ServerCall.Listener;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerMethodDefinition;
+import io.grpc.ServerServiceDefinition;
+import io.grpc.Status;
+import java.io.InputStream;
+
+/**
+ * A {@link ServerCallHandler} that handles calls for a particular method by delegating to a handler
+ * in a {@link ServerServiceDefinition} returned by a factory.
+ *
+ * @param <RequestT> the type of the request payloads
+ * @param <ResponseT> the type of the response payloads
+ */
+public final class ProxyServerCallHandler<RequestT, ResponseT>
+ implements ServerCallHandler<InputStream, InputStream> {
+
+ /**
+ * A factory for the {@link ServerServiceDefinition} that a {@link ProxyServerCallHandler}
+ * delegates to.
+ */
+ public interface ServiceDefinitionFactory {
+ /**
+ * Returns a service definition that contains a {@link ServerCallHandler} for the
+ * {@link ProxyServerCallHandler}'s method.
+ */
+ ServerServiceDefinition getServiceDefinition(Metadata headers);
+ }
+
+ private final MethodDescriptor<RequestT, ResponseT> delegateMethodDescriptor;
+ private final ServiceDefinitionFactory delegateServiceDefinitionFactory;
+
+ /**
+ * Returns a proxy method definition for {@code methodDescriptor}.
+ *
+ * @param delegateServiceDefinitionFactory factory for the delegate service definition
+ */
+ public static <RequestT, ResponseT> ServerMethodDefinition<InputStream, InputStream> proxyMethod(
+ MethodDescriptor<RequestT, ResponseT> delegateMethodDescriptor,
+ ServiceDefinitionFactory delegateServiceDefinitionFactory) {
+ return ServerMethodDefinition.create(
+ MethodDescriptor.create(
+ delegateMethodDescriptor.getType(),
+ delegateMethodDescriptor.getFullMethodName(),
+ IDENTITY_MARSHALLER,
+ IDENTITY_MARSHALLER),
+ new ProxyServerCallHandler<>(delegateMethodDescriptor, delegateServiceDefinitionFactory));
+ }
+
+ ProxyServerCallHandler(
+ MethodDescriptor<RequestT, ResponseT> delegateMethodDescriptor,
+ ServiceDefinitionFactory delegateServiceDefinitionFactory) {
+ this.delegateMethodDescriptor = delegateMethodDescriptor;
+ this.delegateServiceDefinitionFactory = delegateServiceDefinitionFactory;
+ }
+
+ @Override
+ public Listener<InputStream> startCall(
+ ServerCall<InputStream, InputStream> call,
+ Metadata headers) {
+ ServerMethodDefinition<RequestT, ResponseT> delegateMethod = getMethodDefinition(headers);
+ Listener<RequestT> delegateListener =
+ delegateMethod
+ .getServerCallHandler()
+ .startCall(new ServerCallAdapter(call, delegateMethod.getMethodDescriptor()), headers);
+ return new ServerCallListenerAdapter(delegateListener);
+ }
+
+ @SuppressWarnings("unchecked") // Method definition is the correct type.
+ private ServerMethodDefinition<RequestT, ResponseT> getMethodDefinition(Metadata headers) {
+ String fullMethodName = delegateMethodDescriptor.getFullMethodName();
+ for (ServerMethodDefinition<?, ?> methodDefinition :
+ delegateServiceDefinitionFactory.getServiceDefinition(headers).getMethods()) {
+ if (methodDefinition.getMethodDescriptor().getFullMethodName().equals(fullMethodName)) {
+ return (ServerMethodDefinition<RequestT, ResponseT>) methodDefinition;
+ }
+ }
+ throw new IllegalStateException("Could not find " + fullMethodName);
+ }
+
+ private static final Marshaller<InputStream> IDENTITY_MARSHALLER =
+ new Marshaller<InputStream>() {
+ @Override
+ public InputStream stream(InputStream value) {
+ return value;
+ }
+
+ @Override
+ public InputStream parse(InputStream stream) {
+ return stream;
+ }
+ };
+
+ /** A {@link Listener} that adapts {@code Listener<RequestT>} to {@code Listener<InputStream>}. */
+ private final class ServerCallListenerAdapter extends Listener<InputStream> {
+
+ private final Listener<RequestT> delegate;
+
+ public ServerCallListenerAdapter(Listener<RequestT> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void onMessage(InputStream message) {
+ delegate.onMessage(delegateMethodDescriptor.parseRequest(message));
+ }
+
+ @Override
+ public void onHalfClose() {
+ delegate.onHalfClose();
+ }
+
+ @Override
+ public void onCancel() {
+ delegate.onCancel();
+ }
+
+ @Override
+ public void onComplete() {
+ delegate.onComplete();
+ }
+ }
+
+ /**
+ * A {@link ServerCall} that adapts {@code ServerCall<InputStream>} to {@code
+ * ServerCall<ResponseT>}.
+ */
+ final class ServerCallAdapter extends ServerCall<RequestT, ResponseT> {
+
+ private final ServerCall<InputStream, InputStream> delegate;
+ private final MethodDescriptor<RequestT, ResponseT> method;
+
+ ServerCallAdapter(ServerCall<InputStream, InputStream> delegate,
+ MethodDescriptor<RequestT, ResponseT> method) {
+ this.delegate = delegate;
+ this.method = method;
+ }
+
+ @Override
+ public MethodDescriptor<RequestT, ResponseT> getMethodDescriptor() {
+ return method;
+ }
+
+ @Override
+ public void request(int numMessages) {
+ delegate.request(numMessages);
+ }
+
+ @Override
+ public void sendHeaders(Metadata headers) {
+ delegate.sendHeaders(headers);
+ }
+
+ @Override
+ public void sendMessage(ResponseT message) {
+ delegate.sendMessage(delegateMethodDescriptor.streamResponse(message));
+ }
+
+ @Override
+ public void close(Status status, Metadata trailers) {
+ delegate.close(status, trailers);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return delegate.isCancelled();
+ }
+ }
+}
diff --git a/java/dagger/grpc/server/README.md b/java/dagger/grpc/server/README.md
new file mode 100644
index 0000000..5fe8d5c
--- /dev/null
+++ b/java/dagger/grpc/server/README.md
@@ -0,0 +1,10 @@
+# Dagger-gRPC on the Server
+
+This package contains the public types used to create gRPC server applications
+using https://dagger.dev.
+
+It is maintained by the Dagger team.
+
+It is in development, and is planned for open-source release as part of Dagger.
+
+See user documentation at https://dagger.dev/grpc.
diff --git a/java/dagger/grpc/server/ServerModule.java b/java/dagger/grpc/server/ServerModule.java
new file mode 100644
index 0000000..f03f997
--- /dev/null
+++ b/java/dagger/grpc/server/ServerModule.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server;
+
+import dagger.Module;
+import dagger.Provides;
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.ServerServiceDefinition;
+import java.util.Set;
+import javax.inject.Singleton;
+
+/**
+ * Provides a {@link Singleton @Singleton} {@link Server}.
+ */
+@Module
+public final class ServerModule {
+
+ @Provides
+ @Singleton
+ static Server provideServer(
+ ServerBuilder<?> serverBuilder, Set<ServerServiceDefinition> serviceDefinitions) {
+ for (ServerServiceDefinition serverServiceDefinition : serviceDefinitions) {
+ serverBuilder.addService(serverServiceDefinition);
+ }
+ return serverBuilder.build();
+ }
+}
diff --git a/java/dagger/grpc/server/processor/BUILD b/java/dagger/grpc/server/processor/BUILD
new file mode 100644
index 0000000..ce02b06
--- /dev/null
+++ b/java/dagger/grpc/server/processor/BUILD
@@ -0,0 +1,49 @@
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+
+java_library(
+ name = "processor",
+ srcs = glob(["*.java"]),
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ tags = ["maven_coordinates=com.google.dagger:dagger-grpc-server-processor:" + POM_VERSION],
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/grpc/server:annotations",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/google_java_format",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ ],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-grpc-server-processor",
+ artifact_name = "Dagger gRPC Server Processor",
+ targets = [":processor"],
+)
+
+java_plugin(
+ name = "plugin",
+ generates_api = 1,
+ processor_class = "dagger.grpc.server.processor.GrpcServiceProcessor",
+ deps = [":processor"],
+)
+
+filegroup(
+ name = "javadoc-srcs",
+ srcs = glob(["*.java"]),
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "javadoc",
+ srcs = [":javadoc-srcs"],
+ root_packages = ["dagger.grpc.server.processor"],
+ deps = [":processor"],
+)
diff --git a/java/dagger/grpc/server/processor/GrpcServiceModel.java b/java/dagger/grpc/server/processor/GrpcServiceModel.java
new file mode 100644
index 0000000..65d6903
--- /dev/null
+++ b/java/dagger/grpc/server/processor/GrpcServiceModel.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server.processor;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec;
+import static com.google.auto.common.MoreElements.getAnnotationMirror;
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Joiner;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import dagger.grpc.server.ForGrpcService;
+import dagger.grpc.server.GrpcService;
+import dagger.grpc.server.processor.SourceGenerator.IoGrpc;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.SimpleAnnotationValueVisitor7;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic.Kind;
+
+class GrpcServiceModel {
+
+ private static final String GRPC_SERVICE_PARAMETER_NAME = "grpcClass";
+
+ private final Types types;
+ private final Elements elements;
+ private final SourceVersion sourceVersion;
+ private final Messager messager;
+ final TypeElement serviceImplementation;
+ final ClassName serviceImplementationClassName;
+ final ClassName serviceDefinitionTypeName;
+ final ClassName proxyModuleName;
+ final ClassName serviceDefinitionTypeFactoryName;
+ final ClassName serviceModuleName;
+ final ClassName unscopedServiceModuleName;
+
+ GrpcServiceModel(ProcessingEnvironment processingEnv, TypeElement serviceImplementation) {
+ this.types = processingEnv.getTypeUtils();
+ this.elements = processingEnv.getElementUtils();
+ this.sourceVersion = processingEnv.getSourceVersion();
+ this.messager = processingEnv.getMessager();
+ this.serviceImplementation = serviceImplementation;
+ this.serviceImplementationClassName = ClassName.get(serviceImplementation);
+ this.serviceDefinitionTypeName = peerClassWithSuffix("ServiceDefinition");
+ this.serviceDefinitionTypeFactoryName = serviceDefinitionTypeName.nestedClass("Factory");
+ this.proxyModuleName = peerClassWithSuffix("GrpcProxyModule");
+ this.serviceModuleName = peerClassWithSuffix("GrpcServiceModule");
+ this.unscopedServiceModuleName = peerClassWithSuffix("UnscopedGrpcServiceModule");
+ }
+
+ /**
+ * Returns the name of a top-level class in the same package as the service implementation
+ * class, whose name is the simple name of the service implementation class and its enclosing
+ * classes, joined with underscores, and appended with {@code suffix}.
+ */
+ private ClassName peerClassWithSuffix(String suffix) {
+ return serviceImplementationClassName.peerClass(
+ Joiner.on('_').join(serviceImplementationClassName.simpleNames()) + suffix);
+ }
+
+ String packageName() {
+ return serviceImplementationClassName.packageName();
+ }
+
+ public boolean validate() {
+ AnnotationValue argument =
+ getAnnotationValue(grpcServiceAnnotation(), GRPC_SERVICE_PARAMETER_NAME);
+ return argument.accept(
+ new SimpleAnnotationValueVisitor7<Boolean, AnnotationValue>(false) {
+ @Override
+ public Boolean visitType(TypeMirror type, AnnotationValue value) {
+ return validateGrpcClass(type, value);
+ }
+ },
+ argument);
+ }
+
+ private AnnotationMirror grpcServiceAnnotation() {
+ return getAnnotationMirror(serviceImplementation, GrpcService.class).get();
+ }
+
+ /** Returns the gRPC service class declared by {@link GrpcService#grpcClass()}. */
+ protected final TypeElement grpcClass() {
+ AnnotationValue argument =
+ getAnnotationValue(grpcServiceAnnotation(), GRPC_SERVICE_PARAMETER_NAME);
+ return GET_TYPE_ELEMENT_FROM_VALUE.visit(argument, argument);
+ }
+
+ /**
+ * Returns the annotation spec for the {@code @Generated} annotation to add to any
+ * type generated by this processor.
+ */
+ protected final Optional<AnnotationSpec> generatedAnnotation() {
+ return generatedAnnotationSpec(
+ elements,
+ sourceVersion,
+ GrpcService.class,
+ String.format(
+ "@%s annotation on %s",
+ GrpcService.class.getCanonicalName(), serviceImplementationClassName));
+ }
+
+ /**
+ * Returns the annotation spec for a {@link ForGrpcService} annotation whose value is the
+ * gRPC-generated service class.
+ */
+ protected final AnnotationSpec forGrpcService() {
+ return AnnotationSpec.builder(ForGrpcService.class)
+ .addMember("value", "$T.class", grpcClass())
+ .build();
+ }
+
+ protected final String subcomponentServiceDefinitionMethodName() {
+ return UPPER_CAMEL.to(LOWER_CAMEL, simpleServiceName()) + "ServiceDefinition";
+ }
+
+ private String simpleServiceName() {
+ return grpcClass().getSimpleName().toString().replaceFirst("Grpc$", "");
+ }
+
+ private TypeElement serviceImplBase(TypeMirror service) {
+ ClassName serviceClassName = ClassName.get(MoreTypes.asTypeElement(service));
+ ClassName serviceImplBaseName = serviceClassName.nestedClass(simpleServiceName() + "ImplBase");
+ return elements.getTypeElement(serviceImplBaseName.toString());
+ }
+
+ private boolean validateGrpcClass(TypeMirror type, AnnotationValue value) {
+ TypeElement serviceImplBase = serviceImplBase(type);
+ if (serviceImplBase == null || !types.isSubtype(serviceImplBase.asType(), bindableService())) {
+ messager.printMessage(
+ Kind.ERROR,
+ String.format("%s is not a gRPC service class", type),
+ serviceImplementation,
+ grpcServiceAnnotation(),
+ value);
+ return false;
+ }
+ if (!(types.isSubtype(serviceImplementation.asType(), serviceImplBase.asType()))) {
+ messager.printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s must extend %s", serviceImplementation, serviceImplBase.getQualifiedName()),
+ serviceImplementation,
+ grpcServiceAnnotation(),
+ value);
+ return false;
+ }
+ return true;
+ }
+
+ private TypeMirror bindableService() {
+ return elements.getTypeElement(IoGrpc.BINDABLE_SERVICE.toString()).asType();
+ }
+
+ static final AnnotationValueVisitor<TypeElement, AnnotationValue> GET_TYPE_ELEMENT_FROM_VALUE =
+ new SimpleAnnotationValueVisitor7<TypeElement, AnnotationValue>() {
+ @Override
+ public TypeElement visitType(TypeMirror t, AnnotationValue p) {
+ return MoreTypes.asTypeElement(t);
+ }
+
+ @Override
+ protected TypeElement defaultAction(Object o, AnnotationValue p) {
+ throw new IllegalArgumentException("Expected " + p + " to be a class");
+ }
+ };
+}
diff --git a/java/dagger/grpc/server/processor/GrpcServiceModuleGenerator.java b/java/dagger/grpc/server/processor/GrpcServiceModuleGenerator.java
new file mode 100644
index 0000000..bbad143
--- /dev/null
+++ b/java/dagger/grpc/server/processor/GrpcServiceModuleGenerator.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server.processor;
+
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static com.squareup.javapoet.WildcardTypeName.subtypeOf;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.grpc.server.GrpcService;
+import java.util.List;
+
+/**
+ * An object that generates the non-proxying service definition module for a {@link
+ * GrpcService}-annotated service implementation.
+ */
+final class GrpcServiceModuleGenerator extends SourceGenerator {
+
+ private static final TypeName LIST_OF_INTERCEPTORS = ParameterizedTypeName.get(
+ ClassName.get(List.class), subtypeOf(IoGrpc.SERVER_INTERCEPTOR));
+
+ private final GrpcServiceModel grpcServiceModel;
+
+ GrpcServiceModuleGenerator(GrpcServiceModel grpcServiceModel) {
+ super(grpcServiceModel.packageName());
+ this.grpcServiceModel = grpcServiceModel;
+ }
+
+ @Override
+ protected TypeSpec createType() {
+ TypeSpec.Builder serviceModule =
+ classBuilder(grpcServiceModel.serviceModuleName)
+ .addJavadoc(
+ "Install this module in the {@link $T @Singleton} server component\n",
+ JavaxInject.singleton().type)
+ .addJavadoc(
+ "or in the subcomponent that implements {@link $T}.\n",
+ grpcServiceModel.serviceDefinitionTypeName);
+ grpcServiceModel.generatedAnnotation().ifPresent(serviceModule::addAnnotation);
+ return serviceModule
+ .addAnnotation(Dagger.module())
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(provideServiceDefinition())
+ .build();
+ }
+
+ /**
+ * Returns the {@link dagger.Provides @Provides} method for the {@link
+ * io.grpc.ServerServiceDefinition} for the service.
+ */
+ private MethodSpec provideServiceDefinition() {
+ return methodBuilder("serviceDefinition")
+ .addAnnotation(Dagger.provides())
+ .addAnnotation(grpcServiceModel.forGrpcService())
+ .addModifiers(STATIC)
+ .returns(IoGrpc.SERVER_SERVICE_DEFINITION)
+ .addParameter(grpcServiceModel.serviceImplementationClassName, "implementation")
+ .addParameter(
+ ParameterSpec.builder(LIST_OF_INTERCEPTORS, "interceptors")
+ .addAnnotation(grpcServiceModel.forGrpcService())
+ .build())
+ .addStatement(
+ "$T serviceDefinition = implementation.bindService()", IoGrpc.SERVER_SERVICE_DEFINITION)
+ .addStatement(
+ "return $T.intercept(serviceDefinition, interceptors)", IoGrpc.SERVER_INTERCEPTORS)
+ .build();
+ }
+}
diff --git a/java/dagger/grpc/server/processor/GrpcServiceProcessor.java b/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
new file mode 100644
index 0000000..e361fdb
--- /dev/null
+++ b/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server.processor;
+
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.BasicAnnotationProcessor;
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
+import com.google.googlejavaformat.java.filer.FormattingFiler;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import dagger.grpc.server.GrpcService;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * Generates code from types annotated with {@link GrpcService @GrpcService}.
+ *
+ * @see <a href="https://dagger.dev/grpc">https://dagger.dev/grpc</a>
+ */
+@AutoService(Processor.class)
+public class GrpcServiceProcessor extends BasicAnnotationProcessor implements ProcessingStep {
+
+ @Override
+ protected ImmutableList<GrpcServiceProcessor> initSteps() {
+ return ImmutableList.of(this);
+ }
+
+ @Override
+ public ImmutableSet<Class<GrpcService>> annotations() {
+ return ImmutableSet.of(GrpcService.class);
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public Set<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
+ for (TypeElement element : typesIn(elementsByAnnotation.get(GrpcService.class))) {
+ try {
+ GrpcServiceModel grpcServiceModel = new GrpcServiceModel(processingEnv, element);
+ if (grpcServiceModel.validate()) {
+ write(new ServiceDefinitionTypeGenerator(grpcServiceModel), element);
+ write(new ProxyModuleGenerator(grpcServiceModel), element);
+ write(new GrpcServiceModuleGenerator(grpcServiceModel), element);
+ write(new UnscopedGrpcServiceModuleGenerator(grpcServiceModel), element);
+ }
+ } catch (TypeNotPresentException e) {
+ deferredElements.add(element);
+ }
+ }
+ return deferredElements.build();
+ }
+
+ private void write(SourceGenerator grpcServiceTypeWriter, final TypeElement element) {
+ JavaFile javaFile = grpcServiceTypeWriter.javaFile();
+ ClassName outputClassName = ClassName.get(javaFile.packageName, javaFile.typeSpec.name);
+ try {
+ javaFile.writeTo(new FormattingFiler(processingEnv.getFiler()));
+ } catch (IOException e) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Kind.ERROR, String.format("Error writing %s: %s", outputClassName, e), element);
+ }
+ }
+}
diff --git a/java/dagger/grpc/server/processor/ProxyModuleGenerator.java b/java/dagger/grpc/server/processor/ProxyModuleGenerator.java
new file mode 100644
index 0000000..60aea8e
--- /dev/null
+++ b/java/dagger/grpc/server/processor/ProxyModuleGenerator.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server.processor;
+
+import static com.google.auto.common.MoreElements.hasModifiers;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.fieldsIn;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.grpc.server.GrpcService;
+import java.util.List;
+import java.util.function.Function;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * An object that generates the proxying service definition module for a {@link
+ * GrpcService}-annotated service implementation.
+ */
+final class ProxyModuleGenerator extends SourceGenerator {
+
+ private final GrpcServiceModel grpcServiceModel;
+
+ ProxyModuleGenerator(GrpcServiceModel grpcServiceModel) {
+ super(grpcServiceModel.packageName());
+ this.grpcServiceModel = grpcServiceModel;
+ }
+
+ @Override
+ protected TypeSpec createType() {
+ TypeSpec.Builder proxyModule =
+ classBuilder(grpcServiceModel.proxyModuleName)
+ .addModifiers(PUBLIC, FINAL)
+ .addJavadoc(
+ "Install this module in the {@link $T @Singleton} server component.\n",
+ JavaxInject.singleton().type);
+ grpcServiceModel.generatedAnnotation().ifPresent(proxyModule::addAnnotation);
+ return proxyModule
+ .addAnnotation(Dagger.module())
+ .addMethod(provideServiceDefinitionContribution())
+ .addMethod(provideServiceDefinitionFactory())
+ .build();
+ }
+
+ /**
+ * Returns the {@link dagger.Provides @Provides} method for the proxying {@link
+ * io.grpc.ServerServiceDefinition}.
+ */
+ private MethodSpec provideServiceDefinitionContribution() {
+ MethodSpec.Builder method =
+ methodBuilder("serviceDefinition")
+ .addAnnotation(Dagger.provides())
+ .addAnnotation(Dagger.intoSet())
+ .addAnnotation(JavaxInject.singleton())
+ .addModifiers(STATIC)
+ .returns(IoGrpc.SERVER_SERVICE_DEFINITION)
+ .addParameter(
+ ParameterSpec.builder(
+ Dagger.GrpcServer.SERVICE_DEFINITION_FACTORY, "serviceDefinitionFactory")
+ .addAnnotation(grpcServiceModel.forGrpcService())
+ .build())
+ .addCode(
+ "return $T.builder($T.SERVICE_NAME)",
+ IoGrpc.SERVER_SERVICE_DEFINITION,
+ grpcServiceModel.grpcClass());
+ for (CodeBlock methodDescriptor : methodDescriptors()) {
+ method.addCode(
+ ".addMethod($T.proxyMethod($L, serviceDefinitionFactory))",
+ Dagger.GrpcServer.PROXY_SERVER_CALL_HANDLER,
+ methodDescriptor);
+ }
+ method.addCode(".build();");
+ return method.build();
+ }
+
+ /**
+ * Returns the {@link io.grpc.MethodDescriptor} references from the class enclosing the service
+ * interface.
+ *
+ * <p>Looks first for public static methods (new in 1.8), and then for public static fields if it
+ * finds none.
+ */
+ private ImmutableList<CodeBlock> methodDescriptors() {
+ ImmutableList<CodeBlock> staticMethodCalls =
+ findMethodDescriptors(
+ methodsIn(grpcServiceModel.grpcClass().getEnclosedElements()),
+ ExecutableElement::getReturnType,
+ method ->
+ CodeBlock.of("$T.$N()", grpcServiceModel.grpcClass(), method.getSimpleName()));
+ if (!staticMethodCalls.isEmpty()) {
+ return staticMethodCalls;
+ }
+ return findMethodDescriptors(
+ fieldsIn(grpcServiceModel.grpcClass().getEnclosedElements()),
+ VariableElement::asType,
+ field -> CodeBlock.of("$T.$N", grpcServiceModel.grpcClass(), field.getSimpleName()));
+ }
+
+ private <E extends Element> ImmutableList<CodeBlock> findMethodDescriptors(
+ List<E> elements,
+ Function<? super E, TypeMirror> elementType,
+ Function<? super E, CodeBlock> elementReference) {
+ return elements
+ .stream()
+ .filter(hasModifiers(PUBLIC, STATIC)::apply)
+ .filter(
+ method -> {
+ TypeName typeName = TypeName.get(elementType.apply(method));
+ return typeName instanceof ParameterizedTypeName
+ && ((ParameterizedTypeName) typeName).rawType.equals(IoGrpc.METHOD_DESCRIPTOR);
+ })
+ .map(elementReference)
+ .collect(toImmutableList());
+ }
+
+ /**
+ * Returns the {@link dagger.Provides @Provides} method for the {@link
+ * dagger.grpc.server.ProxyServerCallHandler.ServiceDefinitionFactory} used by the proxy.
+ */
+ private MethodSpec provideServiceDefinitionFactory() {
+ return methodBuilder("serviceDefinitionFactory")
+ .addAnnotation(Dagger.provides())
+ .addAnnotation(grpcServiceModel.forGrpcService())
+ .addModifiers(STATIC)
+ .returns(Dagger.GrpcServer.SERVICE_DEFINITION_FACTORY)
+ .addParameter(grpcServiceModel.serviceDefinitionTypeFactoryName, "factory", FINAL)
+ .addStatement("return $L", anonymousServiceDefinitionFactory())
+ .build();
+ }
+
+ /**
+ * Returns the anonymous inner class that implements the {@link
+ * dagger.grpc.server.ProxyServerCallHandler.ServiceDefinitionFactory} used by the proxy.
+ */
+ private TypeSpec anonymousServiceDefinitionFactory() {
+ return anonymousClassBuilder("")
+ .addSuperinterface(Dagger.GrpcServer.SERVICE_DEFINITION_FACTORY)
+ .addMethod(
+ methodBuilder("getServiceDefinition")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(IoGrpc.SERVER_SERVICE_DEFINITION)
+ .addParameter(IoGrpc.METADATA, "headers")
+ .addStatement(
+ "return factory.grpcService(new $T(headers)).$N()",
+ Dagger.GrpcServer.GRPC_CALL_METADATA_MODULE,
+ grpcServiceModel.subcomponentServiceDefinitionMethodName())
+ .build())
+ .build();
+ }
+}
diff --git a/java/dagger/grpc/server/processor/ServiceDefinitionTypeGenerator.java b/java/dagger/grpc/server/processor/ServiceDefinitionTypeGenerator.java
new file mode 100644
index 0000000..15e13fb
--- /dev/null
+++ b/java/dagger/grpc/server/processor/ServiceDefinitionTypeGenerator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server.processor;
+
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.interfaceBuilder;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.squareup.javapoet.TypeSpec;
+import dagger.grpc.server.GrpcService;
+
+/**
+ * An object that generates the component supertype interface for a {@link GrpcService}-annotated
+ * service implementation.
+ */
+final class ServiceDefinitionTypeGenerator extends SourceGenerator {
+
+ private final GrpcServiceModel grpcServiceModel;
+
+ ServiceDefinitionTypeGenerator(GrpcServiceModel grpcServiceModel) {
+ super(grpcServiceModel.packageName());
+ this.grpcServiceModel = grpcServiceModel;
+ }
+
+ @Override
+ protected TypeSpec createType() {
+ TypeSpec.Builder type =
+ interfaceBuilder(grpcServiceModel.serviceDefinitionTypeName.simpleName())
+ .addJavadoc("A component must implement this interface.\n")
+ .addModifiers(PUBLIC);
+ grpcServiceModel.generatedAnnotation().ifPresent(type::addAnnotation);
+ type.addType(
+ interfaceBuilder(grpcServiceModel.serviceDefinitionTypeFactoryName.simpleName())
+ .addModifiers(PUBLIC, STATIC)
+ .addMethod(
+ methodBuilder("grpcService")
+ .addModifiers(PUBLIC, ABSTRACT)
+ .returns(grpcServiceModel.serviceDefinitionTypeName)
+ .addParameter(
+ Dagger.GrpcServer.GRPC_CALL_METADATA_MODULE, "grpcCallMetadataModule")
+ .build())
+ .build());
+ type.addMethod(
+ methodBuilder(grpcServiceModel.subcomponentServiceDefinitionMethodName())
+ .addModifiers(PUBLIC, ABSTRACT)
+ .returns(IoGrpc.SERVER_SERVICE_DEFINITION)
+ .addAnnotation(grpcServiceModel.forGrpcService())
+ .build());
+ return type.build();
+ }
+}
diff --git a/java/dagger/grpc/server/processor/SourceGenerator.java b/java/dagger/grpc/server/processor/SourceGenerator.java
new file mode 100644
index 0000000..806d6e6
--- /dev/null
+++ b/java/dagger/grpc/server/processor/SourceGenerator.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server.processor;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+
+/**
+ * An object that generates one top-level type.
+ */
+abstract class SourceGenerator {
+
+ private final String packageName;
+
+ protected SourceGenerator(String packageName) {
+ this.packageName = packageName;
+ }
+
+ public JavaFile javaFile() {
+ return JavaFile.builder(packageName, createType()).build();
+ }
+
+ /**
+ * Creates the type to write.
+ */
+ protected abstract TypeSpec createType();
+
+ /** Class names and annotation specs for types in the {@link dagger} package. */
+ protected static final class Dagger {
+ private Dagger() {}
+
+ static AnnotationSpec binds() {
+ return AnnotationSpec.builder(ClassName.get("dagger", "Binds")).build();
+ }
+
+ static AnnotationSpec intoSet() {
+ return AnnotationSpec.builder(ClassName.get("dagger.multibindings", "IntoSet")).build();
+ }
+
+ static AnnotationSpec provides() {
+ return AnnotationSpec.builder(ClassName.get("dagger", "Provides")).build();
+ }
+
+ /** A {@code @dagger.Module} annotation that includes the given module classes. */
+ static AnnotationSpec module(ClassName... includedModules) {
+ AnnotationSpec.Builder module = AnnotationSpec.builder(ClassName.get("dagger", "Module"));
+ for (ClassName includedModule : includedModules) {
+ module.addMember("includes", "$T.class", includedModule);
+ }
+ return module.build();
+ }
+
+ /** Class names and annotation specs for types in the {@link dagger.grpc} package. */
+ protected static final class GrpcServer {
+ private GrpcServer() {}
+
+ static final ClassName PROXY_SERVER_CALL_HANDLER =
+ ClassName.get("dagger.grpc.server", "ProxyServerCallHandler");
+
+ static final ClassName GRPC_CALL_METADATA_MODULE =
+ ClassName.get("dagger.grpc.server", "GrpcCallMetadataModule");
+
+ static final ClassName SERVICE_DEFINITION_FACTORY =
+ PROXY_SERVER_CALL_HANDLER.nestedClass("ServiceDefinitionFactory");
+ }
+ }
+
+ /** Class names and annotation specs for types in the {@link io.grpc} package. */
+ protected static final class IoGrpc {
+ private IoGrpc() {}
+
+ static final ClassName BINDABLE_SERVICE = ClassName.get("io.grpc", "BindableService");
+ static final ClassName METADATA = ClassName.get("io.grpc", "Metadata");
+ static final ClassName METHOD_DESCRIPTOR = ClassName.get("io.grpc", "MethodDescriptor");
+ static final ClassName SERVER_INTERCEPTOR =
+ ClassName.get("io.grpc", "ServerInterceptor");
+ static final ClassName SERVER_INTERCEPTORS =
+ ClassName.get("io.grpc", "ServerInterceptors");
+ static final ClassName SERVER_SERVICE_DEFINITION =
+ ClassName.get("io.grpc", "ServerServiceDefinition");
+ }
+
+ /** Class names and annotation specs for types in the {@link javax.inject} package. */
+ protected static final class JavaxInject {
+ private JavaxInject() {}
+
+ static AnnotationSpec inject() {
+ return AnnotationSpec.builder(ClassName.get("javax.inject", "Inject")).build();
+ }
+
+ static AnnotationSpec singleton() {
+ return AnnotationSpec.builder(ClassName.get("javax.inject", "Singleton")).build();
+ }
+ }
+}
diff --git a/java/dagger/grpc/server/processor/UnscopedGrpcServiceModuleGenerator.java b/java/dagger/grpc/server/processor/UnscopedGrpcServiceModuleGenerator.java
new file mode 100644
index 0000000..339fb0f
--- /dev/null
+++ b/java/dagger/grpc/server/processor/UnscopedGrpcServiceModuleGenerator.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.server.processor;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.grpc.server.GrpcService;
+
+/**
+ * An object that generates the unscoped-proxying service definition module for a {@link
+ * GrpcService}-annotated service implementation.
+ */
+final class UnscopedGrpcServiceModuleGenerator extends SourceGenerator {
+
+ private final GrpcServiceModel grpcServiceModel;
+
+ UnscopedGrpcServiceModuleGenerator(GrpcServiceModel grpcServiceModel) {
+ super(grpcServiceModel.packageName());
+ this.grpcServiceModel = grpcServiceModel;
+ }
+
+ @Override
+ protected TypeSpec createType() {
+ ClassName unscopedComponentFactory =
+ grpcServiceModel.unscopedServiceModuleName.nestedClass(
+ grpcServiceModel.serviceImplementationClassName.simpleName() + "ComponentFactory");
+ TypeSpec.Builder unscopedServiceModule =
+ classBuilder(grpcServiceModel.unscopedServiceModuleName)
+ .addJavadoc(
+ "Install this module in the {@link $T @Singleton} server component\n",
+ JavaxInject.singleton().type)
+ .addJavadoc(
+ "if it implements {@link $T}.\n", grpcServiceModel.serviceDefinitionTypeName);
+ grpcServiceModel.generatedAnnotation().ifPresent(unscopedServiceModule::addAnnotation);
+ return unscopedServiceModule
+ .addAnnotation(
+ Dagger.module(grpcServiceModel.proxyModuleName, grpcServiceModel.serviceModuleName))
+ .addModifiers(PUBLIC, ABSTRACT)
+ .addType(unscopedComponentFactory(unscopedComponentFactory.simpleName()))
+ .addMethod(bindSubcomponentFactory(unscopedComponentFactory))
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
+ .build();
+ }
+
+ /**
+ * Returns the class that implements the component factory type by returning the singleton
+ * component itself.
+ */
+ private TypeSpec unscopedComponentFactory(String simpleName) {
+ return TypeSpec.classBuilder(simpleName)
+ .addModifiers(STATIC, FINAL)
+ .addSuperinterface(grpcServiceModel.serviceDefinitionTypeFactoryName)
+ .addField(grpcServiceModel.serviceDefinitionTypeName, "component", PRIVATE, FINAL)
+ .addMethod(
+ MethodSpec.constructorBuilder()
+ .addAnnotation(JavaxInject.inject())
+ .addParameter(grpcServiceModel.serviceDefinitionTypeName, "component")
+ .addStatement("this.component = component")
+ .build())
+ .addMethod(
+ MethodSpec.methodBuilder("grpcService")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(grpcServiceModel.serviceDefinitionTypeName)
+ .addParameter(Dagger.GrpcServer.GRPC_CALL_METADATA_MODULE, "grpcCallMetadataModule")
+ .addStatement("return component")
+ .build())
+ .build();
+ }
+
+ /**
+ * Returns the {@link dagger.Binds @Binds} method that binds the component factory type to the
+ * {@linkplain #unscopedComponentFactory(String) unscoped component factory implementation class}.
+ */
+ private MethodSpec bindSubcomponentFactory(ClassName unscopedComponentFactory) {
+ return MethodSpec.methodBuilder(
+ UPPER_CAMEL.to(
+ LOWER_CAMEL, grpcServiceModel.serviceDefinitionTypeFactoryName.simpleName()))
+ .addAnnotation(Dagger.binds())
+ .addModifiers(ABSTRACT)
+ .returns(grpcServiceModel.serviceDefinitionTypeFactoryName)
+ .addParameter(unscopedComponentFactory, "factory")
+ .build();
+ }
+}
diff --git a/java/dagger/internal/AbstractMapFactory.java b/java/dagger/internal/AbstractMapFactory.java
new file mode 100644
index 0000000..1cf83fa
--- /dev/null
+++ b/java/dagger/internal/AbstractMapFactory.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+import static dagger.internal.Preconditions.checkNotNull;
+import static java.util.Collections.unmodifiableMap;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.inject.Provider;
+
+/**
+ * An {@code abstract} {@link Factory} implementation used to implement {@link Map} bindings.
+ *
+ * @param <K> the key type of the map that this provides
+ * @param <V> the type that each contributing factory
+ * @param <V2> the value type of the map that this provides
+ */
+abstract class AbstractMapFactory<K, V, V2> implements Factory<Map<K, V2>> {
+ private final Map<K, Provider<V>> contributingMap;
+
+ AbstractMapFactory(Map<K, Provider<V>> map) {
+ this.contributingMap = unmodifiableMap(map);
+ }
+
+ /** The map of {@link Provider}s that contribute to this map binding. */
+ final Map<K, Provider<V>> contributingMap() {
+ return contributingMap;
+ }
+
+ /** A builder for {@link AbstractMapFactory}. */
+ public abstract static class Builder<K, V, V2> {
+ final LinkedHashMap<K, Provider<V>> map;
+
+ Builder(int size) {
+ this.map = newLinkedHashMapWithExpectedSize(size);
+ }
+
+ // Unfortunately, we cannot return a self-type here because a raw Provider type passed to one of
+ // these methods affects the returned type of the method. The first put*() call erases the self
+ // type to the "raw" self type, and the second erases the type to the upper bound
+ // (AbstractMapFactory.Builder), which doesn't have a build() method.
+ //
+ // The methods are therefore not declared public so that each subtype will redeclare them and
+ // expand their accessibility
+
+ /** Associates {@code key} with {@code providerOfValue}. */
+ Builder<K, V, V2> put(K key, Provider<V> providerOfValue) {
+ map.put(checkNotNull(key, "key"), checkNotNull(providerOfValue, "provider"));
+ return this;
+ }
+
+ Builder<K, V, V2> putAll(Provider<Map<K, V2>> mapOfProviders) {
+ if (mapOfProviders instanceof DelegateFactory) {
+ @SuppressWarnings("unchecked")
+ DelegateFactory<Map<K, V2>> asDelegateFactory = (DelegateFactory) mapOfProviders;
+ return putAll(asDelegateFactory.getDelegate());
+ }
+ @SuppressWarnings("unchecked")
+ AbstractMapFactory<K, V, ?> asAbstractMapFactory =
+ ((AbstractMapFactory<K, V, ?>) (Provider) mapOfProviders);
+ map.putAll(asAbstractMapFactory.contributingMap);
+ return this;
+ }
+ }
+}
diff --git a/java/dagger/internal/Beta.java b/java/dagger/internal/Beta.java
new file mode 100644
index 0000000..2e97f05
--- /dev/null
+++ b/java/dagger/internal/Beta.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+/**
+ * Signifies that a public API (public class, method or field) is subject to
+ * incompatible changes, or even removal, in a future release. An API bearing
+ * this annotation is exempt from any compatibility guarantees made by its
+ * containing library. Note that the presence of this annotation implies nothing
+ * about the quality or performance of the API in question, only the fact that
+ * it is not "API-frozen."
+ */
+@Documented
+@Retention(SOURCE)
+public @interface Beta {}
diff --git a/java/dagger/internal/ComponentDefinitionType.java b/java/dagger/internal/ComponentDefinitionType.java
new file mode 100644
index 0000000..1ab8b13
--- /dev/null
+++ b/java/dagger/internal/ComponentDefinitionType.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Target;
+
+/** Specifies the user-defined component that is being implemented by the annotated class. */
+@Target(TYPE)
+public @interface ComponentDefinitionType {
+ Class<?> value();
+}
diff --git a/java/dagger/internal/ConfigureInitializationParameters.java b/java/dagger/internal/ConfigureInitializationParameters.java
new file mode 100644
index 0000000..1ca0fbb
--- /dev/null
+++ b/java/dagger/internal/ConfigureInitializationParameters.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a {@code configureInitialization()} method with {@code ComponentRequirement}s that it
+ * accepts as parameters.
+ */
+@Target(METHOD)
+public @interface ConfigureInitializationParameters {
+ /**
+ * The list of parameters.
+ *
+ * Each value is a {@link dagger.internal.codegen.serialization.ComponentRequirementProto}
+ * serialized in Base64.
+ */
+ String[] value() default {};
+}
diff --git a/java/dagger/internal/DaggerCollections.java b/java/dagger/internal/DaggerCollections.java
new file mode 100644
index 0000000..cebca42
--- /dev/null
+++ b/java/dagger/internal/DaggerCollections.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Collection utility methods in service of Dagger internal classes. <em>Do not use</em> in client
+ * code.
+ */
+public final class DaggerCollections {
+ /**
+ * The maximum value for a signed 32-bit integer that is equal to a power of 2.
+ */
+ private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2);
+
+ private DaggerCollections() {}
+
+ /**
+ * Returns a new list that is pre-sized to {@code size}, or {@link Collections#emptyList()} if
+ * empty. The list returned is never intended to grow beyond {@code size}, so adding to a list
+ * when the size is 0 is an error.
+ */
+ public static <T> List<T> presizedList(int size) {
+ if (size == 0) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<T>(size);
+ }
+
+ /**
+ * Returns true if at least one pair of items in {@code list} are equals.
+ */
+ public static boolean hasDuplicates(List<?> list) {
+ if (list.size() < 2) {
+ return false;
+ }
+ Set<Object> asSet = new HashSet<Object>(list);
+ return list.size() != asSet.size();
+ }
+
+ /**
+ * Creates a {@link HashSet} instance, with a high enough "intial capcity" that it <em>should</em>
+ * hold {@code expectedSize} elements without growth.
+ */
+ static <T> HashSet<T> newHashSetWithExpectedSize(int expectedSize) {
+ return new HashSet<T>(calculateInitialCapacity(expectedSize));
+ }
+
+ /**
+ * Creates a {@link LinkedHashMap} instance, with a high enough "initial capacity" that it
+ * <em>should</em> hold {@code expectedSize} elements without growth.
+ */
+ public static <K, V> LinkedHashMap<K, V> newLinkedHashMapWithExpectedSize(int expectedSize) {
+ return new LinkedHashMap<K, V>(calculateInitialCapacity(expectedSize));
+ }
+
+ private static int calculateInitialCapacity(int expectedSize) {
+ if (expectedSize < 3) {
+ return expectedSize + 1;
+ }
+ if (expectedSize < MAX_POWER_OF_TWO) {
+ // This is the calculation used in JDK8 to resize when a putAll
+ // happens; it seems to be the most conservative calculation we
+ // can make. 0.75 is the default load factor.
+ return (int) (expectedSize / 0.75F + 1.0F);
+ }
+ return Integer.MAX_VALUE; // any large value
+ }
+}
diff --git a/java/dagger/internal/DelegateFactory.java b/java/dagger/internal/DelegateFactory.java
new file mode 100644
index 0000000..3b4a30f
--- /dev/null
+++ b/java/dagger/internal/DelegateFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import javax.inject.Provider;
+
+/**
+ * A DelegateFactory that is used to stitch Provider/Lazy indirection based dependency cycles.
+ *
+ * @since 2.0.1
+ */
+public final class DelegateFactory<T> implements Factory<T> {
+ private Provider<T> delegate;
+
+ @Override
+ public T get() {
+ if (delegate == null) {
+ throw new IllegalStateException();
+ }
+ return delegate.get();
+ }
+
+ // TODO(ronshapiro): remove this once we can reasonably expect generated code is no longer using
+ // this method
+ @Deprecated
+ public void setDelegatedProvider(Provider<T> delegate) {
+ setDelegate(this, delegate);
+ }
+
+ /**
+ * Sets {@code delegateFactory}'s delegate provider to {@code delegate}.
+ *
+ * <p>{@code delegateFactory} must be an instance of {@link DelegateFactory}, otherwise this
+ * method will throw a {@link ClassCastException}.
+ */
+ public static <T> void setDelegate(Provider<T> delegateFactory, Provider<T> delegate) {
+ checkNotNull(delegate);
+ DelegateFactory<T> asDelegateFactory = (DelegateFactory<T>) delegateFactory;
+ if (asDelegateFactory.delegate != null) {
+ throw new IllegalStateException();
+ }
+ asDelegateFactory.delegate = delegate;
+ }
+
+ /**
+ * Returns the factory's delegate.
+ *
+ * @throws NullPointerException if the delegate has not been set
+ */
+ Provider<T> getDelegate() {
+ return checkNotNull(delegate);
+ }
+}
+
diff --git a/java/dagger/internal/DoubleCheck.java b/java/dagger/internal/DoubleCheck.java
new file mode 100644
index 0000000..ea07528
--- /dev/null
+++ b/java/dagger/internal/DoubleCheck.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import dagger.Lazy;
+import javax.inject.Provider;
+
+/**
+ * A {@link Lazy} and {@link Provider} implementation that memoizes the value returned from a
+ * delegate using the double-check idiom described in Item 71 of <i>Effective Java 2</i>.
+ */
+public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
+ private static final Object UNINITIALIZED = new Object();
+
+ private volatile Provider<T> provider;
+ private volatile Object instance = UNINITIALIZED;
+
+ private DoubleCheck(Provider<T> provider) {
+ assert provider != null;
+ this.provider = provider;
+ }
+
+ @SuppressWarnings("unchecked") // cast only happens when result comes from the provider
+ @Override
+ public T get() {
+ Object result = instance;
+ if (result == UNINITIALIZED) {
+ synchronized (this) {
+ result = instance;
+ if (result == UNINITIALIZED) {
+ result = provider.get();
+ instance = reentrantCheck(instance, result);
+ /* Null out the reference to the provider. We are never going to need it again, so we
+ * can make it eligible for GC. */
+ provider = null;
+ }
+ }
+ }
+ return (T) result;
+ }
+
+ /**
+ * Checks to see if creating the new instance has resulted in a recursive call. If it has, and the
+ * new instance is the same as the current instance, return the instance. However, if the new
+ * instance differs from the current instance, an {@link IllegalStateException} is thrown.
+ */
+ public static Object reentrantCheck(Object currentInstance, Object newInstance) {
+ boolean isReentrant = !(currentInstance == UNINITIALIZED
+ // This check is needed for fastInit's implementation, which uses MemoizedSentinel types.
+ || currentInstance instanceof MemoizedSentinel);
+
+ if (isReentrant && currentInstance != newInstance) {
+ throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ + "different results: " + currentInstance + " & " + newInstance + ". This is likely "
+ + "due to a circular dependency.");
+ }
+ return newInstance;
+ }
+
+ /** Returns a {@link Provider} that caches the value from the given delegate provider. */
+ // This method is declared this way instead of "<T> Provider<T> provider(Provider<T> delegate)"
+ // to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
+ public static <P extends Provider<T>, T> Provider<T> provider(P delegate) {
+ checkNotNull(delegate);
+ if (delegate instanceof DoubleCheck) {
+ /* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
+ * binding, we shouldn't cache the value again. */
+ return delegate;
+ }
+ return new DoubleCheck<T>(delegate);
+ }
+
+ /** Returns a {@link Lazy} that caches the value from the given provider. */
+ // This method is declared this way instead of "<T> Lazy<T> lazy(Provider<T> delegate)"
+ // to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
+ public static <P extends Provider<T>, T> Lazy<T> lazy(P provider) {
+ if (provider instanceof Lazy) {
+ @SuppressWarnings("unchecked")
+ final Lazy<T> lazy = (Lazy<T>) provider;
+ // Avoids memoizing a value that is already memoized.
+ // NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
+ // are different types using covariant return on get(). Right now this is used with
+ // DoubleCheck<T> exclusively, which is implemented such that P and L are always
+ // the same, so it will be fine for that case.
+ return lazy;
+ }
+ return new DoubleCheck<T>(checkNotNull(provider));
+ }
+}
diff --git a/java/dagger/internal/Factory.java b/java/dagger/internal/Factory.java
new file mode 100644
index 0000000..9c03f81
--- /dev/null
+++ b/java/dagger/internal/Factory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import dagger.Provides;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Scope;
+
+/**
+ * An {@linkplain Scope unscoped} {@link Provider}. While a {@link Provider} <i>may</i> apply
+ * scoping semantics while providing an instance, a factory implementation is guaranteed to exercise
+ * the binding logic ({@link Inject} constructors, {@link Provides} methods) upon each call to
+ * {@link #get}.
+ *
+ * <p>Note that while subsequent calls to {@link #get} will create new instances for bindings such
+ * as those created by {@link Inject} constructors, a new instance is not guaranteed by all
+ * bindings. For example, {@link Provides} methods may be implemented in ways that return the same
+ * instance for each call.
+ */
+public interface Factory<T> extends Provider<T> {
+}
diff --git a/java/dagger/internal/GenerationOptions.java b/java/dagger/internal/GenerationOptions.java
new file mode 100644
index 0000000..996cd1d
--- /dev/null
+++ b/java/dagger/internal/GenerationOptions.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Metadata annotation for base subcomponent implementations in ahead-of-time compilations. This
+ * propagates any compiler options related to code generation so that later compilations can
+ * recreate the model of the generated code of superclass implementations.
+ */
+@Target(ElementType.TYPE)
+public @interface GenerationOptions {
+ boolean fastInit();
+}
diff --git a/java/dagger/internal/GwtIncompatible.java b/java/dagger/internal/GwtIncompatible.java
new file mode 100644
index 0000000..f6100a2
--- /dev/null
+++ b/java/dagger/internal/GwtIncompatible.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+/** Marks an element incompatible with GWT. */
+public @interface GwtIncompatible {}
diff --git a/java/dagger/internal/InstanceFactory.java b/java/dagger/internal/InstanceFactory.java
new file mode 100644
index 0000000..3156fe8
--- /dev/null
+++ b/java/dagger/internal/InstanceFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import dagger.Lazy;
+
+/**
+ * A {@link Factory} implementation that returns a single instance for all invocations of {@link
+ * #get}.
+ *
+ * <p>Note that while this is a {@link Factory} implementation, and thus unscoped, each call to
+ * {@link #get} will always return the same instance. As such, any scoping applied to this factory
+ * is redundant and unnecessary. However, using this with {@link DoubleCheck#provider} is valid and
+ * may be desired for testing or contractual guarantees.
+ */
+public final class InstanceFactory<T> implements Factory<T>, Lazy<T> {
+ public static <T> Factory<T> create(T instance) {
+ return new InstanceFactory<T>(checkNotNull(instance, "instance cannot be null"));
+ }
+
+ public static <T> Factory<T> createNullable(T instance) {
+ return instance == null
+ ? InstanceFactory.<T>nullInstanceFactory()
+ : new InstanceFactory<T>(instance);
+ }
+
+ @SuppressWarnings("unchecked") // bivariant implementation
+ private static <T> InstanceFactory<T> nullInstanceFactory() {
+ return (InstanceFactory<T>) NULL_INSTANCE_FACTORY;
+ }
+
+ private static final InstanceFactory<Object> NULL_INSTANCE_FACTORY =
+ new InstanceFactory<Object>(null);
+
+ private final T instance;
+
+ private InstanceFactory(T instance) {
+ this.instance = instance;
+ }
+
+ @Override
+ public T get() {
+ return instance;
+ }
+}
diff --git a/java/dagger/internal/MapBuilder.java b/java/dagger/internal/MapBuilder.java
new file mode 100644
index 0000000..25e2b5b
--- /dev/null
+++ b/java/dagger/internal/MapBuilder.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * A fluent builder class that returns a {@link Map}. Used in component implementations where a map
+ * must be created in one fluent statement for inlined request fulfillments.
+ */
+public final class MapBuilder<K, V> {
+ private final Map<K, V> contributions;
+
+ private MapBuilder(int size) {
+ contributions = newLinkedHashMapWithExpectedSize(size);
+ }
+
+ /**
+ * Creates a new {@link MapBuilder} with {@code size} elements.
+ */
+ public static <K, V> MapBuilder<K, V> newMapBuilder(int size) {
+ return new MapBuilder<>(size);
+ }
+
+ public MapBuilder<K, V> put(K key, V value) {
+ contributions.put(key, value);
+ return this;
+ }
+
+ public MapBuilder<K, V> putAll(Map<K, V> map) {
+ contributions.putAll(map);
+ return this;
+ }
+
+ public Map<K, V> build() {
+ switch (contributions.size()) {
+ case 0:
+ return Collections.emptyMap();
+ default:
+ return Collections.unmodifiableMap(contributions);
+ }
+ }
+}
diff --git a/java/dagger/internal/MapFactory.java b/java/dagger/internal/MapFactory.java
new file mode 100644
index 0000000..8eb0783
--- /dev/null
+++ b/java/dagger/internal/MapFactory.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+import static java.util.Collections.unmodifiableMap;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.inject.Provider;
+
+/**
+ * A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
+ * {@code Map<K, V>} when calling {@link #get} (as specified by {@link Factory}).
+ */
+public final class MapFactory<K, V> extends AbstractMapFactory<K, V, V> {
+ private static final Provider<Map<Object, Object>> EMPTY =
+ InstanceFactory.create(Collections.emptyMap());
+
+ /** Returns a new {@link Builder} */
+ public static <K, V> Builder<K, V> builder(int size) {
+ return new Builder<>(size);
+ }
+
+ /** Returns a factory of an empty map. */
+ @SuppressWarnings("unchecked") // safe contravariant cast
+ public static <K, V> Provider<Map<K, V>> emptyMapProvider() {
+ return (Provider<Map<K, V>>) (Provider) EMPTY;
+ }
+
+ private MapFactory(Map<K, Provider<V>> map) {
+ super(map);
+ }
+
+ /**
+ * Returns a {@code Map<K, V>} whose iteration order is that of the elements given by each of the
+ * providers, which are invoked in the order given at creation.
+ */
+ @Override
+ public Map<K, V> get() {
+ Map<K, V> result = newLinkedHashMapWithExpectedSize(contributingMap().size());
+ for (Entry<K, Provider<V>> entry : contributingMap().entrySet()) {
+ result.put(entry.getKey(), entry.getValue().get());
+ }
+ return unmodifiableMap(result);
+ }
+
+ /** A builder for {@link MapFactory}. */
+ public static final class Builder<K, V> extends AbstractMapFactory.Builder<K, V, V> {
+ private Builder(int size) {
+ super(size);
+ }
+
+ public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+ super.put(key, providerOfValue);
+ return this;
+ }
+
+ public Builder<K, V> putAll(Provider<Map<K, V>> mapFactory) {
+ super.putAll(mapFactory);
+ return this;
+ }
+
+ /** Returns a new {@link MapProviderFactory}. */
+ public MapFactory<K, V> build() {
+ return new MapFactory<>(map);
+ }
+ }
+}
diff --git a/java/dagger/internal/MapProviderFactory.java b/java/dagger/internal/MapProviderFactory.java
new file mode 100644
index 0000000..1fe4788
--- /dev/null
+++ b/java/dagger/internal/MapProviderFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import dagger.Lazy;
+import java.util.Map;
+import javax.inject.Provider;
+
+/**
+ * A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
+ * {@code Map<K, Provider<V>>} when calling {@link #get} (as specified by {@link Factory}).
+ */
+public final class MapProviderFactory<K, V> extends AbstractMapFactory<K, V, Provider<V>>
+ implements Lazy<Map<K, Provider<V>>> {
+
+ /** Returns a new {@link Builder} */
+ public static <K, V> Builder<K, V> builder(int size) {
+ return new Builder<>(size);
+ }
+
+ private MapProviderFactory(Map<K, Provider<V>> contributingMap) {
+ super(contributingMap);
+ }
+
+ /**
+ * Returns a {@code Map<K, Provider<V>>} whose iteration order is that of the elements given by
+ * each of the providers, which are invoked in the order given at creation.
+ */
+ @Override
+ public Map<K, Provider<V>> get() {
+ return contributingMap();
+ }
+
+ /** A builder for {@link MapProviderFactory}. */
+ public static final class Builder<K, V> extends AbstractMapFactory.Builder<K, V, Provider<V>> {
+ private Builder(int size) {
+ super(size);
+ }
+
+ @Override
+ public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+ super.put(key, providerOfValue);
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> putAll(Provider<Map<K, Provider<V>>> mapProviderFactory) {
+ super.putAll(mapProviderFactory);
+ return this;
+ }
+
+ /** Returns a new {@link MapProviderFactory}. */
+ public MapProviderFactory<K, V> build() {
+ return new MapProviderFactory<>(map);
+ }
+ }
+}
diff --git a/java/dagger/internal/MembersInjectors.java b/java/dagger/internal/MembersInjectors.java
new file mode 100644
index 0000000..aae068c
--- /dev/null
+++ b/java/dagger/internal/MembersInjectors.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import dagger.MembersInjector;
+import javax.inject.Inject;
+
+/**
+ * Basic {@link MembersInjector} implementations used by the framework.
+ */
+public final class MembersInjectors {
+ /**
+ * Returns a {@link MembersInjector} implementation that injects no members
+ *
+ * <p>Note that there is no verification that the type being injected does not have {@link Inject}
+ * members, so care should be taken to ensure appropriate use.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> MembersInjector<T> noOp() {
+ return (MembersInjector<T>) NoOpMembersInjector.INSTANCE;
+ }
+
+ private static enum NoOpMembersInjector implements MembersInjector<Object> {
+ INSTANCE;
+
+ @Override public void injectMembers(Object instance) {
+ checkNotNull(instance, "Cannot inject members into a null reference");
+ }
+ }
+
+ private MembersInjectors() {}
+}
diff --git a/java/dagger/internal/MemoizedSentinel.java b/java/dagger/internal/MemoizedSentinel.java
new file mode 100644
index 0000000..dd24dcd
--- /dev/null
+++ b/java/dagger/internal/MemoizedSentinel.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+/** A sentinel used to memoize a scoped binding in a component. */
+public final class MemoizedSentinel {}
diff --git a/java/dagger/internal/MissingBindingFactory.java b/java/dagger/internal/MissingBindingFactory.java
new file mode 100644
index 0000000..993d150
--- /dev/null
+++ b/java/dagger/internal/MissingBindingFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+/**
+ * A {@link Factory} that always throws on calls to {@link Factory#get()}. This is necessary in
+ * ahead-of-time subcomponents mode, where modifiable binding methods need to return a {@code
+ * Provider<T>} to a framework instance initialization that is pruned and no longer in the binding
+ * graph, but was present in a superclass implementation. This class fulfills that requirement but
+ * is still practically unusable.
+ */
+public final class MissingBindingFactory<T> implements Factory<T> {
+ private static final MissingBindingFactory<Object> INSTANCE = new MissingBindingFactory<>();
+
+ private MissingBindingFactory() {}
+
+ @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast
+ public static <T> Factory<T> create() {
+ return (Factory) INSTANCE;
+ }
+
+ @Override
+ public T get() {
+ throw new AssertionError(
+ "This binding is not part of the final binding graph. The key was requested by a binding "
+ + "that was believed to possibly be part of the graph, but is no longer requested. "
+ + "If this exception is thrown, it is the result of a Dagger bug.");
+ }
+}
diff --git a/java/dagger/internal/ModifiableBinding.java b/java/dagger/internal/ModifiableBinding.java
new file mode 100644
index 0000000..1e658e4
--- /dev/null
+++ b/java/dagger/internal/ModifiableBinding.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Target;
+
+/** Annotates methods that implement bindings that may be modified by subclass implementations. */
+@Target(METHOD)
+public @interface ModifiableBinding {
+ /** {@code ModifiableBindingType} of the binding. */
+ // TODO(ronshapiro): should this be a shared enum with dagger.internal.codegen?
+ String modifiableBindingType();
+
+ /** A {@link dagger.internal.codegen.serialization.BindingRequestProto} serialized in Base64. */
+ String bindingRequest();
+
+ /**
+ * For a multibinding, the keys of all contributions it depends on in this implementation.
+ */
+ String[] multibindingContributions() default {};
+}
diff --git a/java/dagger/internal/ModifiableModule.java b/java/dagger/internal/ModifiableModule.java
new file mode 100644
index 0000000..983321e
--- /dev/null
+++ b/java/dagger/internal/ModifiableModule.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+/**
+ * Annotates methods that return {@linkplain dagger.Module modules} that may be modified by subclass
+ * implementations.
+ */
+public @interface ModifiableModule {
+ /** The serialized {@code ComponentRequirement} of this method's module. */
+ String value();
+}
diff --git a/java/dagger/internal/Preconditions.java b/java/dagger/internal/Preconditions.java
new file mode 100644
index 0000000..714a353
--- /dev/null
+++ b/java/dagger/internal/Preconditions.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+/**
+ * An adaptation of Guava's {@code com.google.common.base.Preconditions} that is specially tailored
+ * to support checks applied in Dagger's generated code.
+ */
+public final class Preconditions {
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference) {
+ if (reference == null) {
+ throw new NullPointerException();
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @param errorMessage the exception message to use if the check fails
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference, String errorMessage) {
+ if (reference == null) {
+ throw new NullPointerException(errorMessage);
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing the single {@code %s} placeholder in the template with
+ * {@code errorMessageArg}.
+ * @param errorMessageArg the argument to be substituted into the message template. Converted to a
+ * string using {@link String#valueOf(Object)}, except for {@link Class} objects, which use
+ * {@link Class#getCanonicalName()}.
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ * @throws IllegalArgumentException if {@code errorMessageTemplate} doesn't contain exactly one
+ * "%s"
+ */
+ public static <T> T checkNotNull(
+ T reference, String errorMessageTemplate, Object errorMessageArg) {
+ if (reference == null) {
+ // Simple implementation of String.format, which is not GWT-compatible
+ if (!errorMessageTemplate.contains("%s")) {
+ throw new IllegalArgumentException("errorMessageTemplate has no format specifiers");
+ }
+ if (errorMessageTemplate.indexOf("%s") != errorMessageTemplate.lastIndexOf("%s")) {
+ throw new IllegalArgumentException(
+ "errorMessageTemplate has more than one format specifier");
+ }
+ String argString =
+ errorMessageArg instanceof Class
+ ? ((Class) errorMessageArg).getCanonicalName()
+ : String.valueOf(errorMessageArg);
+ throw new NullPointerException(errorMessageTemplate.replace("%s", argString));
+ }
+ return reference;
+ }
+
+ /**
+ * Checks that the component builder field {@code requirement} has been initialized.
+ *
+ * @throws IllegalStateException if {@code requirement is null}
+ */
+ public static <T> void checkBuilderRequirement(T requirement, Class<T> clazz) {
+ if (requirement == null) {
+ throw new IllegalStateException(clazz.getCanonicalName() + " must be set");
+ }
+ }
+
+ private Preconditions() {}
+}
diff --git a/java/dagger/internal/ProviderOfLazy.java b/java/dagger/internal/ProviderOfLazy.java
new file mode 100644
index 0000000..23b6afd
--- /dev/null
+++ b/java/dagger/internal/ProviderOfLazy.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import dagger.Lazy;
+import javax.inject.Provider;
+
+/**
+ * A {@link Provider} of {@link Lazy} instances that each delegate to a given {@link Provider}.
+ */
+public final class ProviderOfLazy<T> implements Provider<Lazy<T>> {
+
+ private final Provider<T> provider;
+
+ private ProviderOfLazy(Provider<T> provider) {
+ assert provider != null;
+ this.provider = provider;
+ }
+
+ /**
+ * Returns a new instance of {@link Lazy Lazy<T>}, which calls {@link Provider#get()} at
+ * most once on the {@link Provider} held by this object.
+ */
+ @Override
+ public Lazy<T> get() {
+ return DoubleCheck.lazy(provider);
+ }
+
+ /**
+ * Creates a new {@link Provider Provider<Lazy<T>>} that decorates the given
+ * {@link Provider}.
+ *
+ * @see #get()
+ */
+ public static <T> Provider<Lazy<T>> create(Provider<T> provider) {
+ return new ProviderOfLazy<T>(checkNotNull(provider));
+ }
+}
diff --git a/java/dagger/internal/SetBuilder.java b/java/dagger/internal/SetBuilder.java
new file mode 100644
index 0000000..41a2fc7
--- /dev/null
+++ b/java/dagger/internal/SetBuilder.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A fluent builder class that returns a {@link Set}. Used in component implementations where a set
+ * must be created in one fluent statement for inlined request fulfillments.
+ */
+public final class SetBuilder<T> {
+ private static final String SET_CONTRIBUTIONS_CANNOT_BE_NULL =
+ "Set contributions cannot be null";
+ private final List<T> contributions;
+
+ private SetBuilder(int estimatedSize) {
+ contributions = new ArrayList<>(estimatedSize);
+ }
+
+ /**
+ * {@code estimatedSize} is the number of bindings which contribute to the set. They may each
+ * provide {@code [0..n)} instances to the set. Because the final size is unknown, {@code
+ * contributions} are collected in a list and only hashed in {@link #build()}.
+ */
+ public static <T> SetBuilder<T> newSetBuilder(int estimatedSize) {
+ return new SetBuilder<T>(estimatedSize);
+ }
+
+ public SetBuilder<T> add(T t) {
+ contributions.add(checkNotNull(t, SET_CONTRIBUTIONS_CANNOT_BE_NULL));
+ return this;
+ }
+
+ public SetBuilder<T> addAll(Collection<? extends T> collection) {
+ for (T item : collection) {
+ checkNotNull(item, SET_CONTRIBUTIONS_CANNOT_BE_NULL);
+ }
+ contributions.addAll(collection);
+ return this;
+ }
+
+ public Set<T> build() {
+ switch (contributions.size()) {
+ case 0:
+ return Collections.emptySet();
+ case 1:
+ return Collections.singleton(contributions.get(0));
+ default:
+ return Collections.unmodifiableSet(new HashSet<>(contributions));
+ }
+ }
+}
diff --git a/java/dagger/internal/SetFactory.java b/java/dagger/internal/SetFactory.java
new file mode 100644
index 0000000..349399b
--- /dev/null
+++ b/java/dagger/internal/SetFactory.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.DaggerCollections.hasDuplicates;
+import static dagger.internal.DaggerCollections.newHashSetWithExpectedSize;
+import static dagger.internal.DaggerCollections.presizedList;
+import static dagger.internal.Preconditions.checkNotNull;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.unmodifiableSet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import javax.inject.Provider;
+
+/**
+ * A {@link Factory} implementation used to implement {@link Set} bindings. This factory always
+ * returns a new {@link Set} instance for each call to {@link #get} (as required by {@link Factory})
+ * whose elements are populated by subsequent calls to their {@link Provider#get} methods.
+ */
+public final class SetFactory<T> implements Factory<Set<T>> {
+ private static final Factory<Set<Object>> EMPTY_FACTORY = InstanceFactory.create(emptySet());
+
+ @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast
+ public static <T> Factory<Set<T>> empty() {
+ return (Factory) EMPTY_FACTORY;
+ }
+
+ /**
+ * Constructs a new {@link Builder} for a {@link SetFactory} with {@code individualProviderSize}
+ * individual {@code Provider<T>} and {@code collectionProviderSize} {@code
+ * Provider<Collection<T>>} instances.
+ */
+ public static <T> Builder<T> builder(int individualProviderSize, int collectionProviderSize) {
+ return new Builder<T>(individualProviderSize, collectionProviderSize);
+ }
+
+ /**
+ * A builder to accumulate {@code Provider<T>} and {@code Provider<Collection<T>>} instances.
+ * These are only intended to be single-use and from within generated code. Do <em>NOT</em> add
+ * providers after calling {@link #build()}.
+ */
+ public static final class Builder<T> {
+ private final List<Provider<T>> individualProviders;
+ private final List<Provider<Collection<T>>> collectionProviders;
+
+ private Builder(int individualProviderSize, int collectionProviderSize) {
+ individualProviders = presizedList(individualProviderSize);
+ collectionProviders = presizedList(collectionProviderSize);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Builder<T> addProvider(Provider<? extends T> individualProvider) {
+ assert individualProvider != null : "Codegen error? Null provider";
+ // TODO(ronshapiro): Store a List<? extends Provider<T>> and avoid the cast to Provider<T>
+ individualProviders.add((Provider<T>) individualProvider);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Builder<T> addCollectionProvider(
+ Provider<? extends Collection<? extends T>> collectionProvider) {
+ assert collectionProvider != null : "Codegen error? Null provider";
+ collectionProviders.add((Provider<Collection<T>>) collectionProvider);
+ return this;
+ }
+
+ public SetFactory<T> build() {
+ assert !hasDuplicates(individualProviders)
+ : "Codegen error? Duplicates in the provider list";
+ assert !hasDuplicates(collectionProviders)
+ : "Codegen error? Duplicates in the provider list";
+
+ return new SetFactory<T>(individualProviders, collectionProviders);
+ }
+ }
+
+ private final List<Provider<T>> individualProviders;
+ private final List<Provider<Collection<T>>> collectionProviders;
+
+ private SetFactory(
+ List<Provider<T>> individualProviders, List<Provider<Collection<T>>> collectionProviders) {
+ this.individualProviders = individualProviders;
+ this.collectionProviders = collectionProviders;
+ }
+
+ /**
+ * Returns a {@link Set} that contains the elements given by each of the providers.
+ *
+ * @throws NullPointerException if any of the delegate {@link Set} instances or elements therein
+ * are {@code null}
+ */
+ @Override
+ public Set<T> get() {
+ int size = individualProviders.size();
+ // Profiling revealed that this method was a CPU-consuming hotspot in some applications, so
+ // these loops were changed to use c-style for. Versus enhanced for-each loops, C-style for is
+ // faster for ArrayLists, at least through Java 8.
+
+ List<Collection<T>> providedCollections =
+ new ArrayList<Collection<T>>(collectionProviders.size());
+ for (int i = 0, c = collectionProviders.size(); i < c; i++) {
+ Collection<T> providedCollection = collectionProviders.get(i).get();
+ size += providedCollection.size();
+ providedCollections.add(providedCollection);
+ }
+
+ Set<T> providedValues = newHashSetWithExpectedSize(size);
+ for (int i = 0, c = individualProviders.size(); i < c; i++) {
+ providedValues.add(checkNotNull(individualProviders.get(i).get()));
+ }
+ for (int i = 0, c = providedCollections.size(); i < c; i++) {
+ for (T element : providedCollections.get(i)) {
+ providedValues.add(checkNotNull(element));
+ }
+ }
+
+ return unmodifiableSet(providedValues);
+ }
+}
diff --git a/java/dagger/internal/SingleCheck.java b/java/dagger/internal/SingleCheck.java
new file mode 100644
index 0000000..4128069
--- /dev/null
+++ b/java/dagger/internal/SingleCheck.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import javax.inject.Provider;
+
+/**
+ * A {@link Provider} implementation that memoizes the result of another {@link Provider} using
+ * simple lazy initialization, not the double-checked lock pattern.
+ */
+public final class SingleCheck<T> implements Provider<T> {
+ private static final Object UNINITIALIZED = new Object();
+
+ private volatile Provider<T> provider;
+ private volatile Object instance = UNINITIALIZED;
+
+ private SingleCheck(Provider<T> provider) {
+ assert provider != null;
+ this.provider = provider;
+ }
+
+ @SuppressWarnings("unchecked") // cast only happens when result comes from the delegate provider
+ @Override
+ public T get() {
+ Object local = instance;
+ if (local == UNINITIALIZED) {
+ // provider is volatile and might become null after the check, so retrieve the provider first
+ Provider<T> providerReference = provider;
+ if (providerReference == null) {
+ // The provider was null, so the instance must already be set
+ local = instance;
+ } else {
+ local = providerReference.get();
+ instance = local;
+
+ // Null out the reference to the provider. We are never going to need it again, so we can
+ // make it eligible for GC.
+ provider = null;
+ }
+ }
+ return (T) local;
+ }
+
+ /** Returns a {@link Provider} that caches the value from the given delegate provider. */
+ // This method is declared this way instead of "<T> Provider<T> provider(Provider<T> provider)"
+ // to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
+ public static <P extends Provider<T>, T> Provider<T> provider(P provider) {
+ // If a scoped @Binds delegates to a scoped binding, don't cache the value again.
+ if (provider instanceof SingleCheck || provider instanceof DoubleCheck) {
+ return provider;
+ }
+ return new SingleCheck<T>(checkNotNull(provider));
+ }
+}
diff --git a/java/dagger/internal/codegen/AnnotationCreatorGenerator.java b/java/dagger/internal/codegen/AnnotationCreatorGenerator.java
new file mode 100644
index 0000000..273f552
--- /dev/null
+++ b/java/dagger/internal/codegen/AnnotationCreatorGenerator.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.AnnotationExpression.createMethodName;
+import static dagger.internal.codegen.AnnotationExpression.getAnnotationCreatorClassName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/**
+ * Generates classes that create annotation instances for an annotation type. The generated class
+ * will have a private empty constructor, a static method that creates the annotation type itself,
+ * and a static method that creates each annotation type that is nested in the top-level annotation
+ * type.
+ *
+ * <p>So for an example annotation:
+ *
+ * <pre>
+ * {@literal @interface} Foo {
+ * String s();
+ * int i();
+ * Bar bar(); // an annotation defined elsewhere
+ * }
+ * </pre>
+ *
+ * the generated class will look like:
+ *
+ * <pre>
+ * public final class FooCreator {
+ * private FooCreator() {}
+ *
+ * public static Foo createFoo(String s, int i, Bar bar) { … }
+ * public static Bar createBar(…) { … }
+ * }
+ * </pre>
+ */
+class AnnotationCreatorGenerator extends SourceFileGenerator<TypeElement> {
+ private static final ClassName AUTO_ANNOTATION =
+ ClassName.get("com.google.auto.value", "AutoAnnotation");
+
+ @Inject
+ AnnotationCreatorGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ ClassName nameGeneratedType(TypeElement annotationType) {
+ return getAnnotationCreatorClassName(annotationType);
+ }
+
+ @Override
+ Element originatingElement(TypeElement annotationType) {
+ return annotationType;
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedTypeName, TypeElement annotationType) {
+ TypeSpec.Builder annotationCreatorBuilder =
+ classBuilder(generatedTypeName)
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
+
+ for (TypeElement annotationElement : annotationsToCreate(annotationType)) {
+ annotationCreatorBuilder.addMethod(buildCreateMethod(generatedTypeName, annotationElement));
+ }
+
+ return Optional.of(annotationCreatorBuilder);
+ }
+
+ private MethodSpec buildCreateMethod(ClassName generatedTypeName, TypeElement annotationElement) {
+ String createMethodName = createMethodName(annotationElement);
+ MethodSpec.Builder createMethod =
+ methodBuilder(createMethodName)
+ .addAnnotation(AUTO_ANNOTATION)
+ .addModifiers(PUBLIC, STATIC)
+ .returns(TypeName.get(annotationElement.asType()));
+
+ ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
+ for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
+ String parameterName = annotationMember.getSimpleName().toString();
+ TypeName parameterType = TypeName.get(annotationMember.getReturnType());
+ createMethod.addParameter(parameterType, parameterName);
+ parameters.add(CodeBlock.of("$L", parameterName));
+ }
+
+ ClassName autoAnnotationClass =
+ generatedTypeName.peerClass(
+ "AutoAnnotation_" + generatedTypeName.simpleName() + "_" + createMethodName);
+ createMethod.addStatement(
+ "return new $T($L)", autoAnnotationClass, makeParametersCodeBlock(parameters.build()));
+ return createMethod.build();
+ }
+
+ /**
+ * Returns the annotation types for which {@code @AutoAnnotation static Foo createFoo(…)} methods
+ * should be written.
+ */
+ protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
+ return nestedAnnotationElements(annotationElement, new LinkedHashSet<>());
+ }
+
+ @CanIgnoreReturnValue
+ private static Set<TypeElement> nestedAnnotationElements(
+ TypeElement annotationElement, Set<TypeElement> annotationElements) {
+ if (annotationElements.add(annotationElement)) {
+ for (ExecutableElement method : methodsIn(annotationElement.getEnclosedElements())) {
+ TRAVERSE_NESTED_ANNOTATIONS.visit(method.getReturnType(), annotationElements);
+ }
+ }
+ return annotationElements;
+ }
+
+ private static final SimpleTypeVisitor6<Void, Set<TypeElement>> TRAVERSE_NESTED_ANNOTATIONS =
+ new SimpleTypeVisitor6<Void, Set<TypeElement>>() {
+ @Override
+ public Void visitDeclared(DeclaredType t, Set<TypeElement> p) {
+ TypeElement typeElement = MoreTypes.asTypeElement(t);
+ if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
+ nestedAnnotationElements(typeElement, p);
+ }
+ return null;
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/AnnotationExpression.java b/java/dagger/internal/codegen/AnnotationExpression.java
new file mode 100644
index 0000000..8d729e2
--- /dev/null
+++ b/java/dagger/internal/codegen/AnnotationExpression.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static dagger.internal.codegen.SourceFiles.classFileName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static java.util.stream.Collectors.toList;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
+import java.util.List;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor6;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/**
+ * Returns an expression creating an instance of the visited annotation type. Its parameter must be
+ * a class as generated by {@link AnnotationCreatorGenerator}.
+ *
+ * <p>Note that {@link AnnotationValue#toString()} is the source-code representation of the value
+ * <em>when used in an annotation</em>, which is not always the same as the representation needed
+ * when creating the value in a method body.
+ *
+ * <p>For example, inside an annotation, a nested array of {@code int}s is simply {@code {1, 2, 3}},
+ * but in code it would have to be {@code new int[] {1, 2, 3}}.
+ */
+class AnnotationExpression extends SimpleAnnotationValueVisitor6<CodeBlock, AnnotationValue> {
+
+ private final AnnotationMirror annotation;
+ private final ClassName creatorClass;
+
+ AnnotationExpression(AnnotationMirror annotation) {
+ this.annotation = annotation;
+ this.creatorClass =
+ getAnnotationCreatorClassName(
+ MoreTypes.asTypeElement(annotation.getAnnotationType()));
+ }
+
+ /**
+ * Returns an expression that calls static methods on the annotation's creator class to create an
+ * annotation instance equivalent the annotation passed to the constructor.
+ */
+ CodeBlock getAnnotationInstanceExpression() {
+ return getAnnotationInstanceExpression(annotation);
+ }
+
+ private CodeBlock getAnnotationInstanceExpression(AnnotationMirror annotation) {
+ return CodeBlock.of(
+ "$T.$L($L)",
+ creatorClass,
+ createMethodName(
+ MoreElements.asType(annotation.getAnnotationType().asElement())),
+ makeParametersCodeBlock(
+ getAnnotationValuesWithDefaults(annotation)
+ .entrySet()
+ .stream()
+ .map(entry -> getValueExpression(entry.getKey().getReturnType(), entry.getValue()))
+ .collect(toList())));
+ }
+
+ /**
+ * Returns the name of the generated class that contains the static {@code create} methods for an
+ * annotation type.
+ */
+ static ClassName getAnnotationCreatorClassName(TypeElement annotationType) {
+ ClassName annotationTypeName = ClassName.get(annotationType);
+ return annotationTypeName
+ .topLevelClassName()
+ .peerClass(classFileName(annotationTypeName) + "Creator");
+ }
+
+ static String createMethodName(TypeElement annotationType) {
+ return "create" + annotationType.getSimpleName();
+ }
+
+ /**
+ * Returns an expression that evaluates to a {@code value} of a given type on an {@code
+ * annotation}.
+ */
+ CodeBlock getValueExpression(TypeMirror valueType, AnnotationValue value) {
+ return ARRAY_LITERAL_PREFIX.visit(valueType, this.visit(value, value));
+ }
+
+ @Override
+ public CodeBlock visitEnumConstant(VariableElement c, AnnotationValue p) {
+ return CodeBlock.of("$T.$L", c.getEnclosingElement(), c.getSimpleName());
+ }
+
+ @Override
+ public CodeBlock visitAnnotation(AnnotationMirror a, AnnotationValue p) {
+ return getAnnotationInstanceExpression(a);
+ }
+
+ @Override
+ public CodeBlock visitType(TypeMirror t, AnnotationValue p) {
+ return CodeBlock.of("$T.class", t);
+ }
+
+ @Override
+ public CodeBlock visitString(String s, AnnotationValue p) {
+ return CodeBlock.of("$S", s);
+ }
+
+ @Override
+ public CodeBlock visitByte(byte b, AnnotationValue p) {
+ return CodeBlock.of("(byte) $L", b);
+ }
+
+ @Override
+ public CodeBlock visitChar(char c, AnnotationValue p) {
+ return CodeBlock.of("$L", p);
+ }
+
+ @Override
+ public CodeBlock visitDouble(double d, AnnotationValue p) {
+ return CodeBlock.of("$LD", d);
+ }
+
+ @Override
+ public CodeBlock visitFloat(float f, AnnotationValue p) {
+ return CodeBlock.of("$LF", f);
+ }
+
+ @Override
+ public CodeBlock visitLong(long i, AnnotationValue p) {
+ return CodeBlock.of("$LL", i);
+ }
+
+ @Override
+ public CodeBlock visitShort(short s, AnnotationValue p) {
+ return CodeBlock.of("(short) $L", s);
+ }
+
+ @Override
+ protected CodeBlock defaultAction(Object o, AnnotationValue p) {
+ return CodeBlock.of("$L", o);
+ }
+
+ @Override
+ public CodeBlock visitArray(List<? extends AnnotationValue> values, AnnotationValue p) {
+ ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
+ for (AnnotationValue value : values) {
+ codeBlocks.add(this.visit(value, p));
+ }
+ return CodeBlock.of("{$L}", makeParametersCodeBlock(codeBlocks.build()));
+ }
+
+ /**
+ * If the visited type is an array, prefixes the parameter code block with {@code new T[]}, where
+ * {@code T} is the raw array component type.
+ */
+ private static final SimpleTypeVisitor6<CodeBlock, CodeBlock> ARRAY_LITERAL_PREFIX =
+ new SimpleTypeVisitor6<CodeBlock, CodeBlock>() {
+
+ @Override
+ public CodeBlock visitArray(ArrayType t, CodeBlock p) {
+ return CodeBlock.of("new $T[] $L", RAW_TYPE_NAME.visit(t.getComponentType()), p);
+ }
+
+ @Override
+ protected CodeBlock defaultAction(TypeMirror e, CodeBlock p) {
+ return p;
+ }
+ };
+
+ /**
+ * If the visited type is an array, returns the name of its raw component type; otherwise returns
+ * the name of the type itself.
+ */
+ private static final SimpleTypeVisitor6<TypeName, Void> RAW_TYPE_NAME =
+ new SimpleTypeVisitor6<TypeName, Void>() {
+ @Override
+ public TypeName visitDeclared(DeclaredType t, Void p) {
+ return ClassName.get(MoreTypes.asTypeElement(t));
+ }
+
+ @Override
+ protected TypeName defaultAction(TypeMirror e, Void p) {
+ return TypeName.get(e);
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/AnnotationProtoConverter.java b/java/dagger/internal/codegen/AnnotationProtoConverter.java
new file mode 100644
index 0000000..282d8ca
--- /dev/null
+++ b/java/dagger/internal/codegen/AnnotationProtoConverter.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Maps.transformValues;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static javax.lang.model.util.ElementFilter.fieldsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.serialization.AnnotationProto;
+import dagger.internal.codegen.serialization.AnnotationValueProto;
+import java.util.Collections;
+import java.util.List;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/** Converts {@link AnnotationMirror}s to {@link AnnotationProto}s and vice-versa. */
+final class AnnotationProtoConverter {
+ private final TypeProtoConverter typeProtoConverter;
+
+ @Inject
+ AnnotationProtoConverter(TypeProtoConverter typeProtoConverter) {
+ this.typeProtoConverter = typeProtoConverter;
+ }
+
+ /** Translates an {@link AnnotationMirror} to a proto representation. */
+ static AnnotationProto toProto(AnnotationMirror annotationMirror) {
+ AnnotationProto.Builder builder = AnnotationProto.newBuilder();
+ builder.setAnnotationType(TypeProtoConverter.toProto(annotationMirror.getAnnotationType()));
+ getAnnotationValuesWithDefaults(annotationMirror)
+ .forEach(
+ (attribute, value) ->
+ builder.putAllValues(
+ Collections.singletonMap(
+ attribute.getSimpleName().toString(), annotationValueProto(value))));
+ return builder.build();
+ }
+
+ /** Creates an {@link AnnotationMirror} from its proto representation. */
+ AnnotationMirror fromProto(AnnotationProto annotation) {
+ return SimpleAnnotationMirror.of(
+ MoreTypes.asTypeElement(typeProtoConverter.fromProto(annotation.getAnnotationType())),
+ transformValues(annotation.getValues(), AnnotationValueFromProto::new));
+ }
+
+ private static final AnnotationValueVisitor<
+ AnnotationValueProto.Builder, AnnotationValueProto.Builder>
+ ANNOTATION_VALUE_TO_PROTO =
+ new SimpleAnnotationValueVisitor8<
+ AnnotationValueProto.Builder, AnnotationValueProto.Builder>() {
+ @Override
+ public AnnotationValueProto.Builder visitAnnotation(
+ AnnotationMirror nestedAnnotation, AnnotationValueProto.Builder builder) {
+ return builder
+ .setNestedAnnotation(toProto(nestedAnnotation))
+ .setKind(AnnotationValueProto.Kind.ANNOTATION);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitBoolean(
+ boolean b, AnnotationValueProto.Builder builder) {
+ return builder.setBooleanValue(b).setKind(AnnotationValueProto.Kind.BOOLEAN);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitChar(
+ char c, AnnotationValueProto.Builder builder) {
+ return builder
+ .setStringValue(String.valueOf(c))
+ .setKind(AnnotationValueProto.Kind.CHAR);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitByte(
+ byte b, AnnotationValueProto.Builder builder) {
+ return builder.setIntValue(b).setKind(AnnotationValueProto.Kind.BYTE);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitShort(
+ short s, AnnotationValueProto.Builder builder) {
+ return builder.setIntValue(s).setKind(AnnotationValueProto.Kind.SHORT);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitInt(
+ int i, AnnotationValueProto.Builder builder) {
+ return builder.setIntValue(i).setKind(AnnotationValueProto.Kind.INT);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitFloat(
+ float f, AnnotationValueProto.Builder builder) {
+ return builder.setFloatValue(f).setKind(AnnotationValueProto.Kind.FLOAT);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitLong(
+ long l, AnnotationValueProto.Builder builder) {
+ return builder.setLongValue(l).setKind(AnnotationValueProto.Kind.LONG);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitDouble(
+ double d, AnnotationValueProto.Builder builder) {
+ return builder.setDoubleValue(d).setKind(AnnotationValueProto.Kind.DOUBLE);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitString(
+ String s, AnnotationValueProto.Builder builder) {
+ return builder.setStringValue(s).setKind(AnnotationValueProto.Kind.STRING);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitType(
+ TypeMirror t, AnnotationValueProto.Builder builder) {
+ return builder
+ .setClassLiteral(TypeProtoConverter.toProto(t))
+ .setKind(AnnotationValueProto.Kind.CLASS_LITERAL);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitEnumConstant(
+ VariableElement c, AnnotationValueProto.Builder builder) {
+ return builder
+ .setEnumType(TypeProtoConverter.toProto(c.asType()))
+ .setEnumName(c.getSimpleName().toString())
+ .setKind(AnnotationValueProto.Kind.ENUM);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitArray(
+ List<? extends AnnotationValue> values, AnnotationValueProto.Builder builder) {
+ values.forEach(value -> builder.addArrayValues(annotationValueProto(value)));
+ return builder.setKind(AnnotationValueProto.Kind.ARRAY);
+ }
+
+ @Override
+ public AnnotationValueProto.Builder visitUnknown(
+ AnnotationValue av, AnnotationValueProto.Builder builder) {
+ throw new UnsupportedOperationException(av.toString());
+ }
+ };
+
+ /** Translates an {@link AnnotationValue} to a proto representation. */
+ private static AnnotationValueProto annotationValueProto(AnnotationValue annotationValue) {
+ return annotationValue
+ .accept(ANNOTATION_VALUE_TO_PROTO, AnnotationValueProto.newBuilder())
+ .build();
+ }
+
+ private class AnnotationValueFromProto implements AnnotationValue {
+ private final AnnotationValueProto proto;
+
+ AnnotationValueFromProto(AnnotationValueProto proto) {
+ this.proto = proto;
+ }
+
+ @Override
+ public Object getValue() {
+ switch (proto.getKind()) {
+ case BOOLEAN:
+ return proto.getBooleanValue();
+ case BYTE:
+ return (byte) proto.getIntValue();
+ case SHORT:
+ return (short) proto.getIntValue();
+ case CHAR:
+ return getCharValue();
+ case INT:
+ return proto.getIntValue();
+ case FLOAT:
+ return proto.getFloatValue();
+ case LONG:
+ return proto.getLongValue();
+ case DOUBLE:
+ return proto.getDoubleValue();
+ case STRING:
+ return proto.getStringValue();
+ case CLASS_LITERAL:
+ return typeProtoConverter.fromProto(proto.getClassLiteral());
+ case ENUM:
+ return getEnumConstant();
+ case ANNOTATION:
+ return fromProto(proto.getNestedAnnotation());
+ case ARRAY:
+ return getArrayValues();
+ case UNKNOWN:
+ case UNRECOGNIZED:
+ // fall through
+ }
+ throw new AssertionError(proto);
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> visitor, P passedValue) {
+ switch (proto.getKind()) {
+ case BOOLEAN:
+ return visitor.visitBoolean(proto.getBooleanValue(), passedValue);
+ case BYTE:
+ return visitor.visitByte((byte) proto.getIntValue(), passedValue);
+ case SHORT:
+ return visitor.visitShort((short) proto.getIntValue(), passedValue);
+ case CHAR:
+ return visitor.visitChar(getCharValue(), passedValue);
+ case INT:
+ return visitor.visitInt(proto.getIntValue(), passedValue);
+ case FLOAT:
+ return visitor.visitFloat(proto.getFloatValue(), passedValue);
+ case LONG:
+ return visitor.visitLong(proto.getLongValue(), passedValue);
+ case DOUBLE:
+ return visitor.visitDouble(proto.getDoubleValue(), passedValue);
+ case STRING:
+ return visitor.visitString((String) getValue(), passedValue);
+ case CLASS_LITERAL:
+ return visitor.visitType((TypeMirror) getValue(), passedValue);
+ case ENUM:
+ return visitor.visitEnumConstant((VariableElement) getValue(), passedValue);
+ case ANNOTATION:
+ return visitor.visitAnnotation((AnnotationMirror) getValue(), passedValue);
+ case ARRAY:
+ return visitor.visitArray(getArrayValues(), passedValue);
+ case UNKNOWN:
+ case UNRECOGNIZED:
+ // fall through
+ }
+ throw new AssertionError(proto);
+ }
+
+ private char getCharValue() {
+ checkState(proto.getKind().equals(AnnotationValueProto.Kind.CHAR));
+ return proto.getStringValue().charAt(0);
+ }
+
+ private VariableElement getEnumConstant() {
+ checkState(proto.getKind().equals(AnnotationValueProto.Kind.ENUM));
+ TypeMirror enumType = typeProtoConverter.fromProto(proto.getEnumType());
+ return fieldsIn(MoreTypes.asTypeElement(enumType).getEnclosedElements()).stream()
+ .filter(value -> value.getSimpleName().contentEquals(proto.getEnumName()))
+ .findFirst()
+ .get();
+ }
+
+ private ImmutableList<AnnotationValue> getArrayValues() {
+ checkState(proto.getKind().equals(AnnotationValueProto.Kind.ARRAY));
+ return proto.getArrayValuesList().stream()
+ .map(AnnotationValueFromProto::new)
+ .collect(toImmutableList());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java b/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java
new file mode 100644
index 0000000..fc30eaa
--- /dev/null
+++ b/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.javapoet.CodeBlocks.anonymousProvider;
+import static dagger.model.RequestKind.INSTANCE;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.Expression;
+
+/**
+ * A {@link javax.inject.Provider} creation expression for an anonymous inner class whose
+ * {@code get()} method returns the expression for an instance binding request for its key.
+ */
+final class AnonymousProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final ContributionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ClassName requestingClass;
+
+ AnonymousProviderCreationExpression(
+ ContributionBinding binding,
+ ComponentBindingExpressions componentBindingExpressions,
+ ClassName requestingClass) {
+ this.binding = binding;
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.requestingClass = requestingClass;
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ BindingRequest instanceExpressionRequest = bindingRequest(binding.key(), INSTANCE);
+ Expression instanceExpression =
+ componentBindingExpressions.getDependencyExpression(
+ instanceExpressionRequest,
+ // Not a real class name, but the actual requestingClass is an inner class within the
+ // given class, not that class itself.
+ requestingClass.nestedClass("Anonymous"));
+ return anonymousProvider(instanceExpression);
+ }
+}
diff --git a/java/dagger/internal/codegen/AnyBindingMethodValidator.java b/java/dagger/internal/codegen/AnyBindingMethodValidator.java
new file mode 100644
index 0000000..bad9636
--- /dev/null
+++ b/java/dagger/internal/codegen/AnyBindingMethodValidator.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+
+/** Validates any binding method. */
+final class AnyBindingMethodValidator {
+
+ private final ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators;
+ private final Map<ExecutableElement, ValidationReport<ExecutableElement>> reports =
+ new HashMap<>();
+
+ @Inject
+ AnyBindingMethodValidator(
+ ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators) {
+ this.validators = validators;
+ }
+
+ /** Returns the binding method annotations considered by this validator. */
+ ImmutableSet<Class<? extends Annotation>> methodAnnotations() {
+ return validators.keySet();
+ }
+
+ /**
+ * Returns {@code true} if {@code method} is annotated with at least one of {@link
+ * #methodAnnotations()}.
+ */
+ boolean isBindingMethod(ExecutableElement method) {
+ return isAnyAnnotationPresent(method, methodAnnotations());
+ }
+
+ /**
+ * Returns a validation report for a method.
+ *
+ * <ul>
+ * <li>Reports an error if {@code method} is annotated with more than one {@linkplain
+ * #methodAnnotations() binding method annotation}.
+ * <li>Validates {@code method} with the {@link BindingMethodValidator} for the single
+ * {@linkplain #methodAnnotations() binding method annotation}.
+ * </ul>
+ *
+ * @throws IllegalArgumentException if {@code method} is not annotated by any {@linkplain
+ * #methodAnnotations() binding method annotation}
+ */
+ ValidationReport<ExecutableElement> validate(ExecutableElement method) {
+ return reentrantComputeIfAbsent(reports, method, this::validateUncached);
+ }
+
+ /**
+ * Returns {@code true} if {@code method} was already {@linkplain #validate(ExecutableElement)
+ * validated}.
+ */
+ boolean wasAlreadyValidated(ExecutableElement method) {
+ return reports.containsKey(method);
+ }
+
+ private ValidationReport<ExecutableElement> validateUncached(ExecutableElement method) {
+ ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
+ ImmutableSet<? extends Class<? extends Annotation>> bindingMethodAnnotations =
+ methodAnnotations()
+ .stream()
+ .filter(annotation -> isAnnotationPresent(method, annotation))
+ .collect(toImmutableSet());
+ switch (bindingMethodAnnotations.size()) {
+ case 0:
+ throw new IllegalArgumentException(
+ String.format("%s has no binding method annotation", method));
+
+ case 1:
+ report.addSubreport(
+ validators.get(getOnlyElement(bindingMethodAnnotations)).validate(method));
+ break;
+
+ default:
+ report.addError(
+ String.format(
+ "%s is annotated with more than one of (%s)",
+ method.getSimpleName(),
+ methodAnnotations().stream().map(Class::getCanonicalName).collect(joining(", "))),
+ method);
+ break;
+ }
+ return report.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
new file mode 100644
index 0000000..824a72e
--- /dev/null
+++ b/java/dagger/internal/codegen/BUILD
@@ -0,0 +1,527 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A JSR-330 compliant dependency injection system for android and java
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
+load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+
+EXPERIMENTAL_VISUALIZER_SRCS = ["BindingNetworkVisualizer.java"]
+
+JAVAC_PLUGIN_MODULE_SRCS = ["JavacPluginModule.java"]
+
+KYTHE_SRCS = ["DaggerKythePlugin.java"]
+
+STATISTICS_COLLECTOR_SRCS = ["BindingGraphStatisticsCollector.java"]
+
+CODEGEN_SRCS = glob(
+ ["*.java"],
+ exclude = EXPERIMENTAL_VISUALIZER_SRCS + KYTHE_SRCS + STATISTICS_COLLECTOR_SRCS +
+ JAVAC_PLUGIN_MODULE_SRCS,
+)
+
+CODEGEN_PLUGINS = [":bootstrap_compiler_plugin"]
+
+CODEGEN_SHARED_DEPS = [
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/checker_framework_annotations",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/google_java_format",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@bazel_tools//tools/jdk:langtools-neverlink",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/serialization",
+ "//java/dagger/producers",
+ "//java/dagger/model",
+ "//java/dagger/spi",
+ "//java/dagger/model:internal-proxies",
+]
+
+CODEGEN_DEPS = CODEGEN_SHARED_DEPS + [
+ ":jdk-and-guava-extras",
+ "@google_bazel_common//third_party/java/guava",
+]
+
+# Extra features for the JDK and Guava. This code is merged into both
+# the dagger-compiler and dagger-spi artifacts that are sent to Maven
+java_library(
+ name = "jdk-and-guava-extras",
+ srcs = [
+ "DaggerGraphs.java",
+ "DaggerStreams.java",
+ "Optionals.java",
+ ],
+ plugins = CODEGEN_PLUGINS,
+ tags = ["maven:merged"],
+ deps = [
+ "@google_bazel_common//third_party/java/guava",
+ ],
+)
+
+# Common types needed across all of the codegen package
+java_library(
+ name = "base",
+ srcs = [
+ "AnnotationProtoConverter.java",
+ "ClearableCache.java",
+ "CompilerOptions.java",
+ "ComponentAnnotation.java",
+ "ContributionType.java",
+ "DaggerStatistics.java",
+ "DaggerStatisticsCollectingProcessingStep.java",
+ "DaggerStatisticsCollector.java",
+ "DaggerStatisticsRecorder.java",
+ "DiagnosticFormatting.java",
+ "ElementFormatter.java",
+ "FeatureStatus.java",
+ "Formatter.java",
+ "ForwardingCompilerOptions.java",
+ "FrameworkTypes.java",
+ "InjectionAnnotations.java",
+ "Keys.java",
+ "MapKeyAccessibility.java",
+ "MapType.java",
+ "ModuleAnnotation.java",
+ "MoreAnnotationMirrors.java",
+ "MoreAnnotationValues.java",
+ "MultibindingAnnotations.java",
+ "OptionalType.java",
+ "ProcessingEnvironmentCompilerOptions.java",
+ "ProcessingOptions.java",
+ "RequestKinds.java",
+ "Scopes.java",
+ "SetType.java",
+ "SimpleAnnotationMirror.java",
+ "SimpleTypeAnnotationValue.java",
+ "SourceFileGenerationException.java", # Used in :writing and :processor
+ "SourceFileGenerator.java", # Needed by InjectBindingRegistry in :binding and also :writing
+ "TypeCheckingProcessingStep.java",
+ "TypeProtoConverter.java",
+ "UniqueNameSet.java",
+ "Util.java",
+ "ValidationType.java",
+ "package-info.java",
+ ],
+ plugins = CODEGEN_PLUGINS,
+ tags = ["maven:merged"],
+ deps = CODEGEN_DEPS + [
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/langmodel",
+ ],
+)
+
+# Classes that help to build a model of the binding graph
+java_library(
+ name = "binding",
+ srcs = [
+ "AnnotationExpression.java",
+ "Binding.java",
+ "BindingDeclaration.java",
+ "BindingDeclarationFormatter.java",
+ "BindingFactory.java",
+ "BindingGraph.java",
+ "BindingGraphConverter.java",
+ "BindingGraphFactory.java",
+ "BindingNode.java",
+ "BindingRequest.java",
+ "BindingType.java",
+ "BindsTypeChecker.java",
+ "ChildFactoryMethodEdgeImpl.java",
+ "ComponentCreatorAnnotation.java",
+ "ComponentCreatorDescriptor.java",
+ "ComponentCreatorKind.java",
+ "ComponentDescriptor.java",
+ "ComponentDescriptorFactory.java",
+ "ComponentKind.java",
+ "ComponentNodeImpl.java",
+ "ComponentRequirement.java",
+ "ComponentTreeTraverser.java",
+ "ConfigurationAnnotations.java", # Uses ModuleDescriptors
+ "ContributionBinding.java",
+ "DelegateDeclaration.java",
+ "DependencyEdgeImpl.java",
+ "DependencyRequestFactory.java",
+ "DependencyRequestFormatter.java",
+ "DependencyVariableNamer.java", # Used by SourceFiles
+ "ErrorMessages.java", # Consider splitting this up as it pulls in too much
+ "FrameworkDependency.java",
+ "FrameworkField.java", # Used by SourceFiles
+ "FrameworkType.java",
+ "FrameworkTypeMapper.java",
+ "InjectBindingRegistry.java",
+ "InjectionSiteFactory.java",
+ "KeyFactory.java",
+ "KeyVariableNamer.java", # needs ConfigurationAnnotations, SourceFiles
+ "MapKeys.java",
+ "MembersInjectionBinding.java",
+ "MethodSignature.java",
+ "MethodSignatureFormatter.java",
+ "ModuleDescriptor.java",
+ "ModuleKind.java",
+ "MultibindingDeclaration.java",
+ "OptionalBindingDeclaration.java",
+ "ProductionBinding.java",
+ "ProvisionBinding.java",
+ "ResolvedBindings.java",
+ "SourceFiles.java", # Consider splitting this up?
+ "SubcomponentCreatorBindingEdgeImpl.java",
+ "SubcomponentDeclaration.java",
+ ],
+ plugins = CODEGEN_PLUGINS,
+ tags = ["maven:merged"],
+ deps = CODEGEN_DEPS + [
+ ":base",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/javapoet",
+ ],
+)
+
+# Code related to validating the user-written Dagger code
+java_library(
+ name = "validation",
+ srcs = [
+ "AnyBindingMethodValidator.java",
+ "BindingElementValidator.java",
+ "BindingGraphPlugins.java",
+ "BindingGraphValidator.java",
+ "BindingMethodProcessingStep.java",
+ "BindingMethodValidator.java",
+ "BindsInstanceElementValidator.java",
+ "BindsInstanceMethodValidator.java",
+ "BindsInstanceParameterValidator.java",
+ "BindsInstanceProcessingStep.java",
+ "BindsMethodValidator.java",
+ "BindsOptionalOfMethodValidator.java",
+ "ComponentCreatorValidator.java",
+ "ComponentDescriptorValidator.java",
+ "ComponentHierarchyValidator.java",
+ "ComponentValidator.java",
+ "DependencyRequestValidator.java",
+ "DiagnosticReporterFactory.java",
+ "InjectValidator.java",
+ "MapKeyValidator.java",
+ "MembersInjectionValidator.java",
+ "ModuleValidator.java",
+ "MultibindingAnnotationsProcessingStep.java",
+ "MultibindsMethodValidator.java",
+ "ProducesMethodValidator.java",
+ "ProvidesMethodValidator.java",
+ "Validation.java",
+ "ValidationReport.java",
+ ],
+ plugins = CODEGEN_PLUGINS,
+ tags = ["maven:merged"],
+ deps = CODEGEN_DEPS + [
+ ":base",
+ ":binding",
+ "//java/dagger/internal/codegen/langmodel",
+ ],
+)
+
+java_library(
+ name = "binding_graph_validation",
+ srcs = [
+ "DependencyCycleValidator.java",
+ "DependsOnProductionExecutorValidator.java",
+ "DuplicateBindingsValidator.java",
+ "IncompatiblyScopedBindingsValidator.java",
+ "InjectBindingValidator.java",
+ "MapMultibindingValidator.java",
+ "MissingBindingValidator.java",
+ "NullableBindingValidator.java",
+ "ProvisionDependencyOnProducerBindingValidator.java",
+ "SubcomponentFactoryMethodValidator.java",
+ ],
+ plugins = CODEGEN_PLUGINS,
+ tags = ["maven:merged"],
+ deps = CODEGEN_DEPS + [
+ ":base",
+ ":binding",
+ ":validation",
+ "//java/dagger/internal/codegen/langmodel",
+ ],
+)
+
+# Classes that assemble the model of the generated code and write to the Filer
+java_library(
+ name = "writing",
+ srcs = [
+ "AnnotationCreatorGenerator.java",
+ "AnonymousProviderCreationExpression.java",
+ "BindingExpression.java",
+ "ComponentBindingExpressions.java",
+ "ComponentCreatorImplementation.java",
+ "ComponentImplementation.java",
+ "ComponentInstanceBindingExpression.java",
+ "ComponentMethodBindingExpression.java",
+ "ComponentProvisionBindingExpression.java",
+ "ComponentRequirementBindingExpression.java",
+ "ComponentRequirementExpression.java",
+ "ComponentRequirementExpressions.java",
+ "DeferredModifiableBindingExpression.java",
+ "DelegateBindingExpression.java",
+ "DelegatingFrameworkInstanceCreationExpression.java",
+ "DependencyMethodProducerCreationExpression.java",
+ "DependencyMethodProviderCreationExpression.java",
+ "DerivedFromFrameworkInstanceBindingExpression.java",
+ "FactoryGenerator.java",
+ "FrameworkFieldInitializer.java",
+ "FrameworkInstanceBindingExpression.java",
+ "FrameworkInstanceSupplier.java",
+ "GenerationCompilerOptions.java",
+ "GwtCompatibility.java",
+ "HjarSourceFileGenerator.java",
+ "ImmediateFutureBindingExpression.java",
+ "InaccessibleMapKeyProxyGenerator.java",
+ "InjectionMethod.java",
+ "InjectionMethods.java",
+ "InjectionOrProvisionProviderCreationExpression.java",
+ "InnerSwitchingProviders.java",
+ "InstanceFactoryCreationExpression.java",
+ "MapBindingExpression.java",
+ "MapFactoryCreationExpression.java",
+ "MemberSelect.java",
+ "MembersInjectionBindingExpression.java",
+ "MembersInjectionMethods.java",
+ "MembersInjectorGenerator.java",
+ "MembersInjectorProviderCreationExpression.java",
+ "MethodBindingExpression.java",
+ "MissingBindingExpression.java",
+ "ModifiableAbstractMethodBindingExpression.java",
+ "ModifiableBindingExpressions.java",
+ "ModifiableBindingMethods.java",
+ "ModifiableBindingType.java",
+ "ModifiableConcreteMethodBindingExpression.java",
+ "ModuleConstructorProxyGenerator.java",
+ "ModuleGenerator.java",
+ "ModuleProxies.java",
+ "MonitoringModuleGenerator.java",
+ "MonitoringModuleProcessingStep.java",
+ "MultibindingExpression.java",
+ "MultibindingFactoryCreationExpression.java",
+ "OptionalBindingExpression.java",
+ "OptionalFactories.java",
+ "OptionalFactoryInstanceCreationExpression.java",
+ "ParentComponent.java",
+ "PerComponentImplementation.java",
+ "PerGeneratedFile.java",
+ "PrivateMethodBindingExpression.java",
+ "ProducerCreationExpression.java",
+ "ProducerEntryPointView.java",
+ "ProducerFactoryGenerator.java",
+ "ProducerFromProviderCreationExpression.java",
+ "ProducerNodeInstanceBindingExpression.java",
+ "ProviderInstanceBindingExpression.java",
+ "PrunedConcreteMethodBindingExpression.java",
+ "SetBindingExpression.java",
+ "SetFactoryCreationExpression.java",
+ "SimpleInvocationBindingExpression.java",
+ "SimpleMethodBindingExpression.java",
+ "SubcomponentCreatorBindingExpression.java",
+ "SubcomponentNames.java",
+ "SwitchingProviders.java",
+ "TopLevel.java",
+ "UnwrappedMapKeyGenerator.java",
+ ],
+ plugins = CODEGEN_PLUGINS,
+ tags = ["maven:merged"],
+ deps = CODEGEN_DEPS + [
+ ":base",
+ ":binding",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/langmodel",
+ ],
+)
+
+# The processor's "main", if you will
+java_library(
+ name = "processor",
+ srcs = [
+ "BindingGraphValidationModule.java",
+ "BindingMethodValidatorsModule.java",
+ "ComponentCreatorImplementationFactory.java",
+ "ComponentGenerator.java",
+ "ComponentHjarProcessingStep.java",
+ "ComponentImplementationBuilder.java",
+ "ComponentImplementationFactory.java",
+ "ComponentProcessingStep.java",
+ "ComponentProcessor.java",
+ "CurrentImplementationSubcomponent.java",
+ "DeserializedComponentImplementationBuilder.java",
+ "GenerationOptionsModule.java",
+ "InjectBindingRegistryImpl.java",
+ "InjectBindingRegistryModule.java",
+ "InjectProcessingStep.java",
+ "MapKeyProcessingStep.java",
+ "ModuleProcessingStep.java",
+ "ProcessingEnvironmentModule.java",
+ "ProcessingRoundCacheModule.java",
+ "SourceFileGeneratorsModule.java",
+ "SpiModule.java",
+ "SystemComponentsModule.java",
+ "TopLevelImplementationComponent.java",
+ ],
+ plugins = CODEGEN_PLUGINS,
+ tags = ["maven_coordinates=com.google.dagger:dagger-compiler:" + POM_VERSION],
+ deps = CODEGEN_DEPS + [
+ ":base",
+ ":binding",
+ ":binding_graph_validation",
+ ":writing",
+ ":validation",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/langmodel",
+ "@google_bazel_common//third_party/java/incap",
+ ],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-compiler",
+ artifact_name = "Dagger Compiler",
+ targets = [
+ ":processor",
+ ":base",
+ ":binding",
+ ":binding_graph_validation",
+ ":writing",
+ ":validation",
+ "//java/dagger/internal/codegen/serialization",
+ "//java/dagger/internal/codegen/javapoet",
+ ],
+)
+
+java_library(
+ name = "javac-plugin-module",
+ srcs = JAVAC_PLUGIN_MODULE_SRCS,
+ plugins = [":component-codegen"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":base",
+ ":binding",
+ ":javac",
+ ":processor",
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/langmodel",
+ ],
+)
+
+java_library(
+ name = "kythe",
+ srcs = KYTHE_SRCS,
+ plugins = [":component-codegen"],
+ deps = [
+ ":base",
+ ":binding",
+ ":javac",
+ ":javac-plugin-module",
+ ":kythe_plugin",
+ ":processor",
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/model",
+ "//java/dagger/producers",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/guava",
+ ],
+)
+
+# Replacement for @bazel_tools//third_party/java/jdk/langtools:javac, which seems to have gone away?
+java_import(
+ name = "javac",
+ jars = ["@bazel_tools//third_party/java/jdk/langtools:javac_jar"],
+)
+
+# A _deploy.jar consisting of the java_librarys in https://github.com/kythe/kythe needed to build a
+# Kythe plugin
+# TODO(ronshapiro): replace this with a http_archive of the next release in
+# https://github.com/kythe/kythe/releases
+java_import(
+ name = "kythe_plugin",
+ jars = ["kythe_plugin_deploy.jar"],
+ neverlink = 1,
+)
+
+java_import(
+ name = "bootstrap_compiler",
+ jars = ["bootstrap_compiler_deploy.jar"],
+ visibility = ["//visibility:private"],
+)
+
+java_plugin(
+ name = "bootstrap_compiler_plugin",
+ generates_api = 1,
+ processor_class = "dagger.internal.codegen.ComponentProcessor",
+ deps = [":bootstrap_compiler"],
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "codegen-javadoc",
+ srcs = CODEGEN_SRCS,
+ root_packages = ["dagger.internal.codegen"],
+ deps = [":processor"],
+)
+
+java_library(
+ name = "check-package-javadoc",
+ testonly = 1,
+ srcs = CODEGEN_SRCS,
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ plugins = CODEGEN_PLUGINS,
+ deps = CODEGEN_DEPS + [
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/javapoet",
+ "@google_bazel_common//third_party/java/incap",
+ ],
+)
+
+java_plugin(
+ name = "component-codegen",
+ generates_api = 1,
+ output_licenses = ["unencumbered"],
+ processor_class = "dagger.internal.codegen.ComponentProcessor",
+ tags = [
+ "annotation=dagger.Component;" +
+ "genclass=${package}.Dagger${outerclasses}${classname}",
+ "annotation=dagger.producers.ProductionComponent;" +
+ "genclass=${package}.Dagger${outerclasses}${classname}",
+ ],
+ deps = [":processor"],
+)
+
+java_library(
+ name = "statistics",
+ srcs = STATISTICS_COLLECTOR_SRCS,
+ plugins = [":component-codegen"],
+ deps = [
+ ":base",
+ ":binding",
+ ":javac",
+ ":javac-plugin-module",
+ ":processor",
+ "//java/dagger:core",
+ "//java/dagger/model",
+ "@google_bazel_common//third_party/java/error_prone:check_api",
+ ],
+)
diff --git a/java/dagger/internal/codegen/Binding.java b/java/dagger/internal/codegen/Binding.java
new file mode 100644
index 0000000..c0f6b71
--- /dev/null
+++ b/java/dagger/internal/codegen/Binding.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Suppliers.memoize;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static java.util.stream.Collectors.toSet;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Scope;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/**
+ * An abstract type for classes representing a Dagger binding. Particularly, contains the {@link
+ * Element} that generated the binding and the {@link DependencyRequest} instances that are required
+ * to satisfy the binding, but leaves the specifics of the <i>mechanism</i> of the binding to the
+ * subtypes.
+ */
+abstract class Binding extends BindingDeclaration {
+
+ /**
+ * Returns {@code true} if using this binding requires an instance of the {@link
+ * #contributingModule()}.
+ */
+ boolean requiresModuleInstance() {
+ if (!bindingElement().isPresent() || !contributingModule().isPresent()) {
+ return false;
+ }
+ Set<Modifier> modifiers = bindingElement().get().getModifiers();
+ return !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC);
+ }
+
+ /**
+ * Returns {@code true} if this binding may provide {@code null} instead of an instance of {@link
+ * #key()}. Nullable bindings cannot be requested from {@linkplain DependencyRequest#isNullable()
+ * non-nullable dependency requests}.
+ */
+ abstract boolean isNullable();
+
+ /** The kind of binding this instance represents. */
+ abstract BindingKind kind();
+
+ /** The {@link BindingType} of this binding. */
+ abstract BindingType bindingType();
+
+ /** The {@link FrameworkType} of this binding. */
+ final FrameworkType frameworkType() {
+ return FrameworkType.forBindingType(bindingType());
+ }
+
+ /**
+ * The explicit set of {@link DependencyRequest dependencies} required to satisfy this binding as
+ * defined by the user-defined injection sites.
+ */
+ abstract ImmutableSet<DependencyRequest> explicitDependencies();
+
+ /**
+ * The set of {@link DependencyRequest dependencies} that are added by the framework rather than a
+ * user-defined injection site. This returns an unmodifiable set.
+ */
+ // TODO(gak): this will eventually get changed to return a set of FrameworkDependency
+ ImmutableSet<DependencyRequest> implicitDependencies() {
+ return ImmutableSet.of();
+ }
+
+ private final Supplier<ImmutableSet<DependencyRequest>> dependencies =
+ memoize(
+ () -> {
+ ImmutableSet<DependencyRequest> implicitDependencies = implicitDependencies();
+ return ImmutableSet.copyOf(
+ implicitDependencies.isEmpty()
+ ? explicitDependencies()
+ : Sets.union(implicitDependencies, explicitDependencies()));
+ });
+
+ /**
+ * The set of {@link DependencyRequest dependencies} required to satisfy this binding. This is the
+ * union of {@link #explicitDependencies()} and {@link #implicitDependencies()}. This returns an
+ * unmodifiable set.
+ */
+ final ImmutableSet<DependencyRequest> dependencies() {
+ return dependencies.get();
+ }
+
+ private final Supplier<ImmutableList<FrameworkDependency>> frameworkDependencies =
+ memoize(
+ () ->
+ dependencyAssociations()
+ .stream()
+ .map(DependencyAssociation::frameworkDependency)
+ .collect(toImmutableList()));
+
+ /**
+ * The framework dependencies of {@code binding}. There will be one element for each different
+ * binding key in the <em>{@linkplain Binding#unresolved() unresolved}</em> version of {@code
+ * binding}.
+ *
+ * <p>For example, given the following modules:
+ *
+ * <pre><code>
+ * {@literal @Module} abstract class {@literal BaseModule<T>} {
+ * {@literal @Provides} Foo provideFoo(T t, String string) {
+ * return …;
+ * }
+ * }
+ *
+ * {@literal @Module} class StringModule extends {@literal BaseModule<String>} {}
+ * </code></pre>
+ *
+ * Both dependencies of {@code StringModule.provideFoo} have the same binding key: {@code String}.
+ * But there are still two dependencies, because in the unresolved binding they have different
+ * binding keys:
+ *
+ * <dl>
+ * <dt>{@code T}
+ * <dd>{@code String t}
+ * <dt>{@code String}
+ * <dd>{@code String string}
+ * </dl>
+ *
+ * <p>Note that the sets returned by this method when called on the same binding will be equal,
+ * and their elements will be in the same order.
+ */
+ /* TODO(dpb): The stable-order postcondition is actually hard to verify in code for two equal
+ * instances of Binding, because it really depends on the order of the binding's dependencies,
+ * and two equal instances of Binding may have the same dependencies in a different order. */
+ final ImmutableList<FrameworkDependency> frameworkDependencies() {
+ return frameworkDependencies.get();
+ }
+
+ /**
+ * Associates a {@link FrameworkDependency} with the set of {@link DependencyRequest} instances
+ * that correlate for a binding.
+ */
+ @AutoValue
+ abstract static class DependencyAssociation {
+ abstract FrameworkDependency frameworkDependency();
+
+ abstract ImmutableSet<DependencyRequest> dependencyRequests();
+
+ static DependencyAssociation create(
+ FrameworkDependency frameworkDependency, Iterable<DependencyRequest> dependencyRequests) {
+ return new AutoValue_Binding_DependencyAssociation(
+ frameworkDependency, ImmutableSet.copyOf(dependencyRequests));
+ }
+ }
+
+ private final Supplier<ImmutableList<DependencyAssociation>> dependencyAssociations =
+ memoize(
+ () -> {
+ FrameworkTypeMapper frameworkTypeMapper =
+ FrameworkTypeMapper.forBindingType(bindingType());
+ ImmutableList.Builder<DependencyAssociation> list = ImmutableList.builder();
+ for (Set<DependencyRequest> requests : groupByUnresolvedKey()) {
+ list.add(
+ DependencyAssociation.create(
+ FrameworkDependency.create(
+ getOnlyElement(
+ requests.stream().map(DependencyRequest::key).collect(toSet())),
+ frameworkTypeMapper.getFrameworkType(requests)),
+ requests));
+ }
+ return list.build();
+ });
+
+ /**
+ * Returns the same {@link FrameworkDependency} instances from {@link #frameworkDependencies}, but
+ * with the set of {@link DependencyRequest} instances with which each is associated.
+ *
+ * <p>Ths method returns a list of {@link Map.Entry entries} rather than a {@link Map} or {@link
+ * com.google.common.collect.Multimap} because any given {@link FrameworkDependency} may appear
+ * multiple times if the {@linkplain Binding#unresolved() unresolved} binding requires it. If that
+ * distinction is not important, the entries can be merged into a single mapping.
+ */
+ final ImmutableList<DependencyAssociation> dependencyAssociations() {
+ return dependencyAssociations.get();
+ }
+
+ private final Supplier<ImmutableMap<DependencyRequest, FrameworkDependency>>
+ frameworkDependenciesMap =
+ memoize(
+ () -> {
+ ImmutableMap.Builder<DependencyRequest, FrameworkDependency> frameworkDependencies =
+ ImmutableMap.builder();
+ for (DependencyAssociation dependencyAssociation : dependencyAssociations()) {
+ for (DependencyRequest dependencyRequest :
+ dependencyAssociation.dependencyRequests()) {
+ frameworkDependencies.put(
+ dependencyRequest, dependencyAssociation.frameworkDependency());
+ }
+ }
+ return frameworkDependencies.build();
+ });
+
+ /**
+ * Returns the mapping from each {@linkplain #dependencies dependency} to its associated {@link
+ * FrameworkDependency}.
+ */
+ final ImmutableMap<DependencyRequest, FrameworkDependency>
+ dependenciesToFrameworkDependenciesMap() {
+ return frameworkDependenciesMap.get();
+ }
+
+ /**
+ * Groups {@code binding}'s implicit dependencies by their binding key, using the dependency keys
+ * from the {@link Binding#unresolved()} binding if it exists.
+ */
+ private ImmutableList<Set<DependencyRequest>> groupByUnresolvedKey() {
+ ImmutableSetMultimap.Builder<Key, DependencyRequest> dependenciesByKeyBuilder =
+ ImmutableSetMultimap.builder();
+ Iterator<DependencyRequest> dependencies = dependencies().iterator();
+ Binding unresolved = unresolved().isPresent() ? unresolved().get() : this;
+ Iterator<DependencyRequest> unresolvedDependencies = unresolved.dependencies().iterator();
+ while (dependencies.hasNext()) {
+ dependenciesByKeyBuilder.put(unresolvedDependencies.next().key(), dependencies.next());
+ }
+ return ImmutableList.copyOf(
+ Multimaps.asMap(
+ dependenciesByKeyBuilder.orderValuesBy(SourceFiles.DEPENDENCY_ORDERING).build())
+ .values());
+ }
+
+ /**
+ * If this binding's key's type parameters are different from those of the
+ * {@link #bindingTypeElement()}, this is the binding for the {@link #bindingTypeElement()}'s
+ * unresolved type.
+ */
+ abstract Optional<? extends Binding> unresolved();
+
+ Optional<Scope> scope() {
+ return Optional.empty();
+ }
+
+ // TODO(sameb): Remove the TypeElement parameter and pull it from the TypeMirror.
+ static boolean hasNonDefaultTypeParameters(
+ TypeElement element, TypeMirror type, DaggerTypes types) {
+ // If the element has no type parameters, nothing can be wrong.
+ if (element.getTypeParameters().isEmpty()) {
+ return false;
+ }
+
+ List<TypeMirror> defaultTypes = Lists.newArrayList();
+ for (TypeParameterElement parameter : element.getTypeParameters()) {
+ defaultTypes.add(parameter.asType());
+ }
+
+ List<TypeMirror> actualTypes =
+ type.accept(
+ new SimpleTypeVisitor6<List<TypeMirror>, Void>() {
+ @Override
+ protected List<TypeMirror> defaultAction(TypeMirror e, Void p) {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public List<TypeMirror> visitDeclared(DeclaredType t, Void p) {
+ return ImmutableList.<TypeMirror>copyOf(t.getTypeArguments());
+ }
+ },
+ null);
+
+ // The actual type parameter size can be different if the user is using a raw type.
+ if (defaultTypes.size() != actualTypes.size()) {
+ return true;
+ }
+
+ for (int i = 0; i < defaultTypes.size(); i++) {
+ if (!types.isSameType(defaultTypes.get(i), actualTypes.get(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingDeclaration.java b/java/dagger/internal/codegen/BindingDeclaration.java
new file mode 100644
index 0000000..c9520cd
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingDeclaration.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.BindingKind;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** An object that declares or specifies a binding. */
+abstract class BindingDeclaration {
+
+ /** The {@link Key} of this declaration. */
+ abstract Key key();
+
+ /**
+ * The {@link Element} that declares this binding. Absent for {@linkplain BindingKind binding
+ * kinds} that are not always declared by exactly one element.
+ *
+ * <p>For example, consider {@link BindingKind#MULTIBOUND_SET}. A component with many
+ * {@code @IntoSet} bindings for the same key will have a synthetic binding that depends on all
+ * contributions, but with no identifiying binding element. A {@code @Multibinds} method will also
+ * contribute a synthetic binding, but since multiple {@code @Multibinds} methods can coexist in
+ * the same component (and contribute to one single binding), it has no binding element.
+ */
+ // TODO(ronshapiro): examine whether this wildcard+bound have any benefit.
+ // We never actually refer to the overridden bindingElement methods directly in a way which needs
+ // anything more than an Element. Removing the wildcard would allow for simpler user-written code
+ // when the binding element is passed to a method.
+ abstract Optional<Element> bindingElement();
+
+ /**
+ * The type enclosing the {@link #bindingElement()}, or {@link Optional#empty()} if {@link
+ * #bindingElement()} is empty.
+ */
+ final Optional<TypeElement> bindingTypeElement() {
+ return bindingElement().map(DaggerElements::closestEnclosingTypeElement);
+ }
+
+ /**
+ * The installed module class that contributed the {@link #bindingElement()}. May be a subclass of
+ * the class that contains {@link #bindingElement()}. Absent if {@link #bindingElement()} is
+ * empty.
+ */
+ abstract Optional<TypeElement> contributingModule();
+}
diff --git a/java/dagger/internal/codegen/BindingDeclarationFormatter.java b/java/dagger/internal/codegen/BindingDeclarationFormatter.java
new file mode 100644
index 0000000..d850165
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingDeclarationFormatter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
+import static javax.lang.model.element.ElementKind.PARAMETER;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.EXECUTABLE;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+
+/**
+ * Formats a {@link BindingDeclaration} into a {@link String} suitable for use in error messages.
+ */
+final class BindingDeclarationFormatter extends Formatter<BindingDeclaration> {
+ private static final ImmutableSet<TypeKind> FORMATTABLE_ELEMENT_TYPE_KINDS =
+ immutableEnumSet(EXECUTABLE, DECLARED);
+
+ private final MethodSignatureFormatter methodSignatureFormatter;
+
+ @Inject
+ BindingDeclarationFormatter(MethodSignatureFormatter methodSignatureFormatter) {
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ }
+
+ /**
+ * Returns {@code true} for declarations that this formatter can format. Specifically bindings
+ * from subcomponent declarations or those with {@linkplain BindingDeclaration#bindingElement()
+ * binding elements} that are methods, constructors, or types.
+ */
+ boolean canFormat(BindingDeclaration bindingDeclaration) {
+ if (bindingDeclaration instanceof SubcomponentDeclaration) {
+ return true;
+ }
+ if (bindingDeclaration.bindingElement().isPresent()) {
+ Element bindingElement = bindingDeclaration.bindingElement().get();
+ return bindingElement.getKind().equals(PARAMETER)
+ || FORMATTABLE_ELEMENT_TYPE_KINDS.contains(bindingElement.asType().getKind());
+ }
+ // TODO(dpb): validate whether what this is doing is correct
+ return false;
+ }
+
+ @Override
+ public String format(BindingDeclaration bindingDeclaration) {
+ if (bindingDeclaration instanceof SubcomponentDeclaration) {
+ return formatSubcomponentDeclaration((SubcomponentDeclaration) bindingDeclaration);
+ }
+
+ if (bindingDeclaration.bindingElement().isPresent()) {
+ Element bindingElement = bindingDeclaration.bindingElement().get();
+ if (bindingElement.getKind().equals(PARAMETER)) {
+ return elementToString(bindingElement);
+ }
+
+ switch (bindingElement.asType().getKind()) {
+ case EXECUTABLE:
+ return methodSignatureFormatter.format(
+ MoreElements.asExecutable(bindingElement),
+ bindingDeclaration
+ .contributingModule()
+ .map(module -> MoreTypes.asDeclared(module.asType())));
+
+ case DECLARED:
+ return stripCommonTypePrefixes(bindingElement.asType().toString());
+
+ default:
+ throw new IllegalArgumentException(
+ "Formatting unsupported for element: " + bindingElement);
+ }
+ }
+
+ return String.format(
+ "Dagger-generated binding for %s",
+ stripCommonTypePrefixes(bindingDeclaration.key().toString()));
+ }
+
+ private String formatSubcomponentDeclaration(SubcomponentDeclaration subcomponentDeclaration) {
+ ImmutableList<TypeElement> moduleSubcomponents =
+ subcomponentDeclaration.moduleAnnotation().subcomponents();
+ int index = moduleSubcomponents.indexOf(subcomponentDeclaration.subcomponentType());
+ StringBuilder annotationValue = new StringBuilder();
+ if (moduleSubcomponents.size() != 1) {
+ annotationValue.append("{");
+ }
+ annotationValue.append(
+ formatArgumentInList(
+ index,
+ moduleSubcomponents.size(),
+ subcomponentDeclaration.subcomponentType().getQualifiedName() + ".class"));
+ if (moduleSubcomponents.size() != 1) {
+ annotationValue.append("}");
+ }
+
+ return String.format(
+ "@%s(subcomponents = %s) for %s",
+ subcomponentDeclaration.moduleAnnotation().annotationClass().getSimpleName(),
+ annotationValue,
+ subcomponentDeclaration.contributingModule().get());
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingElementValidator.java b/java/dagger/internal/codegen/BindingElementValidator.java
new file mode 100644
index 0000000..0051912
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingElementValidator.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
+import static dagger.internal.codegen.MapKeys.getMapKeys;
+import static dagger.internal.codegen.Scopes.scopesOf;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static javax.lang.model.type.TypeKind.ARRAY;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.TYPEVAR;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.MapKey;
+import dagger.Provides;
+import dagger.model.Key;
+import dagger.model.Scope;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.producers.Produces;
+import java.lang.annotation.Annotation;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Qualifier;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for elements that represent binding declarations. */
+abstract class BindingElementValidator<E extends Element> {
+ private final Class<? extends Annotation> bindingAnnotation;
+ private final AllowsMultibindings allowsMultibindings;
+ private final AllowsScoping allowsScoping;
+ private final Map<E, ValidationReport<E>> cache = new HashMap<>();
+
+ /**
+ * Creates a validator object.
+ *
+ * @param bindingAnnotation the annotation on an element that identifies it as a binding element
+ */
+ protected BindingElementValidator(
+ Class<? extends Annotation> bindingAnnotation,
+ AllowsMultibindings allowsMultibindings,
+ AllowsScoping allowsScoping) {
+ this.bindingAnnotation = bindingAnnotation;
+ this.allowsMultibindings = allowsMultibindings;
+ this.allowsScoping = allowsScoping;
+ }
+
+ /** Returns a {@link ValidationReport} for {@code element}. */
+ final ValidationReport<E> validate(E element) {
+ return reentrantComputeIfAbsent(cache, element, this::validateUncached);
+ }
+
+ private ValidationReport<E> validateUncached(E element) {
+ return elementValidator(element).validate();
+ }
+
+ /**
+ * Returns an error message of the form "<{@link #bindingElements()}> <i>rule</i>", where
+ * <i>rule</i> comes from calling {@link String#format(String, Object...)} on {@code ruleFormat}
+ * and the other arguments.
+ */
+ @FormatMethod
+ protected final String bindingElements(String ruleFormat, Object... args) {
+ return new Formatter().format("%s ", bindingElements()).format(ruleFormat, args).toString();
+ }
+
+ /**
+ * The kind of elements that this validator validates. Should be plural. Used for error reporting.
+ */
+ protected abstract String bindingElements();
+
+ /** The verb describing the {@link ElementValidator#bindingElementType()} in error messages. */
+ // TODO(ronshapiro,dpb): improve the name of this method and it's documentation.
+ protected abstract String bindingElementTypeVerb();
+
+ /** The error message when a binding element has a bad type. */
+ protected String badTypeMessage() {
+ return bindingElements(
+ "must %s a primitive, an array, a type variable, or a declared type",
+ bindingElementTypeVerb());
+ }
+
+ /**
+ * The error message when a the type for a binding element with {@link
+ * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a not set type.
+ */
+ protected String elementsIntoSetNotASetMessage() {
+ return bindingElements(
+ "annotated with @ElementsIntoSet must %s a Set", bindingElementTypeVerb());
+ }
+
+ /**
+ * The error message when a the type for a binding element with {@link
+ * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a raw set.
+ */
+ protected String elementsIntoSetRawSetMessage() {
+ return bindingElements(
+ "annotated with @ElementsIntoSet cannot %s a raw Set", bindingElementTypeVerb());
+ }
+
+ /*** Returns an {@link ElementValidator} for validating the given {@code element}. */
+ protected abstract ElementValidator elementValidator(E element);
+
+ /** Validator for a single binding element. */
+ protected abstract class ElementValidator {
+ protected final E element;
+ protected final ValidationReport.Builder<E> report;
+
+ protected ElementValidator(E element) {
+ this.element = element;
+ this.report = ValidationReport.about(element);
+ }
+
+ /** Checks the element for validity. */
+ private ValidationReport<E> validate() {
+ checkType();
+ checkQualifiers();
+ checkMapKeys();
+ checkMultibindings();
+ checkScopes();
+ checkAdditionalProperties();
+ return report.build();
+ }
+
+ /** Check any additional properties of the element. Does nothing by default. */
+ protected void checkAdditionalProperties() {}
+
+ /**
+ * The type declared by this binding element. This may differ from a binding's {@link
+ * Key#type()}, for example in multibindings. An {@link Optional#empty()} return value indicates
+ * that the contributed type is ambiguous or missing, i.e. a {@code @BindsInstance} method with
+ * zero or many parameters.
+ */
+ // TODO(dpb): should this be an ImmutableList<TypeMirror>, with this class checking the size?
+ protected abstract Optional<TypeMirror> bindingElementType();
+
+ /**
+ * Adds an error if the {@link #bindingElementType() binding element type} is not appropriate.
+ *
+ * <p>Adds an error if the type is not a primitive, array, declared type, or type variable.
+ *
+ * <p>If the binding is not a multibinding contribution, adds an error if the type is a
+ * framework type.
+ *
+ * <p>If the element has {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES}, adds an
+ * error if the type is not a {@code Set<T>} for some {@code T}
+ */
+ protected void checkType() {
+ switch (ContributionType.fromBindingElement(element)) {
+ case UNIQUE:
+ /* Validate that a unique binding is not attempting to bind a framework type. This
+ * validation is only appropriate for unique bindings because multibindings may collect
+ * framework types. E.g. Set<Provider<Foo>> is perfectly reasonable. */
+ checkFrameworkType();
+ // fall through
+
+ case SET:
+ case MAP:
+ bindingElementType().ifPresent(type -> checkKeyType(type));
+ break;
+
+ case SET_VALUES:
+ checkSetValuesType();
+ }
+ }
+
+ /**
+ * Adds an error if {@code keyType} is not a primitive, declared type, array, or type variable.
+ */
+ protected void checkKeyType(TypeMirror keyType) {
+ TypeKind kind = keyType.getKind();
+ if (kind.equals(VOID)) {
+ report.addError(bindingElements("must %s a value (not void)", bindingElementTypeVerb()));
+ } else if (!(kind.isPrimitive()
+ || kind.equals(DECLARED)
+ || kind.equals(ARRAY)
+ || kind.equals(TYPEVAR))) {
+ report.addError(badTypeMessage());
+ }
+ }
+
+ /**
+ * Adds an error if the type for an element with {@link ElementsIntoSet @ElementsIntoSet} or
+ * {@code SET_VALUES} is not a a {@code Set<T>} for a reasonable {@code T}.
+ */
+ // TODO(gak): should we allow "covariant return" for set values?
+ protected void checkSetValuesType() {
+ bindingElementType().ifPresent(keyType -> checkSetValuesType(keyType));
+ }
+
+ /** Adds an error if {@code type} is not a {@code Set<T>} for a reasonable {@code T}. */
+ protected final void checkSetValuesType(TypeMirror type) {
+ if (!SetType.isSet(type)) {
+ report.addError(elementsIntoSetNotASetMessage());
+ } else {
+ SetType setType = SetType.from(type);
+ if (setType.isRawType()) {
+ report.addError(elementsIntoSetRawSetMessage());
+ } else {
+ checkKeyType(setType.elementType());
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the element has more than one {@linkplain Qualifier qualifier} annotation.
+ */
+ private void checkQualifiers() {
+ ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(element);
+ if (qualifiers.size() > 1) {
+ for (AnnotationMirror qualifier : qualifiers) {
+ report.addError(
+ bindingElements("may not use more than one @Qualifier"),
+ element,
+ qualifier);
+ }
+ }
+ }
+
+ /**
+ * Adds an error if an {@link IntoMap @IntoMap} element doesn't have exactly one {@link
+ * MapKey @MapKey} annotation, or if an element that is {@link IntoMap @IntoMap} has any.
+ */
+ private void checkMapKeys() {
+ if (!allowsMultibindings.allowsMultibindings()) {
+ return;
+ }
+ ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(element);
+ if (ContributionType.fromBindingElement(element).equals(ContributionType.MAP)) {
+ switch (mapKeys.size()) {
+ case 0:
+ report.addError(bindingElements("of type map must declare a map key"));
+ break;
+ case 1:
+ break;
+ default:
+ report.addError(bindingElements("may not have more than one map key"));
+ break;
+ }
+ } else if (!mapKeys.isEmpty()) {
+ report.addError(bindingElements("of non map type cannot declare a map key"));
+ }
+ }
+
+ /**
+ * Adds errors if:
+ *
+ * <ul>
+ * <li>the element doesn't allow {@linkplain MultibindingAnnotations multibinding annotations}
+ * and has any
+ * <li>the element does allow them but has more than one
+ * <li>the element has a multibinding annotation and its {@link Provides} or {@link Produces}
+ * annotation has a {@code type} parameter.
+ * </ul>
+ */
+ private void checkMultibindings() {
+ ImmutableSet<AnnotationMirror> multibindingAnnotations =
+ MultibindingAnnotations.forElement(element);
+
+ switch (allowsMultibindings) {
+ case NO_MULTIBINDINGS:
+ for (AnnotationMirror annotation : multibindingAnnotations) {
+ report.addError(
+ bindingElements("cannot have multibinding annotations"),
+ element,
+ annotation);
+ }
+ break;
+
+ case ALLOWS_MULTIBINDINGS:
+ if (multibindingAnnotations.size() > 1) {
+ for (AnnotationMirror annotation : multibindingAnnotations) {
+ report.addError(
+ bindingElements("cannot have more than one multibinding annotation"),
+ element,
+ annotation);
+ }
+ }
+ break;
+ }
+
+ // TODO(ronshapiro): move this into ProvidesMethodValidator
+ if (bindingAnnotation.equals(Provides.class)) {
+ AnnotationMirror bindingAnnotationMirror =
+ getAnnotationMirror(element, bindingAnnotation).get();
+ boolean usesProvidesType = false;
+ for (ExecutableElement member : bindingAnnotationMirror.getElementValues().keySet()) {
+ usesProvidesType |= member.getSimpleName().contentEquals("type");
+ }
+ if (usesProvidesType && !multibindingAnnotations.isEmpty()) {
+ report.addError(
+ "@Provides.type cannot be used with multibinding annotations", element);
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the element has a scope but doesn't allow scoping, or if it has more than
+ * one {@linkplain Scope scope} annotation.
+ */
+ private void checkScopes() {
+ ImmutableSet<Scope> scopes = scopesOf(element);
+ String error = null;
+ switch (allowsScoping) {
+ case ALLOWS_SCOPING:
+ if (scopes.size() <= 1) {
+ return;
+ }
+ error = bindingElements("cannot use more than one @Scope");
+ break;
+ case NO_SCOPING:
+ error = bindingElements("cannot be scoped");
+ break;
+ }
+ verifyNotNull(error);
+ for (Scope scope : scopes) {
+ report.addError(error, element, scope.scopeAnnotation());
+ }
+ }
+
+ /**
+ * Adds an error if the {@link #bindingElementType() type} is a {@linkplain FrameworkTypes
+ * framework type}.
+ */
+ private void checkFrameworkType() {
+ if (bindingElementType().filter(FrameworkTypes::isFrameworkType).isPresent()) {
+ report.addError(bindingElements("must not %s framework types", bindingElementTypeVerb()));
+ }
+ }
+ }
+
+ /** Whether to check multibinding annotations. */
+ enum AllowsMultibindings {
+ /**
+ * This element disallows multibinding annotations, so don't bother checking for their validity.
+ * {@link MultibindingAnnotationsProcessingStep} will add errors if the element has any
+ * multibinding annotations.
+ */
+ NO_MULTIBINDINGS,
+
+ /** This element allows multibinding annotations, so validate them. */
+ ALLOWS_MULTIBINDINGS,
+ ;
+
+ private boolean allowsMultibindings() {
+ return this == ALLOWS_MULTIBINDINGS;
+ }
+ }
+
+ /** How to check scoping annotations. */
+ enum AllowsScoping {
+ /** This element disallows scoping, so check that no scope annotations are present. */
+ NO_SCOPING,
+
+ /** This element allows scoping, so validate that there's at most one scope annotation. */
+ ALLOWS_SCOPING,
+ ;
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
new file mode 100644
index 0000000..65200f7
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory of code expressions used to access a single request for a binding in a component. */
+// TODO(user): Rename this to RequestExpression?
+abstract class BindingExpression {
+
+ /**
+ * Returns an expression that evaluates to the value of a request based on the given requesting
+ * class.
+ *
+ * @param requestingClass the class that will contain the expression
+ */
+ abstract Expression getDependencyExpression(ClassName requestingClass);
+
+ /**
+ * Equivalent to {@link #getDependencyExpression} that is used only when the request is for an
+ * implementation of a component method. By default, just delegates to {@link
+ * #getDependencyExpression}.
+ */
+ Expression getDependencyExpressionForComponentMethod(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ return getDependencyExpression(component.name());
+ }
+
+ /** Returns {@code true} if this binding expression should be encapsulated in a method. */
+ boolean requiresMethodEncapsulation() {
+ return false;
+ }
+
+ /**
+ * Returns an expression for the implementation of a component method with the given request.
+ *
+ * @param component the component that will contain the implemented method
+ */
+ CodeBlock getComponentMethodImplementation(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ // By default, just delegate to #getDependencyExpression().
+ return CodeBlock.of(
+ "return $L;",
+ getDependencyExpressionForComponentMethod(componentMethod, component).codeBlock());
+ }
+
+ /**
+ * Returns an expression for the implementation of a modifiable binding method for the given
+ * component.
+ */
+ CodeBlock getModifiableBindingMethodImplementation(
+ ModifiableBindingMethod modifiableBindingMethod,
+ ComponentImplementation component,
+ DaggerTypes types) {
+ Expression dependencyExpression = getDependencyExpression(component.name());
+
+ // It's possible to have a case where a modifiable component method delegates to another
+ // binding method from an enclosing class that is not itself a component method. In that case,
+ // the enclosing class's method may return a publicly accessible type, but the nested class will
+ // have a return type that is defined by the component method. In that case, a downcast is
+ // necessary so that the return statement is valid.
+ //
+ // E.g.:
+ //
+ // public class DaggerAncestor implements Ancestor {
+ // protected Object packagePrivateModifiable() { ... }
+ //
+ // protected class LeafImpl extends DaggerLeaf {
+ // @Override
+ // public final PackagePrivateModifiable componentMethod() {
+ // return (PackagePrivateModifiable) DaggerAncestor.this.packagePrivateModifiable();
+ // }
+ // }
+ // }
+ //
+ // DaggerAncestor.packagePrivateModifiable returns Object even though the actual instance's type
+ // is PackagePrivateModifiable. So a cast is necessary.
+ //
+ // This isn't necessary for getComponentMethodImplementation() because that's only used for
+ // non-modifiable bindings
+ TypeMirror returnType = modifiableBindingMethod.returnType();
+ if (!types.isAssignable(dependencyExpression.type(), returnType)
+ && isTypeAccessibleFrom(returnType, component.name().packageName())) {
+ dependencyExpression = dependencyExpression.castTo(returnType);
+ }
+
+ return CodeBlock.of("return $L;", dependencyExpression.codeBlock());
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingFactory.java b/java/dagger/internal/codegen/BindingFactory.java
new file mode 100644
index 0000000..564b412
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingFactory.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.Binding.hasNonDefaultTypeParameters;
+import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod;
+import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.ContributionBinding.bindingKindForMultibindingKey;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
+import static dagger.internal.codegen.MapKeys.getMapKey;
+import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
+import static dagger.internal.codegen.Scopes.uniqueScopeOf;
+import static dagger.model.BindingKind.BOUND_INSTANCE;
+import static dagger.model.BindingKind.COMPONENT;
+import static dagger.model.BindingKind.COMPONENT_DEPENDENCY;
+import static dagger.model.BindingKind.COMPONENT_PRODUCTION;
+import static dagger.model.BindingKind.COMPONENT_PROVISION;
+import static dagger.model.BindingKind.DELEGATE;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.MEMBERS_INJECTOR;
+import static dagger.model.BindingKind.OPTIONAL;
+import static dagger.model.BindingKind.PRODUCTION;
+import static dagger.model.BindingKind.PROVISION;
+import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
+import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
+import static javax.lang.model.element.ElementKind.METHOD;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Iterables;
+import dagger.Module;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.ProductionBinding.ProductionKind;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory for {@link Binding} objects. */
+final class BindingFactory {
+ private final DaggerTypes types;
+ private final KeyFactory keyFactory;
+ private final DependencyRequestFactory dependencyRequestFactory;
+ private final InjectionSiteFactory injectionSiteFactory;
+ private final DaggerElements elements;
+
+ @Inject
+ BindingFactory(
+ DaggerTypes types,
+ DaggerElements elements,
+ KeyFactory keyFactory,
+ DependencyRequestFactory dependencyRequestFactory,
+ InjectionSiteFactory injectionSiteFactory) {
+ this.types = types;
+ this.elements = elements;
+ this.keyFactory = keyFactory;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ this.injectionSiteFactory = injectionSiteFactory;
+ }
+
+ /**
+ * Returns an {@link dagger.model.BindingKind#INJECTION} binding.
+ *
+ * @param constructorElement the {@code @Inject}-annotated constructor
+ * @param resolvedType the parameterized type if the constructor is for a generic class and the
+ * binding should be for the parameterized type
+ */
+ // TODO(dpb): See if we can just pass the parameterized type and not also the constructor.
+ ProvisionBinding injectionBinding(
+ ExecutableElement constructorElement, Optional<TypeMirror> resolvedType) {
+ checkArgument(constructorElement.getKind().equals(CONSTRUCTOR));
+ checkArgument(isAnnotationPresent(constructorElement, Inject.class));
+ checkArgument(!getQualifier(constructorElement).isPresent());
+
+ ExecutableType constructorType = MoreTypes.asExecutable(constructorElement.asType());
+ DeclaredType constructedType =
+ MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
+ // If the class this is constructing has some type arguments, resolve everything.
+ if (!constructedType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
+ DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
+ // Validate that we're resolving from the correct type.
+ checkState(
+ types.isSameType(types.erasure(resolved), types.erasure(constructedType)),
+ "erased expected type: %s, erased actual type: %s",
+ types.erasure(resolved),
+ types.erasure(constructedType));
+ constructorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement));
+ constructedType = resolved;
+ }
+
+ Key key = keyFactory.forInjectConstructorWithResolvedType(constructedType);
+ ImmutableSet<DependencyRequest> provisionDependencies =
+ dependencyRequestFactory.forRequiredResolvedVariables(
+ constructorElement.getParameters(), constructorType.getParameterTypes());
+
+ ProvisionBinding.Builder builder =
+ ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(constructorElement)
+ .key(key)
+ .provisionDependencies(provisionDependencies)
+ .injectionSites(injectionSiteFactory.getInjectionSites(constructedType))
+ .kind(INJECTION)
+ .scope(uniqueScopeOf(constructorElement.getEnclosingElement()));
+
+ TypeElement bindingTypeElement = MoreElements.asType(constructorElement.getEnclosingElement());
+ if (hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types)) {
+ builder.unresolved(injectionBinding(constructorElement, Optional.empty()));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#PROVISION} binding for a {@code @Provides}-annotated
+ * method.
+ *
+ * @param contributedBy the installed module that declares or inherits the method
+ */
+ ProvisionBinding providesMethodBinding(
+ ExecutableElement providesMethod, TypeElement contributedBy) {
+ return setMethodBindingProperties(
+ ProvisionBinding.builder(),
+ providesMethod,
+ contributedBy,
+ keyFactory.forProvidesMethod(providesMethod, contributedBy),
+ this::providesMethodBinding)
+ .kind(PROVISION)
+ .scope(uniqueScopeOf(providesMethod))
+ .nullableType(getNullableType(providesMethod))
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#PRODUCTION} binding for a {@code @Produces}-annotated
+ * method.
+ *
+ * @param contributedBy the installed module that declares or inherits the method
+ */
+ ProductionBinding producesMethodBinding(
+ ExecutableElement producesMethod, TypeElement contributedBy) {
+ // TODO(beder): Add nullability checking with Java 8.
+ ProductionBinding.Builder builder =
+ setMethodBindingProperties(
+ ProductionBinding.builder(),
+ producesMethod,
+ contributedBy,
+ keyFactory.forProducesMethod(producesMethod, contributedBy),
+ this::producesMethodBinding)
+ .kind(PRODUCTION)
+ .productionKind(ProductionKind.fromProducesMethod(producesMethod))
+ .thrownTypes(producesMethod.getThrownTypes())
+ .executorRequest(dependencyRequestFactory.forProductionImplementationExecutor())
+ .monitorRequest(dependencyRequestFactory.forProductionComponentMonitor());
+ return builder.build();
+ }
+
+ private <C extends ContributionBinding, B extends ContributionBinding.Builder<C, B>>
+ B setMethodBindingProperties(
+ B builder,
+ ExecutableElement method,
+ TypeElement contributedBy,
+ Key key,
+ BiFunction<ExecutableElement, TypeElement, C> create) {
+ checkArgument(method.getKind().equals(METHOD));
+ ExecutableType methodType =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(contributedBy.asType()), method));
+ if (!types.isSameType(methodType, method.asType())) {
+ builder.unresolved(create.apply(method, MoreElements.asType(method.getEnclosingElement())));
+ }
+ return builder
+ .contributionType(ContributionType.fromBindingElement(method))
+ .bindingElement(method)
+ .contributingModule(contributedBy)
+ .key(key)
+ .dependencies(
+ dependencyRequestFactory.forRequiredResolvedVariables(
+ method.getParameters(), methodType.getParameterTypes()))
+ .wrappedMapKeyAnnotation(wrapOptionalInEquivalence(getMapKey(method)));
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#MULTIBOUND_MAP} or {@link
+ * dagger.model.BindingKind#MULTIBOUND_SET} binding given a set of multibinding contribution
+ * bindings.
+ *
+ * @param key a key that may be satisfied by a multibinding
+ */
+ ContributionBinding syntheticMultibinding(
+ Key key, Iterable<ContributionBinding> multibindingContributions) {
+ ContributionBinding.Builder<?, ?> builder =
+ multibindingRequiresProduction(key, multibindingContributions)
+ ? ProductionBinding.builder()
+ : ProvisionBinding.builder();
+ return builder
+ .contributionType(ContributionType.UNIQUE)
+ .key(key)
+ .dependencies(
+ dependencyRequestFactory.forMultibindingContributions(key, multibindingContributions))
+ .kind(bindingKindForMultibindingKey(key))
+ .build();
+ }
+
+ private boolean multibindingRequiresProduction(
+ Key key, Iterable<ContributionBinding> multibindingContributions) {
+ if (MapType.isMap(key)) {
+ MapType mapType = MapType.from(key);
+ if (mapType.valuesAreTypeOf(Producer.class) || mapType.valuesAreTypeOf(Produced.class)) {
+ return true;
+ }
+ } else if (SetType.isSet(key) && SetType.from(key).elementsAreTypeOf(Produced.class)) {
+ return true;
+ }
+ return Iterables.any(
+ multibindingContributions, binding -> binding.bindingType().equals(BindingType.PRODUCTION));
+ }
+
+ /** Returns a {@link dagger.model.BindingKind#COMPONENT} binding for the component. */
+ ProvisionBinding componentBinding(TypeElement componentDefinitionType) {
+ checkNotNull(componentDefinitionType);
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(componentDefinitionType)
+ .key(keyFactory.forType(componentDefinitionType.asType()))
+ .kind(COMPONENT)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#COMPONENT_DEPENDENCY} binding for a component's
+ * dependency.
+ */
+ ProvisionBinding componentDependencyBinding(ComponentRequirement dependency) {
+ checkNotNull(dependency);
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(dependency.typeElement())
+ .key(keyFactory.forType(dependency.type()))
+ .kind(COMPONENT_DEPENDENCY)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#COMPONENT_PROVISION} or {@link
+ * dagger.model.BindingKind#COMPONENT_PRODUCTION} binding for a method on a component's
+ * dependency.
+ *
+ * @param componentDescriptor the component with the dependency, not the dependency that has the
+ * method
+ */
+ ContributionBinding componentDependencyMethodBinding(
+ ComponentDescriptor componentDescriptor, ExecutableElement dependencyMethod) {
+ checkArgument(dependencyMethod.getKind().equals(METHOD));
+ checkArgument(dependencyMethod.getParameters().isEmpty());
+ ContributionBinding.Builder<?, ?> builder;
+ if (componentDescriptor.isProduction()
+ && isComponentProductionMethod(elements, dependencyMethod)) {
+ builder =
+ ProductionBinding.builder()
+ .key(keyFactory.forProductionComponentMethod(dependencyMethod))
+ .kind(COMPONENT_PRODUCTION)
+ .thrownTypes(dependencyMethod.getThrownTypes());
+ } else {
+ builder =
+ ProvisionBinding.builder()
+ .key(keyFactory.forComponentMethod(dependencyMethod))
+ .nullableType(getNullableType(dependencyMethod))
+ .kind(COMPONENT_PROVISION)
+ .scope(uniqueScopeOf(dependencyMethod));
+ }
+ return builder
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(dependencyMethod)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#BOUND_INSTANCE} binding for a
+ * {@code @BindsInstance}-annotated builder setter method or factory method parameter.
+ */
+ ProvisionBinding boundInstanceBinding(ComponentRequirement requirement, Element element) {
+ checkArgument(element instanceof VariableElement || element instanceof ExecutableElement);
+ VariableElement parameterElement =
+ element instanceof VariableElement
+ ? MoreElements.asVariable(element)
+ : getOnlyElement(MoreElements.asExecutable(element).getParameters());
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(element)
+ .key(requirement.key().get())
+ .nullableType(getNullableType(parameterElement))
+ .kind(BOUND_INSTANCE)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared by a component
+ * method that returns a subcomponent builder. Use {{@link
+ * #subcomponentCreatorBinding(ImmutableSet)}} for bindings declared using {@link
+ * Module#subcomponents()}.
+ *
+ * @param component the component that declares or inherits the method
+ */
+ ProvisionBinding subcomponentCreatorBinding(
+ ExecutableElement subcomponentCreatorMethod, TypeElement component) {
+ checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD));
+ checkArgument(subcomponentCreatorMethod.getParameters().isEmpty());
+ Key key =
+ keyFactory.forSubcomponentCreatorMethod(
+ subcomponentCreatorMethod, asDeclared(component.asType()));
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(subcomponentCreatorMethod)
+ .key(key)
+ .kind(SUBCOMPONENT_CREATOR)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared using {@link
+ * Module#subcomponents()}.
+ */
+ ProvisionBinding subcomponentCreatorBinding(
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
+ SubcomponentDeclaration subcomponentDeclaration = subcomponentDeclarations.iterator().next();
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .key(subcomponentDeclaration.key())
+ .kind(SUBCOMPONENT_CREATOR)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#DELEGATE} binding.
+ *
+ * @param delegateDeclaration the {@code @Binds}-annotated declaration
+ * @param actualBinding the binding that satisfies the {@code @Binds} declaration
+ */
+ ContributionBinding delegateBinding(
+ DelegateDeclaration delegateDeclaration, ContributionBinding actualBinding) {
+ switch (actualBinding.bindingType()) {
+ case PRODUCTION:
+ return buildDelegateBinding(
+ ProductionBinding.builder().nullableType(actualBinding.nullableType()),
+ delegateDeclaration,
+ Producer.class);
+
+ case PROVISION:
+ return buildDelegateBinding(
+ ProvisionBinding.builder()
+ .scope(uniqueScopeOf(delegateDeclaration.bindingElement().get()))
+ .nullableType(actualBinding.nullableType()),
+ delegateDeclaration,
+ Provider.class);
+
+ case MEMBERS_INJECTION: // fall-through to throw
+ }
+ throw new AssertionError("bindingType: " + actualBinding);
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#DELEGATE} binding used when there is no binding that
+ * satisfies the {@code @Binds} declaration.
+ */
+ ContributionBinding unresolvedDelegateBinding(DelegateDeclaration delegateDeclaration) {
+ return buildDelegateBinding(
+ ProvisionBinding.builder().scope(uniqueScopeOf(delegateDeclaration.bindingElement().get())),
+ delegateDeclaration,
+ Provider.class);
+ }
+
+ private ContributionBinding buildDelegateBinding(
+ ContributionBinding.Builder<?, ?> builder,
+ DelegateDeclaration delegateDeclaration,
+ Class<?> frameworkType) {
+ return builder
+ .contributionType(delegateDeclaration.contributionType())
+ .bindingElement(delegateDeclaration.bindingElement().get())
+ .contributingModule(delegateDeclaration.contributingModule().get())
+ .key(keyFactory.forDelegateBinding(delegateDeclaration, frameworkType))
+ .dependencies(delegateDeclaration.delegateRequest())
+ .wrappedMapKeyAnnotation(delegateDeclaration.wrappedMapKey())
+ .kind(DELEGATE)
+ .build();
+ }
+
+ /**
+ * Returns an {@link dagger.model.BindingKind#OPTIONAL} binding for {@code key}.
+ *
+ * @param requestKind the kind of request for the optional binding
+ * @param underlyingKeyBindings the possibly empty set of bindings that exist in the component for
+ * the underlying (non-optional) key
+ */
+ ContributionBinding syntheticOptionalBinding(
+ Key key, RequestKind requestKind, ResolvedBindings underlyingKeyBindings) {
+ ContributionBinding.Builder<?, ?> builder =
+ syntheticOptionalBindingBuilder(requestKind, underlyingKeyBindings)
+ .contributionType(ContributionType.UNIQUE)
+ .key(key)
+ .kind(OPTIONAL);
+ if (!underlyingKeyBindings.isEmpty()) {
+ builder.dependencies(
+ dependencyRequestFactory.forSyntheticPresentOptionalBinding(key, requestKind));
+ }
+ return builder.build();
+ }
+
+ private ContributionBinding.Builder<?, ?> syntheticOptionalBindingBuilder(
+ RequestKind requestKind, ResolvedBindings underlyingKeyBindings) {
+ return !underlyingKeyBindings.isEmpty()
+ && (underlyingKeyBindings.bindingTypes().contains(BindingType.PRODUCTION)
+ || requestKind.equals(RequestKind.PRODUCER) // handles producerFromProvider cases
+ || requestKind.equals(RequestKind.PRODUCED)) // handles producerFromProvider cases
+ ? ProductionBinding.builder()
+ : ProvisionBinding.builder();
+ }
+
+ /** Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTOR} binding. */
+ ProvisionBinding membersInjectorBinding(
+ Key key, MembersInjectionBinding membersInjectionBinding) {
+ return ProvisionBinding.builder()
+ .key(key)
+ .contributionType(ContributionType.UNIQUE)
+ .kind(MEMBERS_INJECTOR)
+ .bindingElement(MoreTypes.asTypeElement(membersInjectionBinding.key().type()))
+ .provisionDependencies(membersInjectionBinding.dependencies())
+ .injectionSites(membersInjectionBinding.injectionSites())
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTION} binding.
+ *
+ * @param resolvedType if {@code declaredType} is a generic class and {@code resolvedType} is a
+ * parameterization of that type, the returned binding will be for the resolved type
+ */
+ // TODO(dpb): See if we can just pass one nongeneric/parameterized type.
+ MembersInjectionBinding membersInjectionBinding(
+ DeclaredType declaredType, Optional<TypeMirror> resolvedType) {
+ // If the class this is injecting has some type arguments, resolve everything.
+ if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
+ DeclaredType resolved = asDeclared(resolvedType.get());
+ // Validate that we're resolving from the correct type.
+ checkState(
+ types.isSameType(types.erasure(resolved), types.erasure(declaredType)),
+ "erased expected type: %s, erased actual type: %s",
+ types.erasure(resolved),
+ types.erasure(declaredType));
+ declaredType = resolved;
+ }
+ ImmutableSortedSet<InjectionSite> injectionSites =
+ injectionSiteFactory.getInjectionSites(declaredType);
+ ImmutableSet<DependencyRequest> dependencies =
+ injectionSites
+ .stream()
+ .flatMap(injectionSite -> injectionSite.dependencies().stream())
+ .collect(toImmutableSet());
+
+ Key key = keyFactory.forMembersInjectedType(declaredType);
+ TypeElement typeElement = MoreElements.asType(declaredType.asElement());
+ return new AutoValue_MembersInjectionBinding(
+ key,
+ dependencies,
+ typeElement,
+ hasNonDefaultTypeParameters(typeElement, key.type(), types)
+ ? Optional.of(
+ membersInjectionBinding(asDeclared(typeElement.asType()), Optional.empty()))
+ : Optional.empty(),
+ injectionSites);
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
new file mode 100644
index 0000000..2f548b2
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraph.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.DaggerStreams.presentValues;
+import static dagger.internal.codegen.DaggerStreams.stream;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import com.google.common.graph.Traverser;
+import dagger.Subcomponent;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/** The canonical representation of a full-resolved graph. */
+@AutoValue
+abstract class BindingGraph {
+ abstract ComponentDescriptor componentDescriptor();
+
+ /**
+ * The resolved bindings for all {@link ContributionBinding}s in this graph, keyed by {@link Key}.
+ */
+ // TODO(ronshapiro): when MembersInjectionBinding no longer extends Binding, rename this to
+ // bindings()
+ abstract ImmutableMap<Key, ResolvedBindings> contributionBindings();
+
+ /**
+ * The resolved bindings for all {@link MembersInjectionBinding}s in this graph, keyed by {@link
+ * Key}.
+ */
+ abstract ImmutableMap<Key, ResolvedBindings> membersInjectionBindings();
+
+ /**
+ * Returns the {@link ResolvedBindings resolved bindings} instance for {@code
+ * bindingExpressionKey}. If the bindings will be used for members injection, a {@link
+ * ResolvedBindings} with {@linkplain #membersInjectionBindings() members injection bindings} will
+ * be returned, otherwise a {@link ResolvedBindings} with {@link #contributionBindings()} will be
+ * returned.
+ */
+ final ResolvedBindings resolvedBindings(BindingRequest request) {
+ return request.isRequestKind(RequestKind.MEMBERS_INJECTION)
+ ? membersInjectionBindings().get(request.key())
+ : contributionBindings().get(request.key());
+ }
+
+ final Iterable<ResolvedBindings> resolvedBindings() {
+ // Don't return an immutable collection - this is only ever used for looping over all bindings
+ // in the graph. Copying is wasteful, especially if is a hashing collection, since the values
+ // should all, by definition, be distinct.
+ // TODO(dpb): consider inlining this to callers and removing this.
+ return Iterables.concat(membersInjectionBindings().values(), contributionBindings().values());
+ }
+
+ abstract ImmutableList<BindingGraph> subgraphs();
+
+ /**
+ * The type that defines the component for this graph.
+ *
+ * @see ComponentDescriptor#typeElement()
+ */
+ TypeElement componentTypeElement() {
+ return componentDescriptor().typeElement();
+ }
+
+ /**
+ * Returns the set of modules that are owned by this graph regardless of whether or not any of
+ * their bindings are used in this graph. For graphs representing top-level {@link
+ * dagger.Component components}, this set will be the same as {@linkplain
+ * ComponentDescriptor#modules() the component's transitive modules}. For {@linkplain Subcomponent
+ * subcomponents}, this set will be the transitive modules that are not owned by any of their
+ * ancestors.
+ */
+ abstract ImmutableSet<ModuleDescriptor> ownedModules();
+
+ @Memoized
+ ImmutableSet<TypeElement> ownedModuleTypes() {
+ return FluentIterable.from(ownedModules()).transform(ModuleDescriptor::moduleElement).toSet();
+ }
+
+ /**
+ * Returns the factory method for this subcomponent, if it exists.
+ *
+ * <p>This factory method is the one defined in the parent component's interface.
+ *
+ * <p>In the example below, the {@link BindingGraph#factoryMethod} for {@code ChildComponent}
+ * would return the {@link ExecutableElement}: {@code childComponent(ChildModule1)} .
+ *
+ * <pre><code>
+ * {@literal @Component}
+ * interface ParentComponent {
+ * ChildComponent childComponent(ChildModule1 childModule);
+ * }
+ * </code></pre>
+ */
+ // TODO(b/73294201): Consider returning the resolved ExecutableType for the factory method.
+ abstract Optional<ExecutableElement> factoryMethod();
+
+ /**
+ * Returns a map between the {@linkplain ComponentRequirement component requirement} and the
+ * corresponding {@link VariableElement} for each module parameter in the {@linkplain
+ * BindingGraph#factoryMethod factory method}.
+ */
+ // TODO(dpb): Consider disallowing modules if none of their bindings are used.
+ ImmutableMap<ComponentRequirement, VariableElement> factoryMethodParameters() {
+ checkState(factoryMethod().isPresent());
+ ImmutableMap.Builder<ComponentRequirement, VariableElement> builder = ImmutableMap.builder();
+ for (VariableElement parameter : factoryMethod().get().getParameters()) {
+ builder.put(ComponentRequirement.forModule(parameter.asType()), parameter);
+ }
+ return builder.build();
+ }
+
+ private static final Traverser<BindingGraph> SUBGRAPH_TRAVERSER =
+ Traverser.forTree(BindingGraph::subgraphs);
+
+ /**
+ * The types for which the component needs instances.
+ *
+ * <ul>
+ * <li>component dependencies
+ * <li>{@linkplain #ownedModules() owned modules} with concrete instance bindings that are used
+ * in the graph
+ * <li>bound instances
+ * </ul>
+ */
+ @Memoized
+ ImmutableSet<ComponentRequirement> componentRequirements() {
+ ImmutableSet<TypeElement> requiredModules = requiredModuleElements();
+ ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
+ componentDescriptor().requirements().stream()
+ .filter(
+ requirement ->
+ !requirement.kind().isModule()
+ || requiredModules.contains(requirement.typeElement()))
+ .forEach(requirements::add);
+ if (factoryMethod().isPresent()) {
+ requirements.addAll(factoryMethodParameters().keySet());
+ }
+ return requirements.build();
+ }
+
+ private ImmutableSet<TypeElement> requiredModuleElements() {
+ return stream(SUBGRAPH_TRAVERSER.depthFirstPostOrder(this))
+ .flatMap(graph -> graph.contributionBindings().values().stream())
+ .flatMap(bindings -> bindings.contributionBindings().stream())
+ .map(ContributionBinding::contributingModule)
+ .distinct()
+ .flatMap(presentValues())
+ .filter(ownedModuleTypes()::contains)
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the {@link ComponentDescriptor}s for this component and its subcomponents. */
+ ImmutableSet<ComponentDescriptor> componentDescriptors() {
+ return FluentIterable.from(SUBGRAPH_TRAVERSER.depthFirstPreOrder(this))
+ .transform(BindingGraph::componentDescriptor)
+ .toSet();
+ }
+
+ /**
+ * {@code true} if this graph contains all bindings installed in the component; {@code false} if
+ * it contains only those bindings that are reachable from at least one entry point.
+ */
+ abstract boolean isFullBindingGraph();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override // Suppresses ErrorProne warning that hashCode was overridden w/o equals
+ public abstract boolean equals(Object other);
+
+ static BindingGraph create(
+ ComponentDescriptor componentDescriptor,
+ Map<Key, ResolvedBindings> resolvedContributionBindingsMap,
+ Map<Key, ResolvedBindings> resolvedMembersInjectionBindings,
+ List<BindingGraph> subgraphs,
+ Set<ModuleDescriptor> ownedModules,
+ Optional<ExecutableElement> factoryMethod,
+ boolean isFullBindingGraph) {
+ checkForDuplicates(subgraphs);
+ return new AutoValue_BindingGraph(
+ componentDescriptor,
+ ImmutableMap.copyOf(resolvedContributionBindingsMap),
+ ImmutableMap.copyOf(resolvedMembersInjectionBindings),
+ ImmutableList.copyOf(subgraphs),
+ ImmutableSet.copyOf(ownedModules),
+ factoryMethod,
+ isFullBindingGraph);
+ }
+
+ private static final void checkForDuplicates(Iterable<BindingGraph> graphs) {
+ Map<TypeElement, Collection<BindingGraph>> duplicateGraphs =
+ Maps.filterValues(
+ Multimaps.index(graphs, graph -> graph.componentDescriptor().typeElement()).asMap(),
+ overlapping -> overlapping.size() > 1);
+ if (!duplicateGraphs.isEmpty()) {
+ throw new IllegalArgumentException("Expected no duplicates: " + duplicateGraphs);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingGraphConverter.java b/java/dagger/internal/codegen/BindingGraphConverter.java
new file mode 100644
index 0000000..a2cc799
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraphConverter.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.DaggerGraphs.unreachableNodes;
+import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.graph.MutableNetwork;
+import com.google.common.graph.Network;
+import com.google.common.graph.NetworkBuilder;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.MissingBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.BindingGraphProxies;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** Converts {@link dagger.internal.codegen.BindingGraph}s to {@link dagger.model.BindingGraph}s. */
+final class BindingGraphConverter {
+ private final BindingDeclarationFormatter bindingDeclarationFormatter;
+
+ @Inject
+ BindingGraphConverter(BindingDeclarationFormatter bindingDeclarationFormatter) {
+ this.bindingDeclarationFormatter = bindingDeclarationFormatter;
+ }
+
+ /**
+ * Creates the external {@link dagger.model.BindingGraph} representing the given internal {@link
+ * dagger.internal.codegen.BindingGraph}.
+ */
+ dagger.model.BindingGraph convert(BindingGraph bindingGraph) {
+ Traverser traverser = new Traverser(bindingGraph);
+ traverser.traverseComponents();
+
+ // When bindings are copied down into child graphs because they transitively depend on local
+ // multibindings or optional bindings, the parent-owned binding is still there. If that
+ // parent-owned binding is not reachable from its component, it doesn't need to be in the graph
+ // because it will never be used. So remove all nodes that are not reachable from the root
+ // component—unless we're converting a full binding graph.
+ if (!bindingGraph.isFullBindingGraph()) {
+ unreachableNodes(traverser.network.asGraph(), rootComponentNode(traverser.network))
+ .forEach(traverser.network::removeNode);
+ }
+
+ return BindingGraphProxies.bindingGraph(traverser.network, bindingGraph.isFullBindingGraph());
+ }
+
+ // TODO(dpb): Example of BindingGraph logic applied to derived networks.
+ private ComponentNode rootComponentNode(Network<Node, Edge> network) {
+ return (ComponentNode)
+ Iterables.find(
+ network.nodes(),
+ node -> node instanceof ComponentNode && node.componentPath().atRoot());
+ }
+
+ private final class Traverser extends ComponentTreeTraverser {
+ private final MutableNetwork<Node, Edge> network =
+ NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
+ private final boolean isRootSubcomponent;
+ private final boolean isFullBindingGraph;
+
+ private final ComponentPath rootComponentPath;
+ private ComponentNode parentComponent;
+ private ComponentNode currentComponent;
+
+ Traverser(BindingGraph graph) {
+ super(graph);
+ rootComponentPath = ComponentPath.create(ImmutableList.of(graph.componentTypeElement()));
+ isRootSubcomponent = graph.componentDescriptor().isSubcomponent();
+ isFullBindingGraph = graph.isFullBindingGraph();
+ }
+
+ @Override
+ protected void visitComponent(BindingGraph graph) {
+ ComponentNode grandparentComponent = parentComponent;
+ parentComponent = currentComponent;
+ currentComponent = ComponentNodeImpl.create(componentPath(), graph.componentDescriptor());
+
+ network.addNode(currentComponent);
+
+ for (ResolvedBindings resolvedBindings : graph.resolvedBindings()) {
+ for (BindingNode binding : bindingNodes(resolvedBindings)) {
+ addBinding(binding);
+ if (binding.kind().equals(SUBCOMPONENT_CREATOR)
+ && binding.componentPath().equals(currentComponent.componentPath())) {
+ network.addEdge(
+ binding,
+ subcomponentNode(binding.key().type(), graph),
+ new SubcomponentCreatorBindingEdgeImpl(
+ resolvedBindings.subcomponentDeclarations()));
+ }
+ }
+ }
+
+ super.visitComponent(graph);
+
+ currentComponent = parentComponent;
+ parentComponent = grandparentComponent;
+ }
+
+ @Override
+ protected void visitEntryPoint(DependencyRequest entryPoint, BindingGraph graph) {
+ addDependencyEdges(currentComponent, entryPoint);
+ super.visitEntryPoint(entryPoint, graph);
+ }
+
+ @Override
+ protected void visitSubcomponentFactoryMethod(
+ BindingGraph graph, BindingGraph parent, ExecutableElement factoryMethod) {
+ network.addEdge(
+ parentComponent, currentComponent, new ChildFactoryMethodEdgeImpl(factoryMethod));
+ super.visitSubcomponentFactoryMethod(graph, parent, factoryMethod);
+ }
+
+ /**
+ * Adds a {@link dagger.model.BindingGraph.DependencyEdge} from a node to the binding(s) that
+ * satisfy a dependency request.
+ */
+ private void addDependencyEdges(Node source, DependencyRequest dependencyRequest) {
+ ResolvedBindings dependencies = resolvedDependencies(source, dependencyRequest);
+ if (dependencies.isEmpty()) {
+ addDependencyEdge(source, dependencyRequest, missingBindingNode(dependencies));
+ } else {
+ for (BindingNode dependency : bindingNodes(dependencies)) {
+ addDependencyEdge(source, dependencyRequest, dependency);
+ }
+ }
+ }
+
+ private void addDependencyEdge(
+ Node source, DependencyRequest dependencyRequest, Node dependency) {
+ network.addNode(dependency);
+ if (!hasDependencyEdge(source, dependency, dependencyRequest)) {
+ network.addEdge(
+ source,
+ dependency,
+ new DependencyEdgeImpl(dependencyRequest, source instanceof ComponentNode));
+ }
+ }
+
+ private boolean hasDependencyEdge(
+ Node source, Node dependency, DependencyRequest dependencyRequest) {
+ // An iterative approach is used instead of a Stream because this method is called in a hot
+ // loop, and the Stream calculates the size of network.edgesConnecting(), which is slow. This
+ // seems to be because caculating the edges connecting two nodes in a Network that supports
+ // parallel edges is must check the equality of many nodes, and BindingNode's equality
+ // semantics drag in the equality of many other expensive objects
+ for (Edge edge : network.edgesConnecting(source, dependency)) {
+ if (edge instanceof DependencyEdge) {
+ if (((DependencyEdge) edge).dependencyRequest().equals(dependencyRequest)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private ResolvedBindings resolvedDependencies(
+ Node source, DependencyRequest dependencyRequest) {
+ return graphForAncestor(source.componentPath().currentComponent())
+ .resolvedBindings(bindingRequest(dependencyRequest));
+ }
+
+ /** Adds a binding and all its dependencies. */
+ private void addBinding(BindingNode binding) {
+ network.addNode(binding);
+ for (DependencyRequest dependencyRequest : binding.dependencies()) {
+ addDependencyEdges(binding, dependencyRequest);
+ }
+ }
+
+ private ImmutableSet<BindingNode> bindingNodes(ResolvedBindings resolvedBindings) {
+ ImmutableSet.Builder<BindingNode> bindingNodes = ImmutableSet.builder();
+ resolvedBindings
+ .allBindings()
+ .asMap()
+ .forEach(
+ (component, bindings) -> {
+ for (Binding binding : bindings) {
+ bindingNodes.add(bindingNode(resolvedBindings, binding, component));
+ }
+ });
+ return bindingNodes.build();
+ }
+
+ private BindingNode bindingNode(
+ ResolvedBindings resolvedBindings, Binding binding, TypeElement owningComponent) {
+ return BindingNode.create(
+ pathFromRootToAncestor(owningComponent),
+ binding,
+ resolvedBindings.multibindingDeclarations(),
+ resolvedBindings.optionalBindingDeclarations(),
+ resolvedBindings.subcomponentDeclarations(),
+ bindingDeclarationFormatter);
+ }
+
+ private MissingBinding missingBindingNode(ResolvedBindings dependencies) {
+ // TODO(b/117833324): Revisit whether limiting missing binding nodes to the root component is
+ // necessary to limit the amount of missing binding nodes in the network, or if perhaps *all*
+ // missing binding nodes should be structured this way.
+ return BindingGraphProxies.missingBindingNode(
+ isRootSubcomponent && !isFullBindingGraph ? rootComponentPath : componentPath(),
+ dependencies.key());
+ }
+
+ private ComponentNode subcomponentNode(TypeMirror subcomponentBuilderType, BindingGraph graph) {
+ TypeElement subcomponentBuilderElement = asTypeElement(subcomponentBuilderType);
+ ComponentDescriptor subcomponent =
+ graph.componentDescriptor().getChildComponentWithBuilderType(subcomponentBuilderElement);
+ return ComponentNodeImpl.create(
+ componentPath().childPath(subcomponent.typeElement()), subcomponent);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingGraphFactory.java b/java/dagger/internal/codegen/BindingGraphFactory.java
new file mode 100644
index 0000000..d96da8a
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraphFactory.java
@@ -0,0 +1,1061 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.isType;
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.isEmpty;
+import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.RequestKinds.getRequestKind;
+import static dagger.internal.codegen.SourceFiles.generatedMonitoringModuleName;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.model.BindingKind.DELEGATE;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.OPTIONAL;
+import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
+import static dagger.model.RequestKind.MEMBERS_INJECTION;
+import static java.util.function.Predicate.isEqual;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import dagger.MembersInjector;
+import dagger.Reusable;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Scope;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.internal.ProductionExecutorModule;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/** A factory for {@link BindingGraph} objects. */
+@Singleton
+final class BindingGraphFactory implements ClearableCache {
+ private final DaggerElements elements;
+ private final InjectBindingRegistry injectBindingRegistry;
+ private final KeyFactory keyFactory;
+ private final BindingFactory bindingFactory;
+ private final ModuleDescriptor.Factory moduleDescriptorFactory;
+ private final Map<Key, ImmutableSet<Key>> keysMatchingRequestCache = new HashMap<>();
+
+ @Inject
+ BindingGraphFactory(
+ DaggerElements elements,
+ InjectBindingRegistry injectBindingRegistry,
+ KeyFactory keyFactory,
+ BindingFactory bindingFactory,
+ ModuleDescriptor.Factory moduleDescriptorFactory) {
+ this.elements = elements;
+ this.injectBindingRegistry = injectBindingRegistry;
+ this.keyFactory = keyFactory;
+ this.bindingFactory = bindingFactory;
+ this.moduleDescriptorFactory = moduleDescriptorFactory;
+ }
+
+ /**
+ * Creates a binding graph for a component.
+ *
+ * @param createFullBindingGraph if {@code true}, the binding graph will include all bindings;
+ * otherwise it will include only bindings reachable from at least one entry point
+ */
+ BindingGraph create(ComponentDescriptor componentDescriptor, boolean createFullBindingGraph) {
+ return create(Optional.empty(), componentDescriptor, createFullBindingGraph);
+ }
+
+ private BindingGraph create(
+ Optional<Resolver> parentResolver,
+ ComponentDescriptor componentDescriptor,
+ boolean createFullBindingGraph) {
+ ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
+ ImmutableSet.Builder<DelegateDeclaration> delegatesBuilder = ImmutableSet.builder();
+ ImmutableSet.Builder<OptionalBindingDeclaration> optionalsBuilder = ImmutableSet.builder();
+
+ if (componentDescriptor.isRealComponent()) {
+ // binding for the component itself
+ explicitBindingsBuilder.add(
+ bindingFactory.componentBinding(componentDescriptor.typeElement()));
+ }
+
+ // Collect Component dependencies.
+ for (ComponentRequirement dependency : componentDescriptor.dependencies()) {
+ explicitBindingsBuilder.add(bindingFactory.componentDependencyBinding(dependency));
+ List<ExecutableElement> dependencyMethods =
+ methodsIn(elements.getAllMembers(dependency.typeElement()));
+ for (ExecutableElement method : dependencyMethods) {
+ // MembersInjection methods aren't "provided" explicitly, so ignore them.
+ if (isComponentContributionMethod(elements, method)) {
+ explicitBindingsBuilder.add(
+ bindingFactory.componentDependencyMethodBinding(componentDescriptor, method));
+ }
+ }
+ }
+
+ // Collect bindings on the creator.
+ componentDescriptor
+ .creatorDescriptor()
+ .ifPresent(
+ creatorDescriptor ->
+ creatorDescriptor.boundInstanceRequirements().stream()
+ .map(
+ requirement ->
+ bindingFactory.boundInstanceBinding(
+ requirement, creatorDescriptor.elementForRequirement(requirement)))
+ .forEach(explicitBindingsBuilder::add));
+
+ componentDescriptor
+ .childComponentsDeclaredByBuilderEntryPoints()
+ .forEach(
+ (builderEntryPoint, childComponent) -> {
+ if (!componentDescriptor
+ .childComponentsDeclaredByModules()
+ .contains(childComponent)) {
+ explicitBindingsBuilder.add(
+ bindingFactory.subcomponentCreatorBinding(
+ builderEntryPoint.methodElement(), componentDescriptor.typeElement()));
+ }
+ });
+
+ ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations = ImmutableSet.builder();
+ ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations = ImmutableSet.builder();
+
+ // Collect transitive module bindings and multibinding declarations.
+ for (ModuleDescriptor moduleDescriptor : modules(componentDescriptor, parentResolver)) {
+ explicitBindingsBuilder.addAll(moduleDescriptor.bindings());
+ multibindingDeclarations.addAll(moduleDescriptor.multibindingDeclarations());
+ subcomponentDeclarations.addAll(moduleDescriptor.subcomponentDeclarations());
+ delegatesBuilder.addAll(moduleDescriptor.delegateDeclarations());
+ optionalsBuilder.addAll(moduleDescriptor.optionalDeclarations());
+ }
+
+ final Resolver requestResolver =
+ new Resolver(
+ parentResolver,
+ componentDescriptor,
+ indexBindingDeclarationsByKey(explicitBindingsBuilder.build()),
+ indexBindingDeclarationsByKey(multibindingDeclarations.build()),
+ indexBindingDeclarationsByKey(subcomponentDeclarations.build()),
+ indexBindingDeclarationsByKey(delegatesBuilder.build()),
+ indexBindingDeclarationsByKey(optionalsBuilder.build()));
+
+ componentDescriptor.entryPointMethods().stream()
+ .map(method -> method.dependencyRequest().get())
+ .forEach(
+ entryPoint -> {
+ if (entryPoint.kind().equals(MEMBERS_INJECTION)) {
+ requestResolver.resolveMembersInjection(entryPoint.key());
+ } else {
+ requestResolver.resolve(entryPoint.key());
+ }
+ });
+
+ if (createFullBindingGraph) {
+ // Resolve the keys for all bindings in all modules, stripping any multibinding contribution
+ // identifier so that the multibinding itself is resolved.
+ modules(componentDescriptor, parentResolver).stream()
+ .flatMap(module -> module.allBindingKeys().stream())
+ .map(key -> key.toBuilder().multibindingContributionIdentifier(Optional.empty()).build())
+ .forEach(requestResolver::resolve);
+ }
+
+ // Resolve all bindings for subcomponents, creating subgraphs for all subcomponents that have
+ // been detected during binding resolution. If a binding for a subcomponent is never resolved,
+ // no BindingGraph will be created for it and no implementation will be generated. This is
+ // done in a queue since resolving one subcomponent might resolve a key for a subcomponent
+ // from a parent graph. This is done until no more new subcomponents are resolved.
+ Set<ComponentDescriptor> resolvedSubcomponents = new HashSet<>();
+ ImmutableList.Builder<BindingGraph> subgraphs = ImmutableList.builder();
+ for (ComponentDescriptor subcomponent :
+ Iterables.consumingIterable(requestResolver.subcomponentsToResolve)) {
+ if (resolvedSubcomponents.add(subcomponent)) {
+ subgraphs.add(create(Optional.of(requestResolver), subcomponent, createFullBindingGraph));
+ }
+ }
+
+ return BindingGraph.create(
+ componentDescriptor,
+ requestResolver.getResolvedContributionBindings(),
+ requestResolver.getResolvedMembersInjectionBindings(),
+ subgraphs.build(),
+ requestResolver.getOwnedModules(),
+ requestResolver.getFactoryMethod(),
+ createFullBindingGraph);
+ }
+
+ /**
+ * Returns all the modules that should be installed in the component. For production components
+ * and production subcomponents that have a parent that is not a production component or
+ * subcomponent, also includes the production monitoring module for the component and the
+ * production executor module.
+ */
+ private ImmutableSet<ModuleDescriptor> modules(
+ ComponentDescriptor componentDescriptor, Optional<Resolver> parentResolver) {
+ return shouldIncludeImplicitProductionModules(componentDescriptor, parentResolver)
+ ? new ImmutableSet.Builder<ModuleDescriptor>()
+ .addAll(componentDescriptor.modules())
+ .add(descriptorForMonitoringModule(componentDescriptor.typeElement()))
+ .add(descriptorForProductionExecutorModule())
+ .build()
+ : componentDescriptor.modules();
+ }
+
+ private boolean shouldIncludeImplicitProductionModules(
+ ComponentDescriptor component, Optional<Resolver> parentResolver) {
+ return component.isProduction()
+ && ((!component.isSubcomponent() && component.isRealComponent())
+ || (parentResolver.isPresent()
+ && !parentResolver.get().componentDescriptor.isProduction()));
+ }
+
+ /**
+ * Returns a descriptor for a generated module that handles monitoring for production components.
+ * This module is generated in the {@link MonitoringModuleProcessingStep}.
+ *
+ * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
+ * processor to retry in a later processing round.
+ */
+ private ModuleDescriptor descriptorForMonitoringModule(TypeElement componentDefinitionType) {
+ return moduleDescriptorFactory.create(
+ elements.checkTypePresent(
+ generatedMonitoringModuleName(componentDefinitionType).toString()));
+ }
+
+ /** Returns a descriptor {@link ProductionExecutorModule}. */
+ private ModuleDescriptor descriptorForProductionExecutorModule() {
+ return moduleDescriptorFactory.create(elements.getTypeElement(ProductionExecutorModule.class));
+ }
+
+ /** Indexes {@code bindingDeclarations} by {@link BindingDeclaration#key()}. */
+ private static <T extends BindingDeclaration>
+ ImmutableSetMultimap<Key, T> indexBindingDeclarationsByKey(Iterable<T> declarations) {
+ return ImmutableSetMultimap.copyOf(Multimaps.index(declarations, BindingDeclaration::key));
+ }
+
+ @Override
+ public void clearCache() {
+ keysMatchingRequestCache.clear();
+ }
+
+ private final class Resolver {
+ final Optional<Resolver> parentResolver;
+ final ComponentDescriptor componentDescriptor;
+ final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
+ final ImmutableSet<ContributionBinding> explicitBindingsSet;
+ final ImmutableSetMultimap<Key, ContributionBinding> explicitMultibindings;
+ final ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations;
+ final ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations;
+ final ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations;
+ final ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations;
+ final ImmutableSetMultimap<Key, DelegateDeclaration> delegateMultibindingDeclarations;
+ final Map<Key, ResolvedBindings> resolvedContributionBindings = new LinkedHashMap<>();
+ final Map<Key, ResolvedBindings> resolvedMembersInjectionBindings = new LinkedHashMap<>();
+ final Deque<Key> cycleStack = new ArrayDeque<>();
+ final Map<Key, Boolean> keyDependsOnLocalBindingsCache = new HashMap<>();
+ final Map<Binding, Boolean> bindingDependsOnLocalBindingsCache = new HashMap<>();
+ final Queue<ComponentDescriptor> subcomponentsToResolve = new ArrayDeque<>();
+
+ Resolver(
+ Optional<Resolver> parentResolver,
+ ComponentDescriptor componentDescriptor,
+ ImmutableSetMultimap<Key, ContributionBinding> explicitBindings,
+ ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations,
+ ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations,
+ ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations,
+ ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations) {
+ this.parentResolver = parentResolver;
+ this.componentDescriptor = checkNotNull(componentDescriptor);
+ this.explicitBindings = checkNotNull(explicitBindings);
+ this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
+ this.multibindingDeclarations = checkNotNull(multibindingDeclarations);
+ this.subcomponentDeclarations = checkNotNull(subcomponentDeclarations);
+ this.delegateDeclarations = checkNotNull(delegateDeclarations);
+ this.optionalBindingDeclarations = checkNotNull(optionalBindingDeclarations);
+ this.explicitMultibindings = multibindingContributionsByMultibindingKey(explicitBindingsSet);
+ this.delegateMultibindingDeclarations =
+ multibindingContributionsByMultibindingKey(delegateDeclarations.values());
+ subcomponentsToResolve.addAll(
+ componentDescriptor.childComponentsDeclaredByFactoryMethods().values());
+ subcomponentsToResolve.addAll(
+ componentDescriptor.childComponentsDeclaredByBuilderEntryPoints().values());
+ }
+
+ /** Returns the optional factory method for this component. */
+ Optional<ExecutableElement> getFactoryMethod() {
+ return parentResolver
+ .flatMap(
+ parent ->
+ parent.componentDescriptor.getFactoryMethodForChildComponent(componentDescriptor))
+ .map(method -> method.methodElement());
+ }
+
+ /**
+ * Returns the resolved contribution bindings for the given {@link Key}:
+ *
+ * <ul>
+ * <li>All explicit bindings for:
+ * <ul>
+ * <li>the requested key
+ * <li>{@code Set<T>} if the requested key's type is {@code Set<Produced<T>>}
+ * <li>{@code Map<K, Provider<V>>} if the requested key's type is {@code Map<K,
+ * Producer<V>>}.
+ * </ul>
+ * <li>A synthetic binding that depends on {@code Map<K, Producer<V>>} if the requested key's
+ * type is {@code Map<K, V>} and there are some explicit bindings for {@code Map<K,
+ * Producer<V>>}.
+ * <li>A synthetic binding that depends on {@code Map<K, Provider<V>>} if the requested key's
+ * type is {@code Map<K, V>} and there are some explicit bindings for {@code Map<K,
+ * Provider<V>>} but no explicit bindings for {@code Map<K, Producer<V>>}.
+ * <li>An implicit {@link Inject @Inject}-annotated constructor binding if there is one and
+ * there are no explicit bindings or synthetic bindings.
+ * </ul>
+ */
+ ResolvedBindings lookUpBindings(Key requestKey) {
+ Set<ContributionBinding> bindings = new LinkedHashSet<>();
+ bindings.addAll(getExplicitBindings(requestKey));
+
+ ImmutableSet<ContributionBinding> multibindingContributions =
+ getAllMatchingBindingDeclarations(requestKey, this::getExplicitMultibindings);
+ ImmutableSet<MultibindingDeclaration> multibindingDeclarations =
+ getAllMatchingBindingDeclarations(requestKey, this::getMultibindingDeclarations);
+
+ syntheticMultibinding(requestKey, multibindingContributions, multibindingDeclarations)
+ .ifPresent(bindings::add);
+
+ ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations =
+ getAllMatchingBindingDeclarations(requestKey, this::getOptionalBindingDeclarations);
+ syntheticOptionalBinding(requestKey, optionalBindingDeclarations).ifPresent(bindings::add);
+
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations =
+ getSubcomponentDeclarations(requestKey);
+ syntheticSubcomponentBuilderBinding(subcomponentDeclarations)
+ .ifPresent(
+ binding -> {
+ bindings.add(binding);
+ addSubcomponentToOwningResolver(binding);
+ });
+
+ if (isType(requestKey.type()) && isTypeOf(MembersInjector.class, requestKey.type())) {
+ injectBindingRegistry
+ .getOrFindMembersInjectorProvisionBinding(requestKey)
+ .ifPresent(bindings::add);
+ }
+
+ // If there are no bindings, add the implicit @Inject-constructed binding if there is one.
+ if (bindings.isEmpty()) {
+ injectBindingRegistry.getOrFindProvisionBinding(requestKey)
+ .filter(binding -> !isIncorrectlyScopedInPartialGraph(binding))
+ .ifPresent(bindings::add);
+ }
+
+ return ResolvedBindings.forContributionBindings(
+ requestKey,
+ indexBindingsByOwningComponent(requestKey, ImmutableSet.copyOf(bindings)),
+ multibindingDeclarations,
+ subcomponentDeclarations,
+ optionalBindingDeclarations);
+ }
+
+ /**
+ * Returns true if this binding graph resolution is for a partial graph and the {@code @Inject}
+ * binding's scope doesn't match any of the components in the current component ancestry. If so,
+ * the binding is not owned by any of the currently known components, and will be owned by a
+ * future ancestor (or, if never owned, will result in an incompatibly scoped binding error at
+ * the root component).
+ */
+ private boolean isIncorrectlyScopedInPartialGraph(ProvisionBinding binding) {
+ checkArgument(binding.kind().equals(INJECTION));
+ Resolver owningResolver = getOwningResolver(binding).orElse(this);
+ ComponentDescriptor owningComponent = owningResolver.componentDescriptor;
+ return rootComponent().isSubcomponent()
+ && binding.scope().isPresent()
+ && !binding.scope().get().isReusable()
+ && !owningComponent.scopes().contains(binding.scope().get());
+ }
+
+ private ComponentDescriptor rootComponent() {
+ return parentResolver.map(Resolver::rootComponent).orElse(componentDescriptor);
+ }
+
+ /** Returns the resolved members injection bindings for the given {@link Key}. */
+ ResolvedBindings lookUpMembersInjectionBinding(Key requestKey) {
+ // no explicit deps for members injection, so just look it up
+ Optional<MembersInjectionBinding> binding =
+ injectBindingRegistry.getOrFindMembersInjectionBinding(requestKey);
+ return binding.isPresent()
+ ? ResolvedBindings.forMembersInjectionBinding(
+ requestKey, componentDescriptor, binding.get())
+ : ResolvedBindings.noBindings(requestKey);
+ }
+
+ /**
+ * When a binding is resolved for a {@link SubcomponentDeclaration}, adds corresponding {@link
+ * ComponentDescriptor subcomponent} to a queue in the owning component's resolver. The queue
+ * will be used to detect which subcomponents need to be resolved.
+ */
+ private void addSubcomponentToOwningResolver(ProvisionBinding subcomponentCreatorBinding) {
+ checkArgument(subcomponentCreatorBinding.kind().equals(SUBCOMPONENT_CREATOR));
+ Resolver owningResolver = getOwningResolver(subcomponentCreatorBinding).get();
+
+ TypeElement builderType = MoreTypes.asTypeElement(subcomponentCreatorBinding.key().type());
+ owningResolver.subcomponentsToResolve.add(
+ owningResolver.componentDescriptor.getChildComponentWithBuilderType(builderType));
+ }
+
+ /**
+ * Profiling has determined that computing the keys matching {@code requestKey} has measurable
+ * performance impact. It is called repeatedly (at least 3 times per key resolved per {@link
+ * BindingGraph}. {@code javac}'s name-checking performance seems suboptimal (converting byte
+ * strings to Strings repeatedly), and the matching keys creations relies on that. This also
+ * ensures that the resulting keys have their hash codes cached on successive calls to this
+ * method.
+ *
+ * <p>This caching may become obsolete if:
+ *
+ * <ul>
+ * <li>We decide to intern all {@link Key} instances
+ * <li>We fix javac's name-checking peformance (though we may want to keep this for older
+ * javac users)
+ * </ul>
+ */
+ private ImmutableSet<Key> keysMatchingRequest(Key requestKey) {
+ return keysMatchingRequestCache.computeIfAbsent(
+ requestKey, this::keysMatchingRequestUncached);
+ }
+
+ private ImmutableSet<Key> keysMatchingRequestUncached(Key requestKey) {
+ ImmutableSet.Builder<Key> keys = ImmutableSet.builder();
+ keys.add(requestKey);
+ keyFactory.unwrapSetKey(requestKey, Produced.class).ifPresent(keys::add);
+ keyFactory.rewrapMapKey(requestKey, Producer.class, Provider.class).ifPresent(keys::add);
+ keyFactory.rewrapMapKey(requestKey, Provider.class, Producer.class).ifPresent(keys::add);
+ keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
+ return keys.build();
+ }
+
+ /**
+ * Returns a synthetic binding that depends on individual multibinding contributions.
+ *
+ * <p>If there are no {@code multibindingContributions} or {@code multibindingDeclarations},
+ * returns {@link Optional#empty()}.
+ *
+ * <p>If there are production {@code multibindingContributions} or the request is for any of the
+ * following types, returns a {@link ProductionBinding}.
+ *
+ * <ul>
+ * <li>{@code Set<Produced<T>>}
+ * <li>{@code Map<K, Producer<V>>}
+ * <li>{@code Map<K, Produced<V>>}
+ * </ul>
+ *
+ * Otherwise, returns a {@link ProvisionBinding}.
+ */
+ private Optional<ContributionBinding> syntheticMultibinding(
+ Key key,
+ Iterable<ContributionBinding> multibindingContributions,
+ Iterable<MultibindingDeclaration> multibindingDeclarations) {
+ return isEmpty(multibindingContributions) && isEmpty(multibindingDeclarations)
+ ? Optional.empty()
+ : Optional.of(bindingFactory.syntheticMultibinding(key, multibindingContributions));
+ }
+
+ private Optional<ProvisionBinding> syntheticSubcomponentBuilderBinding(
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
+ return subcomponentDeclarations.isEmpty()
+ ? Optional.empty()
+ : Optional.of(bindingFactory.subcomponentCreatorBinding(subcomponentDeclarations));
+ }
+
+ /**
+ * Returns a synthetic binding for {@code @Qualifier Optional<Type>} if there are any {@code
+ * optionalBindingDeclarations}.
+ *
+ * <p>If there are no bindings for the underlying key (the key for dependency requests for
+ * {@code Type}), returns a provision binding that always returns {@link Optional#empty()}.
+ *
+ * <p>If there are any production bindings for the underlying key, returns a production binding.
+ * Otherwise returns a provision binding.
+ */
+ private Optional<ContributionBinding> syntheticOptionalBinding(
+ Key key, ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations) {
+ return optionalBindingDeclarations.isEmpty()
+ ? Optional.empty()
+ : Optional.of(
+ bindingFactory.syntheticOptionalBinding(
+ key,
+ getRequestKind(OptionalType.from(key).valueType()),
+ lookUpBindings(keyFactory.unwrapOptional(key).get())));
+ }
+
+ private ImmutableSet<ContributionBinding> createDelegateBindings(
+ ImmutableSet<DelegateDeclaration> delegateDeclarations) {
+ ImmutableSet.Builder<ContributionBinding> builder = ImmutableSet.builder();
+ for (DelegateDeclaration delegateDeclaration : delegateDeclarations) {
+ builder.add(createDelegateBinding(delegateDeclaration));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Creates one (and only one) delegate binding for a delegate declaration, based on the resolved
+ * bindings of the right-hand-side of a {@link dagger.Binds} method. If there are duplicate
+ * bindings for the dependency key, there should still be only one binding for the delegate key.
+ */
+ private ContributionBinding createDelegateBinding(DelegateDeclaration delegateDeclaration) {
+ Key delegateKey = delegateDeclaration.delegateRequest().key();
+ if (cycleStack.contains(delegateKey)) {
+ return bindingFactory.unresolvedDelegateBinding(delegateDeclaration);
+ }
+
+ ResolvedBindings resolvedDelegate;
+ try {
+ cycleStack.push(delegateKey);
+ resolvedDelegate = lookUpBindings(delegateKey);
+ } finally {
+ cycleStack.pop();
+ }
+ if (resolvedDelegate.contributionBindings().isEmpty()) {
+ // This is guaranteed to result in a missing binding error, so it doesn't matter if the
+ // binding is a Provision or Production, except if it is a @IntoMap method, in which
+ // case the key will be of type Map<K, Provider<V>>, which will be "upgraded" into a
+ // Map<K, Producer<V>> if it's requested in a ProductionComponent. This may result in a
+ // strange error, that the RHS needs to be provided with an @Inject or @Provides
+ // annotated method, but a user should be able to figure out if a @Produces annotation
+ // is needed.
+ // TODO(gak): revisit how we model missing delegates if/when we clean up how we model
+ // binding declarations
+ return bindingFactory.unresolvedDelegateBinding(delegateDeclaration);
+ }
+ // It doesn't matter which of these is selected, since they will later on produce a
+ // duplicate binding error.
+ ContributionBinding explicitDelegate =
+ resolvedDelegate.contributionBindings().iterator().next();
+ return bindingFactory.delegateBinding(delegateDeclaration, explicitDelegate);
+ }
+
+ // TODO(dpb,ronshapiro): requestKey appears to be interchangeable with each binding's .key(),
+ // but should it? We're currently conflating the two all over the place and it would be good
+ // to unify, or if it's necessary, clarify why with docs+tests. Specifically, should we also
+ // be checking these for keysMatchingRequest?
+ private ImmutableSetMultimap<TypeElement, ContributionBinding> indexBindingsByOwningComponent(
+ Key requestKey, Iterable<? extends ContributionBinding> bindings) {
+ ImmutableSetMultimap.Builder<TypeElement, ContributionBinding> index =
+ ImmutableSetMultimap.builder();
+ for (ContributionBinding binding : bindings) {
+ index.put(getOwningComponent(requestKey, binding), binding);
+ }
+ return index.build();
+ }
+
+ /**
+ * Returns the component that should contain the framework field for {@code binding}.
+ *
+ * <p>If {@code binding} is either not bound in an ancestor component or depends transitively on
+ * bindings in this component, returns this component.
+ *
+ * <p>Otherwise, resolves {@code request} in this component's parent in order to resolve any
+ * multibinding contributions in the parent, and returns the parent-resolved {@link
+ * ResolvedBindings#owningComponent(ContributionBinding)}.
+ */
+ private TypeElement getOwningComponent(Key requestKey, ContributionBinding binding) {
+ if (isResolvedInParent(requestKey, binding)
+ && !new LocalDependencyChecker().dependsOnLocalBindings(binding)) {
+ ResolvedBindings parentResolvedBindings =
+ parentResolver.get().resolvedContributionBindings.get(requestKey);
+ return parentResolvedBindings.owningComponent(binding);
+ } else {
+ return componentDescriptor.typeElement();
+ }
+ }
+
+ /**
+ * Returns {@code true} if {@code binding} is owned by an ancestor. If so, {@linkplain #resolve
+ * resolves} the {@link Key} in this component's parent. Don't resolve directly in the owning
+ * component in case it depends on multibindings in any of its descendants.
+ */
+ private boolean isResolvedInParent(Key requestKey, ContributionBinding binding) {
+ Optional<Resolver> owningResolver = getOwningResolver(binding);
+ if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
+ parentResolver.get().resolve(requestKey);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private Optional<Resolver> getOwningResolver(ContributionBinding binding) {
+ // TODO(ronshapiro): extract the different pieces of this method into their own methods
+ if ((binding.scope().isPresent() && binding.scope().get().isProductionScope())
+ || binding.bindingType().equals(BindingType.PRODUCTION)) {
+ for (Resolver requestResolver : getResolverLineage()) {
+ // Resolve @Inject @ProductionScope bindings at the highest production component.
+ if (binding.kind().equals(INJECTION)
+ && requestResolver.componentDescriptor.isProduction()) {
+ return Optional.of(requestResolver);
+ }
+
+ // Resolve explicit @Produces and @ProductionScope bindings at the highest component that
+ // installs the binding.
+ if (requestResolver.containsExplicitBinding(binding)) {
+ return Optional.of(requestResolver);
+ }
+ }
+ }
+
+ if (binding.scope().isPresent() && binding.scope().get().isReusable()) {
+ for (Resolver requestResolver : getResolverLineage().reverse()) {
+ // If a @Reusable binding was resolved in an ancestor, use that component.
+ ResolvedBindings resolvedBindings =
+ requestResolver.resolvedContributionBindings.get(binding.key());
+ if (resolvedBindings != null
+ && resolvedBindings.contributionBindings().contains(binding)) {
+ return Optional.of(requestResolver);
+ }
+ }
+ // If a @Reusable binding was not resolved in any ancestor, resolve it here.
+ return Optional.empty();
+ }
+
+ for (Resolver requestResolver : getResolverLineage().reverse()) {
+ if (requestResolver.containsExplicitBinding(binding)) {
+ return Optional.of(requestResolver);
+ }
+ }
+
+ // look for scope separately. we do this for the case where @Singleton can appear twice
+ // in the † compatibility mode
+ Optional<Scope> bindingScope = binding.scope();
+ if (bindingScope.isPresent()) {
+ for (Resolver requestResolver : getResolverLineage().reverse()) {
+ if (requestResolver.componentDescriptor.scopes().contains(bindingScope.get())) {
+ return Optional.of(requestResolver);
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ private boolean containsExplicitBinding(ContributionBinding binding) {
+ return explicitBindingsSet.contains(binding)
+ || resolverContainsDelegateDeclarationForBinding(binding)
+ || subcomponentDeclarations.containsKey(binding.key());
+ }
+
+ /** Returns true if {@code binding} was installed in a module in this resolver's component. */
+ private boolean resolverContainsDelegateDeclarationForBinding(ContributionBinding binding) {
+ return binding.kind().equals(DELEGATE)
+ && delegateDeclarations.get(binding.key()).stream()
+ .anyMatch(
+ declaration ->
+ declaration.contributingModule().equals(binding.contributingModule())
+ && declaration.bindingElement().equals(binding.bindingElement()));
+ }
+
+ /** Returns the resolver lineage from parent to child. */
+ private ImmutableList<Resolver> getResolverLineage() {
+ ImmutableList.Builder<Resolver> resolverList = ImmutableList.builder();
+ for (Optional<Resolver> currentResolver = Optional.of(this);
+ currentResolver.isPresent();
+ currentResolver = currentResolver.get().parentResolver) {
+ resolverList.add(currentResolver.get());
+ }
+ return resolverList.build().reverse();
+ }
+
+ /**
+ * For all {@linkplain #keysMatchingRequest(Key) keys matching {@code requestKey}}, applies
+ * {@code getDeclarationsPerKey} and collects the values into an {@link ImmutableSet}.
+ */
+ private <T extends BindingDeclaration> ImmutableSet<T> getAllMatchingBindingDeclarations(
+ Key requestKey, Function<Key, Collection<T>> getDeclarationsPerKey) {
+ return keysMatchingRequest(requestKey)
+ .stream()
+ .flatMap(key -> getDeclarationsPerKey.apply(key).stream())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the explicit {@link ContributionBinding}s that match the {@code key} from this and
+ * all ancestor resolvers.
+ */
+ private ImmutableSet<ContributionBinding> getExplicitBindings(Key key) {
+ ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
+ for (Resolver resolver : getResolverLineage()) {
+ bindings.addAll(resolver.getLocalExplicitBindings(key));
+ }
+ return bindings.build();
+ }
+
+ /**
+ * Returns the explicit {@link ContributionBinding}s that match the {@code key} from this
+ * resolver.
+ */
+ private ImmutableSet<ContributionBinding> getLocalExplicitBindings(Key key) {
+ return new ImmutableSet.Builder<ContributionBinding>()
+ .addAll(explicitBindings.get(key))
+ // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
+ // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
+ // value type if it's a Map<K, Provider/Producer<V>> before looking in
+ // delegateDeclarations. createDelegateBindings() will create bindings with the properly
+ // wrapped key type.
+ .addAll(
+ createDelegateBindings(delegateDeclarations.get(keyFactory.unwrapMapValueType(key))))
+ .build();
+ }
+
+ /**
+ * Returns the explicit multibinding contributions that contribute to the map or set requested
+ * by {@code key} from this and all ancestor resolvers.
+ */
+ private ImmutableSet<ContributionBinding> getExplicitMultibindings(Key key) {
+ ImmutableSet.Builder<ContributionBinding> multibindings = ImmutableSet.builder();
+ for (Resolver resolver : getResolverLineage()) {
+ multibindings.addAll(resolver.getLocalExplicitMultibindings(key));
+ }
+ return multibindings.build();
+ }
+
+ /**
+ * Returns the explicit multibinding contributions that contribute to the map or set requested
+ * by {@code key} from this resolver.
+ */
+ private ImmutableSet<ContributionBinding> getLocalExplicitMultibindings(Key key) {
+ ImmutableSet.Builder<ContributionBinding> multibindings = ImmutableSet.builder();
+ multibindings.addAll(explicitMultibindings.get(key));
+ if (!MapType.isMap(key)
+ || MapType.from(key).isRawType()
+ || MapType.from(key).valuesAreFrameworkType()) {
+ // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
+ // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
+ // value type if it's a Map<K, Provider/Producer<V>> before looking in
+ // delegateMultibindingDeclarations. createDelegateBindings() will create bindings with the
+ // properly wrapped key type.
+ multibindings.addAll(
+ createDelegateBindings(
+ delegateMultibindingDeclarations.get(keyFactory.unwrapMapValueType(key))));
+ }
+ return multibindings.build();
+ }
+
+ /**
+ * Returns the {@link MultibindingDeclaration}s that match the {@code key} from this and all
+ * ancestor resolvers.
+ */
+ private ImmutableSet<MultibindingDeclaration> getMultibindingDeclarations(Key key) {
+ ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
+ ImmutableSet.builder();
+ for (Resolver resolver : getResolverLineage()) {
+ multibindingDeclarations.addAll(resolver.multibindingDeclarations.get(key));
+ }
+ return multibindingDeclarations.build();
+ }
+
+ /**
+ * Returns the {@link SubcomponentDeclaration}s that match the {@code key} from this and all
+ * ancestor resolvers.
+ */
+ private ImmutableSet<SubcomponentDeclaration> getSubcomponentDeclarations(Key key) {
+ ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations =
+ ImmutableSet.builder();
+ for (Resolver resolver : getResolverLineage()) {
+ subcomponentDeclarations.addAll(resolver.subcomponentDeclarations.get(key));
+ }
+ return subcomponentDeclarations.build();
+ }
+ /**
+ * Returns the {@link OptionalBindingDeclaration}s that match the {@code key} from this and all
+ * ancestor resolvers.
+ */
+ private ImmutableSet<OptionalBindingDeclaration> getOptionalBindingDeclarations(Key key) {
+ Optional<Key> unwrapped = keyFactory.unwrapOptional(key);
+ if (!unwrapped.isPresent()) {
+ return ImmutableSet.of();
+ }
+ ImmutableSet.Builder<OptionalBindingDeclaration> declarations = ImmutableSet.builder();
+ for (Resolver resolver : getResolverLineage()) {
+ declarations.addAll(resolver.optionalBindingDeclarations.get(unwrapped.get()));
+ }
+ return declarations.build();
+ }
+
+ /**
+ * Returns the {@link ResolvedBindings} for {@code key} that was resolved in this resolver or an
+ * ancestor resolver. Only checks for {@link ContributionBinding}s as {@link
+ * MembersInjectionBinding}s are not inherited.
+ */
+ private Optional<ResolvedBindings> getPreviouslyResolvedBindings(Key key) {
+ Optional<ResolvedBindings> result =
+ Optional.ofNullable(resolvedContributionBindings.get(key));
+ if (result.isPresent()) {
+ return result;
+ } else if (parentResolver.isPresent()) {
+ return parentResolver.get().getPreviouslyResolvedBindings(key);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private void resolveMembersInjection(Key key) {
+ ResolvedBindings bindings = lookUpMembersInjectionBinding(key);
+ resolveDependencies(bindings);
+ resolvedMembersInjectionBindings.put(key, bindings);
+ }
+
+ void resolve(Key key) {
+ // If we find a cycle, stop resolving. The original request will add it with all of the
+ // other resolved deps.
+ if (cycleStack.contains(key)) {
+ return;
+ }
+
+ // If the binding was previously resolved in this (sub)component, don't resolve it again.
+ if (resolvedContributionBindings.containsKey(key)) {
+ return;
+ }
+
+ /*
+ * If the binding was previously resolved in an ancestor component, then we may be able to
+ * avoid resolving it here and just depend on the ancestor component resolution.
+ *
+ * 1. If it depends transitively on multibinding contributions or optional bindings with
+ * bindings from this subcomponent, then we have to resolve it in this subcomponent so
+ * that it sees the local bindings.
+ *
+ * 2. If there are any explicit bindings in this component, they may conflict with those in
+ * the ancestor component, so resolve them here so that conflicts can be caught.
+ */
+ if (getPreviouslyResolvedBindings(key).isPresent()) {
+ /* Resolve in the parent in case there are multibinding contributions or conflicts in some
+ * component between this one and the previously-resolved one. */
+ parentResolver.get().resolve(key);
+ if (!new LocalDependencyChecker().dependsOnLocalBindings(key)
+ && getLocalExplicitBindings(key).isEmpty()) {
+ /* Cache the inherited parent component's bindings in case resolving at the parent found
+ * bindings in some component between this one and the previously-resolved one. */
+ resolvedContributionBindings.put(key, getPreviouslyResolvedBindings(key).get());
+ return;
+ }
+ }
+
+ cycleStack.push(key);
+ try {
+ ResolvedBindings bindings = lookUpBindings(key);
+ resolvedContributionBindings.put(key, bindings);
+ resolveDependencies(bindings);
+ } finally {
+ cycleStack.pop();
+ }
+ }
+
+ /**
+ * {@link #resolve(Key) Resolves} each of the dependencies of the bindings owned by this
+ * component.
+ */
+ private void resolveDependencies(ResolvedBindings resolvedBindings) {
+ for (Binding binding : resolvedBindings.bindingsOwnedBy(componentDescriptor)) {
+ for (DependencyRequest dependency : binding.dependencies()) {
+ resolve(dependency.key());
+ }
+ }
+ }
+
+ /**
+ * Returns all of the {@link ResolvedBindings} for {@link ContributionBinding}s from this and
+ * all ancestor resolvers, indexed by {@link ResolvedBindings#key()}.
+ */
+ Map<Key, ResolvedBindings> getResolvedContributionBindings() {
+ Map<Key, ResolvedBindings> bindings = new LinkedHashMap<>();
+ parentResolver.ifPresent(parent -> bindings.putAll(parent.getResolvedContributionBindings()));
+ bindings.putAll(resolvedContributionBindings);
+ return bindings;
+ }
+
+ /**
+ * Returns all of the {@link ResolvedBindings} for {@link MembersInjectionBinding} from this
+ * resolvers, indexed by {@link ResolvedBindings#key()}.
+ */
+ ImmutableMap<Key, ResolvedBindings> getResolvedMembersInjectionBindings() {
+ return ImmutableMap.copyOf(resolvedMembersInjectionBindings);
+ }
+
+ ImmutableSet<ModuleDescriptor> getInheritedModules() {
+ return parentResolver.isPresent()
+ ? Sets.union(
+ parentResolver.get().getInheritedModules(),
+ parentResolver.get().componentDescriptor.modules())
+ .immutableCopy()
+ : ImmutableSet.<ModuleDescriptor>of();
+ }
+
+ ImmutableSet<ModuleDescriptor> getOwnedModules() {
+ return Sets.difference(componentDescriptor.modules(), getInheritedModules()).immutableCopy();
+ }
+
+ private final class LocalDependencyChecker {
+ private final Set<Object> cycleChecker = new HashSet<>();
+
+ /**
+ * Returns {@code true} if any of the bindings resolved for {@code key} are multibindings with
+ * contributions declared within this component's modules or optional bindings with present
+ * values declared within this component's modules, or if any of its unscoped dependencies
+ * depend on such bindings.
+ *
+ * <p>We don't care about scoped dependencies because they will never depend on bindings from
+ * subcomponents.
+ *
+ * @throws IllegalArgumentException if {@link #getPreviouslyResolvedBindings(Key)} is empty
+ */
+ boolean dependsOnLocalBindings(Key key) {
+ // Don't recur infinitely if there are valid cycles in the dependency graph.
+ // http://b/23032377
+ if (!cycleChecker.add(key)) {
+ return false;
+ }
+ return reentrantComputeIfAbsent(
+ keyDependsOnLocalBindingsCache, key, this::dependsOnLocalBindingsUncached);
+ }
+
+ private boolean dependsOnLocalBindingsUncached(Key key) {
+ checkArgument(
+ getPreviouslyResolvedBindings(key).isPresent(),
+ "no previously resolved bindings in %s for %s",
+ Resolver.this,
+ key);
+ ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
+ if (hasLocalMultibindingContributions(key)
+ || hasLocalOptionalBindingContribution(previouslyResolvedBindings)) {
+ return true;
+ }
+
+ for (Binding binding : previouslyResolvedBindings.bindings()) {
+ if (dependsOnLocalBindings(binding)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if {@code binding} is unscoped (or has {@link Reusable @Reusable}
+ * scope) and depends on multibindings with contributions declared within this component's
+ * modules, or if any of its unscoped or {@link Reusable @Reusable} scoped dependencies depend
+ * on such local multibindings.
+ *
+ * <p>We don't care about non-reusable scoped dependencies because they will never depend on
+ * multibindings with contributions from subcomponents.
+ */
+ boolean dependsOnLocalBindings(Binding binding) {
+ if (!cycleChecker.add(binding)) {
+ return false;
+ }
+ return reentrantComputeIfAbsent(
+ bindingDependsOnLocalBindingsCache, binding, this::dependsOnLocalBindingsUncached);
+ }
+
+ private boolean dependsOnLocalBindingsUncached(Binding binding) {
+ if ((!binding.scope().isPresent() || binding.scope().get().isReusable())
+ // TODO(beder): Figure out what happens with production subcomponents.
+ && !binding.bindingType().equals(BindingType.PRODUCTION)) {
+ for (DependencyRequest dependency : binding.dependencies()) {
+ if (dependsOnLocalBindings(dependency.key())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if there is at least one multibinding contribution declared within
+ * this component's modules that matches the key.
+ */
+ private boolean hasLocalMultibindingContributions(Key requestKey) {
+ return keysMatchingRequest(requestKey)
+ .stream()
+ .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
+ }
+
+ /**
+ * Returns {@code true} if there is a contribution in this component for an {@code
+ * Optional<Foo>} key that has not been contributed in a parent.
+ */
+ private boolean hasLocalOptionalBindingContribution(ResolvedBindings resolvedBindings) {
+ if (resolvedBindings
+ .contributionBindings()
+ .stream()
+ .map(ContributionBinding::kind)
+ .anyMatch(isEqual(OPTIONAL))) {
+ return !getLocalExplicitBindings(keyFactory.unwrapOptional(resolvedBindings.key()).get())
+ .isEmpty();
+ } else {
+ // If a parent contributes a @Provides Optional<Foo> binding and a child has a
+ // @BindsOptionalOf Foo method, the two should conflict, even if there is no binding for
+ // Foo on its own
+ return !getOptionalBindingDeclarations(resolvedBindings.key()).isEmpty();
+ }
+ }
+ }
+ }
+
+ /**
+ * A multimap of those {@code declarations} that are multibinding contribution declarations,
+ * indexed by the key of the set or map to which they contribute.
+ */
+ static <T extends BindingDeclaration>
+ ImmutableSetMultimap<Key, T> multibindingContributionsByMultibindingKey(
+ Iterable<T> declarations) {
+ ImmutableSetMultimap.Builder<Key, T> builder = ImmutableSetMultimap.builder();
+ for (T declaration : declarations) {
+ if (declaration.key().multibindingContributionIdentifier().isPresent()) {
+ builder.put(
+ declaration
+ .key()
+ .toBuilder()
+ .multibindingContributionIdentifier(Optional.empty())
+ .build(),
+ declaration);
+ }
+ }
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingGraphPlugins.java b/java/dagger/internal/codegen/BindingGraphPlugins.java
new file mode 100644
index 0000000..e2c3812
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraphPlugins.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.spi.BindingGraphPlugin;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+
+/** Initializes {@link BindingGraphPlugin}s. */
+final class BindingGraphPlugins {
+ private final ImmutableSet<BindingGraphPlugin> plugins;
+ private final Filer filer;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final Map<String, String> processingOptions;
+
+ @Inject
+ BindingGraphPlugins(
+ @Validation Set<BindingGraphPlugin> validationPlugins,
+ ImmutableSet<BindingGraphPlugin> externalPlugins,
+ Filer filer,
+ DaggerTypes types,
+ DaggerElements elements,
+ @ProcessingOptions Map<String, String> processingOptions) {
+ this.plugins = Sets.union(validationPlugins, externalPlugins).immutableCopy();
+ this.filer = filer;
+ this.types = types;
+ this.elements = elements;
+ this.processingOptions = processingOptions;
+ }
+
+ /** Returns {@link BindingGraphPlugin#supportedOptions()} from all the plugins. */
+ ImmutableSet<String> allSupportedOptions() {
+ return plugins.stream()
+ .flatMap(plugin -> plugin.supportedOptions().stream())
+ .collect(toImmutableSet());
+ }
+
+ /** Initializes the plugins. */
+ // TODO(ronshapiro): Should we validate the uniqueness of plugin names?
+ void initializePlugins() {
+ plugins.forEach(this::initializePlugin);
+ }
+
+ private void initializePlugin(BindingGraphPlugin plugin) {
+ plugin.initFiler(filer);
+ plugin.initTypes(types);
+ plugin.initElements(elements);
+ Set<String> supportedOptions = plugin.supportedOptions();
+ if (!supportedOptions.isEmpty()) {
+ plugin.initOptions(Maps.filterKeys(processingOptions, supportedOptions::contains));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java b/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java
new file mode 100644
index 0000000..129647f
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.errorprone.util.ASTHelpers.getSymbol;
+import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotation;
+
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.sun.source.tree.ClassTree;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.util.Context;
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.model.BindingGraph;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A {@link BugChecker} that collects statistics derived from a {@link BindingGraph}. */
+public abstract class BindingGraphStatisticsCollector extends BugChecker
+ implements ClassTreeMatcher {
+ private BindingGraphConverter bindingGraphConverter;
+ private BindingGraphFactory bindingGraphFactory;
+ private ComponentDescriptorFactory componentDescriptorFactory;
+ private boolean isInjected;
+
+ @Singleton
+ @Component(modules = JavacPluginModule.class)
+ interface Injector {
+ void inject(BindingGraphStatisticsCollector collector);
+
+ @Component.Factory
+ interface Factory {
+ Injector create(@BindsInstance Context context);
+ }
+ }
+
+ // BugCheckers must have no-arg constructors, so we'll use method injection instead.
+ @Inject
+ void inject(
+ BindingGraphConverter bindingGraphConverter,
+ BindingGraphFactory bindingGraphFactory,
+ ComponentDescriptorFactory componentDescriptorFactory) {
+ this.bindingGraphConverter = bindingGraphConverter;
+ this.bindingGraphFactory = bindingGraphFactory;
+ this.componentDescriptorFactory = componentDescriptorFactory;
+ }
+
+ @Override
+ public final Description matchClass(ClassTree tree, VisitorState state) {
+ injectIfNecessary(state.context);
+
+ ClassSymbol symbol = getSymbol(tree);
+ rootComponentAnnotation(symbol)
+ .map(annotation -> createBindingGraph(symbol))
+ .ifPresent(graph -> visitBindingGraph(graph, state));
+
+ return Description.NO_MATCH;
+ }
+
+ private BindingGraph createBindingGraph(ClassSymbol component) {
+ return bindingGraphConverter.convert(
+ bindingGraphFactory.create(
+ componentDescriptorFactory.rootComponentDescriptor(component), false));
+ }
+
+ /** Visits a {@link BindingGraph} and emits stats to a {@link VisitorState}. */
+ protected abstract void visitBindingGraph(BindingGraph graph, VisitorState state);
+
+ private void injectIfNecessary(Context context) {
+ if (isInjected) {
+ return;
+ }
+ DaggerBindingGraphStatisticsCollector_Injector.factory().create(context).inject(this);
+ isInjected = true;
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingGraphValidationModule.java b/java/dagger/internal/codegen/BindingGraphValidationModule.java
new file mode 100644
index 0000000..63e1fa2
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraphValidationModule.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoSet;
+import dagger.spi.BindingGraphPlugin;
+
+/** Binds the set of {@link BindingGraphPlugin}s used to implement Dagger validation. */
+@Module
+interface BindingGraphValidationModule {
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin dependencyCycle(DependencyCycleValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin dependsOnProductionExecutor(DependsOnProductionExecutorValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin duplicateBindings(DuplicateBindingsValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin incompatiblyScopedBindings(IncompatiblyScopedBindingsValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin injectBinding(InjectBindingValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin mapMultibinding(MapMultibindingValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin missingBinding(MissingBindingValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin nullableBinding(NullableBindingValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin provisionDependencyOnProducerBinding(
+ ProvisionDependencyOnProducerBindingValidator validation);
+
+ @Binds
+ @IntoSet
+ @Validation
+ BindingGraphPlugin subcomponentFactoryMethod(SubcomponentFactoryMethodValidator validation);
+}
diff --git a/java/dagger/internal/codegen/BindingGraphValidator.java b/java/dagger/internal/codegen/BindingGraphValidator.java
new file mode 100644
index 0000000..df17b15
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraphValidator.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.DiagnosticReporterFactory.DiagnosticReporterImpl;
+import dagger.model.BindingGraph;
+import dagger.spi.BindingGraphPlugin;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Validates a {@link BindingGraph}. */
+@Singleton
+final class BindingGraphValidator {
+ private final ImmutableSet<BindingGraphPlugin> validationPlugins;
+ private final ImmutableSet<BindingGraphPlugin> externalPlugins;
+ private final DiagnosticReporterFactory diagnosticReporterFactory;
+
+ @Inject
+ BindingGraphValidator(
+ @Validation Set<BindingGraphPlugin> validationPlugins,
+ ImmutableSet<BindingGraphPlugin> externalPlugins,
+ DiagnosticReporterFactory diagnosticReporterFactory) {
+ this.validationPlugins = ImmutableSet.copyOf(validationPlugins);
+ this.externalPlugins = ImmutableSet.copyOf(externalPlugins);
+ this.diagnosticReporterFactory = checkNotNull(diagnosticReporterFactory);
+ }
+
+ /** Returns {@code true} if no errors are reported for {@code graph}. */
+ boolean isValid(BindingGraph graph) {
+ return isValid(validationPlugins, graph) && isValid(externalPlugins, graph);
+ }
+
+ private boolean isValid(ImmutableSet<BindingGraphPlugin> plugins, BindingGraph graph) {
+ boolean isValid = true;
+ for (BindingGraphPlugin plugin : plugins) {
+ DiagnosticReporterImpl reporter = diagnosticReporterFactory.reporter(graph, plugin);
+ plugin.visitGraph(graph, reporter);
+ if (reporter.reportedDiagnosticKinds().contains(ERROR)) {
+ isValid = false;
+ }
+ }
+ return isValid;
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingMethodProcessingStep.java b/java/dagger/internal/codegen/BindingMethodProcessingStep.java
new file mode 100644
index 0000000..e6c4f8e
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingMethodProcessingStep.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+
+/** A step that validates all binding methods that were not validated while processing modules. */
+final class BindingMethodProcessingStep extends TypeCheckingProcessingStep<ExecutableElement> {
+
+ private final Messager messager;
+ private final AnyBindingMethodValidator anyBindingMethodValidator;
+
+ @Inject
+ BindingMethodProcessingStep(
+ Messager messager, AnyBindingMethodValidator anyBindingMethodValidator) {
+ super(MoreElements::asExecutable);
+ this.messager = messager;
+ this.anyBindingMethodValidator = anyBindingMethodValidator;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return anyBindingMethodValidator.methodAnnotations();
+ }
+
+ @Override
+ protected void process(
+ ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
+ checkArgument(
+ anyBindingMethodValidator.isBindingMethod(method),
+ "%s is not annotated with any of %s",
+ method,
+ annotations());
+ if (!anyBindingMethodValidator.wasAlreadyValidated(method)) {
+ anyBindingMethodValidator.validate(method).printMessagesTo(messager);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingMethodValidator.java b/java/dagger/internal/codegen/BindingMethodValidator.java
new file mode 100644
index 0000000..21c05cc
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingMethodValidator.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for methods that represent binding declarations. */
+abstract class BindingMethodValidator extends BindingElementValidator<ExecutableElement> {
+
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final DependencyRequestValidator dependencyRequestValidator;
+ private final Class<? extends Annotation> methodAnnotation;
+ private final ImmutableSet<? extends Class<? extends Annotation>> enclosingElementAnnotations;
+ private final Abstractness abstractness;
+ private final ExceptionSuperclass exceptionSuperclass;
+
+ /**
+ * Creates a validator object.
+ *
+ * @param methodAnnotation the annotation on a method that identifies it as a binding method
+ * @param enclosingElementAnnotation the method must be declared in a class or interface annotated
+ * with this annotation
+ */
+ protected BindingMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestValidator dependencyRequestValidator,
+ Class<? extends Annotation> methodAnnotation,
+ Class<? extends Annotation> enclosingElementAnnotation,
+ Abstractness abstractness,
+ ExceptionSuperclass exceptionSuperclass,
+ AllowsMultibindings allowsMultibindings,
+ AllowsScoping allowsScoping) {
+ this(
+ elements,
+ types,
+ methodAnnotation,
+ ImmutableSet.of(enclosingElementAnnotation),
+ dependencyRequestValidator,
+ abstractness,
+ exceptionSuperclass,
+ allowsMultibindings,
+ allowsScoping);
+ }
+
+ /**
+ * Creates a validator object.
+ *
+ * @param methodAnnotation the annotation on a method that identifies it as a binding method
+ * @param enclosingElementAnnotations the method must be declared in a class or interface
+ * annotated with one of these annotations
+ */
+ protected BindingMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ Class<? extends Annotation> methodAnnotation,
+ Iterable<? extends Class<? extends Annotation>> enclosingElementAnnotations,
+ DependencyRequestValidator dependencyRequestValidator,
+ Abstractness abstractness,
+ ExceptionSuperclass exceptionSuperclass,
+ AllowsMultibindings allowsMultibindings,
+ AllowsScoping allowsScoping) {
+ super(methodAnnotation, allowsMultibindings, allowsScoping);
+ this.elements = elements;
+ this.types = types;
+ this.methodAnnotation = methodAnnotation;
+ this.enclosingElementAnnotations = ImmutableSet.copyOf(enclosingElementAnnotations);
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ this.abstractness = abstractness;
+ this.exceptionSuperclass = exceptionSuperclass;
+ }
+
+ /** The annotation that identifies binding methods validated by this object. */
+ final Class<? extends Annotation> methodAnnotation() {
+ return methodAnnotation;
+ }
+
+ /**
+ * Returns an error message of the form "@<i>annotation</i> methods <i>rule</i>", where
+ * <i>rule</i> comes from calling {@link String#format(String, Object...)} on {@code ruleFormat}
+ * and the other arguments.
+ */
+ @FormatMethod
+ protected final String bindingMethods(String ruleFormat, Object... args) {
+ return bindingElements(ruleFormat, args);
+ }
+
+ @Override
+ protected final String bindingElements() {
+ return String.format("@%s methods", methodAnnotation.getSimpleName());
+ }
+
+ @Override
+ protected final String bindingElementTypeVerb() {
+ return "return";
+ }
+
+ /** Abstract validator for individual binding method elements. */
+ protected abstract class MethodValidator extends ElementValidator {
+ protected MethodValidator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected final Optional<TypeMirror> bindingElementType() {
+ return Optional.of(element.getReturnType());
+ }
+
+ @Override
+ protected final void checkAdditionalProperties() {
+ checkEnclosingElement();
+ checkTypeParameters();
+ checkNotPrivate();
+ checkAbstractness();
+ checkThrows();
+ checkParameters();
+ checkAdditionalMethodProperties();
+ }
+
+ /** Checks additional properties of the binding method. */
+ protected void checkAdditionalMethodProperties() {}
+
+ /**
+ * Adds an error if the method is not declared in a class or interface annotated with one of the
+ * {@link #enclosingElementAnnotations}.
+ */
+ private void checkEnclosingElement() {
+ if (!isAnyAnnotationPresent(
+ element.getEnclosingElement(), enclosingElementAnnotations)) {
+ report.addError(
+ bindingMethods(
+ "can only be present within a @%s",
+ enclosingElementAnnotations.stream()
+ .map(Class::getSimpleName)
+ .collect(joining(" or @"))));
+ }
+ }
+
+ /** Adds an error if the method is generic. */
+ private void checkTypeParameters() {
+ if (!element.getTypeParameters().isEmpty()) {
+ report.addError(bindingMethods("may not have type parameters"));
+ }
+ }
+
+ /** Adds an error if the method is private. */
+ private void checkNotPrivate() {
+ if (element.getModifiers().contains(PRIVATE)) {
+ report.addError(bindingMethods("cannot be private"));
+ }
+ }
+
+ /** Adds an error if the method is abstract but must not be, or is not and must be. */
+ private void checkAbstractness() {
+ boolean isAbstract = element.getModifiers().contains(ABSTRACT);
+ switch (abstractness) {
+ case MUST_BE_ABSTRACT:
+ if (!isAbstract) {
+ report.addError(bindingMethods("must be abstract"));
+ }
+ break;
+
+ case MUST_BE_CONCRETE:
+ if (isAbstract) {
+ report.addError(bindingMethods("cannot be abstract"));
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
+ * subtype of {@link Exception}.
+ */
+ private void checkThrows() {
+ exceptionSuperclass.checkThrows(BindingMethodValidator.this, element, report);
+ }
+
+ /** Adds errors for the method parameters. */
+ protected void checkParameters() {
+ for (VariableElement parameter : element.getParameters()) {
+ checkParameter(parameter);
+ }
+ }
+
+ /**
+ * Adds errors for a method parameter. This implementation reports an error if the parameter has
+ * more than one qualifier.
+ */
+ protected void checkParameter(VariableElement parameter) {
+ dependencyRequestValidator.validateDependencyRequest(report, parameter, parameter.asType());
+ }
+ }
+
+ /** An abstract/concrete restriction on methods. */
+ protected enum Abstractness {
+ MUST_BE_ABSTRACT,
+ MUST_BE_CONCRETE
+ }
+
+ /**
+ * The exception class that all {@code throws}-declared throwables must extend, other than {@link
+ * Error}.
+ */
+ protected enum ExceptionSuperclass {
+ /** Methods may not declare any throwable types. */
+ NO_EXCEPTIONS {
+ @Override
+ protected String errorMessage(BindingMethodValidator validator) {
+ return validator.bindingMethods("may not throw");
+ }
+
+ @Override
+ protected void checkThrows(
+ BindingMethodValidator validator,
+ ExecutableElement element,
+ ValidationReport.Builder<ExecutableElement> report) {
+ if (!element.getThrownTypes().isEmpty()) {
+ report.addError(validator.bindingMethods("may not throw"));
+ return;
+ }
+ }
+ },
+
+ /** Methods may throw checked or unchecked exceptions or errors. */
+ EXCEPTION(Exception.class) {
+ @Override
+ protected String errorMessage(BindingMethodValidator validator) {
+ return validator.bindingMethods(
+ "may only throw unchecked exceptions or exceptions subclassing Exception");
+ }
+ },
+
+ /** Methods may throw unchecked exceptions or errors. */
+ RUNTIME_EXCEPTION(RuntimeException.class) {
+ @Override
+ protected String errorMessage(BindingMethodValidator validator) {
+ return validator.bindingMethods("may only throw unchecked exceptions");
+ }
+ },
+ ;
+
+ private final Class<? extends Exception> superclass;
+
+ ExceptionSuperclass() {
+ this(null);
+ }
+
+ ExceptionSuperclass(Class<? extends Exception> superclass) {
+ this.superclass = superclass;
+ }
+
+ /**
+ * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
+ * subtype of {@link Exception}.
+ *
+ * <p>This method is overridden in {@link #NO_EXCEPTIONS}.
+ */
+ protected void checkThrows(
+ BindingMethodValidator validator,
+ ExecutableElement element,
+ ValidationReport.Builder<ExecutableElement> report) {
+ TypeMirror exceptionSupertype = validator.elements.getTypeElement(superclass).asType();
+ TypeMirror errorType = validator.elements.getTypeElement(Error.class).asType();
+ for (TypeMirror thrownType : element.getThrownTypes()) {
+ if (!validator.types.isSubtype(thrownType, exceptionSupertype)
+ && !validator.types.isSubtype(thrownType, errorType)) {
+ report.addError(errorMessage(validator));
+ break;
+ }
+ }
+ }
+
+ protected abstract String errorMessage(BindingMethodValidator validator);
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingMethodValidatorsModule.java b/java/dagger/internal/codegen/BindingMethodValidatorsModule.java
new file mode 100644
index 0000000..28a272d
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingMethodValidatorsModule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Maps.uniqueIndex;
+
+import com.google.common.collect.ImmutableMap;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * Binds each {@link BindingMethodValidator} into a map, keyed by {@link
+ * BindingMethodValidator#methodAnnotation()}.
+ */
+@Module
+interface BindingMethodValidatorsModule {
+ @Provides
+ static ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> indexValidators(
+ Set<BindingMethodValidator> validators) {
+ return uniqueIndex(validators, BindingMethodValidator::methodAnnotation);
+ }
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator provides(ProvidesMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator produces(ProducesMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator binds(BindsMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator multibinds(MultibindsMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator bindsOptionalOf(BindsOptionalOfMethodValidator validator);
+}
diff --git a/java/dagger/internal/codegen/BindingNode.java b/java/dagger/internal/codegen/BindingNode.java
new file mode 100644
index 0000000..a7da092
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingNode.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.BindingType.PRODUCTION;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.model.BindingKind;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Scope;
+import dagger.multibindings.Multibinds;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * An implementation of {@link dagger.model.Binding} that also exposes {@link BindingDeclaration}s
+ * associated with the binding.
+ */
+// TODO(dpb): Consider a supertype of dagger.model.Binding that dagger.internal.codegen.Binding
+// could also implement.
+@AutoValue
+abstract class BindingNode implements dagger.model.Binding {
+ static BindingNode create(
+ ComponentPath component,
+ Binding delegate,
+ ImmutableSet<MultibindingDeclaration> multibindingDeclarations,
+ ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations,
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations,
+ BindingDeclarationFormatter bindingDeclarationFormatter) {
+ BindingNode node =
+ new AutoValue_BindingNode(
+ component,
+ delegate,
+ multibindingDeclarations,
+ optionalBindingDeclarations,
+ subcomponentDeclarations);
+ node.bindingDeclarationFormatter = checkNotNull(bindingDeclarationFormatter);
+ return node;
+ }
+
+ private BindingDeclarationFormatter bindingDeclarationFormatter;
+
+ abstract Binding delegate();
+
+ abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
+
+ abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();
+
+ abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
+
+ /**
+ * The {@link Element}s (other than the binding's {@link #bindingElement()}) that are associated
+ * with the binding.
+ *
+ * <ul>
+ * <li>{@linkplain BindsOptionalOf optional binding} declarations
+ * <li>{@linkplain Module#subcomponents() module subcomponent} declarations
+ * <li>{@linkplain Multibinds multibinding} declarations
+ * </ul>
+ */
+ final Iterable<BindingDeclaration> associatedDeclarations() {
+ return Iterables.concat(
+ multibindingDeclarations(), optionalBindingDeclarations(), subcomponentDeclarations());
+ }
+
+ @Override
+ public Key key() {
+ return delegate().key();
+ }
+
+ @Override
+ public ImmutableSet<DependencyRequest> dependencies() {
+ return delegate().dependencies();
+ }
+
+ @Override
+ public Optional<Element> bindingElement() {
+ return delegate().bindingElement();
+ }
+
+ @Override
+ public Optional<TypeElement> contributingModule() {
+ return delegate().contributingModule();
+ }
+
+ @Override
+ public boolean requiresModuleInstance() {
+ return delegate().requiresModuleInstance();
+ }
+
+ @Override
+ public Optional<Scope> scope() {
+ return delegate().scope();
+ }
+
+ @Override
+ public boolean isNullable() {
+ return delegate().isNullable();
+ }
+
+ @Override
+ public boolean isProduction() {
+ return delegate().bindingType().equals(PRODUCTION);
+ }
+
+ @Override
+ public BindingKind kind() {
+ return delegate().kind();
+ }
+
+ @Override
+ public final String toString() {
+ return bindingDeclarationFormatter.format(delegate());
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingRequest.java b/java/dagger/internal/codegen/BindingRequest.java
new file mode 100644
index 0000000..27067aa
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingRequest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.RequestKinds.requestType;
+
+import com.google.auto.value.AutoValue;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.BindingRequestProto;
+import dagger.internal.codegen.serialization.FrameworkTypeWrapper;
+import dagger.internal.codegen.serialization.RequestKindWrapper;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A request for a binding, which may be in the form of a request for a dependency to pass to a
+ * constructor or module method ({@link RequestKind}) or an internal request for a framework
+ * instance ({@link FrameworkType}).
+ */
+@AutoValue
+abstract class BindingRequest {
+ /** Creates a {@link BindingRequest} for the given {@link DependencyRequest}. */
+ static BindingRequest bindingRequest(DependencyRequest dependencyRequest) {
+ return bindingRequest(dependencyRequest.key(), dependencyRequest.kind());
+ }
+
+ /**
+ * Creates a {@link BindingRequest} for a normal dependency request for the given {@link Key} and
+ * {@link RequestKind}.
+ */
+ static BindingRequest bindingRequest(Key key, RequestKind requestKind) {
+ // When there's a request that has a 1:1 mapping to a FrameworkType, the request should be
+ // associated with that FrameworkType as well, because we want to ensure that if a request
+ // comes in for that as a dependency first and as a framework instance later, they resolve to
+ // the same binding expression.
+ // TODO(cgdecker): Instead of doing this, make ComponentBindingExpressions create a
+ // BindingExpression for the RequestKind that simply delegates to the BindingExpression for the
+ // FrameworkType. Then there are separate BindingExpressions, but we don't end up doing weird
+ // things like creating two fields when there should only be one.
+ return new AutoValue_BindingRequest(
+ key, Optional.of(requestKind), FrameworkType.forRequestKind(requestKind));
+ }
+
+ /**
+ * Creates a {@link BindingRequest} for a request for a framework instance for the given {@link
+ * Key} with the given {@link FrameworkType}.
+ */
+ static BindingRequest bindingRequest(Key key, FrameworkType frameworkType) {
+ return new AutoValue_BindingRequest(
+ key, frameworkType.requestKind(), Optional.of(frameworkType));
+ }
+
+ /** Creates a {@link BindingRequest} for the given {@link FrameworkDependency}. */
+ static BindingRequest bindingRequest(FrameworkDependency frameworkDependency) {
+ return bindingRequest(frameworkDependency.key(), frameworkDependency.frameworkType());
+ }
+
+ /** Returns the {@link Key} for the requested binding. */
+ abstract Key key();
+
+ /** Returns the request kind associated with this request, if any. */
+ abstract Optional<RequestKind> requestKind();
+
+ /** Returns the framework type associated with this request, if any. */
+ abstract Optional<FrameworkType> frameworkType();
+
+ /** Returns whether this request is of the given kind. */
+ final boolean isRequestKind(RequestKind requestKind) {
+ return requestKind.equals(requestKind().orElse(null));
+ }
+
+ final TypeMirror requestedType(TypeMirror contributedType, DaggerTypes types) {
+ if (requestKind().isPresent()) {
+ return requestType(requestKind().get(), contributedType, types);
+ }
+ return types.wrapType(contributedType, frameworkType().get().frameworkClass());
+ }
+
+ /** Returns a name that can be used for the kind of request this is. */
+ final String kindName() {
+ Object requestKindObject =
+ requestKind().isPresent()
+ ? requestKind().get()
+ : frameworkType().get().frameworkClass().getSimpleName();
+ return requestKindObject.toString();
+ }
+
+ /** Returns {@code true} if this request can be satisfied by a production binding. */
+ final boolean canBeSatisfiedByProductionBinding() {
+ if (requestKind().isPresent()) {
+ return RequestKinds.canBeSatisfiedByProductionBinding(requestKind().get());
+ }
+ return frameworkType().get().equals(FrameworkType.PRODUCER_NODE);
+ }
+
+ /** Creates a proto representation of this binding request. */
+ BindingRequestProto toProto() {
+ BindingRequestProto.Builder builder =
+ BindingRequestProto.newBuilder().setKey(KeyFactory.toProto(key()));
+ if (frameworkType().isPresent()) {
+ builder.setFrameworkType(
+ FrameworkTypeWrapper.FrameworkType.valueOf(frameworkType().get().name()));
+ } else {
+ builder.setRequestKind(RequestKindWrapper.RequestKind.valueOf(requestKind().get().name()));
+ }
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/BindingType.java b/java/dagger/internal/codegen/BindingType.java
new file mode 100644
index 0000000..37109c7
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingType.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.MembersInjector;
+
+/** Whether a binding or declaration is for provision, production, or a {@link MembersInjector}. */
+enum BindingType {
+ /** A binding with this type is a {@link ProvisionBinding}. */
+ PROVISION,
+
+ /** A binding with this type is a {@link MembersInjectionBinding}. */
+ MEMBERS_INJECTION,
+
+ /** A binding with this type is a {@link ProductionBinding}. */
+ PRODUCTION,
+}
diff --git a/java/dagger/internal/codegen/BindsInstanceElementValidator.java b/java/dagger/internal/codegen/BindsInstanceElementValidator.java
new file mode 100644
index 0000000..9249c8e
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsInstanceElementValidator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.BindsInstance;
+import javax.lang.model.element.Element;
+
+abstract class BindsInstanceElementValidator<E extends Element> extends BindingElementValidator<E> {
+ BindsInstanceElementValidator() {
+ super(BindsInstance.class, AllowsMultibindings.NO_MULTIBINDINGS, AllowsScoping.NO_SCOPING);
+ }
+
+ @Override
+ protected final String bindingElements() {
+ // Even though @BindsInstance may be placed on methods, the subject of errors is the
+ // parameter
+ return "@BindsInstance parameters";
+ }
+
+ @Override
+ protected final String bindingElementTypeVerb() {
+ return "be";
+ }
+}
diff --git a/java/dagger/internal/codegen/BindsInstanceMethodValidator.java b/java/dagger/internal/codegen/BindsInstanceMethodValidator.java
new file mode 100644
index 0000000..1a491c7
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsInstanceMethodValidator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ComponentAnnotation.anyComponentAnnotation;
+import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+
+import com.google.auto.common.MoreElements;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+final class BindsInstanceMethodValidator extends BindsInstanceElementValidator<ExecutableElement> {
+ @Inject
+ BindsInstanceMethodValidator() {}
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends ElementValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalProperties() {
+ if (!element.getModifiers().contains(ABSTRACT)) {
+ report.addError("@BindsInstance methods must be abstract");
+ }
+ if (element.getParameters().size() != 1) {
+ report.addError(
+ "@BindsInstance methods should have exactly one parameter for the bound type");
+ }
+ TypeElement enclosingType = MoreElements.asType(element.getEnclosingElement());
+ moduleAnnotation(enclosingType)
+ .ifPresent(moduleAnnotation -> report.addError(didYouMeanBinds(moduleAnnotation)));
+ anyComponentAnnotation(enclosingType)
+ .ifPresent(
+ componentAnnotation ->
+ report.addError(
+ String.format(
+ "@BindsInstance methods should not be included in @%1$ss. "
+ + "Did you mean to put it in a @%1$s.Builder?",
+ componentAnnotation.simpleName())));
+ }
+
+ @Override
+ protected Optional<TypeMirror> bindingElementType() {
+ List<? extends VariableElement> parameters =
+ MoreElements.asExecutable(element).getParameters();
+ return parameters.size() == 1
+ ? Optional.of(getOnlyElement(parameters).asType())
+ : Optional.empty();
+ }
+ }
+
+ private static String didYouMeanBinds(ModuleAnnotation moduleAnnotation) {
+ return String.format(
+ "@BindsInstance methods should not be included in @%ss. Did you mean @Binds?",
+ moduleAnnotation.annotationClass().getSimpleName());
+ }
+}
diff --git a/java/dagger/internal/codegen/BindsInstanceParameterValidator.java b/java/dagger/internal/codegen/BindsInstanceParameterValidator.java
new file mode 100644
index 0000000..b2dc8d8
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsInstanceParameterValidator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static javax.lang.model.element.ElementKind.METHOD;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.TYPEVAR;
+
+import com.google.auto.common.MoreElements;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+final class BindsInstanceParameterValidator extends BindsInstanceElementValidator<VariableElement> {
+ @Inject
+ BindsInstanceParameterValidator() {}
+
+ @Override
+ protected ElementValidator elementValidator(VariableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends ElementValidator {
+ Validator(VariableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalProperties() {
+ Element enclosing = element.getEnclosingElement();
+ if (!enclosing.getKind().equals(METHOD)) {
+ report.addError(
+ "@BindsInstance should only be applied to methods or parameters of methods");
+ return;
+ }
+
+ ExecutableElement method = MoreElements.asExecutable(enclosing);
+ if (!method.getModifiers().contains(ABSTRACT)) {
+ report.addError("@BindsInstance parameters may only be used in abstract methods");
+ }
+
+ TypeKind returnKind = method.getReturnType().getKind();
+ if (!(returnKind.equals(DECLARED) || returnKind.equals(TYPEVAR))) {
+ report.addError(
+ "@BindsInstance parameters may not be used in methods with a void, array or primitive "
+ + "return type");
+ }
+ }
+
+ @Override
+ protected Optional<TypeMirror> bindingElementType() {
+ return Optional.of(element.asType());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindsInstanceProcessingStep.java b/java/dagger/internal/codegen/BindsInstanceProcessingStep.java
new file mode 100644
index 0000000..4c222a9
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsInstanceProcessingStep.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import dagger.BindsInstance;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+
+/**
+ * Processing step that validates that the {@code BindsInstance} annotation is applied to the
+ * correct elements.
+ */
+final class BindsInstanceProcessingStep extends TypeCheckingProcessingStep<Element> {
+ private final BindsInstanceMethodValidator methodValidator;
+ private final BindsInstanceParameterValidator parameterValidator;
+ private final Messager messager;
+
+ @Inject
+ BindsInstanceProcessingStep(
+ BindsInstanceMethodValidator methodValidator,
+ BindsInstanceParameterValidator parameterValidator,
+ Messager messager) {
+ super(element -> element);
+ this.methodValidator = methodValidator;
+ this.parameterValidator = parameterValidator;
+ this.messager = messager;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(BindsInstance.class);
+ }
+
+ @Override
+ protected void process(Element element, ImmutableSet<Class<? extends Annotation>> annotations) {
+ switch (element.getKind()) {
+ case PARAMETER:
+ parameterValidator.validate(MoreElements.asVariable(element)).printMessagesTo(messager);
+ break;
+ case METHOD:
+ methodValidator.validate(MoreElements.asExecutable(element)).printMessagesTo(messager);
+ break;
+ default:
+ throw new AssertionError(element);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindsMethodValidator.java b/java/dagger/internal/codegen/BindsMethodValidator.java
new file mode 100644
index 0000000..e198c3a
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsMethodValidator.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
+import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
+import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Binds;
+import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link Binds} methods. */
+final class BindsMethodValidator extends BindingMethodValidator {
+ private final DaggerTypes types;
+ private final BindsTypeChecker bindsTypeChecker;
+
+ @Inject
+ BindsMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestValidator dependencyRequestValidator) {
+ super(
+ elements,
+ types,
+ Binds.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_ABSTRACT,
+ RUNTIME_EXCEPTION,
+ ALLOWS_MULTIBINDINGS,
+ ALLOWS_SCOPING);
+ this.types = types;
+ this.bindsTypeChecker = new BindsTypeChecker(types, elements);
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkParameters() {
+ if (element.getParameters().size() != 1) {
+ report.addError(
+ bindingMethods(
+ "must have exactly one parameter, whose type is assignable to the return type"));
+ } else {
+ super.checkParameters();
+ }
+ }
+
+ @Override
+ protected void checkParameter(VariableElement parameter) {
+ super.checkParameter(parameter);
+ TypeMirror leftHandSide = boxIfNecessary(element.getReturnType());
+ TypeMirror rightHandSide = parameter.asType();
+ ContributionType contributionType = ContributionType.fromBindingElement(element);
+ if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
+ report.addError(
+ "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter");
+ }
+
+ if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
+ // TODO(ronshapiro): clarify this error message for @ElementsIntoSet cases, where the
+ // right-hand-side might not be assignable to the left-hand-side, but still compatible with
+ // Set.addAll(Collection<? extends E>)
+ report.addError("@Binds methods' parameter type must be assignable to the return type");
+ }
+ }
+
+ private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
+ if (maybePrimitive.getKind().isPrimitive()) {
+ return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
+ }
+ return maybePrimitive;
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java b/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java
new file mode 100644
index 0000000..e1c9d73
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
+import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
+import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
+import static dagger.internal.codegen.InjectionAnnotations.injectedConstructors;
+import static dagger.internal.codegen.Keys.isValidImplicitProvisionKey;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link BindsOptionalOf} methods. */
+final class BindsOptionalOfMethodValidator extends BindingMethodValidator {
+
+ private final DaggerTypes types;
+
+ @Inject
+ BindsOptionalOfMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestValidator dependencyRequestValidator) {
+ super(
+ elements,
+ types,
+ BindsOptionalOf.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_ABSTRACT,
+ NO_EXCEPTIONS,
+ NO_MULTIBINDINGS,
+ NO_SCOPING);
+ this.types = types;
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkKeyType(TypeMirror keyType) {
+ super.checkKeyType(keyType);
+ if (isValidImplicitProvisionKey(
+ getQualifiers(element).stream().findFirst(), keyType, types)
+ && !injectedConstructors(MoreElements.asType(MoreTypes.asDeclared(keyType).asElement()))
+ .isEmpty()) {
+ report.addError(
+ "@BindsOptionalOf methods cannot return unqualified types that have an @Inject-"
+ + "annotated constructor because those are always present");
+ }
+ }
+
+ @Override
+ protected void checkParameters() {
+ if (!element.getParameters().isEmpty()) {
+ report.addError("@BindsOptionalOf methods cannot have parameters");
+ }
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/BindsTypeChecker.java b/java/dagger/internal/codegen/BindsTypeChecker.java
new file mode 100644
index 0000000..acecc9e
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsTypeChecker.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Checks the assignability of one type to another, given a {@link ContributionType} context. This
+ * is used by {@link BindsMethodValidator} to validate that the right-hand-side of a {@link
+ * dagger.Binds} method is valid, as well as in {@link DelegateBindingExpression} when the
+ * right-hand-side in generated code might be an erased type due to accessibility.
+ */
+final class BindsTypeChecker {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ @Inject
+ BindsTypeChecker(DaggerTypes types, DaggerElements elements) {
+ this.types = types;
+ this.elements = elements;
+ }
+
+ /**
+ * Checks the assignability of {@code rightHandSide} to {@code leftHandSide} given a {@link
+ * ContributionType} context.
+ */
+ boolean isAssignable(
+ TypeMirror rightHandSide, TypeMirror leftHandSide, ContributionType contributionType) {
+ return types.isAssignable(rightHandSide, desiredAssignableType(leftHandSide, contributionType));
+ }
+
+ private TypeMirror desiredAssignableType(
+ TypeMirror leftHandSide, ContributionType contributionType) {
+ switch (contributionType) {
+ case UNIQUE:
+ return leftHandSide;
+ case SET:
+ DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
+ return methodParameterType(parameterizedSetType, "add");
+ case SET_VALUES:
+ return methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll");
+ case MAP:
+ DeclaredType parameterizedMapType =
+ types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
+ return methodParameterTypes(parameterizedMapType, "put").get(1);
+ }
+ throw new AssertionError("Unknown contribution type: " + contributionType);
+ }
+
+ private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
+ ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder();
+ for (ExecutableElement method :
+ // type.asElement().getEnclosedElements() is not used because some non-standard JDKs (e.g.
+ // J2CL) don't redefine Set.add() (whose only purpose of being redefined in the standard JDK
+ // is documentation, and J2CL's implementation doesn't declare docs for JDK types).
+ // MoreElements.getLocalAndInheritedMethods ensures that the method will always be present.
+ MoreElements.getLocalAndInheritedMethods(MoreTypes.asTypeElement(type), types, elements)) {
+ if (method.getSimpleName().contentEquals(methodName)) {
+ methodsForName.add(method);
+ }
+ }
+ ExecutableElement method = getOnlyElement(methodsForName.build());
+ return ImmutableList.copyOf(
+ MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes());
+ }
+
+ private TypeMirror methodParameterType(DeclaredType type, String methodName) {
+ return getOnlyElement(methodParameterTypes(type, methodName));
+ }
+
+ private TypeElement setElement() {
+ return elements.getTypeElement(Set.class);
+ }
+
+ private TypeElement mapElement() {
+ return elements.getTypeElement(Map.class);
+ }
+
+ private TypeMirror unboundedWildcard() {
+ return types.getWildcardType(null, null);
+ }
+}
diff --git a/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java b/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java
new file mode 100644
index 0000000..a5e0219
--- /dev/null
+++ b/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.ElementFormatter.elementToString;
+
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import javax.lang.model.element.ExecutableElement;
+
+/** An implementation of {@link ChildFactoryMethodEdge}. */
+final class ChildFactoryMethodEdgeImpl implements ChildFactoryMethodEdge {
+
+ private final ExecutableElement factoryMethod;
+
+ ChildFactoryMethodEdgeImpl(ExecutableElement factoryMethod) {
+ this.factoryMethod = factoryMethod;
+ }
+
+ @Override
+ public ExecutableElement factoryMethod() {
+ return factoryMethod;
+ }
+
+ @Override
+ public String toString() {
+ return elementToString(factoryMethod);
+ }
+}
diff --git a/java/dagger/internal/codegen/ClearableCache.java b/java/dagger/internal/codegen/ClearableCache.java
new file mode 100644
index 0000000..66ce3ef
--- /dev/null
+++ b/java/dagger/internal/codegen/ClearableCache.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+/** A cache of objects that can be cleared. */
+interface ClearableCache {
+ /** Releases cached references. */
+ void clearCache();
+}
diff --git a/java/dagger/internal/codegen/CompilerOptions.java b/java/dagger/internal/codegen/CompilerOptions.java
new file mode 100644
index 0000000..bc3cbf8
--- /dev/null
+++ b/java/dagger/internal/codegen/CompilerOptions.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.AnnotationSpec;
+import dagger.internal.GenerationOptions;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+/** A collection of options that dictate how the compiler will run. */
+abstract class CompilerOptions {
+ abstract boolean usesProducers();
+
+ /**
+ * Returns true if the fast initialization flag, {@code fastInit}, is enabled.
+ *
+ * <p>If enabled, the generated code will attempt to optimize for fast component initialization.
+ * This is done by reducing the number of factory classes loaded during initialization and the
+ * number of eagerly initialized fields at the cost of potential memory leaks and higher
+ * per-provision instantiation time.
+ */
+ abstract boolean fastInit();
+
+ abstract boolean formatGeneratedSource();
+
+ abstract boolean writeProducerNameInToken();
+
+ abstract Diagnostic.Kind nullableValidationKind();
+
+ final boolean doCheckForNulls() {
+ return nullableValidationKind().equals(Diagnostic.Kind.ERROR);
+ }
+
+ abstract Diagnostic.Kind privateMemberValidationKind();
+
+ abstract Diagnostic.Kind staticMemberValidationKind();
+
+ /**
+ * If {@code true}, Dagger will generate factories and components even if some members-injected
+ * types have {@code private} or {@code static} {@code @Inject}-annotated members.
+ *
+ * <p>This should only ever be enabled by the TCK tests. Disabling this validation could lead to
+ * generating code that does not compile.
+ */
+ abstract boolean ignorePrivateAndStaticInjectionForComponent();
+
+ abstract ValidationType scopeCycleValidationType();
+
+ abstract boolean warnIfInjectionFactoryNotGeneratedUpstream();
+
+ abstract boolean headerCompilation();
+
+ abstract boolean aheadOfTimeSubcomponents();
+
+ /**
+ * Enables a testing configuration where all superclass {@link ComponentImplementation}s are
+ * derived from their serialized forms.
+ */
+ abstract boolean forceUseSerializedComponentImplementations();
+
+ /**
+ * If {@code true}, in {@link #aheadOfTimeSubcomponents()} mode, Dagger will emit metadata
+ * annotations to deserialize aspects of the {@link ComponentImplementation}.
+ *
+ * This should only be disabled in compile-testing tests that want to ignore the annotations when
+ * asserting on generated source.
+ */
+ abstract boolean emitModifiableMetadataAnnotations();
+
+ abstract boolean useGradleIncrementalProcessing();
+
+ /**
+ * Returns the validation that should be done for the full binding graph for the element.
+ *
+ * @throws IllegalArgumentException if {@code element} is not a module or (sub)component
+ */
+ abstract ValidationType fullBindingGraphValidationType(TypeElement element);
+
+ abstract Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind();
+
+ abstract ValidationType explicitBindingConflictsWithInjectValidationType();
+
+ /**
+ * Creates a new {@link CompilerOptions} from the serialized {@link GenerationOptions} of a base
+ * component implementation.
+ */
+ final CompilerOptions withGenerationOptions(GenerationOptions generationOptions) {
+ return new ForwardingCompilerOptions(this) {
+ @Override
+ public boolean fastInit() {
+ return generationOptions.fastInit();
+ }
+ };
+ }
+
+ /**
+ * Returns a {@link GenerationOptions} annotation that serializes any options for this compilation
+ * that should be reused in future compilations.
+ */
+ final AnnotationSpec toGenerationOptionsAnnotation() {
+ return AnnotationSpec.builder(GenerationOptions.class)
+ .addMember("fastInit", "$L", fastInit())
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentAnnotation.java b/java/dagger/internal/codegen/ComponentAnnotation.java
new file mode 100644
index 0000000..a8f2ece
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentAnnotation.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreTypes.asTypeElements;
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.Component;
+import dagger.Subcomponent;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A {@code @Component}, {@code @Subcomponent}, {@code @ProductionComponent}, or
+ * {@code @ProductionSubcomponent} annotation, or a {@code @Module} or {@code @ProducerModule}
+ * annotation that is being treated as a component annotation when validating full binding graphs
+ * for modules.
+ */
+abstract class ComponentAnnotation {
+ /** The root component annotation types. */
+ private static final ImmutableSet<Class<? extends Annotation>> ROOT_COMPONENT_ANNOTATIONS =
+ ImmutableSet.of(Component.class, ProductionComponent.class);
+
+ /** The subcomponent annotation types. */
+ private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_ANNOTATIONS =
+ ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
+
+ /** All component annotation types. */
+ private static final ImmutableSet<Class<? extends Annotation>> ALL_COMPONENT_ANNOTATIONS =
+ ImmutableSet.<Class<? extends Annotation>>builder()
+ .addAll(ROOT_COMPONENT_ANNOTATIONS)
+ .addAll(SUBCOMPONENT_ANNOTATIONS)
+ .build();
+
+ /** The annotation itself. */
+ abstract AnnotationMirror annotation();
+
+ /** The simple name of the annotation type. */
+ String simpleName() {
+ return MoreAnnotationMirrors.simpleName(annotation()).toString();
+ }
+
+ /**
+ * Returns {@code true} if the annotation is a {@code @Subcomponent} or
+ * {@code @ProductionSubcomponent}.
+ */
+ abstract boolean isSubcomponent();
+
+ /**
+ * Returns {@code true} if the annotation is a {@code @ProductionComponent},
+ * {@code @ProductionSubcomponent}, or {@code @ProducerModule}.
+ */
+ abstract boolean isProduction();
+
+ /**
+ * Returns {@code true} if the annotation is a real component annotation and not a module
+ * annotation.
+ */
+ abstract boolean isRealComponent();
+
+ /** The values listed as {@code dependencies}. */
+ abstract ImmutableList<AnnotationValue> dependencyValues();
+
+ /** The types listed as {@code dependencies}. */
+ ImmutableList<TypeMirror> dependencyTypes() {
+ return dependencyValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
+ }
+
+ /**
+ * The types listed as {@code dependencies}.
+ *
+ * @throws IllegalArgumentException if any of {@link #dependencyTypes()} are error types
+ */
+ ImmutableList<TypeElement> dependencies() {
+ return asTypeElements(dependencyTypes()).asList();
+ }
+
+ /** The values listed as {@code modules}. */
+ abstract ImmutableList<AnnotationValue> moduleValues();
+
+ /** The types listed as {@code modules}. */
+ ImmutableList<TypeMirror> moduleTypes() {
+ return moduleValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
+ }
+
+ /**
+ * The types listed as {@code modules}.
+ *
+ * @throws IllegalArgumentException if any of {@link #moduleTypes()} are error types
+ */
+ ImmutableSet<TypeElement> modules() {
+ return asTypeElements(moduleTypes());
+ }
+
+ protected final ImmutableList<AnnotationValue> getAnnotationValues(String parameterName) {
+ return asAnnotationValues(getAnnotationValue(annotation(), parameterName));
+ }
+
+ /**
+ * Returns an object representing a root component annotation, not a subcomponent annotation, if
+ * one is present on {@code typeElement}.
+ */
+ static Optional<ComponentAnnotation> rootComponentAnnotation(TypeElement typeElement) {
+ return anyComponentAnnotation(typeElement, ROOT_COMPONENT_ANNOTATIONS);
+ }
+
+ /**
+ * Returns an object representing a subcomponent annotation, if one is present on {@code
+ * typeElement}.
+ */
+ static Optional<ComponentAnnotation> subcomponentAnnotation(TypeElement typeElement) {
+ return anyComponentAnnotation(typeElement, SUBCOMPONENT_ANNOTATIONS);
+ }
+
+ /**
+ * Returns an object representing a root component or subcomponent annotation, if one is present
+ * on {@code typeElement}.
+ */
+ static Optional<ComponentAnnotation> anyComponentAnnotation(TypeElement typeElement) {
+ return anyComponentAnnotation(typeElement, ALL_COMPONENT_ANNOTATIONS);
+ }
+
+ private static Optional<ComponentAnnotation> anyComponentAnnotation(
+ TypeElement typeElement, Collection<Class<? extends Annotation>> annotations) {
+ return getAnyAnnotation(typeElement, annotations).map(ComponentAnnotation::componentAnnotation);
+ }
+
+ /** Returns {@code true} if the argument is a component annotation. */
+ static boolean isComponentAnnotation(AnnotationMirror annotation) {
+ return ALL_COMPONENT_ANNOTATIONS.stream()
+ .anyMatch(annotationClass -> isTypeOf(annotationClass, annotation.getAnnotationType()));
+ }
+
+ /** Creates an object representing a component or subcomponent annotation. */
+ static ComponentAnnotation componentAnnotation(AnnotationMirror annotation) {
+ RealComponentAnnotation.Builder annotationBuilder =
+ RealComponentAnnotation.builder().annotation(annotation);
+
+ if (isTypeOf(Component.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(false).isSubcomponent(false).build();
+ }
+ if (isTypeOf(Subcomponent.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(false).isSubcomponent(true).build();
+ }
+ if (isTypeOf(ProductionComponent.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(true).isSubcomponent(false).build();
+ }
+ if (isTypeOf(ProductionSubcomponent.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(true).isSubcomponent(true).build();
+ }
+ throw new IllegalArgumentException(
+ annotation
+ + " must be a Component, Subcomponent, ProductionComponent, "
+ + "or ProductionSubcomponent annotation");
+ }
+
+ /** Creates a fictional component annotation representing a module. */
+ static ComponentAnnotation fromModuleAnnotation(ModuleAnnotation moduleAnnotation) {
+ return new AutoValue_ComponentAnnotation_FictionalComponentAnnotation(moduleAnnotation);
+ }
+
+ /** The root component annotation types. */
+ static ImmutableSet<Class<? extends Annotation>> rootComponentAnnotations() {
+ return ROOT_COMPONENT_ANNOTATIONS;
+ }
+
+ /** The subcomponent annotation types. */
+ static ImmutableSet<Class<? extends Annotation>> subcomponentAnnotations() {
+ return SUBCOMPONENT_ANNOTATIONS;
+ }
+
+ /** All component annotation types. */
+ static ImmutableSet<Class<? extends Annotation>> allComponentAnnotations() {
+ return ALL_COMPONENT_ANNOTATIONS;
+ }
+
+ /**
+ * An actual component annotation.
+ *
+ * @see FictionalComponentAnnotation
+ */
+ @AutoValue
+ abstract static class RealComponentAnnotation extends ComponentAnnotation {
+
+ @Override
+ @Memoized
+ ImmutableList<AnnotationValue> dependencyValues() {
+ return isSubcomponent() ? ImmutableList.of() : getAnnotationValues("dependencies");
+ }
+
+ @Override
+ @Memoized
+ ImmutableList<TypeMirror> dependencyTypes() {
+ return super.dependencyTypes();
+ }
+
+ @Override
+ @Memoized
+ ImmutableList<TypeElement> dependencies() {
+ return super.dependencies();
+ }
+
+ @Override
+ boolean isRealComponent() {
+ return true;
+ }
+
+ @Override
+ @Memoized
+ ImmutableList<AnnotationValue> moduleValues() {
+ return getAnnotationValues("modules");
+ }
+
+ @Override
+ @Memoized
+ ImmutableList<TypeMirror> moduleTypes() {
+ return super.moduleTypes();
+ }
+
+ @Override
+ @Memoized
+ ImmutableSet<TypeElement> modules() {
+ return super.modules();
+ }
+
+ static Builder builder() {
+ return new AutoValue_ComponentAnnotation_RealComponentAnnotation.Builder();
+ }
+
+ @AutoValue.Builder
+ interface Builder {
+ Builder annotation(AnnotationMirror annotation);
+
+ Builder isSubcomponent(boolean isSubcomponent);
+
+ Builder isProduction(boolean isProduction);
+
+ RealComponentAnnotation build();
+ }
+ }
+
+ /**
+ * A fictional component annotation used to represent modules or other collections of bindings as
+ * a component.
+ */
+ @AutoValue
+ abstract static class FictionalComponentAnnotation extends ComponentAnnotation {
+
+ @Override
+ AnnotationMirror annotation() {
+ return moduleAnnotation().annotation();
+ }
+
+ @Override
+ boolean isSubcomponent() {
+ return false;
+ }
+
+ @Override
+ boolean isProduction() {
+ return moduleAnnotation().annotationClass().equals(ProducerModule.class);
+ }
+
+ @Override
+ boolean isRealComponent() {
+ return false;
+ }
+
+ @Override
+ ImmutableList<AnnotationValue> dependencyValues() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ ImmutableList<AnnotationValue> moduleValues() {
+ return moduleAnnotation().includesAsAnnotationValues();
+ }
+
+ @Override
+ @Memoized
+ ImmutableList<TypeMirror> moduleTypes() {
+ return super.moduleTypes();
+ }
+
+ @Override
+ @Memoized
+ ImmutableSet<TypeElement> modules() {
+ return super.modules();
+ }
+
+ abstract ModuleAnnotation moduleAnnotation();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
new file mode 100644
index 0000000..37cf1e8
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.BindingType.MEMBERS_INJECTION;
+import static dagger.internal.codegen.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope;
+import static dagger.internal.codegen.MemberSelect.staticFactoryCreation;
+import static dagger.internal.codegen.RequestKinds.isDerivedFromProvider;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.model.BindingKind.DELEGATE;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static dagger.model.BindingKind.MULTIBOUND_SET;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.MethodBindingExpression.MethodImplementationStrategy;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.type.TypeMirror;
+
+/** A central repository of code expressions used to access any binding available to a component. */
+@PerComponentImplementation
+final class ComponentBindingExpressions {
+ // TODO(dpb,ronshapiro): refactor this and ComponentRequirementExpressions into a
+ // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
+ // parents? If so, maybe make BindingExpression.Factory create it.
+
+ private final Optional<ComponentBindingExpressions> parent;
+ private final BindingGraph graph;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final OptionalFactories optionalFactories;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final SourceVersion sourceVersion;
+ private final CompilerOptions compilerOptions;
+ private final MembersInjectionMethods membersInjectionMethods;
+ private final InnerSwitchingProviders innerSwitchingProviders;
+ private final ModifiableBindingExpressions modifiableBindingExpressions;
+ private final Map<BindingRequest, BindingExpression> expressions = new HashMap<>();
+
+ @Inject
+ ComponentBindingExpressions(
+ @ParentComponent Optional<ComponentBindingExpressions> parent,
+ BindingGraph graph,
+ ComponentImplementation componentImplementation,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ OptionalFactories optionalFactories,
+ DaggerTypes types,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ @GenerationCompilerOptions CompilerOptions compilerOptions) {
+ this.parent = parent;
+ this.graph = graph;
+ this.componentImplementation = componentImplementation;
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.optionalFactories = checkNotNull(optionalFactories);
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ this.sourceVersion = checkNotNull(sourceVersion);
+ this.compilerOptions = checkNotNull(compilerOptions);
+ this.membersInjectionMethods =
+ new MembersInjectionMethods(componentImplementation, this, graph, elements, types);
+ this.innerSwitchingProviders =
+ new InnerSwitchingProviders(componentImplementation, this, types);
+ this.modifiableBindingExpressions =
+ new ModifiableBindingExpressions(
+ parent.map(cbe -> cbe.modifiableBindingExpressions),
+ this,
+ graph,
+ componentImplementation,
+ compilerOptions,
+ types);
+ }
+
+ /* Returns the {@link ModifiableBindingExpressions} for this component. */
+ ModifiableBindingExpressions modifiableBindingExpressions() {
+ return modifiableBindingExpressions;
+ }
+
+ /**
+ * Returns an expression that evaluates to the value of a binding request for a binding owned by
+ * this component or an ancestor.
+ *
+ * @param requestingClass the class that will contain the expression
+ * @throws IllegalStateException if there is no binding expression that satisfies the request
+ */
+ Expression getDependencyExpression(BindingRequest request, ClassName requestingClass) {
+ return getBindingExpression(request).getDependencyExpression(requestingClass);
+ }
+
+ /**
+ * Equivalent to {@link #getDependencyExpression(BindingRequest, ClassName)} that is used only
+ * when the request is for implementation of a component method.
+ *
+ * @throws IllegalStateException if there is no binding expression that satisfies the request
+ */
+ Expression getDependencyExpressionForComponentMethod(
+ BindingRequest request,
+ ComponentMethodDescriptor componentMethod,
+ ComponentImplementation componentImplementation) {
+ return getBindingExpression(request)
+ .getDependencyExpressionForComponentMethod(componentMethod, componentImplementation);
+ }
+
+ /**
+ * Returns the {@link CodeBlock} for the method arguments used with the factory {@code create()}
+ * method for the given {@link ContributionBinding binding}.
+ */
+ CodeBlock getCreateMethodArgumentsCodeBlock(ContributionBinding binding) {
+ return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding));
+ }
+
+ private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(ContributionBinding binding) {
+ ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+
+ if (binding.requiresModuleInstance()) {
+ arguments.add(
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ ComponentRequirement.forModule(binding.contributingModule().get().asType()),
+ componentImplementation.name()));
+ }
+
+ binding.frameworkDependencies().stream()
+ .map(BindingRequest::bindingRequest)
+ .map(request -> getDependencyExpression(request, componentImplementation.name()))
+ .map(Expression::codeBlock)
+ .forEach(arguments::add);
+
+ return arguments.build();
+ }
+
+ /**
+ * Returns an expression that evaluates to the value of a dependency request, for passing to a
+ * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one.
+ *
+ * <p>If the method is a generated static {@link InjectionMethods injection method}, each
+ * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the
+ * case for this dependency, the returned expression will use a cast to evaluate to the raw type.
+ *
+ * @param requestingClass the class that will contain the expression
+ */
+ Expression getDependencyArgumentExpression(
+ DependencyRequest dependencyRequest, ClassName requestingClass) {
+
+ TypeMirror dependencyType = dependencyRequest.key().type();
+ BindingRequest bindingRequest = bindingRequest(dependencyRequest);
+ Expression dependencyExpression = getDependencyExpression(bindingRequest, requestingClass);
+
+ if (compilerOptions.aheadOfTimeSubcomponents()) {
+ TypeMirror requestedType =
+ bindingRequest.requestedType(dependencyRequest.key().type(), types);
+ // If dependencyExpression.type() has been erased to it's publicly accessible type in AOT,
+ // we must sometimes cast the expression so that it is usable in the current component. To do
+ // so, we check that without the cast the assignment would fail, that argument to this proxy
+ // method erased the type, and that the raw type of the requested type is actually accessible
+ // in the current class so that the cast is valid.
+ if (!types.isAssignable(dependencyExpression.type(), requestedType)
+ && !isRawTypePubliclyAccessible(requestedType)
+ && isRawTypeAccessible(requestedType, requestingClass.packageName())) {
+ return dependencyExpression.castTo(types.erasure(requestedType));
+ }
+ }
+
+ if (dependencyRequest.kind().equals(RequestKind.INSTANCE)
+ && !isTypeAccessibleFrom(dependencyType, requestingClass.packageName())
+ && isRawTypeAccessible(dependencyType, requestingClass.packageName())) {
+ return dependencyExpression.castTo(types.erasure(dependencyType));
+ }
+
+ return dependencyExpression;
+ }
+
+ /** Returns the implementation of a component method. */
+ MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) {
+ checkArgument(componentMethod.dependencyRequest().isPresent());
+ BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get());
+ MethodSpec.Builder method =
+ MethodSpec.overriding(
+ componentMethod.methodElement(),
+ MoreTypes.asDeclared(graph.componentTypeElement().asType()),
+ types);
+ // Even though this is not used if the method is abstract, we need to invoke the binding
+ // expression in order for the side of effect of the method being added to the
+ // ComponentImplementation
+ CodeBlock methodBody =
+ getBindingExpression(request)
+ .getComponentMethodImplementation(componentMethod, componentImplementation);
+ if (!componentImplementation.superclassImplementation().isPresent()
+ && !modifiableBindingExpressions
+ .getModifiableBindingType(request)
+ .hasBaseClassImplementation()
+ && !componentImplementation.getModifiableBindingMethod(request).isPresent()) {
+ return method.addModifiers(ABSTRACT).build();
+ }
+ return method.addCode(methodBody).build();
+ }
+
+ /** Returns the {@link BindingExpression} for the given {@link BindingRequest}. */
+ BindingExpression getBindingExpression(BindingRequest request) {
+ if (expressions.containsKey(request)) {
+ return expressions.get(request);
+ }
+ Optional<BindingExpression> expression =
+ modifiableBindingExpressions.maybeCreateModifiableBindingExpression(request);
+ if (!expression.isPresent()) {
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
+ if (resolvedBindings != null
+ && !resolvedBindings.bindingsOwnedBy(graph.componentDescriptor()).isEmpty()) {
+ expression = Optional.of(createBindingExpression(resolvedBindings, request));
+ }
+ }
+ if (!expression.isPresent()
+ && compilerOptions.aheadOfTimeSubcomponents()
+ && request.requestKind().isPresent()
+ && isDerivedFromProvider(request.requestKind().get())) {
+ RequestKind requestKind = request.requestKind().get();
+ expression =
+ Optional.of(
+ new DerivedFromFrameworkInstanceBindingExpression(
+ request.key(), FrameworkType.PROVIDER, requestKind, this, types));
+ }
+
+ if (expression.isPresent()) {
+ expressions.put(request, expression.get());
+ return expression.get();
+ }
+ checkArgument(parent.isPresent(), "no expression found for %s", request);
+ return parent.get().getBindingExpression(request);
+ }
+
+ /** Creates a binding expression. */
+ BindingExpression createBindingExpression(
+ ResolvedBindings resolvedBindings, BindingRequest request) {
+ switch (resolvedBindings.bindingType()) {
+ case MEMBERS_INJECTION:
+ checkArgument(request.isRequestKind(RequestKind.MEMBERS_INJECTION));
+ return new MembersInjectionBindingExpression(resolvedBindings, membersInjectionMethods);
+
+ case PROVISION:
+ return provisionBindingExpression(resolvedBindings, request);
+
+ case PRODUCTION:
+ return productionBindingExpression(resolvedBindings, request);
+ }
+ throw new AssertionError(resolvedBindings);
+ }
+
+ /**
+ * Returns a binding expression that uses a {@link javax.inject.Provider} for provision bindings
+ * or a {@link dagger.producers.Producer} for production bindings.
+ */
+ private BindingExpression frameworkInstanceBindingExpression(ResolvedBindings resolvedBindings) {
+ // TODO(user): Consider merging the static factory creation logic into CreationExpressions?
+ Optional<MemberSelect> staticMethod =
+ useStaticFactoryCreation(resolvedBindings.contributionBinding())
+ ? staticFactoryCreation(resolvedBindings)
+ : Optional.empty();
+ FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
+ resolvedBindings.scope().isPresent()
+ ? scope(resolvedBindings, frameworkInstanceCreationExpression(resolvedBindings))
+ : frameworkInstanceCreationExpression(resolvedBindings);
+ FrameworkInstanceSupplier frameworkInstanceSupplier =
+ staticMethod.isPresent()
+ ? staticMethod::get
+ : new FrameworkFieldInitializer(
+ componentImplementation, resolvedBindings, frameworkInstanceCreationExpression);
+
+ switch (resolvedBindings.bindingType()) {
+ case PROVISION:
+ return new ProviderInstanceBindingExpression(
+ resolvedBindings, frameworkInstanceSupplier, types, elements);
+ case PRODUCTION:
+ return new ProducerNodeInstanceBindingExpression(
+ resolvedBindings, frameworkInstanceSupplier, types, elements, componentImplementation);
+ default:
+ throw new AssertionError("invalid binding type: " + resolvedBindings.bindingType());
+ }
+ }
+
+ private FrameworkInstanceCreationExpression scope(
+ ResolvedBindings resolvedBindings, FrameworkInstanceCreationExpression unscoped) {
+ return () ->
+ CodeBlock.of(
+ "$T.provider($L)",
+ resolvedBindings.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK,
+ unscoped.creationExpression());
+ }
+
+ /**
+ * Returns a creation expression for a {@link javax.inject.Provider} for provision bindings or a
+ * {@link dagger.producers.Producer} for production bindings.
+ */
+ private FrameworkInstanceCreationExpression frameworkInstanceCreationExpression(
+ ResolvedBindings resolvedBindings) {
+ checkArgument(!resolvedBindings.bindingType().equals(MEMBERS_INJECTION));
+ ContributionBinding binding = resolvedBindings.contributionBinding();
+ switch (binding.kind()) {
+ case COMPONENT:
+ // The cast can be removed when we drop java 7 source support
+ return new InstanceFactoryCreationExpression(
+ () -> CodeBlock.of("($T) this", binding.key().type()));
+
+ case BOUND_INSTANCE:
+ return instanceFactoryCreationExpression(
+ binding, ComponentRequirement.forBoundInstance(binding));
+
+ case COMPONENT_DEPENDENCY:
+ return instanceFactoryCreationExpression(
+ binding, ComponentRequirement.forDependency(binding.key().type()));
+
+ case COMPONENT_PROVISION:
+ return new DependencyMethodProviderCreationExpression(
+ binding,
+ componentImplementation,
+ componentRequirementExpressions,
+ compilerOptions,
+ graph);
+
+ case SUBCOMPONENT_CREATOR:
+ return new AnonymousProviderCreationExpression(
+ binding, this, componentImplementation.name());
+
+ case INJECTION:
+ case PROVISION:
+ return new InjectionOrProvisionProviderCreationExpression(binding, this);
+
+ case COMPONENT_PRODUCTION:
+ return new DependencyMethodProducerCreationExpression(
+ binding, componentImplementation, componentRequirementExpressions, graph);
+
+ case PRODUCTION:
+ return new ProducerCreationExpression(binding, this);
+
+ case MULTIBOUND_SET:
+ return new SetFactoryCreationExpression(binding, componentImplementation, this, graph);
+
+ case MULTIBOUND_MAP:
+ return new MapFactoryCreationExpression(
+ binding, componentImplementation, this, graph, elements);
+
+ case DELEGATE:
+ return new DelegatingFrameworkInstanceCreationExpression(
+ binding, componentImplementation, this);
+
+ case OPTIONAL:
+ return new OptionalFactoryInstanceCreationExpression(
+ optionalFactories, binding, componentImplementation, this);
+
+ case MEMBERS_INJECTOR:
+ return new MembersInjectorProviderCreationExpression((ProvisionBinding) binding, this);
+
+ default:
+ throw new AssertionError(binding);
+ }
+ }
+
+ private InstanceFactoryCreationExpression instanceFactoryCreationExpression(
+ ContributionBinding binding, ComponentRequirement componentRequirement) {
+ return new InstanceFactoryCreationExpression(
+ binding.nullableType().isPresent(),
+ () ->
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ componentRequirement, componentImplementation.name()));
+ }
+
+ /** Returns a binding expression for a provision binding. */
+ private BindingExpression provisionBindingExpression(
+ ResolvedBindings resolvedBindings, BindingRequest request) {
+ if (!request.requestKind().isPresent()) {
+ verify(
+ request.frameworkType().get().equals(FrameworkType.PRODUCER_NODE),
+ "expected a PRODUCER_NODE: %s",
+ request);
+ return producerFromProviderBindingExpression(resolvedBindings);
+ }
+ RequestKind requestKind = request.requestKind().get();
+ switch (requestKind) {
+ case INSTANCE:
+ return instanceBindingExpression(resolvedBindings);
+
+ case PROVIDER:
+ return providerBindingExpression(resolvedBindings);
+
+ case LAZY:
+ case PRODUCED:
+ case PROVIDER_OF_LAZY:
+ return new DerivedFromFrameworkInstanceBindingExpression(
+ resolvedBindings.key(), FrameworkType.PROVIDER, requestKind, this, types);
+
+ case PRODUCER:
+ return producerFromProviderBindingExpression(resolvedBindings);
+
+ case FUTURE:
+ return new ImmediateFutureBindingExpression(resolvedBindings, this, types, sourceVersion);
+
+ case MEMBERS_INJECTION:
+ throw new IllegalArgumentException();
+ }
+
+ throw new AssertionError();
+ }
+
+ /** Returns a binding expression for a production binding. */
+ private BindingExpression productionBindingExpression(
+ ResolvedBindings resolvedBindings, BindingRequest request) {
+ if (request.frameworkType().isPresent()) {
+ return frameworkInstanceBindingExpression(resolvedBindings);
+ } else {
+ // If no FrameworkType is present, a RequestKind is guaranteed to be present.
+ RequestKind requestKind = request.requestKind().get();
+ return new DerivedFromFrameworkInstanceBindingExpression(
+ resolvedBindings.key(), FrameworkType.PRODUCER_NODE, requestKind, this, types);
+ }
+ }
+
+ /**
+ * Returns a binding expression for {@link RequestKind#PROVIDER} requests.
+ *
+ * <p>{@code @Binds} bindings that don't {@linkplain #needsCaching(ResolvedBindings) need to be
+ * cached} can use a {@link DelegateBindingExpression}.
+ *
+ * <p>In fastInit mode, use an {@link InnerSwitchingProviders inner switching provider} unless
+ * that provider's case statement will simply call {@code get()} on another {@link Provider} (in
+ * which case, just use that Provider directly).
+ *
+ * <p>Otherwise, return a {@link FrameworkInstanceBindingExpression}.
+ */
+ private BindingExpression providerBindingExpression(ResolvedBindings resolvedBindings) {
+ if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)
+ && !needsCaching(resolvedBindings)) {
+ return new DelegateBindingExpression(
+ resolvedBindings, RequestKind.PROVIDER, this, types, elements);
+ } else if (compilerOptions.fastInit()
+ && frameworkInstanceCreationExpression(resolvedBindings).useInnerSwitchingProvider()
+ && !(instanceBindingExpression(resolvedBindings)
+ instanceof DerivedFromFrameworkInstanceBindingExpression)) {
+ return wrapInMethod(
+ resolvedBindings,
+ bindingRequest(resolvedBindings.key(), RequestKind.PROVIDER),
+ innerSwitchingProviders.newBindingExpression(resolvedBindings.contributionBinding()));
+ }
+ return frameworkInstanceBindingExpression(resolvedBindings);
+ }
+
+ /**
+ * Returns a binding expression that uses a {@link dagger.producers.Producer} field for a
+ * provision binding.
+ */
+ private FrameworkInstanceBindingExpression producerFromProviderBindingExpression(
+ ResolvedBindings resolvedBindings) {
+ checkArgument(resolvedBindings.bindingType().equals(BindingType.PROVISION));
+ return new ProducerNodeInstanceBindingExpression(
+ resolvedBindings,
+ new FrameworkFieldInitializer(
+ componentImplementation,
+ resolvedBindings,
+ new ProducerFromProviderCreationExpression(
+ resolvedBindings.contributionBinding(), componentImplementation, this)),
+ types,
+ elements,
+ componentImplementation);
+ }
+
+ /**
+ * Returns a binding expression for {@link RequestKind#INSTANCE} requests.
+ *
+ * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an
+ * instance of this binding, return it, wrapped in a method if the binding {@linkplain
+ * #needsCaching(ResolvedBindings) needs to be cached} or the expression has dependencies.
+ *
+ * <p>In fastInit mode, we can use direct expressions unless the binding needs to be cached.
+ */
+ private BindingExpression instanceBindingExpression(ResolvedBindings resolvedBindings) {
+ Optional<BindingExpression> maybeDirectInstanceExpression =
+ unscopedDirectInstanceExpression(resolvedBindings);
+ if (canUseDirectInstanceExpression(resolvedBindings)
+ && maybeDirectInstanceExpression.isPresent()) {
+ BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get();
+ return directInstanceExpression.requiresMethodEncapsulation()
+ || needsCaching(resolvedBindings)
+ ? wrapInMethod(
+ resolvedBindings,
+ bindingRequest(resolvedBindings.key(), RequestKind.INSTANCE),
+ directInstanceExpression)
+ : directInstanceExpression;
+ }
+ return new DerivedFromFrameworkInstanceBindingExpression(
+ resolvedBindings.key(), FrameworkType.PROVIDER, RequestKind.INSTANCE, this, types);
+ }
+
+ /**
+ * Returns an unscoped binding expression for an {@link RequestKind#INSTANCE} that does not call
+ * {@code get()} on its provider, if there is one.
+ */
+ private Optional<BindingExpression> unscopedDirectInstanceExpression(
+ ResolvedBindings resolvedBindings) {
+ switch (resolvedBindings.contributionBinding().kind()) {
+ case DELEGATE:
+ return Optional.of(
+ new DelegateBindingExpression(
+ resolvedBindings, RequestKind.INSTANCE, this, types, elements));
+
+ case COMPONENT:
+ return Optional.of(
+ new ComponentInstanceBindingExpression(
+ resolvedBindings, componentImplementation.name()));
+
+ case COMPONENT_DEPENDENCY:
+ return Optional.of(
+ new ComponentRequirementBindingExpression(
+ resolvedBindings,
+ ComponentRequirement.forDependency(resolvedBindings.key().type()),
+ componentRequirementExpressions));
+
+ case COMPONENT_PROVISION:
+ return Optional.of(
+ new ComponentProvisionBindingExpression(
+ resolvedBindings, graph, componentRequirementExpressions, compilerOptions));
+
+ case SUBCOMPONENT_CREATOR:
+ return Optional.of(
+ new SubcomponentCreatorBindingExpression(
+ resolvedBindings,
+ componentImplementation.getSubcomponentCreatorSimpleName(resolvedBindings.key())));
+
+ case MULTIBOUND_SET:
+ return Optional.of(
+ new SetBindingExpression(
+ resolvedBindings, componentImplementation, graph, this, types, elements));
+
+ case MULTIBOUND_MAP:
+ return Optional.of(
+ new MapBindingExpression(
+ resolvedBindings, componentImplementation, graph, this, types, elements));
+
+ case OPTIONAL:
+ return Optional.of(
+ new OptionalBindingExpression(resolvedBindings, this, types, sourceVersion));
+
+ case BOUND_INSTANCE:
+ return Optional.of(
+ new ComponentRequirementBindingExpression(
+ resolvedBindings,
+ ComponentRequirement.forBoundInstance(resolvedBindings.contributionBinding()),
+ componentRequirementExpressions));
+
+ case INJECTION:
+ case PROVISION:
+ return Optional.of(
+ new SimpleMethodBindingExpression(
+ resolvedBindings,
+ compilerOptions,
+ this,
+ membersInjectionMethods,
+ componentRequirementExpressions,
+ types,
+ elements,
+ sourceVersion));
+
+ case MEMBERS_INJECTOR:
+ return Optional.empty();
+
+ case MEMBERS_INJECTION:
+ case COMPONENT_PRODUCTION:
+ case PRODUCTION:
+ throw new IllegalArgumentException(
+ resolvedBindings.contributionBinding().kind().toString());
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns {@code true} if the binding should use the static factory creation strategy.
+ *
+ * <p>In default mode, we always use the static factory creation strategy. In fastInit mode, we
+ * prefer to use a SwitchingProvider instead of static factories in order to reduce class loading;
+ * however, we allow static factories that can reused across multiple bindings, e.g. {@code
+ * MapFactory} or {@code SetFactory}.
+ */
+ private boolean useStaticFactoryCreation(ContributionBinding binding) {
+ return !compilerOptions.fastInit()
+ || binding.kind().equals(MULTIBOUND_MAP)
+ || binding.kind().equals(MULTIBOUND_SET);
+ }
+
+ /**
+ * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this
+ * binding. If the binding doesn't {@linkplain #needsCaching(ResolvedBindings) need to be cached},
+ * we can.
+ *
+ * <p>In fastInit mode, we can use a direct expression even if the binding {@linkplain
+ * #needsCaching(ResolvedBindings) needs to be cached}.
+ */
+ private boolean canUseDirectInstanceExpression(ResolvedBindings resolvedBindings) {
+ return !needsCaching(resolvedBindings) || compilerOptions.fastInit();
+ }
+
+ /**
+ * Returns a binding expression that uses a given one as the body of a method that users call. If
+ * a component provision method matches it, it will be the method implemented. If it does not
+ * match a component provision method and the binding is modifiable, then a new public modifiable
+ * binding method will be written. If the binding doesn't match a component method and is not
+ * modifiable, then a new private method will be written.
+ */
+ BindingExpression wrapInMethod(
+ ResolvedBindings resolvedBindings,
+ BindingRequest request,
+ BindingExpression bindingExpression) {
+ // If we've already wrapped the expression, then use the delegate.
+ if (bindingExpression instanceof MethodBindingExpression) {
+ return bindingExpression;
+ }
+
+ MethodImplementationStrategy methodImplementationStrategy =
+ methodImplementationStrategy(resolvedBindings, request);
+ Optional<ComponentMethodDescriptor> matchingComponentMethod =
+ graph.componentDescriptor().firstMatchingComponentMethod(request);
+
+ if (modifiableBindingExpressions.getModifiableBindingType(request).isModifiable()
+ && (componentImplementation.superclassImplementation().isPresent()
+ || !matchingComponentMethod.isPresent())) {
+ return modifiableBindingExpressions.wrapInModifiableMethodBindingExpression(
+ request, resolvedBindings, methodImplementationStrategy, bindingExpression);
+ } else if (matchingComponentMethod.isPresent()) {
+ ComponentMethodDescriptor componentMethod = matchingComponentMethod.get();
+ return new ComponentMethodBindingExpression(
+ request,
+ resolvedBindings,
+ methodImplementationStrategy,
+ bindingExpression,
+ componentImplementation,
+ componentMethod,
+ types);
+ } else {
+ return new PrivateMethodBindingExpression(
+ request,
+ resolvedBindings,
+ methodImplementationStrategy,
+ bindingExpression,
+ componentImplementation,
+ types);
+ }
+ }
+
+ private MethodImplementationStrategy methodImplementationStrategy(
+ ResolvedBindings resolvedBindings, BindingRequest request) {
+ if (compilerOptions.fastInit()) {
+ if (request.isRequestKind(RequestKind.PROVIDER)) {
+ return MethodImplementationStrategy.SINGLE_CHECK;
+ } else if (request.isRequestKind(RequestKind.INSTANCE) && needsCaching(resolvedBindings)) {
+ return resolvedBindings.scope().get().isReusable()
+ ? MethodImplementationStrategy.SINGLE_CHECK
+ : MethodImplementationStrategy.DOUBLE_CHECK;
+ }
+ }
+ return MethodImplementationStrategy.SIMPLE;
+ }
+
+ /**
+ * Returns {@code true} if the component needs to make sure the provided value is cached.
+ *
+ * <p>The component needs to cache the value for scoped bindings except for {@code @Binds}
+ * bindings whose scope is no stronger than their delegate's.
+ */
+ private boolean needsCaching(ResolvedBindings resolvedBindings) {
+ if (!resolvedBindings.scope().isPresent()) {
+ return false;
+ }
+ if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)) {
+ return isBindsScopeStrongerThanDependencyScope(resolvedBindings, graph);
+ }
+ return true;
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentCreatorAnnotation.java b/java/dagger/internal/codegen/ComponentCreatorAnnotation.java
new file mode 100644
index 0000000..31bbfab
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentCreatorAnnotation.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Ascii.toUpperCase;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.DaggerStreams.valuesOf;
+import static java.util.stream.Collectors.mapping;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Component;
+import dagger.Subcomponent;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.stream.Collector;
+import java.util.stream.Stream;
+import javax.lang.model.element.TypeElement;
+
+/** Simple representation of a component creator annotation type. */
+enum ComponentCreatorAnnotation {
+ COMPONENT_BUILDER(Component.Builder.class),
+ COMPONENT_FACTORY(Component.Factory.class),
+ SUBCOMPONENT_BUILDER(Subcomponent.Builder.class),
+ SUBCOMPONENT_FACTORY(Subcomponent.Factory.class),
+ PRODUCTION_COMPONENT_BUILDER(ProductionComponent.Builder.class),
+ PRODUCTION_COMPONENT_FACTORY(ProductionComponent.Factory.class),
+ PRODUCTION_SUBCOMPONENT_BUILDER(ProductionSubcomponent.Builder.class),
+ PRODUCTION_SUBCOMPONENT_FACTORY(ProductionSubcomponent.Factory.class),
+ ;
+
+ private final Class<? extends Annotation> annotation;
+ private final ComponentCreatorKind creatorKind;
+ private final Class<? extends Annotation> componentAnnotation;
+
+ ComponentCreatorAnnotation(Class<? extends Annotation> annotation) {
+ this.annotation = annotation;
+ this.creatorKind = ComponentCreatorKind.valueOf(toUpperCase(annotation.getSimpleName()));
+ this.componentAnnotation = (Class<? extends Annotation>) annotation.getEnclosingClass();
+ }
+
+ /** The actual annotation type. */
+ Class<? extends Annotation> annotation() {
+ return annotation;
+ }
+
+ /** The component annotation type that encloses this creator annotation type. */
+ final Class<? extends Annotation> componentAnnotation() {
+ return componentAnnotation;
+ }
+
+ /** Returns {@code true} if the creator annotation is for a subcomponent. */
+ final boolean isSubcomponentCreatorAnnotation() {
+ return componentAnnotation().getSimpleName().endsWith("Subcomponent");
+ }
+
+ /**
+ * Returns {@code true} if the creator annotation is for a production component or subcomponent.
+ */
+ final boolean isProductionCreatorAnnotation() {
+ return componentAnnotation().getSimpleName().startsWith("Production");
+ }
+
+ /** The creator kind the annotation is associated with. */
+ // TODO(dpb): Remove ComponentCreatorKind.
+ ComponentCreatorKind creatorKind() {
+ return creatorKind;
+ }
+
+ @Override
+ public final String toString() {
+ return annotation().getName();
+ }
+
+ /** Returns all component creator annotations. */
+ static ImmutableSet<Class<? extends Annotation>> allCreatorAnnotations() {
+ return stream().collect(toAnnotationClasses());
+ }
+
+ /** Returns all root component creator annotations. */
+ static ImmutableSet<Class<? extends Annotation>> rootComponentCreatorAnnotations() {
+ return stream()
+ .filter(
+ componentCreatorAnnotation ->
+ !componentCreatorAnnotation.isSubcomponentCreatorAnnotation())
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns all subcomponent creator annotations. */
+ static ImmutableSet<Class<? extends Annotation>> subcomponentCreatorAnnotations() {
+ return stream()
+ .filter(
+ componentCreatorAnnotation ->
+ componentCreatorAnnotation.isSubcomponentCreatorAnnotation())
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns all production component creator annotations. */
+ static ImmutableSet<Class<? extends Annotation>> productionCreatorAnnotations() {
+ return stream()
+ .filter(
+ componentCreatorAnnotation ->
+ componentCreatorAnnotation.isProductionCreatorAnnotation())
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns the legal creator annotations for the given {@code componentAnnotation}. */
+ static ImmutableSet<Class<? extends Annotation>> creatorAnnotationsFor(
+ ComponentAnnotation componentAnnotation) {
+ return stream()
+ .filter(
+ creatorAnnotation ->
+ creatorAnnotation
+ .componentAnnotation()
+ .getSimpleName()
+ .equals(componentAnnotation.simpleName()))
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns all creator annotations present on the given {@code type}. */
+ static ImmutableSet<ComponentCreatorAnnotation> getCreatorAnnotations(TypeElement type) {
+ return stream()
+ .filter(cca -> isAnnotationPresent(type, cca.annotation()))
+ .collect(toImmutableSet());
+ }
+
+ private static Stream<ComponentCreatorAnnotation> stream() {
+ return valuesOf(ComponentCreatorAnnotation.class);
+ }
+
+ private static Collector<ComponentCreatorAnnotation, ?, ImmutableSet<Class<? extends Annotation>>>
+ toAnnotationClasses() {
+ return mapping(ComponentCreatorAnnotation::annotation, toImmutableSet());
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentCreatorDescriptor.java b/java/dagger/internal/codegen/ComponentCreatorDescriptor.java
new file mode 100644
index 0000000..f6ec7a2
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentCreatorDescriptor.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import dagger.BindsInstance;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import java.util.List;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A descriptor for a component <i>creator</i> type: that is, a type annotated with
+ * {@code @Component.Builder} (or one of the corresponding production or subcomponent versions).
+ */
+@AutoValue
+abstract class ComponentCreatorDescriptor {
+
+ /** Returns the annotation marking this creator. */
+ abstract ComponentCreatorAnnotation annotation();
+
+ /** The kind of this creator. */
+ final ComponentCreatorKind kind() {
+ return annotation().creatorKind();
+ }
+
+ /** The annotated creator type. */
+ abstract TypeElement typeElement();
+
+ /** The method that creates and returns a component instance. */
+ abstract ExecutableElement factoryMethod();
+
+ /**
+ * Multimap of component requirements to setter methods that set that requirement.
+ *
+ * <p>In a valid creator, there will be exactly one element per component requirement, so this
+ * method should only be called when validating the descriptor.
+ */
+ abstract ImmutableSetMultimap<ComponentRequirement, ExecutableElement> unvalidatedSetterMethods();
+
+ /**
+ * Multimap of component requirements to factory method parameters that set that requirement.
+ *
+ * <p>In a valid creator, there will be exactly one element per component requirement, so this
+ * method should only be called when validating the descriptor.
+ */
+ abstract ImmutableSetMultimap<ComponentRequirement, VariableElement>
+ unvalidatedFactoryParameters();
+
+ /**
+ * Multimap of component requirements to elements (methods or parameters) that set that
+ * requirement.
+ *
+ * <p>In a valid creator, there will be exactly one element per component requirement, so this
+ * method should only be called when validating the descriptor.
+ */
+ final ImmutableSetMultimap<ComponentRequirement, Element> unvalidatedRequirementElements() {
+ // ComponentCreatorValidator ensures that there are either setter methods or factory method
+ // parameters, but not both, so we can cheat a little here since we know that only one of
+ // the two multimaps will be non-empty.
+ return ImmutableSetMultimap.copyOf( // no actual copy
+ unvalidatedSetterMethods().isEmpty()
+ ? unvalidatedFactoryParameters()
+ : unvalidatedSetterMethods());
+ }
+
+ /**
+ * Map of component requirements to elements (setter methods or factory method parameters) that
+ * set them.
+ */
+ @Memoized
+ ImmutableMap<ComponentRequirement, Element> requirementElements() {
+ return flatten(unvalidatedRequirementElements());
+ }
+
+ /** Map of component requirements to setter methods for those requirements. */
+ @Memoized
+ ImmutableMap<ComponentRequirement, ExecutableElement> setterMethods() {
+ return flatten(unvalidatedSetterMethods());
+ }
+
+ /** Map of component requirements to factory method parameters for those requirements. */
+ @Memoized
+ ImmutableMap<ComponentRequirement, VariableElement> factoryParameters() {
+ return flatten(unvalidatedFactoryParameters());
+ }
+
+ private static <K, V> ImmutableMap<K, V> flatten(Multimap<K, V> multimap) {
+ return ImmutableMap.copyOf(
+ Maps.transformValues(multimap.asMap(), values -> getOnlyElement(values)));
+ }
+
+ /** Returns the set of component requirements this creator allows the user to set. */
+ final ImmutableSet<ComponentRequirement> userSettableRequirements() {
+ // Note: they should have been validated at the point this is used, so this set is valid.
+ return unvalidatedRequirementElements().keySet();
+ }
+
+ /** Returns the set of requirements for modules and component dependencies for this creator. */
+ final ImmutableSet<ComponentRequirement> moduleAndDependencyRequirements() {
+ return userSettableRequirements().stream()
+ .filter(requirement -> !requirement.isBoundInstance())
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the set of bound instance requirements for this creator. */
+ final ImmutableSet<ComponentRequirement> boundInstanceRequirements() {
+ return userSettableRequirements().stream()
+ .filter(ComponentRequirement::isBoundInstance)
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the element in this creator that sets the given {@code requirement}. */
+ final Element elementForRequirement(ComponentRequirement requirement) {
+ return requirementElements().get(requirement);
+ }
+
+ /** Creates a new {@link ComponentCreatorDescriptor} for the given creator {@code type}. */
+ static ComponentCreatorDescriptor create(
+ DeclaredType type,
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestFactory dependencyRequestFactory) {
+ TypeElement typeElement = asTypeElement(type);
+ TypeMirror componentType = typeElement.getEnclosingElement().asType();
+
+ ImmutableSetMultimap.Builder<ComponentRequirement, ExecutableElement> setterMethods =
+ ImmutableSetMultimap.builder();
+
+ ExecutableElement factoryMethod = null;
+ for (ExecutableElement method : elements.getUnimplementedMethods(typeElement)) {
+ ExecutableType resolvedMethodType = MoreTypes.asExecutable(types.asMemberOf(type, method));
+
+ if (types.isSubtype(componentType, resolvedMethodType.getReturnType())) {
+ factoryMethod = method;
+ } else {
+ VariableElement parameter = getOnlyElement(method.getParameters());
+ TypeMirror parameterType = getOnlyElement(resolvedMethodType.getParameterTypes());
+ setterMethods.put(
+ requirement(method, parameter, parameterType, dependencyRequestFactory, method),
+ method);
+ }
+ }
+ verify(factoryMethod != null); // validation should have ensured this.
+
+ ImmutableSetMultimap.Builder<ComponentRequirement, VariableElement> factoryParameters =
+ ImmutableSetMultimap.builder();
+
+ ExecutableType resolvedFactoryMethodType =
+ MoreTypes.asExecutable(types.asMemberOf(type, factoryMethod));
+ List<? extends VariableElement> parameters = factoryMethod.getParameters();
+ List<? extends TypeMirror> parameterTypes = resolvedFactoryMethodType.getParameterTypes();
+ for (int i = 0; i < parameters.size(); i++) {
+ VariableElement parameter = parameters.get(i);
+ TypeMirror parameterType = parameterTypes.get(i);
+ factoryParameters.put(
+ requirement(factoryMethod, parameter, parameterType, dependencyRequestFactory, parameter),
+ parameter);
+ }
+
+ // Validation should have ensured exactly one creator annotation is present on the type.
+ ComponentCreatorAnnotation annotation = getOnlyElement(getCreatorAnnotations(typeElement));
+ return new AutoValue_ComponentCreatorDescriptor(
+ annotation, typeElement, factoryMethod, setterMethods.build(), factoryParameters.build());
+ }
+
+ private static ComponentRequirement requirement(
+ ExecutableElement method,
+ VariableElement parameter,
+ TypeMirror type,
+ DependencyRequestFactory dependencyRequestFactory,
+ Element elementForVariableName) {
+ if (isAnnotationPresent(method, BindsInstance.class)
+ || isAnnotationPresent(parameter, BindsInstance.class)) {
+ DependencyRequest request =
+ dependencyRequestFactory.forRequiredResolvedVariable(parameter, type);
+ String variableName = elementForVariableName.getSimpleName().toString();
+ return ComponentRequirement.forBoundInstance(
+ request.key(), request.isNullable(), variableName);
+ }
+
+ return moduleAnnotation(asTypeElement(type)).isPresent()
+ ? ComponentRequirement.forModule(type)
+ : ComponentRequirement.forDependency(type);
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentCreatorImplementation.java b/java/dagger/internal/codegen/ComponentCreatorImplementation.java
new file mode 100644
index 0000000..a5eb680
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentCreatorImplementation.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeSpec;
+
+/** The implementation of a component creator type. */
+@AutoValue
+abstract class ComponentCreatorImplementation {
+
+ /** Creates a new {@link ComponentCreatorImplementation}. */
+ static ComponentCreatorImplementation create(
+ TypeSpec spec,
+ ClassName name,
+ ImmutableMap<ComponentRequirement, FieldSpec> fields) {
+ return new AutoValue_ComponentCreatorImplementation(spec, name, fields);
+ }
+
+ /** The type spec for the creator implementation. */
+ abstract TypeSpec spec();
+
+ /** The name of the creator implementation class. */
+ abstract ClassName name();
+
+ /**
+ * All fields that are present in this implementation or its supertype.
+ *
+ * <p>In the case of ahead-of-time subcomponents, not all fields will necessarily be passed to
+ * the component's constructor (because, for example, it turns out that a particular module that
+ * the creator can set is actually inherited from an ancestor module).
+ */
+ abstract ImmutableMap<ComponentRequirement, FieldSpec> fields();
+}
diff --git a/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java b/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java
new file mode 100644
index 0000000..4f06b90
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.SourceFiles.simpleVariableName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PROTECTED;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.ComponentRequirement.NullPolicy;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+
+/** Factory for creating {@link ComponentCreatorImplementation} instances. */
+final class ComponentCreatorImplementationFactory {
+
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+
+ @Inject
+ ComponentCreatorImplementationFactory(DaggerElements elements, DaggerTypes types) {
+ this.elements = elements;
+ this.types = types;
+ }
+
+ /** Returns a new creator implementation for the given component, if necessary. */
+ Optional<ComponentCreatorImplementation> create(
+ ComponentImplementation componentImplementation, Optional<BindingGraph> graph) {
+ if (!componentImplementation.componentDescriptor().hasCreator()) {
+ return Optional.empty();
+ }
+
+ Optional<ComponentCreatorDescriptor> creatorDescriptor =
+ componentImplementation.componentDescriptor().creatorDescriptor();
+
+ if (componentImplementation.isAbstract()
+ && (hasNoSetterMethods(creatorDescriptor)
+ || componentImplementation.superclassImplementation().isPresent())) {
+ // 1. Factory-like creators (those with no setter methods) are only generated in concrete
+ // components, because they only have a factory method and the factory method must call
+ // a concrete component's constructor.
+ // 2. The component builder in ahead-of-time mode is generated with the base subcomponent
+ // implementation, with the exception of the build method since that requires invoking the
+ // constructor of a concrete component implementation. Intermediate component
+ // implementations, because they still can't invoke the eventual constructor and have no
+ // additional extensions to the builder, can ignore generating a builder implementation.
+ return Optional.empty();
+ }
+
+ Builder builder =
+ creatorDescriptor.isPresent()
+ ? new BuilderForCreatorDescriptor(
+ componentImplementation, creatorDescriptor.get(), graph)
+ : new BuilderForGeneratedRootComponentBuilder(componentImplementation);
+ return Optional.of(builder.build());
+ }
+
+ private static boolean hasNoSetterMethods(
+ Optional<ComponentCreatorDescriptor> creatorDescriptor) {
+ return creatorDescriptor.filter(descriptor -> descriptor.setterMethods().isEmpty()).isPresent();
+ }
+
+ /** Base class for building a creator implementation. */
+ private abstract class Builder {
+ final ComponentImplementation componentImplementation;
+ final ClassName className;
+ final TypeSpec.Builder classBuilder;
+
+ private ImmutableMap<ComponentRequirement, FieldSpec> fields;
+
+ Builder(ComponentImplementation componentImplementation) {
+ this.componentImplementation = componentImplementation;
+ this.className = componentImplementation.getCreatorName();
+ this.classBuilder = classBuilder(className);
+ }
+
+ /** Builds the {@link ComponentCreatorImplementation}. */
+ ComponentCreatorImplementation build() {
+ setModifiers();
+ setSupertype();
+ this.fields = getOrAddFields();
+ addConstructor();
+ addSetterMethods();
+ addFactoryMethod();
+ return ComponentCreatorImplementation.create(classBuilder.build(), className, fields);
+ }
+
+ /** Returns the descriptor for the component. */
+ final ComponentDescriptor componentDescriptor() {
+ return componentImplementation.componentDescriptor();
+ }
+
+ /**
+ * The set of requirements that must be passed to the component's constructor in the order
+ * they must be passed.
+ */
+ final ImmutableSet<ComponentRequirement> componentConstructorRequirements() {
+ return componentImplementation.requirements();
+ }
+
+ /** Returns the requirements that have setter methods on the creator type. */
+ abstract ImmutableSet<ComponentRequirement> setterMethods();
+
+ /**
+ * Returns the component requirements that have factory method parameters, mapped to the name
+ * for that parameter.
+ */
+ abstract ImmutableMap<ComponentRequirement, String> factoryMethodParameters();
+
+ /**
+ * The {@link ComponentRequirement}s that this creator allows users to set. Values are a status
+ * for each requirement indicating what's needed for that requirement in the implementation
+ * class currently being generated.
+ */
+ abstract ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements();
+
+ /**
+ * Component requirements that are both settable by the creator and needed to construct the
+ * component.
+ */
+ private Set<ComponentRequirement> neededUserSettableRequirements() {
+ return Sets.intersection(
+ userSettableRequirements().keySet(), componentConstructorRequirements());
+ }
+
+ private void setModifiers() {
+ visibility().ifPresent(classBuilder::addModifiers);
+ if (!componentImplementation.isNested()) {
+ classBuilder.addModifiers(STATIC);
+ }
+ classBuilder.addModifiers(componentImplementation.isAbstract() ? ABSTRACT : FINAL);
+ }
+
+ /** Returns the visibility modifier the generated class should have, if any. */
+ protected abstract Optional<Modifier> visibility();
+
+ /** Sets the superclass being extended or interface being implemented for this creator. */
+ protected abstract void setSupertype();
+
+ /** Adds a constructor for the creator type, if needed. */
+ protected abstract void addConstructor();
+
+ private ImmutableMap<ComponentRequirement, FieldSpec> getOrAddFields() {
+ // If a base implementation is present, any fields are already defined there and don't need to
+ // be created in this implementation.
+ return componentImplementation
+ .baseCreatorImplementation()
+ .map(ComponentCreatorImplementation::fields)
+ .orElseGet(this::addFields);
+ }
+
+ private ImmutableMap<ComponentRequirement, FieldSpec> addFields() {
+ // Fields in an abstract creator class need to be visible from subclasses.
+ Modifier modifier = componentImplementation.isAbstract() ? PROTECTED : PRIVATE;
+ UniqueNameSet fieldNames = new UniqueNameSet();
+ ImmutableMap<ComponentRequirement, FieldSpec> result =
+ Maps.toMap(
+ Sets.intersection(neededUserSettableRequirements(), setterMethods()),
+ requirement ->
+ FieldSpec.builder(
+ TypeName.get(requirement.type()),
+ fieldNames.getUniqueName(requirement.variableName()),
+ modifier)
+ .build());
+ classBuilder.addFields(result.values());
+ return result;
+ }
+
+ private void addSetterMethods() {
+ Maps.filterKeys(userSettableRequirements(), setterMethods()::contains)
+ .forEach(
+ (requirement, status) ->
+ createSetterMethod(requirement, status).ifPresent(classBuilder::addMethod));
+ }
+
+ /** Creates a new setter method builder, with no method body, for the given requirement. */
+ protected abstract MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement);
+
+ private Optional<MethodSpec> createSetterMethod(
+ ComponentRequirement requirement, RequirementStatus status) {
+ switch (status) {
+ case NEEDED:
+ return Optional.of(normalSetterMethod(requirement));
+ case UNNEEDED:
+ return Optional.of(noopSetterMethod(requirement));
+ case UNSETTABLE_REPEATED_MODULE:
+ return Optional.of(repeatedModuleSetterMethod(requirement));
+ case IMPLEMENTED_IN_SUPERTYPE:
+ return Optional.empty();
+ }
+ throw new AssertionError();
+ }
+
+ private MethodSpec normalSetterMethod(ComponentRequirement requirement) {
+ MethodSpec.Builder method = setterMethodBuilder(requirement);
+ ParameterSpec parameter = parameter(method.build());
+ method.addStatement(
+ "this.$N = $L",
+ fields.get(requirement),
+ requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW)
+ ? CodeBlock.of("$N", parameter)
+ : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter));
+ return maybeReturnThis(method);
+ }
+
+ private MethodSpec noopSetterMethod(ComponentRequirement requirement) {
+ MethodSpec.Builder method = setterMethodBuilder(requirement);
+ ParameterSpec parameter = parameter(method.build());
+ method
+ .addAnnotation(Deprecated.class)
+ .addJavadoc(
+ "@deprecated This module is declared, but an instance is not used in the component. "
+ + "This method is a no-op. For more, see https://dagger.dev/unused-modules.\n")
+ .addStatement("$T.checkNotNull($N)", Preconditions.class, parameter);
+ return maybeReturnThis(method);
+ }
+
+ private MethodSpec repeatedModuleSetterMethod(ComponentRequirement requirement) {
+ return setterMethodBuilder(requirement)
+ .addStatement(
+ "throw new $T($T.format($S, $T.class.getCanonicalName()))",
+ UnsupportedOperationException.class,
+ String.class,
+ "%s cannot be set because it is inherited from the enclosing component",
+ TypeNames.rawTypeName(TypeName.get(requirement.type())))
+ .build();
+ }
+
+ private ParameterSpec parameter(MethodSpec method) {
+ return getOnlyElement(method.parameters);
+ }
+
+ private MethodSpec maybeReturnThis(MethodSpec.Builder method) {
+ MethodSpec built = method.build();
+ return built.returnType.equals(TypeName.VOID)
+ ? built
+ : method.addStatement("return this").build();
+ }
+
+ private void addFactoryMethod() {
+ if (!componentImplementation.isAbstract()) {
+ classBuilder.addMethod(factoryMethod());
+ }
+ }
+
+ MethodSpec factoryMethod() {
+ MethodSpec.Builder factoryMethod = factoryMethodBuilder();
+ factoryMethod
+ .returns(ClassName.get(componentDescriptor().typeElement()))
+ .addModifiers(PUBLIC);
+
+ ImmutableMap<ComponentRequirement, String> factoryMethodParameters =
+ factoryMethodParameters();
+ userSettableRequirements()
+ .keySet()
+ .forEach(
+ requirement -> {
+ if (fields.containsKey(requirement)
+ && componentConstructorRequirements().contains(requirement)) {
+ // In AOT mode, there can be a field for a requirement even if the component's
+ // constructor doesn't need it, because the base class for the creator was created
+ // before the final graph for the component was known.
+ FieldSpec field = fields.get(requirement);
+ addNullHandlingForField(requirement, field, factoryMethod);
+ } else if (factoryMethodParameters.containsKey(requirement)) {
+ String parameterName = factoryMethodParameters.get(requirement);
+ addNullHandlingForParameter(requirement, parameterName, factoryMethod);
+ }
+ });
+ factoryMethod.addStatement(
+ "return new $T($L)",
+ componentImplementation.name(),
+ componentConstructorArgs(factoryMethodParameters));
+ return factoryMethod.build();
+ }
+
+ private void addNullHandlingForField(
+ ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) {
+ switch (requirement.nullPolicy(elements, types)) {
+ case NEW:
+ checkState(requirement.kind().isModule());
+ factoryMethod
+ .beginControlFlow("if ($N == null)", field)
+ .addStatement("this.$N = $L", field, newModuleInstance(requirement))
+ .endControlFlow();
+ break;
+ case THROW:
+ // TODO(cgdecker,ronshapiro): ideally this should use the key instead of a class for
+ // @BindsInstance requirements, but that's not easily proguardable.
+ factoryMethod.addStatement(
+ "$T.checkBuilderRequirement($N, $T.class)",
+ Preconditions.class,
+ field,
+ TypeNames.rawTypeName(field.type));
+ break;
+ case ALLOW:
+ break;
+ }
+ }
+
+ private void addNullHandlingForParameter(
+ ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) {
+ if (!requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW)) {
+ // Factory method parameters are always required unless they are a nullable
+ // binds-instance (i.e. ALLOW)
+ factoryMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, parameter);
+ }
+ }
+
+ /** Returns a builder for the creator's factory method. */
+ protected abstract MethodSpec.Builder factoryMethodBuilder();
+
+ private CodeBlock componentConstructorArgs(
+ ImmutableMap<ComponentRequirement, String> factoryMethodParameters) {
+ return componentConstructorRequirements().stream()
+ .map(
+ requirement -> {
+ if (fields.containsKey(requirement)) {
+ return CodeBlock.of("$N", fields.get(requirement));
+ } else if (factoryMethodParameters.containsKey(requirement)) {
+ return CodeBlock.of("$L", factoryMethodParameters.get(requirement));
+ } else {
+ return newModuleInstance(requirement);
+ }
+ })
+ .collect(toParametersCodeBlock());
+ }
+
+ private CodeBlock newModuleInstance(ComponentRequirement requirement) {
+ checkArgument(requirement.kind().isModule()); // this should be guaranteed to be true here
+ return ModuleProxies.newModuleInstance(requirement.typeElement(), className, elements);
+ }
+ }
+
+ /** Builder for a creator type defined by a {@code ComponentCreatorDescriptor}. */
+ private final class BuilderForCreatorDescriptor extends Builder {
+ final ComponentCreatorDescriptor creatorDescriptor;
+ private final Optional<BindingGraph> graph;
+
+ BuilderForCreatorDescriptor(
+ ComponentImplementation componentImplementation,
+ ComponentCreatorDescriptor creatorDescriptor,
+ Optional<BindingGraph> graph) {
+ super(componentImplementation);
+ this.creatorDescriptor = creatorDescriptor;
+ this.graph = graph;
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
+ return Maps.toMap(creatorDescriptor.userSettableRequirements(), this::requirementStatus);
+ }
+
+ @Override
+ protected Optional<Modifier> visibility() {
+ if (componentImplementation.isAbstract()) {
+ // The component creator class of a top-level component implementation in ahead-of-time
+ // subcomponents mode must be public, not protected, because the creator's subclass will
+ // be a sibling of the component subclass implementation, not nested.
+ return Optional.of(componentImplementation.isNested() ? PROTECTED : PUBLIC);
+ }
+ return Optional.of(PRIVATE);
+ }
+
+ @Override
+ protected void setSupertype() {
+ if (componentImplementation.baseCreatorImplementation().isPresent()) {
+ // If an abstract base implementation for this creator exists, extend that class.
+ classBuilder.superclass(componentImplementation.baseCreatorImplementation().get().name());
+ } else {
+ addSupertype(classBuilder, creatorDescriptor.typeElement());
+ }
+ }
+
+ @Override
+ protected void addConstructor() {
+ // Just use the implicit no-arg public constructor.
+ }
+
+ @Override
+ protected ImmutableSet<ComponentRequirement> setterMethods() {
+ return ImmutableSet.copyOf(creatorDescriptor.setterMethods().keySet());
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
+ return ImmutableMap.copyOf(
+ Maps.transformValues(
+ creatorDescriptor.factoryParameters(),
+ element -> element.getSimpleName().toString()));
+ }
+
+ private DeclaredType creatorType() {
+ return asDeclared(creatorDescriptor.typeElement().asType());
+ }
+
+ @Override
+ protected MethodSpec.Builder factoryMethodBuilder() {
+ return MethodSpec.overriding(creatorDescriptor.factoryMethod(), creatorType(), types);
+ }
+
+ private RequirementStatus requirementStatus(ComponentRequirement requirement) {
+ // In ahead-of-time subcomponents mode, all builder methods are defined at the base
+ // implementation. The only case where a method needs to be overridden is for a repeated
+ // module, which is unknown at the point when a base implementation is generated. We do this
+ // at the root for simplicity (and as an aside, repeated modules are never used in google
+ // as of 11/28/18, and thus the additional cost of including these methods at the root is
+ // negligible).
+ if (isRepeatedModule(requirement)) {
+ return RequirementStatus.UNSETTABLE_REPEATED_MODULE;
+ }
+
+ if (hasBaseCreatorImplementation()) {
+ return RequirementStatus.IMPLEMENTED_IN_SUPERTYPE;
+ }
+
+ return componentConstructorRequirements().contains(requirement)
+ ? RequirementStatus.NEEDED
+ : RequirementStatus.UNNEEDED;
+ }
+
+ /**
+ * Returns whether the given requirement is for a repeat of a module inherited from an ancestor
+ * component. This creator is not allowed to set such a module.
+ */
+ final boolean isRepeatedModule(ComponentRequirement requirement) {
+ return !componentConstructorRequirements().contains(requirement)
+ && !isOwnedModule(requirement);
+ }
+
+ /**
+ * Returns whether the given {@code requirement} is for a module type owned by the component.
+ */
+ private boolean isOwnedModule(ComponentRequirement requirement) {
+ return graph.map(g -> g.ownedModuleTypes().contains(requirement.typeElement())).orElse(true);
+ }
+
+ private boolean hasBaseCreatorImplementation() {
+ return !componentImplementation.isAbstract()
+ && componentImplementation.baseImplementation().isPresent();
+ }
+
+ @Override
+ protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
+ ExecutableElement supertypeMethod = creatorDescriptor.setterMethods().get(requirement);
+ MethodSpec.Builder method = MethodSpec.overriding(supertypeMethod, creatorType(), types);
+ if (!supertypeMethod.getReturnType().getKind().equals(TypeKind.VOID)) {
+ // Take advantage of covariant returns so that we don't have to worry about type variables
+ method.returns(className);
+ }
+ return method;
+ }
+ }
+
+ /**
+ * Builder for a component builder class that is automatically generated for a root component that
+ * does not have its own user-defined creator type (i.e. a {@code ComponentCreatorDescriptor}).
+ */
+ private final class BuilderForGeneratedRootComponentBuilder extends Builder {
+ BuilderForGeneratedRootComponentBuilder(ComponentImplementation componentImplementation) {
+ super(componentImplementation);
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
+ return Maps.toMap(
+ setterMethods(),
+ requirement ->
+ componentConstructorRequirements().contains(requirement)
+ ? RequirementStatus.NEEDED
+ : RequirementStatus.UNNEEDED);
+ }
+
+ @Override
+ protected Optional<Modifier> visibility() {
+ return componentImplementation
+ .componentDescriptor()
+ .typeElement()
+ .getModifiers()
+ .contains(PUBLIC) ? Optional.of(PUBLIC) : Optional.empty();
+ }
+
+ @Override
+ protected void setSupertype() {
+ // There's never a supertype for a root component auto-generated builder type.
+ }
+
+ @Override
+ protected void addConstructor() {
+ classBuilder.addMethod(constructorBuilder().addModifiers(PRIVATE).build());
+ }
+
+ @Override
+ protected ImmutableSet<ComponentRequirement> setterMethods() {
+ return componentDescriptor().dependenciesAndConcreteModules();
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
+ return ImmutableMap.of();
+ }
+
+ @Override
+ protected MethodSpec.Builder factoryMethodBuilder() {
+ return methodBuilder("build");
+ }
+
+ @Override
+ protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
+ String name = simpleVariableName(requirement.typeElement());
+ return methodBuilder(name)
+ .addModifiers(PUBLIC)
+ .addParameter(TypeName.get(requirement.type()), name)
+ .returns(className);
+ }
+ }
+
+ /** Enumeration of statuses a component requirement may have in a creator. */
+ enum RequirementStatus {
+ /** An instance is needed to create the component. */
+ NEEDED,
+
+ /**
+ * An instance is not needed to create the component, but the requirement is for a module owned
+ * by the component. Setting the requirement is a no-op and any setter method should be marked
+ * deprecated on the generated type as a warning to the user.
+ */
+ UNNEEDED,
+
+ /**
+ * The requirement may not be set in this creator because the module it is for is already
+ * inherited from an ancestor component. Any setter method for it should throw an exception.
+ */
+ UNSETTABLE_REPEATED_MODULE,
+
+ /**
+ * The requirement is settable by the creator, but the setter method implementation already
+ * exists in a supertype.
+ */
+ IMPLEMENTED_IN_SUPERTYPE,
+ ;
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentCreatorKind.java b/java/dagger/internal/codegen/ComponentCreatorKind.java
new file mode 100644
index 0000000..dc203de
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentCreatorKind.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+
+import com.google.common.base.Ascii;
+
+/** Enumeration of the different kinds of component creators. */
+enum ComponentCreatorKind {
+ /** {@code @Component.Builder} or one of its subcomponent/production variants. */
+ BUILDER,
+
+ /** {@code @Component.Factory} or one of its subcomponent/production variants. */
+ FACTORY,
+ ;
+
+ /** Name to use as (or as part of) a type name for a creator of this kind. */
+ String typeName() {
+ return UPPER_UNDERSCORE.to(UPPER_CAMEL, name());
+ }
+
+ /** Name to use for a component's static method returning a creator of this kind. */
+ String methodName() {
+ return Ascii.toLowerCase(name());
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentCreatorValidator.java b/java/dagger/internal/codegen/ComponentCreatorValidator.java
new file mode 100644
index 0000000..c55dadd
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentCreatorValidator.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ObjectArrays;
+import dagger.BindsInstance;
+import dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.List;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+
+/** Validates types annotated with component creator annotations. */
+final class ComponentCreatorValidator {
+
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+
+ @Inject
+ ComponentCreatorValidator(DaggerElements elements, DaggerTypes types) {
+ this.elements = elements;
+ this.types = types;
+ }
+
+ /** Validates that the given {@code type} is potentially a valid component creator type. */
+ public ValidationReport<TypeElement> validate(TypeElement type) {
+ ValidationReport.Builder<TypeElement> report = ValidationReport.about(type);
+
+ ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations = getCreatorAnnotations(type);
+ if (!validateOnlyOneCreatorAnnotation(creatorAnnotations, report)) {
+ return report.build();
+ }
+
+ // Note: there's more validation in ComponentDescriptorValidator:
+ // - to make sure the setter methods/factory parameters mirror the deps
+ // - to make sure each type or key is set by only one method or parameter
+ ElementValidator validator =
+ new ElementValidator(type, report, getOnlyElement(creatorAnnotations));
+ return validator.validate();
+ }
+
+ private boolean validateOnlyOneCreatorAnnotation(
+ ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations,
+ ValidationReport.Builder<?> report) {
+ // creatorAnnotations should never be empty because this should only ever be called for
+ // types that have been found to have some creator annotation
+ if (creatorAnnotations.size() > 1) {
+ String error =
+ "May not have more than one component Factory or Builder annotation on a type"
+ + ": found "
+ + creatorAnnotations;
+ report.addError(error);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validator for a single {@link TypeElement} that is annotated with a {@code Builder} or {@code
+ * Factory} annotation.
+ */
+ private final class ElementValidator {
+ private final TypeElement type;
+ private final Element component;
+ private final ValidationReport.Builder<TypeElement> report;
+ private final ComponentCreatorAnnotation annotation;
+ private final ComponentCreatorMessages messages;
+
+ private ElementValidator(
+ TypeElement type,
+ ValidationReport.Builder<TypeElement> report,
+ ComponentCreatorAnnotation annotation) {
+ this.type = type;
+ this.component = type.getEnclosingElement();
+ this.report = report;
+ this.annotation = annotation;
+ this.messages = ErrorMessages.creatorMessagesFor(annotation);
+ }
+
+ /** Validates the creator type. */
+ final ValidationReport<TypeElement> validate() {
+ if (!isAnnotationPresent(component, annotation.componentAnnotation())) {
+ report.addError(messages.mustBeInComponent());
+ }
+
+ // If the type isn't a class or interface, don't validate anything else since the rest of the
+ // messages will be bogus.
+ if (!validateIsClassOrInterface()) {
+ return report.build();
+ }
+
+ validateTypeRequirements();
+ switch (annotation.creatorKind()) {
+ case FACTORY:
+ validateFactory();
+ break;
+ case BUILDER:
+ validateBuilder();
+ }
+
+ return report.build();
+ }
+
+ /** Validates that the type is a class or interface type and returns true if it is. */
+ private boolean validateIsClassOrInterface() {
+ switch (type.getKind()) {
+ case CLASS:
+ validateConstructor();
+ return true;
+ case INTERFACE:
+ return true;
+ default:
+ report.addError(messages.mustBeClassOrInterface());
+ }
+ return false;
+ }
+
+ private void validateConstructor() {
+ List<? extends Element> allElements = type.getEnclosedElements();
+ List<ExecutableElement> constructors = ElementFilter.constructorsIn(allElements);
+
+ boolean valid = true;
+ if (constructors.size() != 1) {
+ valid = false;
+ } else {
+ ExecutableElement constructor = getOnlyElement(constructors);
+ valid =
+ constructor.getParameters().isEmpty() && !constructor.getModifiers().contains(PRIVATE);
+ }
+
+ if (!valid) {
+ report.addError(messages.invalidConstructor());
+ }
+ }
+
+ /** Validates basic requirements about the type that are common to both creator kinds. */
+ private void validateTypeRequirements() {
+ if (!type.getTypeParameters().isEmpty()) {
+ report.addError(messages.generics());
+ }
+
+ Set<Modifier> modifiers = type.getModifiers();
+ if (modifiers.contains(PRIVATE)) {
+ report.addError(messages.isPrivate());
+ }
+ if (!modifiers.contains(STATIC)) {
+ report.addError(messages.mustBeStatic());
+ }
+ // Note: Must be abstract, so no need to check for final.
+ if (!modifiers.contains(ABSTRACT)) {
+ report.addError(messages.mustBeAbstract());
+ }
+ }
+
+ private void validateBuilder() {
+ ExecutableElement buildMethod = null;
+ for (ExecutableElement method : elements.getUnimplementedMethods(type)) {
+ switch (method.getParameters().size()) {
+ case 0: // If this is potentially a build() method, validate it returns the correct type.
+ if (validateFactoryMethodReturnType(method)) {
+ if (buildMethod != null) {
+ // If we found more than one build-like method, fail.
+ error(
+ method,
+ messages.twoFactoryMethods(),
+ messages.inheritedTwoFactoryMethods(),
+ buildMethod);
+ }
+ }
+ // We set the buildMethod regardless of the return type to reduce error spam.
+ buildMethod = method;
+ break;
+
+ case 1: // If this correctly had one parameter, make sure the return types are valid.
+ validateSetterMethod(method);
+ break;
+
+ default: // more than one parameter
+ error(
+ method,
+ messages.setterMethodsMustTakeOneArg(),
+ messages.inheritedSetterMethodsMustTakeOneArg());
+ break;
+ }
+ }
+
+ if (buildMethod == null) {
+ report.addError(messages.missingFactoryMethod());
+ } else {
+ validateNotGeneric(buildMethod);
+ }
+ }
+
+ private void validateSetterMethod(ExecutableElement method) {
+ TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
+ if (returnType.getKind() != TypeKind.VOID && !types.isSubtype(type.asType(), returnType)) {
+ error(
+ method,
+ messages.setterMethodsMustReturnVoidOrBuilder(),
+ messages.inheritedSetterMethodsMustReturnVoidOrBuilder());
+ }
+
+ validateNotGeneric(method);
+
+ VariableElement parameter = method.getParameters().get(0);
+
+ boolean methodIsBindsInstance = isAnnotationPresent(method, BindsInstance.class);
+ boolean parameterIsBindsInstance = isAnnotationPresent(parameter, BindsInstance.class);
+ boolean bindsInstance = methodIsBindsInstance || parameterIsBindsInstance;
+
+ if (methodIsBindsInstance && parameterIsBindsInstance) {
+ error(
+ method,
+ messages.bindsInstanceNotAllowedOnBothSetterMethodAndParameter(),
+ messages.inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter());
+ }
+
+ if (!bindsInstance && parameter.asType().getKind().isPrimitive()) {
+ error(
+ method,
+ messages.nonBindsInstanceParametersMayNotBePrimitives(),
+ messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
+ }
+ }
+
+ private void validateFactory() {
+ ImmutableList<ExecutableElement> abstractMethods =
+ elements.getUnimplementedMethods(type).asList();
+ switch (abstractMethods.size()) {
+ case 0:
+ report.addError(messages.missingFactoryMethod());
+ return;
+ case 1:
+ break; // good
+ default:
+ error(
+ abstractMethods.get(1),
+ messages.twoFactoryMethods(),
+ messages.inheritedTwoFactoryMethods(),
+ abstractMethods.get(0));
+ return;
+ }
+
+ validateFactoryMethod(getOnlyElement(abstractMethods));
+ }
+
+ /** Validates that the given {@code method} is a valid component factory method. */
+ private void validateFactoryMethod(ExecutableElement method) {
+ validateNotGeneric(method);
+
+ if (!validateFactoryMethodReturnType(method)) {
+ // If we can't determine that the single method is a valid factory method, don't bother
+ // validating its parameters.
+ return;
+ }
+
+ for (VariableElement parameter : method.getParameters()) {
+ if (!isAnnotationPresent(parameter, BindsInstance.class)
+ && parameter.asType().getKind().isPrimitive()) {
+ error(
+ method,
+ messages.nonBindsInstanceParametersMayNotBePrimitives(),
+ messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
+ }
+ }
+ }
+
+ /**
+ * Validates that the factory method that actually returns a new component instance. Returns
+ * true if the return type was valid.
+ */
+ private boolean validateFactoryMethodReturnType(ExecutableElement method) {
+ TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
+
+ if (!types.isSubtype(component.asType(), returnType)) {
+ error(
+ method,
+ messages.factoryMethodMustReturnComponentType(),
+ messages.inheritedFactoryMethodMustReturnComponentType());
+ return false;
+ }
+
+ if (isAnnotationPresent(method, BindsInstance.class)) {
+ error(
+ method,
+ messages.factoryMethodMayNotBeAnnotatedWithBindsInstance(),
+ messages.inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance());
+ return false;
+ }
+
+ TypeElement componentType = MoreElements.asType(component);
+ if (!types.isSameType(componentType.asType(), returnType)) {
+ ImmutableSet<ExecutableElement> methodsOnlyInComponent =
+ methodsOnlyInComponent(componentType);
+ if (!methodsOnlyInComponent.isEmpty()) {
+ report.addWarning(
+ messages.factoryMethodReturnsSupertypeWithMissingMethods(
+ componentType, type, returnType, method, methodsOnlyInComponent),
+ method);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Generates one of two error messages. If the method is enclosed in the subject, we target the
+ * error to the method itself. Otherwise we target the error to the subject and list the method
+ * as an argument. (Otherwise we have no way of knowing if the method is being compiled in this
+ * pass too, so javac might not be able to pinpoint it's line of code.)
+ */
+ /*
+ * For Component.Builder, the prototypical example would be if someone had:
+ * libfoo: interface SharedBuilder { void badSetter(A a, B b); }
+ * libbar: BarComponent { BarBuilder extends SharedBuilder } }
+ * ... the compiler only validates BarBuilder when compiling libbar, but it fails because
+ * of libfoo's SharedBuilder (which could have been compiled in a previous pass).
+ * So we can't point to SharedBuilder#badSetter as the subject of the BarBuilder validation
+ * failure.
+ *
+ * This check is a little more strict than necessary -- ideally we'd check if method's enclosing
+ * class was included in this compile run. But that's hard, and this is close enough.
+ */
+ private void error(
+ ExecutableElement method,
+ String enclosedError,
+ String inheritedError,
+ Object... extraArgs) {
+ if (method.getEnclosingElement().equals(type)) {
+ report.addError(String.format(enclosedError, extraArgs), method);
+ } else {
+ report.addError(String.format(inheritedError, ObjectArrays.concat(extraArgs, method)));
+ }
+ }
+
+ /** Validates that the given {@code method} is not generic. * */
+ private void validateNotGeneric(ExecutableElement method) {
+ if (!method.getTypeParameters().isEmpty()) {
+ error(
+ method,
+ messages.methodsMayNotHaveTypeParameters(),
+ messages.inheritedMethodsMayNotHaveTypeParameters());
+ }
+ }
+
+ /**
+ * Returns all methods defind in {@code componentType} which are not inherited from a supertype.
+ */
+ private ImmutableSet<ExecutableElement> methodsOnlyInComponent(TypeElement componentType) {
+ // TODO(ronshapiro): Ideally this shouldn't return methods which are redeclared from a
+ // supertype, but do not change the return type. We don't have a good/simple way of checking
+ // that, and it doesn't seem likely, so the warning won't be too bad.
+ return ImmutableSet.copyOf(methodsIn(componentType.getEnclosedElements()));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentDescriptor.java b/java/dagger/internal/codegen/ComponentDescriptor.java
new file mode 100644
index 0000000..769cc4c
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentDescriptor.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.Component;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Scope;
+import dagger.producers.CancellationPolicy;
+import dagger.producers.ProductionComponent;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A component declaration.
+ *
+ * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent},
+ * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}.
+ *
+ * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also
+ * represent a synthetic component for the module, where there is an entry point for each binding in
+ * the module.
+ */
+@AutoValue
+abstract class ComponentDescriptor {
+ /** The annotation that specifies that {@link #typeElement()} is a component. */
+ abstract ComponentAnnotation annotation();
+
+ /** Returns {@code true} if this is a subcomponent. */
+ final boolean isSubcomponent() {
+ return annotation().isSubcomponent();
+ }
+
+ /**
+ * Returns {@code true} if this is a production component or subcomponent, or a
+ * {@code @ProducerModule} when doing module binding validation.
+ */
+ final boolean isProduction() {
+ return annotation().isProduction();
+ }
+
+ /**
+ * Returns {@code true} if this is a real component, and not a fictional one used to validate
+ * module bindings.
+ */
+ final boolean isRealComponent() {
+ return annotation().isRealComponent();
+ }
+
+ /**
+ * The element that defines the component. This is the element to which the {@link #annotation()}
+ * was applied.
+ */
+ abstract TypeElement typeElement();
+
+ /**
+ * The set of component dependencies listed in {@link Component#dependencies} or {@link
+ * ProductionComponent#dependencies()}.
+ */
+ abstract ImmutableSet<ComponentRequirement> dependencies();
+
+ /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */
+ final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() {
+ return Stream.concat(
+ moduleTypes().stream()
+ .filter(dep -> !dep.getModifiers().contains(ABSTRACT))
+ .map(module -> ComponentRequirement.forModule(module.asType())),
+ dependencies().stream())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by
+ * traversing {@link Module#includes()}.
+ */
+ abstract ImmutableSet<ModuleDescriptor> modules();
+
+ /** The types of the {@link #modules()}. */
+ final ImmutableSet<TypeElement> moduleTypes() {
+ return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
+ }
+
+ /**
+ * The types for which the component will need instances if all of its bindings are used. For the
+ * types the component will need in a given binding graph, use {@link
+ * BindingGraph#componentRequirements()}.
+ *
+ * <ul>
+ * <li>{@linkplain #modules()} modules} with concrete instance bindings
+ * <li>Bound instances
+ * <li>{@linkplain #dependencies() dependencies}
+ * </ul>
+ */
+ @Memoized
+ ImmutableSet<ComponentRequirement> requirements() {
+ ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
+ modules().stream()
+ .filter(
+ module ->
+ module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance))
+ .map(module -> ComponentRequirement.forModule(module.moduleElement().asType()))
+ .forEach(requirements::add);
+ requirements.addAll(dependencies());
+ requirements.addAll(
+ creatorDescriptor()
+ .map(ComponentCreatorDescriptor::boundInstanceRequirements)
+ .orElse(ImmutableSet.of()));
+ return requirements.build();
+ }
+
+ /**
+ * This component's {@linkplain #dependencies() dependencies} keyed by each provision or
+ * production method defined by that dependency. Note that the dependencies' types are not simply
+ * the enclosing type of the method; a method may be declared by a supertype of the actual
+ * dependency.
+ */
+ abstract ImmutableMap<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod();
+
+ /** The {@linkplain #dependencies() component dependency} that defines a method. */
+ final ComponentRequirement getDependencyThatDefinesMethod(Element method) {
+ checkArgument(
+ method instanceof ExecutableElement, "method must be an executable element: %s", method);
+ return checkNotNull(
+ dependenciesByDependencyMethod().get(method), "no dependency implements %s", method);
+ }
+
+ /**
+ * The scopes of the component.
+ */
+ abstract ImmutableSet<Scope> scopes();
+
+ /**
+ * All {@link Subcomponent}s which are direct children of this component. This includes
+ * subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain
+ * #childComponentsDeclaredByFactoryMethods() factory methods} and {@linkplain
+ * #childComponentsDeclaredByBuilderEntryPoints() builder methods}.
+ */
+ final ImmutableSet<ComponentDescriptor> childComponents() {
+ return ImmutableSet.<ComponentDescriptor>builder()
+ .addAll(childComponentsDeclaredByFactoryMethods().values())
+ .addAll(childComponentsDeclaredByBuilderEntryPoints().values())
+ .addAll(childComponentsDeclaredByModules())
+ .build();
+ }
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain
+ * Module#subcomponents() module's subcomponents}.
+ */
+ abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules();
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
+ * factory method.
+ */
+ abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
+ childComponentsDeclaredByFactoryMethods();
+
+ /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
+ @Memoized
+ ImmutableMap<TypeElement, ComponentDescriptor> childComponentsByElement() {
+ return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement);
+ }
+
+ /** Returns the factory method that declares a child component. */
+ final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent(
+ ComponentDescriptor childComponent) {
+ return Optional.ofNullable(
+ childComponentsDeclaredByFactoryMethods().inverse().get(childComponent));
+ }
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
+ * builder method.
+ */
+ abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
+ childComponentsDeclaredByBuilderEntryPoints();
+
+ private final Supplier<ImmutableMap<TypeElement, ComponentDescriptor>>
+ childComponentsByBuilderType =
+ Suppliers.memoize(
+ () ->
+ childComponents().stream()
+ .filter(child -> child.creatorDescriptor().isPresent())
+ .collect(
+ toImmutableMap(
+ child -> child.creatorDescriptor().get().typeElement(),
+ child -> child)));
+
+ /** Returns the child component with the given builder type. */
+ final ComponentDescriptor getChildComponentWithBuilderType(TypeElement builderType) {
+ return checkNotNull(
+ childComponentsByBuilderType.get().get(builderType),
+ "no child component found for builder type %s",
+ builderType.getQualifiedName());
+ }
+
+ abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
+
+ /** Returns the first component method associated with this binding request, if one exists. */
+ Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) {
+ return componentMethods().stream()
+ .filter(method -> doesComponentMethodMatch(method, request))
+ .findFirst();
+ }
+
+ /** Returns true if the component method matches the binding request. */
+ private static boolean doesComponentMethodMatch(
+ ComponentMethodDescriptor componentMethod, BindingRequest request) {
+ return componentMethod
+ .dependencyRequest()
+ .map(BindingRequest::bindingRequest)
+ .filter(request::equals)
+ .isPresent();
+ }
+
+ /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */
+ final ImmutableSet<ComponentMethodDescriptor> entryPointMethods() {
+ return componentMethods()
+ .stream()
+ .filter(method -> method.dependencyRequest().isPresent())
+ .collect(toImmutableSet());
+ }
+
+ // TODO(gak): Consider making this non-optional and revising the
+ // interaction between the spec & generation
+ /** Returns a descriptor for the creator type for this component type, if the user defined one. */
+ abstract Optional<ComponentCreatorDescriptor> creatorDescriptor();
+
+ /**
+ * Returns {@code true} for components that have a creator, either because the user {@linkplain
+ * #creatorDescriptor() specified one} or because it's a top-level component with an implicit
+ * builder.
+ */
+ final boolean hasCreator() {
+ return !isSubcomponent() || creatorDescriptor().isPresent();
+ }
+
+ /**
+ * Returns the {@link CancellationPolicy} for this component, or an empty optional if either the
+ * component is not a production component or no {@code CancellationPolicy} annotation is present.
+ */
+ final Optional<CancellationPolicy> cancellationPolicy() {
+ return isProduction()
+ ? Optional.ofNullable(typeElement().getAnnotation(CancellationPolicy.class))
+ : Optional.empty();
+ }
+
+ @Memoized
+ @Override
+ public int hashCode() {
+ // TODO(b/122962745): Only use typeElement().hashCode()
+ return Objects.hash(typeElement(), annotation());
+ }
+
+ // TODO(ronshapiro): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** A component method. */
+ @AutoValue
+ abstract static class ComponentMethodDescriptor {
+ /** The method itself. Note that this may be declared on a supertype of the component. */
+ abstract ExecutableElement methodElement();
+
+ /**
+ * The dependency request for production, provision, and subcomponent creator methods. Absent
+ * for subcomponent factory methods.
+ */
+ abstract Optional<DependencyRequest> dependencyRequest();
+
+ /** The subcomponent for subcomponent factory methods and subcomponent creator methods. */
+ abstract Optional<ComponentDescriptor> subcomponent();
+
+ /**
+ * Returns the return type of {@link #methodElement()} as resolved in the {@link
+ * ComponentDescriptor#typeElement() component type}. If there are no type variables in the
+ * return type, this is the equivalent of {@code methodElement().getReturnType()}.
+ */
+ TypeMirror resolvedReturnType(DaggerTypes types) {
+ checkState(dependencyRequest().isPresent());
+
+ TypeMirror returnType = methodElement().getReturnType();
+ if (returnType.getKind().isPrimitive() || returnType.getKind().equals(VOID)) {
+ return returnType;
+ }
+ return BindingRequest.bindingRequest(dependencyRequest().get())
+ .requestedType(dependencyRequest().get().key().type(), types);
+ }
+
+ /** A {@link ComponentMethodDescriptor}builder for a method. */
+ static Builder builder(ExecutableElement method) {
+ return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor.Builder()
+ .methodElement(method);
+ }
+
+ /** A builder of {@link ComponentMethodDescriptor}s. */
+ @AutoValue.Builder
+ @CanIgnoreReturnValue
+ interface Builder {
+ /** @see ComponentMethodDescriptor#methodElement() */
+ Builder methodElement(ExecutableElement methodElement);
+
+ /** @see ComponentMethodDescriptor#dependencyRequest() */
+ Builder dependencyRequest(DependencyRequest dependencyRequest);
+
+ /** @see ComponentMethodDescriptor#subcomponent() */
+ Builder subcomponent(ComponentDescriptor subcomponent);
+
+ /** Builds the descriptor. */
+ @CheckReturnValue
+ ComponentMethodDescriptor build();
+ }
+ }
+
+ /** No-argument methods defined on {@link Object} that are ignored for contribution. */
+ private static final ImmutableSet<String> NON_CONTRIBUTING_OBJECT_METHOD_NAMES =
+ ImmutableSet.of("toString", "hashCode", "clone", "getClass");
+
+ /**
+ * Returns {@code true} if a method could be a component entry point but not a members-injection
+ * method.
+ */
+ static boolean isComponentContributionMethod(DaggerElements elements, ExecutableElement method) {
+ return method.getParameters().isEmpty()
+ && !method.getReturnType().getKind().equals(VOID)
+ && !elements.getTypeElement(Object.class).equals(method.getEnclosingElement())
+ && !NON_CONTRIBUTING_OBJECT_METHOD_NAMES.contains(method.getSimpleName().toString());
+ }
+
+ /** Returns {@code true} if a method could be a component production entry point. */
+ static boolean isComponentProductionMethod(DaggerElements elements, ExecutableElement method) {
+ return isComponentContributionMethod(elements, method) && isFutureType(method.getReturnType());
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentDescriptorFactory.java b/java/dagger/internal/codegen/ComponentDescriptorFactory.java
new file mode 100644
index 0000000..7d87eac
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentDescriptorFactory.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.creatorAnnotationsFor;
+import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
+import static dagger.internal.codegen.ConfigurationAnnotations.enclosedAnnotatedTypes;
+import static dagger.internal.codegen.ConfigurationAnnotations.isSubcomponentCreator;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
+import static dagger.internal.codegen.Scopes.productionScope;
+import static dagger.internal.codegen.Scopes.scopesOf;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.VOID;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Scope;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory for {@link ComponentDescriptor}s. */
+final class ComponentDescriptorFactory {
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final DependencyRequestFactory dependencyRequestFactory;
+ private final ModuleDescriptor.Factory moduleDescriptorFactory;
+
+ @Inject
+ ComponentDescriptorFactory(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestFactory dependencyRequestFactory,
+ ModuleDescriptor.Factory moduleDescriptorFactory) {
+ this.elements = elements;
+ this.types = types;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ this.moduleDescriptorFactory = moduleDescriptorFactory;
+ }
+
+ /** Returns a descriptor for a root component type. */
+ ComponentDescriptor rootComponentDescriptor(TypeElement typeElement) {
+ return create(
+ typeElement,
+ checkAnnotation(
+ typeElement,
+ ComponentAnnotation::rootComponentAnnotation,
+ "must have a component annotation"));
+ }
+
+ /** Returns a descriptor for a subcomponent type. */
+ ComponentDescriptor subcomponentDescriptor(TypeElement typeElement) {
+ return create(
+ typeElement,
+ checkAnnotation(
+ typeElement,
+ ComponentAnnotation::subcomponentAnnotation,
+ "must have a subcomponent annotation"));
+ }
+
+ /**
+ * Returns a descriptor for a fictional component based on a module type in order to validate its
+ * bindings.
+ */
+ ComponentDescriptor moduleComponentDescriptor(TypeElement typeElement) {
+ return create(
+ typeElement,
+ ComponentAnnotation.fromModuleAnnotation(
+ checkAnnotation(
+ typeElement, ModuleAnnotation::moduleAnnotation, "must have a module annotation")));
+ }
+
+ private static <A> A checkAnnotation(
+ TypeElement typeElement,
+ Function<TypeElement, Optional<A>> annotationFunction,
+ String message) {
+ return annotationFunction
+ .apply(typeElement)
+ .orElseThrow(() -> new IllegalArgumentException(typeElement + " " + message));
+ }
+
+ private ComponentDescriptor create(
+ TypeElement typeElement, ComponentAnnotation componentAnnotation) {
+ ImmutableSet<ComponentRequirement> componentDependencies =
+ componentAnnotation.dependencyTypes().stream()
+ .map(ComponentRequirement::forDependency)
+ .collect(toImmutableSet());
+
+ ImmutableMap.Builder<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod =
+ ImmutableMap.builder();
+
+ for (ComponentRequirement componentDependency : componentDependencies) {
+ for (ExecutableElement dependencyMethod :
+ methodsIn(elements.getAllMembers(componentDependency.typeElement()))) {
+ if (isComponentContributionMethod(elements, dependencyMethod)) {
+ dependenciesByDependencyMethod.put(dependencyMethod, componentDependency);
+ }
+ }
+ }
+
+ // Start with the component's modules. For fictional components built from a module, start with
+ // that module.
+ ImmutableSet<TypeElement> modules =
+ componentAnnotation.isRealComponent()
+ ? componentAnnotation.modules()
+ : ImmutableSet.of(typeElement);
+
+ ImmutableSet<ModuleDescriptor> transitiveModules =
+ moduleDescriptorFactory.transitiveModules(modules);
+
+ ImmutableSet.Builder<ComponentDescriptor> subcomponentsFromModules = ImmutableSet.builder();
+ for (ModuleDescriptor module : transitiveModules) {
+ for (SubcomponentDeclaration subcomponentDeclaration : module.subcomponentDeclarations()) {
+ TypeElement subcomponent = subcomponentDeclaration.subcomponentType();
+ subcomponentsFromModules.add(subcomponentDescriptor(subcomponent));
+ }
+ }
+
+ ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
+ ImmutableSet.builder();
+ ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
+ subcomponentsByFactoryMethod = ImmutableBiMap.builder();
+ ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
+ subcomponentsByBuilderMethod = ImmutableBiMap.builder();
+ if (componentAnnotation.isRealComponent()) {
+ ImmutableSet<ExecutableElement> unimplementedMethods =
+ elements.getUnimplementedMethods(typeElement);
+ for (ExecutableElement componentMethod : unimplementedMethods) {
+ ComponentMethodDescriptor componentMethodDescriptor =
+ getDescriptorForComponentMethod(typeElement, componentAnnotation, componentMethod);
+ componentMethodsBuilder.add(componentMethodDescriptor);
+ componentMethodDescriptor
+ .subcomponent()
+ .ifPresent(
+ subcomponent -> {
+ // If the dependency request is present, that means the method returns the
+ // subcomponent factory.
+ if (componentMethodDescriptor.dependencyRequest().isPresent()) {
+ subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
+ } else {
+ subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
+ }
+ });
+ }
+ }
+
+ // Validation should have ensured that this set will have at most one element.
+ ImmutableSet<DeclaredType> enclosedCreators =
+ creatorAnnotationsFor(componentAnnotation).stream()
+ .flatMap(
+ creatorAnnotation ->
+ enclosedAnnotatedTypes(typeElement, creatorAnnotation).stream())
+ .collect(toImmutableSet());
+ Optional<ComponentCreatorDescriptor> creatorDescriptor =
+ enclosedCreators.isEmpty()
+ ? Optional.empty()
+ : Optional.of(
+ ComponentCreatorDescriptor.create(
+ getOnlyElement(enclosedCreators), elements, types, dependencyRequestFactory));
+
+ ImmutableSet<Scope> scopes = scopesOf(typeElement);
+ if (componentAnnotation.isProduction()) {
+ scopes = ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(elements)).build();
+ }
+
+ return new AutoValue_ComponentDescriptor(
+ componentAnnotation,
+ typeElement,
+ componentDependencies,
+ transitiveModules,
+ dependenciesByDependencyMethod.build(),
+ scopes,
+ subcomponentsFromModules.build(),
+ subcomponentsByFactoryMethod.build(),
+ subcomponentsByBuilderMethod.build(),
+ componentMethodsBuilder.build(),
+ creatorDescriptor);
+ }
+
+ private ComponentMethodDescriptor getDescriptorForComponentMethod(
+ TypeElement componentElement,
+ ComponentAnnotation componentAnnotation,
+ ExecutableElement componentMethod) {
+ ComponentMethodDescriptor.Builder descriptor =
+ ComponentMethodDescriptor.builder(componentMethod);
+
+ ExecutableType resolvedComponentMethod =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(componentElement.asType()), componentMethod));
+ TypeMirror returnType = resolvedComponentMethod.getReturnType();
+ if (returnType.getKind().equals(DECLARED) && !getQualifier(componentMethod).isPresent()) {
+ TypeElement returnTypeElement = asTypeElement(returnType);
+ if (subcomponentAnnotation(returnTypeElement).isPresent()) {
+ // It's a subcomponent factory method. There is no dependency request, and there could be
+ // any number of parameters. Just return the descriptor.
+ return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
+ }
+ if (isSubcomponentCreator(returnTypeElement)) {
+ descriptor.subcomponent(
+ subcomponentDescriptor(asType(returnTypeElement.getEnclosingElement())));
+ }
+ }
+
+ switch (componentMethod.getParameters().size()) {
+ case 0:
+ checkArgument(
+ !returnType.getKind().equals(VOID),
+ "component method cannot be void: %s",
+ componentMethod);
+ descriptor.dependencyRequest(
+ componentAnnotation.isProduction()
+ ? dependencyRequestFactory.forComponentProductionMethod(
+ componentMethod, resolvedComponentMethod)
+ : dependencyRequestFactory.forComponentProvisionMethod(
+ componentMethod, resolvedComponentMethod));
+ break;
+
+ case 1:
+ checkArgument(
+ returnType.getKind().equals(VOID)
+ || MoreTypes.equivalence()
+ .equivalent(returnType, resolvedComponentMethod.getParameterTypes().get(0)),
+ "members injection method must return void or parameter type: %s",
+ componentMethod);
+ descriptor.dependencyRequest(
+ dependencyRequestFactory.forComponentMembersInjectionMethod(
+ componentMethod, resolvedComponentMethod));
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "component method has too many parameters: " + componentMethod);
+ }
+
+ return descriptor.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentDescriptorValidator.java b/java/dagger/internal/codegen/ComponentDescriptorValidator.java
new file mode 100644
index 0000000..8f85b3a
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentDescriptorValidator.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.in;
+import static com.google.common.collect.Collections2.transform;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotation;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
+import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.Formatter.INDENT;
+import static dagger.internal.codegen.Scopes.getReadableSource;
+import static dagger.internal.codegen.Scopes.scopesOf;
+import static dagger.internal.codegen.Scopes.singletonScope;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.ComponentRequirement.NullPolicy;
+import dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Scope;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringJoiner;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+
+/**
+ * Reports errors in the component hierarchy.
+ *
+ * <ul>
+ * <li>Validates scope hierarchy of component dependencies and subcomponents.
+ * <li>Reports errors if there are component dependency cycles.
+ * <li>Reports errors if any abstract modules have non-abstract instance binding methods.
+ * <li>Validates component creator types.
+ * </ul>
+ */
+// TODO(dpb): Combine with ComponentHierarchyValidator.
+final class ComponentDescriptorValidator {
+
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final CompilerOptions compilerOptions;
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final ComponentHierarchyValidator componentHierarchyValidator;
+
+ @Inject
+ ComponentDescriptorValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ CompilerOptions compilerOptions,
+ MethodSignatureFormatter methodSignatureFormatter,
+ ComponentHierarchyValidator componentHierarchyValidator) {
+ this.elements = elements;
+ this.types = types;
+ this.compilerOptions = compilerOptions;
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.componentHierarchyValidator = componentHierarchyValidator;
+ }
+
+ ValidationReport<TypeElement> validate(ComponentDescriptor component) {
+ ComponentValidation validation = new ComponentValidation(component);
+ validation.visitComponent(component);
+ validation.report(component).addSubreport(componentHierarchyValidator.validate(component));
+ return validation.buildReport();
+ }
+
+ private final class ComponentValidation {
+ final ComponentDescriptor rootComponent;
+ final Map<ComponentDescriptor, ValidationReport.Builder<TypeElement>> reports =
+ new LinkedHashMap<>();
+
+ ComponentValidation(ComponentDescriptor rootComponent) {
+ this.rootComponent = checkNotNull(rootComponent);
+ }
+
+ /** Returns a report that contains all validation messages found during traversal. */
+ ValidationReport<TypeElement> buildReport() {
+ ValidationReport.Builder<TypeElement> report =
+ ValidationReport.about(rootComponent.typeElement());
+ reports.values().forEach(subreport -> report.addSubreport(subreport.build()));
+ return report.build();
+ }
+
+ /** Returns the report builder for a (sub)component. */
+ private ValidationReport.Builder<TypeElement> report(ComponentDescriptor component) {
+ return reentrantComputeIfAbsent(
+ reports, component, descriptor -> ValidationReport.about(descriptor.typeElement()));
+ }
+
+ private void reportComponentItem(
+ Diagnostic.Kind kind, ComponentDescriptor component, String message) {
+ report(component)
+ .addItem(message, kind, component.typeElement(), component.annotation().annotation());
+ }
+
+ private void reportComponentError(ComponentDescriptor component, String error) {
+ reportComponentItem(ERROR, component, error);
+ }
+
+ void visitComponent(ComponentDescriptor component) {
+ validateDependencyScopes(component);
+ validateComponentDependencyHierarchy(component);
+ validateModules(component);
+ validateCreators(component);
+ component.childComponents().forEach(this::visitComponent);
+ }
+
+ /** Validates that component dependencies do not form a cycle. */
+ private void validateComponentDependencyHierarchy(ComponentDescriptor component) {
+ validateComponentDependencyHierarchy(component, component.typeElement(), new ArrayDeque<>());
+ }
+
+ /** Recursive method to validate that component dependencies do not form a cycle. */
+ private void validateComponentDependencyHierarchy(
+ ComponentDescriptor component, TypeElement dependency, Deque<TypeElement> dependencyStack) {
+ if (dependencyStack.contains(dependency)) {
+ // Current component has already appeared in the component chain.
+ StringBuilder message = new StringBuilder();
+ message.append(component.typeElement().getQualifiedName());
+ message.append(" contains a cycle in its component dependencies:\n");
+ dependencyStack.push(dependency);
+ appendIndentedComponentsList(message, dependencyStack);
+ dependencyStack.pop();
+ reportComponentItem(
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ component,
+ message.toString());
+ } else {
+ rootComponentAnnotation(dependency)
+ .ifPresent(
+ componentAnnotation -> {
+ dependencyStack.push(dependency);
+
+ for (TypeElement nextDependency : componentAnnotation.dependencies()) {
+ validateComponentDependencyHierarchy(
+ component, nextDependency, dependencyStack);
+ }
+
+ dependencyStack.pop();
+ });
+ }
+ }
+
+ /**
+ * Validates that among the dependencies are at most one scoped dependency, that there are no
+ * cycles within the scoping chain, and that singleton components have no scoped dependencies.
+ */
+ private void validateDependencyScopes(ComponentDescriptor component) {
+ ImmutableSet<Scope> scopes = component.scopes();
+ ImmutableSet<TypeElement> scopedDependencies =
+ scopedTypesIn(
+ component
+ .dependencies()
+ .stream()
+ .map(ComponentRequirement::typeElement)
+ .collect(toImmutableSet()));
+ if (!scopes.isEmpty()) {
+ Scope singletonScope = singletonScope(elements);
+ // Dagger 1.x scope compatibility requires this be suppress-able.
+ if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()
+ && scopes.contains(singletonScope)) {
+ // Singleton is a special-case representing the longest lifetime, and therefore
+ // @Singleton components may not depend on scoped components
+ if (!scopedDependencies.isEmpty()) {
+ StringBuilder message =
+ new StringBuilder(
+ "This @Singleton component cannot depend on scoped components:\n");
+ appendIndentedComponentsList(message, scopedDependencies);
+ reportComponentItem(
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ component,
+ message.toString());
+ }
+ } else if (scopedDependencies.size() > 1) {
+ // Scoped components may depend on at most one scoped component.
+ StringBuilder message = new StringBuilder();
+ for (Scope scope : scopes) {
+ message.append(getReadableSource(scope)).append(' ');
+ }
+ message
+ .append(component.typeElement().getQualifiedName())
+ .append(" depends on more than one scoped component:\n");
+ appendIndentedComponentsList(message, scopedDependencies);
+ reportComponentError(component, message.toString());
+ } else {
+ // Dagger 1.x scope compatibility requires this be suppress-able.
+ if (!compilerOptions.scopeCycleValidationType().equals(ValidationType.NONE)) {
+ validateDependencyScopeHierarchy(
+ component, component.typeElement(), new ArrayDeque<>(), new ArrayDeque<>());
+ }
+ }
+ } else {
+ // Scopeless components may not depend on scoped components.
+ if (!scopedDependencies.isEmpty()) {
+ StringBuilder message =
+ new StringBuilder(component.typeElement().getQualifiedName())
+ .append(" (unscoped) cannot depend on scoped components:\n");
+ appendIndentedComponentsList(message, scopedDependencies);
+ reportComponentError(component, message.toString());
+ }
+ }
+ }
+
+ private void validateModules(ComponentDescriptor component) {
+ for (ModuleDescriptor module : component.modules()) {
+ if (module.moduleElement().getModifiers().contains(Modifier.ABSTRACT)) {
+ for (ContributionBinding binding : module.bindings()) {
+ if (binding.requiresModuleInstance()) {
+ report(component).addError(abstractModuleHasInstanceBindingMethodsError(module));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private String abstractModuleHasInstanceBindingMethodsError(ModuleDescriptor module) {
+ String methodAnnotations;
+ switch (module.kind()) {
+ case MODULE:
+ methodAnnotations = "@Provides";
+ break;
+ case PRODUCER_MODULE:
+ methodAnnotations = "@Provides or @Produces";
+ break;
+ default:
+ throw new AssertionError(module.kind());
+ }
+ return String.format(
+ "%s is abstract and has instance %s methods. Consider making the methods static or "
+ + "including a non-abstract subclass of the module instead.",
+ module.moduleElement(), methodAnnotations);
+ }
+
+ private void validateCreators(ComponentDescriptor component) {
+ if (!component.creatorDescriptor().isPresent()) {
+ // If no builder, nothing to validate.
+ return;
+ }
+
+ ComponentCreatorDescriptor creator = component.creatorDescriptor().get();
+ ComponentCreatorMessages messages = ErrorMessages.creatorMessagesFor(creator.annotation());
+
+ // Requirements for modules and dependencies that the creator can set
+ Set<ComponentRequirement> creatorModuleAndDependencyRequirements =
+ creator.moduleAndDependencyRequirements();
+ // Modules and dependencies the component requires
+ Set<ComponentRequirement> componentModuleAndDependencyRequirements =
+ component.dependenciesAndConcreteModules();
+
+ // Requirements that the creator can set that don't match any requirements that the component
+ // actually has.
+ Set<ComponentRequirement> inapplicableRequirementsOnCreator =
+ Sets.difference(
+ creatorModuleAndDependencyRequirements, componentModuleAndDependencyRequirements);
+
+ DeclaredType container = asDeclared(creator.typeElement().asType());
+ if (!inapplicableRequirementsOnCreator.isEmpty()) {
+ Collection<Element> excessElements =
+ Multimaps.filterKeys(
+ creator.unvalidatedRequirementElements(), in(inapplicableRequirementsOnCreator))
+ .values();
+ String formatted =
+ excessElements.stream()
+ .map(element -> formatElement(element, container))
+ .collect(joining(", ", "[", "]"));
+ report(component)
+ .addError(String.format(messages.extraSetters(), formatted), creator.typeElement());
+ }
+
+ // Component requirements that the creator must be able to set
+ Set<ComponentRequirement> mustBePassed =
+ Sets.filter(
+ componentModuleAndDependencyRequirements,
+ input -> input.nullPolicy(elements, types).equals(NullPolicy.THROW));
+ // Component requirements that the creator must be able to set, but can't
+ Set<ComponentRequirement> missingRequirements =
+ Sets.difference(mustBePassed, creatorModuleAndDependencyRequirements);
+
+ if (!missingRequirements.isEmpty()) {
+ report(component)
+ .addError(
+ String.format(
+ messages.missingSetters(),
+ missingRequirements.stream().map(ComponentRequirement::type).collect(toList())),
+ creator.typeElement());
+ }
+
+ // Validate that declared creator requirements (modules, dependencies) have unique types.
+ ImmutableSetMultimap<Wrapper<TypeMirror>, Element> declaredRequirementsByType =
+ Multimaps.filterKeys(
+ creator.unvalidatedRequirementElements(),
+ creatorModuleAndDependencyRequirements::contains)
+ .entries().stream()
+ .collect(
+ toImmutableSetMultimap(entry -> entry.getKey().wrappedType(), Entry::getValue));
+ declaredRequirementsByType
+ .asMap()
+ .forEach(
+ (typeWrapper, elementsForType) -> {
+ if (elementsForType.size() > 1) {
+ TypeMirror type = typeWrapper.get();
+ // TODO(cgdecker): Attach this error message to the factory method rather than
+ // the component type if the elements are factory method parameters AND the
+ // factory method is defined by the factory type itself and not by a supertype.
+ report(component)
+ .addError(
+ String.format(
+ messages.multipleSettersForModuleOrDependencyType(),
+ type,
+ transform(
+ elementsForType, element -> formatElement(element, container))),
+ creator.typeElement());
+ }
+ });
+
+ // TODO(cgdecker): Duplicate binding validation should handle the case of multiple elements
+ // that set the same bound-instance Key, but validating that here would make it fail faster
+ // for subcomponents.
+ }
+
+ private String formatElement(Element element, DeclaredType container) {
+ // TODO(cgdecker): Extract some or all of this to another class?
+ // But note that it does different formatting for parameters than
+ // DaggerElements.elementToString(Element).
+ switch (element.getKind()) {
+ case METHOD:
+ return methodSignatureFormatter.format(
+ MoreElements.asExecutable(element), Optional.of(container));
+ case PARAMETER:
+ return formatParameter(MoreElements.asVariable(element), container);
+ default:
+ // This method shouldn't be called with any other type of element.
+ throw new AssertionError();
+ }
+ }
+
+ private String formatParameter(VariableElement parameter, DeclaredType container) {
+ // TODO(cgdecker): Possibly leave the type (and annotations?) off of the parameters here and
+ // just use their names, since the type will be redundant in the context of the error message.
+ StringJoiner joiner = new StringJoiner(" ");
+ parameter.getAnnotationMirrors().stream().map(Object::toString).forEach(joiner::add);
+ TypeMirror parameterType = resolveParameterType(parameter, container);
+ return joiner
+ .add(stripCommonTypePrefixes(parameterType.toString()))
+ .add(parameter.getSimpleName())
+ .toString();
+ }
+
+ private TypeMirror resolveParameterType(VariableElement parameter, DeclaredType container) {
+ ExecutableElement method =
+ MoreElements.asExecutable(parameter.getEnclosingElement());
+ int parameterIndex = method.getParameters().indexOf(parameter);
+
+ ExecutableType methodType = MoreTypes.asExecutable(types.asMemberOf(container, method));
+ return methodType.getParameterTypes().get(parameterIndex);
+ }
+
+ /**
+ * Validates that scopes do not participate in a scoping cycle - that is to say, scoped
+ * components are in a hierarchical relationship terminating with Singleton.
+ *
+ * <p>As a side-effect, this means scoped components cannot have a dependency cycle between
+ * themselves, since a component's presence within its own dependency path implies a cyclical
+ * relationship between scopes. However, cycles in component dependencies are explicitly checked
+ * in {@link #validateComponentDependencyHierarchy(ComponentDescriptor)}.
+ */
+ private void validateDependencyScopeHierarchy(
+ ComponentDescriptor component,
+ TypeElement dependency,
+ Deque<ImmutableSet<Scope>> scopeStack,
+ Deque<TypeElement> scopedDependencyStack) {
+ ImmutableSet<Scope> scopes = scopesOf(dependency);
+ if (stackOverlaps(scopeStack, scopes)) {
+ scopedDependencyStack.push(dependency);
+ // Current scope has already appeared in the component chain.
+ StringBuilder message = new StringBuilder();
+ message.append(component.typeElement().getQualifiedName());
+ message.append(" depends on scoped components in a non-hierarchical scope ordering:\n");
+ appendIndentedComponentsList(message, scopedDependencyStack);
+ if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()) {
+ reportComponentItem(
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ component,
+ message.toString());
+ }
+ scopedDependencyStack.pop();
+ } else {
+ // TODO(beder): transitively check scopes of production components too.
+ rootComponentAnnotation(dependency)
+ .filter(componentAnnotation -> !componentAnnotation.isProduction())
+ .ifPresent(
+ componentAnnotation -> {
+ ImmutableSet<TypeElement> scopedDependencies =
+ scopedTypesIn(componentAnnotation.dependencies());
+ if (scopedDependencies.size() == 1) {
+ // empty can be ignored (base-case), and > 1 is a separately-reported error.
+ scopeStack.push(scopes);
+ scopedDependencyStack.push(dependency);
+ validateDependencyScopeHierarchy(
+ component,
+ getOnlyElement(scopedDependencies),
+ scopeStack,
+ scopedDependencyStack);
+ scopedDependencyStack.pop();
+ scopeStack.pop();
+ }
+ }); // else: we skip component dependencies which are not components
+ }
+ }
+
+ private <T> boolean stackOverlaps(Deque<ImmutableSet<T>> stack, ImmutableSet<T> set) {
+ for (ImmutableSet<T> entry : stack) {
+ if (!Sets.intersection(entry, set).isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Appends and formats a list of indented component types (with their scope annotations). */
+ private void appendIndentedComponentsList(StringBuilder message, Iterable<TypeElement> types) {
+ for (TypeElement scopedComponent : types) {
+ message.append(INDENT);
+ for (Scope scope : scopesOf(scopedComponent)) {
+ message.append(getReadableSource(scope)).append(' ');
+ }
+ message
+ .append(stripCommonTypePrefixes(scopedComponent.getQualifiedName().toString()))
+ .append('\n');
+ }
+ }
+
+ /**
+ * Returns a set of type elements containing only those found in the input set that have a
+ * scoping annotation.
+ */
+ private ImmutableSet<TypeElement> scopedTypesIn(Collection<TypeElement> types) {
+ return types.stream().filter(type -> !scopesOf(type).isEmpty()).collect(toImmutableSet());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentGenerator.java b/java/dagger/internal/codegen/ComponentGenerator.java
new file mode 100644
index 0000000..330ec2d
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentGenerator.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.SourceFiles.classFileName;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.Component;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Generates the implementation of the abstract types annotated with {@link Component}.
+ */
+final class ComponentGenerator extends SourceFileGenerator<BindingGraph> {
+ private final ComponentImplementationFactory componentImplementationFactory;
+
+ @Inject
+ ComponentGenerator(
+ Filer filer,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ ComponentImplementationFactory componentImplementationFactory) {
+ super(filer, elements, sourceVersion);
+ this.componentImplementationFactory = componentImplementationFactory;
+ }
+
+ @Override
+ ClassName nameGeneratedType(BindingGraph input) {
+ return componentName(input.componentTypeElement());
+ }
+
+ static ClassName componentName(TypeElement componentDefinitionType) {
+ ClassName componentName = ClassName.get(componentDefinitionType);
+ return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
+ }
+
+ @Override
+ Element originatingElement(BindingGraph input) {
+ return input.componentTypeElement();
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName componentName, BindingGraph bindingGraph) {
+ ComponentImplementation componentImplementation =
+ componentImplementationFactory.createComponentImplementation(bindingGraph);
+ verify(componentImplementation.name().equals(componentName));
+ return Optional.of(componentImplementation.generate());
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentHierarchyValidator.java b/java/dagger/internal/codegen/ComponentHierarchyValidator.java
new file mode 100644
index 0000000..d1e5333
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentHierarchyValidator.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Functions.constant;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.base.Predicates.in;
+import static com.google.common.base.Predicates.not;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.Scopes.getReadableSource;
+import static dagger.internal.codegen.Scopes.uniqueScopeOf;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.model.Scope;
+import java.util.Collection;
+import java.util.Formatter;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/** Validates the relationships between parent components and subcomponents. */
+final class ComponentHierarchyValidator {
+ private static final Joiner COMMA_SEPARATED_JOINER = Joiner.on(", ");
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ ComponentHierarchyValidator(CompilerOptions compilerOptions) {
+ this.compilerOptions = compilerOptions;
+ }
+
+ ValidationReport<TypeElement> validate(ComponentDescriptor componentDescriptor) {
+ ValidationReport.Builder<TypeElement> report =
+ ValidationReport.about(componentDescriptor.typeElement());
+ validateSubcomponentMethods(
+ report,
+ componentDescriptor,
+ Maps.toMap(componentDescriptor.moduleTypes(), constant(componentDescriptor.typeElement())));
+ validateRepeatedScopedDeclarations(report, componentDescriptor, LinkedHashMultimap.create());
+
+ if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()) {
+ validateScopeHierarchy(
+ report, componentDescriptor, LinkedHashMultimap.<ComponentDescriptor, Scope>create());
+ }
+ validateProductionModuleUniqueness(report, componentDescriptor, LinkedHashMultimap.create());
+ return report.build();
+ }
+
+ private void validateSubcomponentMethods(
+ ValidationReport.Builder<?> report,
+ ComponentDescriptor componentDescriptor,
+ ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
+ componentDescriptor
+ .childComponentsDeclaredByFactoryMethods()
+ .forEach(
+ (method, childComponent) -> {
+ if (childComponent.hasCreator()) {
+ report.addError(
+ "Components may not have factory methods for subcomponents that define a "
+ + "builder.",
+ method.methodElement());
+ } else {
+ validateFactoryMethodParameters(report, method, existingModuleToOwners);
+ }
+
+ validateSubcomponentMethods(
+ report,
+ childComponent,
+ new ImmutableMap.Builder<TypeElement, TypeElement>()
+ .putAll(existingModuleToOwners)
+ .putAll(
+ Maps.toMap(
+ Sets.difference(
+ childComponent.moduleTypes(), existingModuleToOwners.keySet()),
+ constant(childComponent.typeElement())))
+ .build());
+ });
+ }
+
+ private void validateFactoryMethodParameters(
+ ValidationReport.Builder<?> report,
+ ComponentMethodDescriptor subcomponentMethodDescriptor,
+ ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
+ for (VariableElement factoryMethodParameter :
+ subcomponentMethodDescriptor.methodElement().getParameters()) {
+ TypeElement moduleType = MoreTypes.asTypeElement(factoryMethodParameter.asType());
+ TypeElement originatingComponent = existingModuleToOwners.get(moduleType);
+ if (originatingComponent != null) {
+ /* Factory method tries to pass a module that is already present in the parent.
+ * This is an error. */
+ report.addError(
+ String.format(
+ "%s is present in %s. A subcomponent cannot use an instance of a "
+ + "module that differs from its parent.",
+ moduleType.getSimpleName(), originatingComponent.getQualifiedName()),
+ factoryMethodParameter);
+ }
+ }
+ }
+
+ /**
+ * Checks that components do not have any scopes that are also applied on any of their ancestors.
+ */
+ private void validateScopeHierarchy(
+ ValidationReport.Builder<TypeElement> report,
+ ComponentDescriptor subject,
+ SetMultimap<ComponentDescriptor, Scope> scopesByComponent) {
+ scopesByComponent.putAll(subject, subject.scopes());
+
+ for (ComponentDescriptor childComponent : subject.childComponents()) {
+ validateScopeHierarchy(report, childComponent, scopesByComponent);
+ }
+
+ scopesByComponent.removeAll(subject);
+
+ Predicate<Scope> subjectScopes =
+ subject.isProduction()
+ // TODO(beder): validate that @ProductionScope is only applied on production components
+ ? and(in(subject.scopes()), not(Scope::isProductionScope))
+ : in(subject.scopes());
+ SetMultimap<ComponentDescriptor, Scope> overlappingScopes =
+ Multimaps.filterValues(scopesByComponent, subjectScopes);
+ if (!overlappingScopes.isEmpty()) {
+ StringBuilder error =
+ new StringBuilder()
+ .append(subject.typeElement().getQualifiedName())
+ .append(" has conflicting scopes:");
+ for (Map.Entry<ComponentDescriptor, Scope> entry : overlappingScopes.entries()) {
+ Scope scope = entry.getValue();
+ error
+ .append("\n ")
+ .append(entry.getKey().typeElement().getQualifiedName())
+ .append(" also has ")
+ .append(getReadableSource(scope));
+ }
+ report.addItem(
+ error.toString(),
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ subject.typeElement());
+ }
+ }
+
+ private void validateProductionModuleUniqueness(
+ ValidationReport.Builder<TypeElement> report,
+ ComponentDescriptor componentDescriptor,
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> producerModulesByComponent) {
+ ImmutableSet<ModuleDescriptor> producerModules =
+ componentDescriptor.modules().stream()
+ .filter(module -> module.kind().equals(ModuleKind.PRODUCER_MODULE))
+ .collect(toImmutableSet());
+
+ producerModulesByComponent.putAll(componentDescriptor, producerModules);
+ for (ComponentDescriptor childComponent : componentDescriptor.childComponents()) {
+ validateProductionModuleUniqueness(report, childComponent, producerModulesByComponent);
+ }
+ producerModulesByComponent.removeAll(componentDescriptor);
+
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules =
+ Multimaps.filterValues(producerModulesByComponent, producerModules::contains);
+ if (repeatedModules.isEmpty()) {
+ return;
+ }
+
+ StringBuilder error = new StringBuilder();
+ Formatter formatter = new Formatter(error);
+
+ formatter.format("%s repeats @ProducerModules:", componentDescriptor.typeElement());
+
+ for (Map.Entry<ComponentDescriptor, Collection<ModuleDescriptor>> entry :
+ repeatedModules.asMap().entrySet()) {
+ formatter.format("\n %s also installs: ", entry.getKey().typeElement());
+ COMMA_SEPARATED_JOINER
+ .appendTo(error, Iterables.transform(entry.getValue(), m -> m.moduleElement()));
+ }
+
+ report.addError(error.toString());
+ }
+
+ private void validateRepeatedScopedDeclarations(
+ ValidationReport.Builder<TypeElement> report,
+ ComponentDescriptor component,
+ // TODO(ronshapiro): optimize ModuleDescriptor.hashCode()/equals. Otherwise this could be
+ // quite costly
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> modulesWithScopes) {
+ ImmutableSet<ModuleDescriptor> modules =
+ component.modules().stream().filter(this::hasScopedDeclarations).collect(toImmutableSet());
+ modulesWithScopes.putAll(component, modules);
+ for (ComponentDescriptor childComponent : component.childComponents()) {
+ validateRepeatedScopedDeclarations(report, childComponent, modulesWithScopes);
+ }
+ modulesWithScopes.removeAll(component);
+
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules =
+ Multimaps.filterValues(modulesWithScopes, modules::contains);
+ if (repeatedModules.isEmpty()) {
+ return;
+ }
+
+ report.addError(
+ repeatedModulesWithScopeError(component, ImmutableSetMultimap.copyOf(repeatedModules)));
+ }
+
+ private boolean hasScopedDeclarations(ModuleDescriptor module) {
+ return !moduleScopes(module).isEmpty();
+ }
+
+ private String repeatedModulesWithScopeError(
+ ComponentDescriptor component,
+ ImmutableSetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules) {
+ StringBuilder error =
+ new StringBuilder()
+ .append(component.typeElement().getQualifiedName())
+ .append(" repeats modules with scoped bindings or declarations:");
+
+ repeatedModules
+ .asMap()
+ .forEach(
+ (conflictingComponent, conflictingModules) -> {
+ error
+ .append("\n - ")
+ .append(conflictingComponent.typeElement().getQualifiedName())
+ .append(" also includes:");
+ for (ModuleDescriptor conflictingModule : conflictingModules) {
+ error
+ .append("\n - ")
+ .append(conflictingModule.moduleElement().getQualifiedName())
+ .append(" with scopes: ")
+ .append(COMMA_SEPARATED_JOINER.join(moduleScopes(conflictingModule)));
+ }
+ });
+ return error.toString();
+ }
+
+ private ImmutableSet<Scope> moduleScopes(ModuleDescriptor module) {
+ return FluentIterable.concat(module.allBindingDeclarations())
+ .transform(declaration -> uniqueScopeOf(declaration.bindingElement().get()))
+ .filter(scope -> scope.isPresent() && !scope.get().isReusable())
+ .transform(scope -> scope.get())
+ .toSet();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentHjarProcessingStep.java b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
new file mode 100644
index 0000000..47857ca
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotations;
+import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.ComponentGenerator.componentName;
+import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.BindsInstance;
+import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.internal.CancellationListener;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+
+/**
+ * A processing step that emits the API of a generated component, without any actual implementation.
+ *
+ * <p>When compiling a header jar (hjar), Bazel needs to run annotation processors that generate
+ * API, like Dagger, to see what code they might output. Full {@link BindingGraph} analysis is
+ * costly and unnecessary from the perspective of the header compiler; it's sole goal is to pass
+ * along a slimmed down version of what will be the jar for a particular compilation, whether or not
+ * that compilation succeeds. If it does not, the compilation pipeline will fail, even if header
+ * compilation succeeded.
+ *
+ * <p>The components emitted by this processing step include all of the API elements exposed by the
+ * normal step. Method bodies are omitted as Turbine ignores them entirely.
+ */
+final class ComponentHjarProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+ private final SourceVersion sourceVersion;
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final Filer filer;
+ private final Messager messager;
+ private final ComponentValidator componentValidator;
+ private final ComponentDescriptorFactory componentDescriptorFactory;
+
+ @Inject
+ ComponentHjarProcessingStep(
+ SourceVersion sourceVersion,
+ DaggerElements elements,
+ DaggerTypes types,
+ Filer filer,
+ Messager messager,
+ ComponentValidator componentValidator,
+ ComponentDescriptorFactory componentDescriptorFactory) {
+ super(MoreElements::asType);
+ this.sourceVersion = sourceVersion;
+ this.elements = elements;
+ this.types = types;
+ this.filer = filer;
+ this.messager = messager;
+ this.componentValidator = componentValidator;
+ this.componentDescriptorFactory = componentDescriptorFactory;
+ }
+
+ @Override
+ public Set<Class<? extends Annotation>> annotations() {
+ return rootComponentAnnotations();
+ }
+
+ @Override
+ protected void process(
+ TypeElement componentTypeElement, ImmutableSet<Class<? extends Annotation>> annotations) {
+ // TODO(ronshapiro): component validation might not be necessary. We should measure it and
+ // figure out if it's worth seeing if removing it will still work. We could potentially add a
+ // new catch clause for any exception that's not TypeNotPresentException and ignore the
+ // component entirely in that case.
+ ComponentValidationReport validationReport =
+ componentValidator.validate(componentTypeElement, ImmutableSet.of(), ImmutableSet.of());
+ validationReport.report().printMessagesTo(messager);
+ if (validationReport.report().isClean()) {
+ new EmptyComponentGenerator(filer, elements, sourceVersion)
+ .generate(
+ componentDescriptorFactory.rootComponentDescriptor(componentTypeElement), messager);
+ }
+ }
+
+ private final class EmptyComponentGenerator extends SourceFileGenerator<ComponentDescriptor> {
+ EmptyComponentGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ ClassName nameGeneratedType(ComponentDescriptor input) {
+ return componentName(input.typeElement());
+ }
+
+ @Override
+ Element originatingElement(ComponentDescriptor input) {
+ return input.typeElement();
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(
+ ClassName generatedTypeName, ComponentDescriptor componentDescriptor) {
+ TypeSpec.Builder generatedComponent =
+ TypeSpec.classBuilder(generatedTypeName)
+ .addModifiers(FINAL)
+ .addMethod(privateConstructor());
+ if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+ generatedComponent.addModifiers(PUBLIC);
+ }
+
+ TypeElement componentElement = componentDescriptor.typeElement();
+ addSupertype(generatedComponent, componentElement);
+
+ TypeName builderMethodReturnType;
+ ComponentCreatorKind creatorKind;
+ boolean noArgFactoryMethod;
+ if (componentDescriptor.creatorDescriptor().isPresent()) {
+ ComponentCreatorDescriptor creatorDescriptor =
+ componentDescriptor.creatorDescriptor().get();
+ builderMethodReturnType = ClassName.get(creatorDescriptor.typeElement());
+ creatorKind = creatorDescriptor.kind();
+ noArgFactoryMethod = creatorDescriptor.factoryParameters().isEmpty();
+ } else {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder("Builder")
+ .addModifiers(STATIC, FINAL)
+ .addMethod(privateConstructor());
+ if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+ builder.addModifiers(PUBLIC);
+ }
+
+ ClassName builderClassName = generatedTypeName.nestedClass("Builder");
+ builderMethodReturnType = builderClassName;
+ creatorKind = BUILDER;
+ noArgFactoryMethod = true;
+ componentRequirements(componentDescriptor)
+ .map(requirement -> builderSetterMethod(requirement.typeElement(), builderClassName))
+ .forEach(builder::addMethod);
+ builder.addMethod(builderBuildMethod(componentDescriptor));
+ generatedComponent.addType(builder.build());
+ }
+
+ generatedComponent.addMethod(staticCreatorMethod(builderMethodReturnType, creatorKind));
+
+ if (noArgFactoryMethod
+ && !hasBindsInstanceMethods(componentDescriptor)
+ && componentRequirements(componentDescriptor)
+ .noneMatch(requirement -> requirement.requiresAPassedInstance(elements, types))) {
+ generatedComponent.addMethod(createMethod(componentDescriptor));
+ }
+
+ DeclaredType componentType = MoreTypes.asDeclared(componentElement.asType());
+ // TODO(ronshapiro): unify with ComponentImplementationBuilder
+ Set<MethodSignature> methodSignatures =
+ Sets.newHashSetWithExpectedSize(componentDescriptor.componentMethods().size());
+ componentDescriptor
+ .componentMethods()
+ .stream()
+ .filter(
+ method -> {
+ return methodSignatures.add(
+ MethodSignature.forComponentMethod(method, componentType, types));
+ })
+ .forEach(
+ method ->
+ generatedComponent.addMethod(
+ emptyComponentMethod(componentElement, method.methodElement())));
+
+ if (componentDescriptor.isProduction()) {
+ generatedComponent
+ .addSuperinterface(ClassName.get(CancellationListener.class))
+ .addMethod(onProducerFutureCancelledMethod());
+ }
+
+ return Optional.of(generatedComponent);
+ }
+ }
+
+ private MethodSpec emptyComponentMethod(TypeElement typeElement, ExecutableElement baseMethod) {
+ return MethodSpec.overriding(baseMethod, MoreTypes.asDeclared(typeElement.asType()), types)
+ .build();
+ }
+
+ private MethodSpec privateConstructor() {
+ return constructorBuilder().addModifiers(PRIVATE).build();
+ }
+
+ /**
+ * Returns the {@link ComponentRequirement}s for a component that does not have a {@link
+ * ComponentDescriptor#creatorDescriptor()}.
+ */
+ private Stream<ComponentRequirement> componentRequirements(ComponentDescriptor component) {
+ checkArgument(!component.isSubcomponent());
+ return Stream.concat(
+ component.dependencies().stream(),
+ component.modules().stream()
+ .filter(module -> !module.moduleElement().getModifiers().contains(ABSTRACT))
+ .map(module -> ComponentRequirement.forModule(module.moduleElement().asType())));
+ }
+
+ private boolean hasBindsInstanceMethods(ComponentDescriptor componentDescriptor) {
+ return componentDescriptor.creatorDescriptor().isPresent()
+ && elements
+ .getUnimplementedMethods(componentDescriptor.creatorDescriptor().get().typeElement())
+ .stream()
+ .anyMatch(method -> isBindsInstance(method));
+ }
+
+ private static boolean isBindsInstance(ExecutableElement method) {
+ if (isAnnotationPresent(method, BindsInstance.class)) {
+ return true;
+ }
+
+ if (method.getParameters().size() == 1) {
+ return isAnnotationPresent(method.getParameters().get(0), BindsInstance.class);
+ }
+
+ return false;
+ }
+
+ private MethodSpec builderSetterMethod(
+ TypeElement componentRequirement, ClassName builderClass) {
+ String simpleName =
+ UPPER_CAMEL.to(LOWER_CAMEL, componentRequirement.getSimpleName().toString());
+ return MethodSpec.methodBuilder(simpleName)
+ .addModifiers(PUBLIC)
+ .addParameter(ClassName.get(componentRequirement), simpleName)
+ .returns(builderClass)
+ .build();
+ }
+
+ private MethodSpec builderBuildMethod(ComponentDescriptor component) {
+ return MethodSpec.methodBuilder("build")
+ .addModifiers(PUBLIC)
+ .returns(ClassName.get(component.typeElement()))
+ .build();
+ }
+
+ private MethodSpec staticCreatorMethod(
+ TypeName creatorMethodReturnType, ComponentCreatorKind creatorKind) {
+ return MethodSpec.methodBuilder(Ascii.toLowerCase(creatorKind.typeName()))
+ .addModifiers(PUBLIC, STATIC)
+ .returns(creatorMethodReturnType)
+ .build();
+ }
+
+ private MethodSpec createMethod(ComponentDescriptor componentDescriptor) {
+ return MethodSpec.methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(ClassName.get(componentDescriptor.typeElement()))
+ .build();
+ }
+
+ private MethodSpec onProducerFutureCancelledMethod() {
+ return MethodSpec.methodBuilder("onProducerFutureCancelled")
+ .addModifiers(PUBLIC)
+ .addParameter(TypeName.BOOLEAN, "mayInterruptIfRunning")
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentImplementation.java b/java/dagger/internal/codegen/ComponentImplementation.java
new file mode 100644
index 0000000..340da14
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentImplementation.java
@@ -0,0 +1,929 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.serialization.ProtoSerialization.toAnnotationValue;
+import static java.util.stream.Collectors.toList;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.ConfigureInitializationParameters;
+import dagger.internal.ModifiableBinding;
+import dagger.internal.ModifiableModule;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.TypeSpecs;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** The implementation of a component type. */
+final class ComponentImplementation {
+ /** A type of field that this component can contain. */
+ enum FieldSpecKind {
+
+ /** A field required by the component, e.g. module instances. */
+ COMPONENT_REQUIREMENT_FIELD,
+
+ /**
+ * A field for the lock and cached value for {@linkplain PrivateMethodBindingExpression
+ * private-method scoped bindings}.
+ */
+ PRIVATE_METHOD_SCOPED_FIELD,
+
+ /** A framework field for type T, e.g. {@code Provider<T>}. */
+ FRAMEWORK_FIELD,
+
+ /** A static field that always returns an absent {@code Optional} value for the binding. */
+ ABSENT_OPTIONAL_FIELD
+ }
+
+ /** A type of method that this component can contain. */
+ // TODO(user, dpb): Change the oder to constructor, initialize, component, then private
+ // (including MIM and AOM—why treat those separately?).
+ enum MethodSpecKind {
+ /** The component constructor. */
+ CONSTRUCTOR,
+
+ /**
+ * In ahead-of-time subcomponents, this method coordinates the invocation of {@link
+ * #INITIALIZE_METHOD initialization methods} instead of constructors.
+ */
+ // TODO(b/117833324): try to merge this with other initialize() methods so it looks more natural
+ CONFIGURE_INITIALIZATION_METHOD,
+
+ /** A builder method for the component. (Only used by the root component.) */
+ BUILDER_METHOD,
+
+ /** A private method that wraps dependency expressions. */
+ PRIVATE_METHOD,
+
+ /** An initialization method that initializes component requirements and framework types. */
+ INITIALIZE_METHOD,
+
+ /** An implementation of a component interface method. */
+ COMPONENT_METHOD,
+
+ /** A private method that encapsulates members injection logic for a binding. */
+ MEMBERS_INJECTION_METHOD,
+
+ /** A static method that always returns an absent {@code Optional} value for the binding. */
+ ABSENT_OPTIONAL_METHOD,
+
+ /**
+ * A method that encapsulates a modifiable binding. A binding is modifiable if it can change
+ * across implementations of a subcomponent. This is only relevant for ahead-of-time
+ * subcomponents.
+ */
+ MODIFIABLE_BINDING_METHOD,
+
+ /**
+ * The {@link dagger.producers.internal.CancellationListener#onProducerFutureCancelled(boolean)}
+ * method for a production component.
+ */
+ CANCELLATION_LISTENER_METHOD,
+ ;
+ }
+
+ /** A type of nested class that this component can contain. */
+ enum TypeSpecKind {
+ /** A factory class for a present optional binding. */
+ PRESENT_FACTORY,
+
+ /** A class for the component creator (only used by the root component.) */
+ COMPONENT_CREATOR,
+
+ /** A provider class for a component provision. */
+ COMPONENT_PROVISION_FACTORY,
+
+ /** A class for the subcomponent or subcomponent builder. */
+ SUBCOMPONENT
+ }
+
+ /**
+ * The method spec for a {@code configureInitialization} method plus details on the component
+ * requirements that its parameters are associated with.
+ */
+ @AutoValue
+ abstract static class ConfigureInitializationMethod {
+ /** Creates a new {@link ConfigureInitializationMethod}. */
+ static ConfigureInitializationMethod create(
+ MethodSpec spec, ImmutableSet<ComponentRequirement> parameters) {
+ return new AutoValue_ComponentImplementation_ConfigureInitializationMethod(spec, parameters);
+ }
+
+ /** The spec for the method. */
+ abstract MethodSpec spec();
+
+ /**
+ * The component requirements associated with the method's parameters, in the same order as the
+ * parameters.
+ */
+ abstract ImmutableSet<ComponentRequirement> parameters();
+ }
+
+ private final CompilerOptions compilerOptions;
+ private final ComponentDescriptor componentDescriptor;
+ private final Optional<BindingGraph> graph;
+ private final ClassName name;
+ private final NestingKind nestingKind;
+ private final boolean isAbstract;
+ private final Optional<ComponentImplementation> superclassImplementation;
+ private Optional<ComponentCreatorImplementation> creatorImplementation;
+ private final Map<TypeElement, ComponentImplementation> childImplementations = new HashMap<>();
+ private final TypeSpec.Builder component;
+ private final Optional<SubcomponentNames> subcomponentNames;
+ private final UniqueNameSet componentFieldNames = new UniqueNameSet();
+ private final UniqueNameSet componentMethodNames = new UniqueNameSet();
+ private final List<CodeBlock> initializations = new ArrayList<>();
+ private final Set<ComponentRequirement> componentRequirementParameters = new HashSet<>();
+ private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
+ private final Map<ComponentRequirement, String> componentRequirementParameterNames =
+ new HashMap<>();
+ private final Set<Key> cancellableProducerKeys = new LinkedHashSet<>();
+ private final ListMultimap<FieldSpecKind, FieldSpec> fieldSpecsMap =
+ MultimapBuilder.enumKeys(FieldSpecKind.class).arrayListValues().build();
+ private final ListMultimap<MethodSpecKind, MethodSpec> methodSpecsMap =
+ MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build();
+ private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
+ MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
+ private final List<Supplier<TypeSpec>> switchingProviderSupplier = new ArrayList<>();
+ private final ModifiableBindingMethods modifiableBindingMethods = new ModifiableBindingMethods();
+ private final SetMultimap<BindingRequest, Key> multibindingContributionsMade =
+ LinkedHashMultimap.create();
+ private Optional<ConfigureInitializationMethod> configureInitializationMethod = Optional.empty();
+ private final Map<ComponentRequirement, String> modifiableModuleMethods = new LinkedHashMap<>();
+
+ private ComponentImplementation(
+ ComponentDescriptor componentDescriptor,
+ Optional<BindingGraph> graph,
+ ClassName name,
+ NestingKind nestingKind,
+ Optional<ComponentImplementation> superclassImplementation,
+ Optional<SubcomponentNames> subcomponentNames,
+ CompilerOptions compilerOptions,
+ ImmutableSet<Modifier> modifiers) {
+ checkName(name, nestingKind);
+ this.compilerOptions = compilerOptions;
+ this.componentDescriptor = componentDescriptor;
+ this.graph = graph;
+ this.name = name;
+ this.nestingKind = nestingKind;
+ this.isAbstract = modifiers.contains(ABSTRACT);
+ this.superclassImplementation = superclassImplementation;
+ this.component = classBuilder(name);
+ modifiers.forEach(component::addModifiers);
+ this.subcomponentNames = subcomponentNames;
+ }
+
+ /** Returns a component implementation for a top-level component. */
+ static ComponentImplementation topLevelComponentImplementation(
+ BindingGraph graph,
+ ClassName name,
+ SubcomponentNames subcomponentNames,
+ CompilerOptions compilerOptions) {
+ return new ComponentImplementation(
+ graph.componentDescriptor(),
+ Optional.of(graph),
+ name,
+ NestingKind.TOP_LEVEL,
+ Optional.empty(), // superclass implementation
+ Optional.of(subcomponentNames),
+ compilerOptions,
+ topLevelComponentImplementationModifiers(graph));
+ }
+
+ private static ImmutableSet<Modifier> topLevelComponentImplementationModifiers(
+ BindingGraph graph) {
+ ImmutableSet.Builder<Modifier> modifiers = ImmutableSet.builder();
+ if (graph.componentTypeElement().getModifiers().contains(PUBLIC)
+ || graph.componentDescriptor().isSubcomponent()) {
+ // TODO(ronshapiro): perhaps all generated components should be non-public?
+ modifiers.add(PUBLIC);
+ }
+ return modifiers.add(graph.componentDescriptor().isSubcomponent() ? ABSTRACT : FINAL).build();
+ }
+
+ /** Returns a component implementation that is a child of the current implementation. */
+ ComponentImplementation childComponentImplementation(
+ BindingGraph graph,
+ Optional<ComponentImplementation> superclassImplementation,
+ Modifier... modifiers) {
+ return new ComponentImplementation(
+ graph.componentDescriptor(),
+ Optional.of(graph),
+ getSubcomponentName(graph.componentDescriptor()),
+ NestingKind.MEMBER,
+ superclassImplementation,
+ subcomponentNames,
+ compilerOptions,
+ ImmutableSet.copyOf(modifiers));
+ }
+
+ /**
+ * Returns a component implementation that models a previously compiled class. This {@link
+ * ComponentImplementation} is not used for code generation itself; it is used to determine what
+ * methods need to be implemented in a subclass implementation.
+ */
+ static ComponentImplementation forDeserializedComponent(
+ ComponentDescriptor componentDescriptor,
+ ClassName name,
+ NestingKind nestingKind,
+ Optional<ComponentImplementation> superclassImplementation,
+ CompilerOptions compilerOptions) {
+ return new ComponentImplementation(
+ componentDescriptor,
+ Optional.empty(),
+ name,
+ nestingKind,
+ superclassImplementation,
+ Optional.empty(),
+ compilerOptions,
+ ImmutableSet.of(PUBLIC, ABSTRACT));
+ }
+
+ // TODO(dpb): Just determine the nesting kind from the name.
+ private static void checkName(ClassName name, NestingKind nestingKind) {
+ switch (nestingKind) {
+ case TOP_LEVEL:
+ checkArgument(
+ name.enclosingClassName() == null, "must be a top-level class name: %s", name);
+ break;
+
+ case MEMBER:
+ checkNotNull(name.enclosingClassName(), "must not be a top-level class name: %s", name);
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "nestingKind must be TOP_LEVEL or MEMBER: " + nestingKind);
+ }
+ }
+
+ /**
+ * Returns {@code true} if this component implementation represents a component that has already
+ * been compiled. If this returns true, the implementation will have no {@link #graph
+ * BindingGraph}.
+ */
+ boolean isDeserializedImplementation() {
+ return !graph.isPresent();
+ }
+
+ // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that
+ // need it.
+ /** Returns the binding graph for the component being generated. */
+ BindingGraph graph() {
+ checkState(!isDeserializedImplementation(),
+ "A BindingGraph is not available for deserialized component implementations.");
+ return graph.get();
+ }
+
+ /** Returns the descriptor for the component being generated. */
+ ComponentDescriptor componentDescriptor() {
+ return componentDescriptor;
+ }
+
+ /** Returns the name of the component. */
+ ClassName name() {
+ return name;
+ }
+
+ /** Returns whether or not the implementation is nested within another class. */
+ boolean isNested() {
+ return nestingKind.isNested();
+ }
+
+ /** Returns whether or not the implementation is abstract. */
+ boolean isAbstract() {
+ return isAbstract;
+ }
+
+ /** Returns the superclass implementation. */
+ Optional<ComponentImplementation> superclassImplementation() {
+ return superclassImplementation;
+ }
+
+ /**
+ * Returns the base implementation of this component in ahead-of-time subcomponents mode. If this
+ * is the base implementation, this returns {@link Optional#empty()}.
+ */
+ Optional<ComponentImplementation> baseImplementation() {
+ return superclassImplementation.isPresent()
+ ? Optional.of(Optionals.rootmostValue(this, c -> c.superclassImplementation))
+ : Optional.empty();
+ }
+
+ /**
+ * Returns the {@link #configureInitializationMethod()} of the nearest supertype that defines one,
+ * if any.
+ *
+ * <p>Only returns a present value in {@link CompilerOptions#aheadOfTimeSubcomponents()}.
+ */
+ Optional<ConfigureInitializationMethod> superConfigureInitializationMethod() {
+ for (Optional<ComponentImplementation> currentSuper = superclassImplementation;
+ currentSuper.isPresent();
+ currentSuper = currentSuper.get().superclassImplementation) {
+ if (currentSuper.get().configureInitializationMethod.isPresent()) {
+ return currentSuper.get().configureInitializationMethod;
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * The requirements for creating an instance of this component implementation type.
+ *
+ * <p>If this component implementation is concrete, these requirements will be in the order that
+ * the implementation's constructor takes them as parameters.
+ */
+ ImmutableSet<ComponentRequirement> requirements() {
+ // If the base implementation's creator is being generated in ahead-of-time-subcomponents
+ // mode, this uses the ComponentDescriptor's requirements() since Dagger doesn't know what
+ // modules may end being unused or owned by an ancestor component. Otherwise, we use the
+ // necessary component requirements.
+ // TODO(ronshapiro): can we remove the second condition here? Or, is it never going to be
+ // called, so we should enforce that invariant?
+ return isAbstract() && !superclassImplementation().isPresent()
+ ? componentDescriptor().requirements()
+ : graph().componentRequirements();
+ }
+
+ /**
+ * Returns the {@link MethodSpecKind#CONFIGURE_INITIALIZATION_METHOD} of this implementation if
+ * there is one.
+ *
+ * <p>Only returns a present value in {@link CompilerOptions#aheadOfTimeSubcomponents()}.
+ */
+ Optional<ConfigureInitializationMethod> configureInitializationMethod() {
+ return configureInitializationMethod;
+ }
+
+ /**
+ * Set's this component implementation's {@code configureInitialization()} method and {@linkplain
+ * #addMethod(MethodSpecKind, MethodSpec) adds the method}.
+ */
+ void setConfigureInitializationMethod(ConfigureInitializationMethod method) {
+ configureInitializationMethod = Optional.of(method);
+ addMethod(
+ MethodSpecKind.CONFIGURE_INITIALIZATION_METHOD,
+ addConfigureInitializationMetadata(method));
+ }
+
+ private MethodSpec addConfigureInitializationMetadata(ConfigureInitializationMethod method) {
+ if (!shouldEmitModifiableMetadataAnnotations()) {
+ return method.spec();
+ }
+ AnnotationSpec.Builder annotation =
+ AnnotationSpec.builder(ConfigureInitializationParameters.class);
+ for (ComponentRequirement parameter : method.parameters()) {
+ annotation.addMember("value", toAnnotationValue(parameter.toProto()));
+ }
+
+ return method.spec().toBuilder().addAnnotation(annotation.build()).build();
+ }
+
+ void setCreatorImplementation(Optional<ComponentCreatorImplementation> creatorImplementation) {
+ checkState(
+ this.creatorImplementation == null, "setCreatorImplementation has already been called");
+ this.creatorImplementation = creatorImplementation;
+ }
+
+ Optional<ComponentCreatorImplementation> creatorImplementation() {
+ checkState(creatorImplementation != null, "setCreatorImplementation has not been called yet");
+ return creatorImplementation;
+ }
+
+ /**
+ * Returns the {@link ComponentCreatorImplementation} defined in the base implementation for this
+ * component, if one exists.
+ */
+ Optional<ComponentCreatorImplementation> baseCreatorImplementation() {
+ return baseImplementation().flatMap(baseImpl -> baseImpl.creatorImplementation());
+ }
+
+ /**
+ * Returns the kind of this component's creator.
+ *
+ * @throws IllegalStateException if the component has no creator
+ */
+ private ComponentCreatorKind creatorKind() {
+ checkState(componentDescriptor().hasCreator());
+ return componentDescriptor()
+ .creatorDescriptor()
+ .map(ComponentCreatorDescriptor::kind)
+ .orElse(BUILDER);
+ }
+
+ /**
+ * Returns the name of the creator class for this component. It will be a sibling of this
+ * generated class unless this is a top-level component, in which case it will be nested.
+ */
+ ClassName getCreatorName() {
+ return isNested()
+ ? name.peerClass(subcomponentNames().getCreatorName(componentDescriptor()))
+ : name.nestedClass(creatorKind().typeName());
+ }
+
+ /** Returns the name of the nested implementation class for a child component. */
+ ClassName getSubcomponentName(ComponentDescriptor childDescriptor) {
+ checkArgument(
+ componentDescriptor().childComponents().contains(childDescriptor),
+ "%s is not a child component of %s",
+ childDescriptor.typeElement(),
+ componentDescriptor().typeElement());
+ return name.nestedClass(subcomponentNames().get(childDescriptor) + "Impl");
+ }
+
+ /**
+ * Returns the simple name of the creator implementation class for the given subcomponent creator
+ * {@link Key}.
+ */
+ String getSubcomponentCreatorSimpleName(Key key) {
+ return subcomponentNames().getCreatorName(key);
+ }
+
+ private SubcomponentNames subcomponentNames() {
+ checkState(
+ subcomponentNames.isPresent(),
+ "SubcomponentNames is not available for deserialized component implementations.");
+ return subcomponentNames.get();
+ }
+
+ /** Returns the child implementation. */
+ Optional<ComponentImplementation> childImplementation(ComponentDescriptor child) {
+ return Optional.ofNullable(childImplementations.get(child.typeElement()));
+ }
+
+ /** Returns {@code true} if {@code type} is accessible from the generated component. */
+ boolean isTypeAccessible(TypeMirror type) {
+ return isTypeAccessibleFrom(type, name.packageName());
+ }
+
+ /** Adds the given super type to the component. */
+ void addSupertype(TypeElement supertype) {
+ TypeSpecs.addSupertype(component, supertype);
+ }
+
+ /** Adds the given super class to the subcomponent. */
+ void addSuperclass(ClassName className) {
+ checkState(
+ superclassImplementation.isPresent(),
+ "Setting the superclass for component [%s] when there is no superclass implementation.",
+ name);
+ component.superclass(className);
+ }
+
+ // TODO(dpb): Consider taking FieldSpec, and returning identical FieldSpec with unique name?
+ /** Adds the given field to the component. */
+ void addField(FieldSpecKind fieldKind, FieldSpec fieldSpec) {
+ fieldSpecsMap.put(fieldKind, fieldSpec);
+ }
+
+ /** Adds the given fields to the component. */
+ void addFields(FieldSpecKind fieldKind, Iterable<FieldSpec> fieldSpecs) {
+ fieldSpecsMap.putAll(fieldKind, fieldSpecs);
+ }
+
+ // TODO(dpb): Consider taking MethodSpec, and returning identical MethodSpec with unique name?
+ /** Adds the given method to the component. */
+ void addMethod(MethodSpecKind methodKind, MethodSpec methodSpec) {
+ methodSpecsMap.put(methodKind, methodSpec);
+ }
+
+ /** Adds the given annotation to the component. */
+ void addAnnotation(AnnotationSpec annotation) {
+ component.addAnnotation(annotation);
+ }
+
+ /**
+ * Adds the given method to the component. In this case, the method represents an encapsulation of
+ * a modifiable binding between implementations of a subcomponent. This is only relevant for
+ * ahead-of-time subcomponents.
+ */
+ void addModifiableBindingMethod(
+ ModifiableBindingType type,
+ BindingRequest request,
+ TypeMirror returnType,
+ MethodSpec methodSpec,
+ boolean finalized) {
+ addModifiableMethod(
+ MethodSpecKind.MODIFIABLE_BINDING_METHOD, type, request, returnType, methodSpec, finalized);
+ }
+
+ /**
+ * Adds a component method that is modifiable to the component. In this case, the method
+ * represents an encapsulation of a modifiable binding between implementations of a subcomponent.
+ * This is only relevant for ahead-of-time subcomponents.
+ */
+ void addModifiableComponentMethod(
+ ModifiableBindingType type,
+ BindingRequest request,
+ TypeMirror returnType,
+ MethodSpec methodSpec,
+ boolean finalized) {
+ addModifiableMethod(
+ MethodSpecKind.COMPONENT_METHOD, type, request, returnType, methodSpec, finalized);
+ }
+
+ private void addModifiableMethod(
+ MethodSpecKind methodKind,
+ ModifiableBindingType type,
+ BindingRequest request,
+ TypeMirror returnType,
+ MethodSpec methodSpec,
+ boolean finalized) {
+ modifiableBindingMethods.addModifiableMethod(
+ type, request, returnType, methodSpec, finalized);
+ methodSpecsMap.put(methodKind, withModifiableBindingMetadata(methodSpec, type, request));
+ }
+
+ /** Adds the implementation for the given {@link ModifiableBindingMethod} to the component. */
+ void addImplementedModifiableBindingMethod(ModifiableBindingMethod method) {
+ modifiableBindingMethods.addReimplementedMethod(method);
+ methodSpecsMap.put(
+ MethodSpecKind.MODIFIABLE_BINDING_METHOD,
+ withModifiableBindingMetadata(method.methodSpec(), method.type(), method.request()));
+ }
+
+ private MethodSpec withModifiableBindingMetadata(
+ MethodSpec method, ModifiableBindingType type, BindingRequest request) {
+ if (!shouldEmitModifiableMetadataAnnotations()) {
+ return method;
+ }
+ AnnotationSpec.Builder metadata =
+ AnnotationSpec.builder(ModifiableBinding.class)
+ .addMember("modifiableBindingType", "$S", type.name())
+ .addMember("bindingRequest", toAnnotationValue(request.toProto()));
+ for (Key multibindingContribution : multibindingContributionsMade.get(request)) {
+ metadata.addMember(
+ "multibindingContributions",
+ toAnnotationValue(KeyFactory.toProto(multibindingContribution)));
+ }
+ return method.toBuilder().addAnnotation(metadata.build()).build();
+ }
+
+ /** Add's a modifiable module method to this implementation. */
+ void addModifiableModuleMethod(ComponentRequirement module, MethodSpec method) {
+ registerModifiableModuleMethod(module, method.name);
+ methodSpecsMap.put(
+ MethodSpecKind.MODIFIABLE_BINDING_METHOD, withModifiableModuleMetadata(module, method));
+ }
+
+ /** Registers a modifiable module method with {@code name} for {@code module}. */
+ void registerModifiableModuleMethod(ComponentRequirement module, String name) {
+ checkArgument(module.kind().isModule());
+ checkState(modifiableModuleMethods.put(module, name) == null);
+ }
+
+ private MethodSpec withModifiableModuleMetadata(ComponentRequirement module, MethodSpec method) {
+ if (!shouldEmitModifiableMetadataAnnotations()) {
+ return method;
+ }
+ return method
+ .toBuilder()
+ .addAnnotation(
+ AnnotationSpec.builder(ModifiableModule.class)
+ .addMember("value", toAnnotationValue(module.toProto()))
+ .build())
+ .build();
+ }
+
+ /**
+ * Returns {@code true} if the generated component should include metadata annotations with
+ * information to deserialize this {@link ComponentImplementation} in future compilations.
+ */
+ boolean shouldEmitModifiableMetadataAnnotations() {
+ return isAbstract && compilerOptions.emitModifiableMetadataAnnotations();
+ }
+
+ /** Adds the given type to the component. */
+ void addType(TypeSpecKind typeKind, TypeSpec typeSpec) {
+ typeSpecsMap.put(typeKind, typeSpec);
+ }
+
+ /** Adds the type generated from the given child implementation. */
+ void addChild(ComponentDescriptor child, ComponentImplementation childImplementation) {
+ childImplementations.put(child.typeElement(), childImplementation);
+ addType(TypeSpecKind.SUBCOMPONENT, childImplementation.generate().build());
+ }
+
+ /** Adds a {@link Supplier} for the SwitchingProvider for the component. */
+ void addSwitchingProvider(Supplier<TypeSpec> typeSpecSupplier) {
+ switchingProviderSupplier.add(typeSpecSupplier);
+ }
+
+ /** Adds the given code block to the initialize methods of the component. */
+ void addInitialization(CodeBlock codeBlock) {
+ initializations.add(codeBlock);
+ }
+
+ /**
+ * Adds the given component requirement as one that should have a parameter in the component's
+ * initialization methods.
+ */
+ void addComponentRequirementParameter(ComponentRequirement requirement) {
+ componentRequirementParameters.add(requirement);
+ }
+
+ /**
+ * The set of component requirements that have parameters in the component's initialization
+ * methods.
+ */
+ ImmutableSet<ComponentRequirement> getComponentRequirementParameters() {
+ return ImmutableSet.copyOf(componentRequirementParameters);
+ }
+
+ /** Adds the given code block that initializes a {@link ComponentRequirement}. */
+ void addComponentRequirementInitialization(CodeBlock codeBlock) {
+ componentRequirementInitializations.add(codeBlock);
+ }
+
+ /**
+ * Marks the given key of a producer as one that should have a cancellation statement in the
+ * cancellation listener method of the component.
+ */
+ void addCancellableProducerKey(Key key) {
+ cancellableProducerKeys.add(key);
+ }
+
+ /** Returns a new, unique field name for the component based on the given name. */
+ String getUniqueFieldName(String name) {
+ return componentFieldNames.getUniqueName(name);
+ }
+
+ /** Returns a new, unique method name for the component based on the given name. */
+ String getUniqueMethodName(String name) {
+ return componentMethodNames.getUniqueName(name);
+ }
+
+ /** Returns a new, unique method name for a getter method for the given request. */
+ String getUniqueMethodName(BindingRequest request) {
+ return uniqueMethodName(request, KeyVariableNamer.name(request.key()));
+ }
+
+ private String uniqueMethodName(BindingRequest request, String bindingName) {
+ String baseMethodName =
+ "get"
+ + LOWER_CAMEL.to(UPPER_CAMEL, bindingName)
+ + (request.isRequestKind(RequestKind.INSTANCE)
+ ? ""
+ : UPPER_UNDERSCORE.to(UPPER_CAMEL, request.kindName()));
+ return getUniqueMethodName(baseMethodName);
+ }
+
+ /** Gets the parameter name to use for the given requirement for this component. */
+ String getParameterName(ComponentRequirement requirement) {
+ return getParameterName(requirement, requirement.variableName());
+ }
+
+ /**
+ * Gets the parameter name to use for the given requirement for this component, starting with the
+ * given base name if no parameter name has already been selected for the requirement.
+ */
+ String getParameterName(ComponentRequirement requirement, String baseName) {
+ return componentRequirementParameterNames.computeIfAbsent(
+ requirement, r -> getUniqueFieldName(baseName));
+ }
+
+ /** Claims a new method name for the component. Does nothing if method name already exists. */
+ void claimMethodName(CharSequence name) {
+ componentMethodNames.claim(name);
+ }
+
+ /** Returns the list of {@link CodeBlock}s that need to go in the initialize method. */
+ ImmutableList<CodeBlock> getInitializations() {
+ return ImmutableList.copyOf(initializations);
+ }
+
+ /**
+ * Returns a list of {@link CodeBlock}s for initializing {@link ComponentRequirement}s.
+ *
+ * <p>These initializations are kept separate from {@link #getInitializations()} because they must
+ * be executed before the initializations of any framework instance initializations in a
+ * superclass implementation that may depend on the instances. We cannot use the same strategy
+ * that we use for framework instances (i.e. wrap in a {@link dagger.internal.DelegateFactory} or
+ * {@link dagger.producers.internal.DelegateProducer} since the types of these initialized fields
+ * have no interface type that we can write a proxy for.
+ */
+ ImmutableList<CodeBlock> getComponentRequirementInitializations() {
+ return ImmutableList.copyOf(componentRequirementInitializations);
+ }
+
+ /**
+ * Returns whether or not this component has any {@linkplain #getInitializations() initilizations}
+ * or {@linkplain #getComponentRequirementInitializations() component requirement
+ * initializations}.
+ */
+ boolean hasInitializations() {
+ return !initializations.isEmpty() || !componentRequirementInitializations.isEmpty();
+ }
+
+ /**
+ * Returns the list of producer {@link Key}s that need cancellation statements in the cancellation
+ * listener method.
+ */
+ ImmutableList<Key> getCancellableProducerKeys() {
+ Optional<ComponentImplementation> currentSuperImplementation = superclassImplementation;
+ Set<Key> cancelledKeysFromSuperclass = new HashSet<>();
+ while (currentSuperImplementation.isPresent()) {
+ cancelledKeysFromSuperclass.addAll(currentSuperImplementation.get().cancellableProducerKeys);
+ currentSuperImplementation = currentSuperImplementation.get().superclassImplementation;
+ }
+ return Sets.difference(cancellableProducerKeys, cancelledKeysFromSuperclass)
+ .immutableCopy()
+ .asList();
+ }
+
+ /**
+ * Returns the {@link ModifiableBindingMethod}s for this subcomponent implementation and its
+ * superclasses.
+ */
+ ImmutableMap<BindingRequest, ModifiableBindingMethod> getModifiableBindingMethods() {
+ Map<BindingRequest, ModifiableBindingMethod> modifiableBindingMethodsBuilder =
+ new LinkedHashMap<>();
+ if (superclassImplementation.isPresent()) {
+ modifiableBindingMethodsBuilder.putAll(
+ Maps.filterValues(
+ superclassImplementation.get().getModifiableBindingMethods(),
+ // filters the modifiable methods of a superclass that are finalized in this component
+ method -> !modifiableBindingMethods.finalized(method)));
+ }
+ // replace superclass modifiable binding methods with any that are defined in this component
+ // implementation
+ modifiableBindingMethodsBuilder.putAll(modifiableBindingMethods.getNonFinalizedMethods());
+ return ImmutableMap.copyOf(modifiableBindingMethodsBuilder);
+ }
+
+ /**
+ * Returns the names of every modifiable method of this implementation and any superclass
+ * implementations.
+ */
+ ImmutableSet<String> getAllModifiableMethodNames() {
+ ImmutableSet.Builder<String> names = ImmutableSet.builder();
+ modifiableBindingMethods.allMethods().forEach(method -> names.add(method.methodSpec().name));
+ names.addAll(modifiableModuleMethods.values());
+ superclassImplementation.ifPresent(
+ superclass -> names.addAll(superclass.getAllModifiableMethodNames()));
+ return names.build();
+ }
+
+ /**
+ * Returns the {@link ModifiableBindingMethod} for this subcomponent for the given binding, if it
+ * exists.
+ */
+ Optional<ModifiableBindingMethod> getModifiableBindingMethod(BindingRequest request) {
+ Optional<ModifiableBindingMethod> method = modifiableBindingMethods.getMethod(request);
+ if (!method.isPresent() && superclassImplementation.isPresent()) {
+ return superclassImplementation.get().getModifiableBindingMethod(request);
+ }
+ return method;
+ }
+
+ /**
+ * Returns the {@link ModifiableBindingMethod} of a supertype for this method's {@code request},
+ * if one exists.
+ */
+ Optional<ModifiableBindingMethod> supertypeModifiableBindingMethod(BindingRequest request) {
+ return superclassImplementation()
+ .flatMap(superImplementation -> superImplementation.getModifiableBindingMethod(request));
+ }
+
+ /**
+ * Returns the names of modifiable module methods for this implementation and all inherited
+ * implementations, keyed by the corresponding module's {@link ComponentRequirement}.
+ */
+ ImmutableMap<ComponentRequirement, String> getAllModifiableModuleMethods() {
+ ImmutableMap.Builder<ComponentRequirement, String> methods = ImmutableMap.builder();
+ methods.putAll(modifiableModuleMethods);
+ superclassImplementation.ifPresent(
+ superclass -> methods.putAll(superclass.getAllModifiableModuleMethods()));
+ return methods.build();
+ }
+
+ /**
+ * Returns the name of the modifiable module method for {@code module} that is inherited in this
+ * implementation, or empty if none has been defined.
+ */
+ Optional<String> supertypeModifiableModuleMethodName(ComponentRequirement module) {
+ checkArgument(module.kind().isModule());
+ if (!superclassImplementation.isPresent()) {
+ return Optional.empty();
+ }
+ String methodName = superclassImplementation.get().modifiableModuleMethods.get(module);
+ if (methodName == null) {
+ return superclassImplementation.get().supertypeModifiableModuleMethodName(module);
+ }
+ return Optional.of(methodName);
+ }
+
+ /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
+ TypeSpec.Builder generate() {
+ fieldSpecsMap.asMap().values().forEach(component::addFields);
+ methodSpecsMap.asMap().values().forEach(component::addMethods);
+ typeSpecsMap.asMap().values().forEach(component::addTypes);
+ switchingProviderSupplier.stream().map(Supplier::get).forEach(component::addType);
+ return component;
+ }
+
+ /**
+ * Registers a {@ProvisionBinding} representing a multibinding as having been implemented in this
+ * component. Multibindings are modifiable across subcomponent implementations and this allows us
+ * to know whether a contribution has been made by a superclass implementation. This is only
+ * relevant for ahead-of-time subcomponents.
+ */
+ void registerImplementedMultibinding(
+ ContributionBinding multibinding, BindingRequest bindingRequest) {
+ checkArgument(multibinding.isSyntheticMultibinding());
+ // We register a multibinding as implemented each time we request the multibinding expression,
+ // so only modify the set of contributions once.
+ if (!multibindingContributionsMade.containsKey(bindingRequest)) {
+ registerImplementedMultibindingKeys(
+ bindingRequest,
+ multibinding.dependencies().stream().map(DependencyRequest::key).collect(toList()));
+ }
+ }
+
+ /**
+ * Registers the multibinding contributions represented by {@code keys} as having been implemented
+ * in this component. Multibindings are modifiable across subcomponent implementations and this
+ * allows us to know whether a contribution has been made by a superclass implementation. This is
+ * only relevant for ahead-of-time subcomponents.
+ */
+ void registerImplementedMultibindingKeys(BindingRequest bindingRequest, Iterable<Key> keys) {
+ multibindingContributionsMade.putAll(bindingRequest, keys);
+ }
+
+ /**
+ * Returns the set of multibinding contributions associated with all superclass implementations of
+ * a multibinding.
+ */
+ ImmutableSet<Key> superclassContributionsMade(BindingRequest bindingRequest) {
+ return superclassImplementation
+ .map(s -> s.getAllMultibindingContributions(bindingRequest))
+ .orElse(ImmutableSet.of());
+ }
+
+ /**
+ * Returns the set of multibinding contributions associated with all implementations of a
+ * multibinding.
+ */
+ private ImmutableSet<Key> getAllMultibindingContributions(BindingRequest bindingRequest) {
+ return ImmutableSet.copyOf(
+ Sets.union(
+ multibindingContributionsMade.get(bindingRequest),
+ superclassContributionsMade(bindingRequest)));
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentImplementationBuilder.java b/java/dagger/internal/codegen/ComponentImplementationBuilder.java
new file mode 100644
index 0000000..7579ac9
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentImplementationBuilder.java
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Predicates.in;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.BUILDER_METHOD;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CANCELLATION_LISTENER_METHOD;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CONSTRUCTOR;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.INITIALIZE_METHOD;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MODIFIABLE_BINDING_METHOD;
+import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_CREATOR;
+import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.SUBCOMPONENT;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PROTECTED;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreTypes;
+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.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.ComponentDefinitionType;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.producers.internal.CancellationListener;
+import dagger.producers.internal.Producers;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+
+/** A builder of {@link ComponentImplementation}s. */
+abstract class ComponentImplementationBuilder {
+ private static final String MAY_INTERRUPT_IF_RUNNING = "mayInterruptIfRunning";
+
+ /**
+ * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method
+ * before they get partitioned.
+ */
+ private static final int STATEMENTS_PER_METHOD = 100;
+
+ private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled";
+
+ // TODO(ronshapiro): replace this with composition instead of inheritance so we don't have
+ // non-final fields
+ @Inject BindingGraph graph;
+ @Inject ComponentBindingExpressions bindingExpressions;
+ @Inject ComponentRequirementExpressions componentRequirementExpressions;
+ @Inject ComponentImplementation componentImplementation;
+ @Inject ComponentCreatorImplementationFactory componentCreatorImplementationFactory;
+ @Inject DaggerTypes types;
+ @Inject DaggerElements elements;
+ @Inject CompilerOptions compilerOptions;
+ @Inject ComponentImplementationFactory componentImplementationFactory;
+ @Inject TopLevelImplementationComponent topLevelImplementationComponent;
+ private boolean done;
+
+ /**
+ * Returns a {@link ComponentImplementation} for this component. This is only intended to be
+ * called once (and will throw on successive invocations). If the component must be regenerated,
+ * use a new instance.
+ */
+ final ComponentImplementation build() {
+ checkState(
+ !done,
+ "ComponentImplementationBuilder has already built the ComponentImplementation for [%s].",
+ componentImplementation.name());
+ setSupertype();
+ componentImplementation.setCreatorImplementation(
+ componentCreatorImplementationFactory.create(
+ componentImplementation, Optional.of(componentImplementation.graph())));
+ componentImplementation
+ .creatorImplementation()
+ .map(ComponentCreatorImplementation::spec)
+ .ifPresent(this::addCreatorClass);
+
+ getLocalAndInheritedMethods(graph.componentTypeElement(), types, elements)
+ .forEach(method -> componentImplementation.claimMethodName(method.getSimpleName()));
+ componentImplementation
+ .superclassImplementation()
+ .ifPresent(
+ superclassImplementation -> {
+ superclassImplementation
+ .getAllModifiableMethodNames()
+ .forEach(componentImplementation::claimMethodName);
+ });
+
+ addFactoryMethods();
+ addInterfaceMethods();
+ addChildComponents();
+ implementModifiableModuleMethods();
+
+ addConstructorAndInitializationMethods();
+
+ if (graph.componentDescriptor().isProduction()) {
+ addCancellationListenerImplementation();
+ }
+
+ if (componentImplementation.isAbstract()
+ && !componentImplementation.baseImplementation().isPresent()) {
+ componentImplementation.addAnnotation(compilerOptions.toGenerationOptionsAnnotation());
+ }
+
+ if (componentImplementation.shouldEmitModifiableMetadataAnnotations()) {
+ componentImplementation.addAnnotation(
+ AnnotationSpec.builder(ComponentDefinitionType.class)
+ .addMember("value", "$T.class", graph.componentTypeElement())
+ .build());
+ }
+
+ done = true;
+ return componentImplementation;
+ }
+
+ /** Set the supertype for this generated class. */
+ private void setSupertype() {
+ if (componentImplementation.superclassImplementation().isPresent()) {
+ componentImplementation.addSuperclass(
+ componentImplementation.superclassImplementation().get().name());
+ } else {
+ componentImplementation.addSupertype(graph.componentTypeElement());
+ }
+ }
+
+ /**
+ * Adds {@code creator} as a nested creator class. Root components and subcomponents will nest
+ * this in different classes.
+ */
+ protected abstract void addCreatorClass(TypeSpec creator);
+
+ /** Adds component factory methods. */
+ protected abstract void addFactoryMethods();
+
+ protected void addInterfaceMethods() {
+ // Each component method may have been declared by several supertypes. We want to implement
+ // only one method for each distinct signature.
+ ImmutableListMultimap<MethodSignature, ComponentMethodDescriptor> componentMethodsBySignature =
+ Multimaps.index(graph.componentDescriptor().entryPointMethods(), this::getMethodSignature);
+ for (List<ComponentMethodDescriptor> methodsWithSameSignature :
+ Multimaps.asMap(componentMethodsBySignature).values()) {
+ ComponentMethodDescriptor anyOneMethod = methodsWithSameSignature.stream().findAny().get();
+ MethodSpec methodSpec = bindingExpressions.getComponentMethod(anyOneMethod);
+
+ if (compilerOptions.aheadOfTimeSubcomponents()) {
+ addPossiblyModifiableInterfaceMethod(anyOneMethod, methodSpec);
+ } else {
+ componentImplementation.addMethod(COMPONENT_METHOD, methodSpec);
+ }
+ }
+ }
+
+ /**
+ * Adds a component interface method in ahead-of-time subcomponents mode. If the binding that
+ * implements the method is modifiable, registers the method.
+ */
+ private void addPossiblyModifiableInterfaceMethod(
+ ComponentMethodDescriptor methodDescriptor, MethodSpec implementedComponentMethod) {
+ if (methodDescriptor.dependencyRequest().isPresent()
+ && componentImplementation
+ .getModifiableBindingMethod(bindingRequest(methodDescriptor.dependencyRequest().get()))
+ .isPresent()) {
+ // If there are multiple component methods that are modifiable and for the same binding
+ // request, implement all but one in the base implementation to delegate to the one that
+ // will remain (and be registered) modifiable
+ checkState(componentImplementation.isAbstract() && !componentImplementation.isNested());
+ componentImplementation.addMethod(
+ COMPONENT_METHOD, implementedComponentMethod.toBuilder().addModifiers(FINAL).build());
+ } else {
+ // TODO(b/117833324): Can this class be the one to interface with ComponentImplementation
+ // instead of having it go through ModifiableBindingExpressions?
+ bindingExpressions
+ .modifiableBindingExpressions()
+ .addPossiblyModifiableComponentMethod(methodDescriptor, implementedComponentMethod);
+ }
+ }
+
+ private void addCancellationListenerImplementation() {
+ componentImplementation.addSupertype(elements.getTypeElement(CancellationListener.class));
+ componentImplementation.claimMethodName(CANCELLATION_LISTENER_METHOD_NAME);
+
+ ImmutableList<ParameterSpec> parameters =
+ ImmutableList.of(ParameterSpec.builder(boolean.class, MAY_INTERRUPT_IF_RUNNING).build());
+
+ MethodSpec.Builder methodBuilder =
+ methodBuilder(CANCELLATION_LISTENER_METHOD_NAME)
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameters(parameters);
+ if (componentImplementation.superclassImplementation().isPresent()) {
+ methodBuilder.addStatement(
+ "super.$L($L)", CANCELLATION_LISTENER_METHOD_NAME, MAY_INTERRUPT_IF_RUNNING);
+ }
+
+ ImmutableList<CodeBlock> cancellationStatements = cancellationStatements();
+
+ if (cancellationStatements.size() < STATEMENTS_PER_METHOD) {
+ methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build();
+ } else {
+ ImmutableList<MethodSpec> cancelProducersMethods =
+ createPartitionedMethods(
+ "cancelProducers",
+ parameters,
+ cancellationStatements,
+ methodName -> methodBuilder(methodName).addModifiers(PRIVATE));
+ for (MethodSpec cancelProducersMethod : cancelProducersMethods) {
+ methodBuilder.addStatement("$N($L)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING);
+ componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, cancelProducersMethod);
+ }
+ }
+
+ Optional<CodeBlock> cancelParentStatement = cancelParentStatement();
+ cancelParentStatement.ifPresent(methodBuilder::addCode);
+
+ if (cancellationStatements.isEmpty()
+ && !cancelParentStatement.isPresent()
+ && componentImplementation.superclassImplementation().isPresent()) {
+ // Partial child implementations that have no new cancellations don't need to override
+ // the method just to call super().
+ return;
+ }
+
+ componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, methodBuilder.build());
+ }
+
+ private ImmutableList<CodeBlock> cancellationStatements() {
+ // Reversing should order cancellations starting from entry points and going down to leaves
+ // rather than the other way around. This shouldn't really matter but seems *slightly*
+ // preferable because:
+ // When a future that another future depends on is cancelled, that cancellation will propagate
+ // up the future graph toward the entry point. Cancelling in reverse order should ensure that
+ // everything that depends on a particular node has already been cancelled when that node is
+ // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might
+ // propagate through most of the graph, making most of the cancel calls that follow in the
+ // onProducerFutureCancelled method do nothing.
+ ImmutableList<Key> cancellationKeys =
+ componentImplementation.getCancellableProducerKeys().reverse();
+
+ ImmutableList.Builder<CodeBlock> cancellationStatements = ImmutableList.builder();
+ for (Key cancellationKey : cancellationKeys) {
+ cancellationStatements.add(
+ CodeBlock.of(
+ "$T.cancel($L, $N);",
+ Producers.class,
+ bindingExpressions
+ .getDependencyExpression(
+ bindingRequest(cancellationKey, FrameworkType.PRODUCER_NODE),
+ componentImplementation.name())
+ .codeBlock(),
+ MAY_INTERRUPT_IF_RUNNING));
+ }
+ return cancellationStatements.build();
+ }
+
+ protected Optional<CodeBlock> cancelParentStatement() {
+ // Returns empty by default. Overridden in subclass(es) to add a statement if and only if the
+ // component being generated is a concrete subcomponent implementation with a parent that
+ // allows cancellation to propagate to it from subcomponents.
+ return Optional.empty();
+ }
+
+ /**
+ * For final components, reimplements all modifiable module methods that may have been modified.
+ */
+ private void implementModifiableModuleMethods() {
+ if (componentImplementation.isAbstract()) {
+ return;
+ }
+ componentImplementation
+ .getAllModifiableModuleMethods()
+ .forEach(this::implementModifiableModuleMethod);
+ }
+
+ private void implementModifiableModuleMethod(ComponentRequirement module, String methodName) {
+ // TODO(b/117833324): only reimplement methods for modules that were abstract or were repeated
+ // by an ancestor component.
+ componentImplementation.addMethod(
+ MODIFIABLE_BINDING_METHOD,
+ methodBuilder(methodName)
+ .addAnnotation(Override.class)
+ .addModifiers(PROTECTED)
+ .returns(TypeName.get(module.type()))
+ .addStatement(
+ componentRequirementExpressions
+ .getExpression(module)
+ .getModifiableModuleMethodExpression(componentImplementation.name()))
+ .build());
+ }
+
+ private MethodSignature getMethodSignature(ComponentMethodDescriptor method) {
+ return MethodSignature.forComponentMethod(
+ method, MoreTypes.asDeclared(graph.componentTypeElement().asType()), types);
+ }
+
+ private void addChildComponents() {
+ for (BindingGraph subgraph : graph.subgraphs()) {
+ // TODO(b/117833324): Can an abstract inner subcomponent implementation be elided if it's
+ // totally empty?
+ componentImplementation.addChild(
+ subgraph.componentDescriptor(), buildChildImplementation(subgraph));
+ }
+ }
+
+ private ComponentImplementation buildChildImplementation(BindingGraph childGraph) {
+ ComponentImplementation childImplementation =
+ compilerOptions.aheadOfTimeSubcomponents()
+ ? abstractInnerSubcomponent(childGraph)
+ : concreteSubcomponent(childGraph);
+ return topLevelImplementationComponent
+ .currentImplementationSubcomponentBuilder()
+ .componentImplementation(childImplementation)
+ .bindingGraph(childGraph)
+ .parentBuilder(Optional.of(this))
+ .parentBindingExpressions(Optional.of(bindingExpressions))
+ .parentRequirementExpressions(Optional.of(componentRequirementExpressions))
+ .build()
+ .subcomponentBuilder()
+ .build();
+ }
+
+ /** Creates an inner abstract subcomponent implementation. */
+ private ComponentImplementation abstractInnerSubcomponent(BindingGraph childGraph) {
+ return componentImplementation.childComponentImplementation(
+ childGraph,
+ Optional.of(
+ componentImplementationFactory.findChildSuperclassImplementation(
+ childGraph.componentDescriptor(), componentImplementation)),
+ PROTECTED,
+ componentImplementation.isAbstract() ? ABSTRACT : FINAL);
+ }
+
+ /** Creates a concrete inner subcomponent implementation. */
+ private ComponentImplementation concreteSubcomponent(BindingGraph childGraph) {
+ return componentImplementation.childComponentImplementation(
+ childGraph,
+ Optional.empty(), // superclassImplementation
+ PRIVATE,
+ FINAL);
+ }
+
+ /** Creates and adds the constructor and methods needed for initializing the component. */
+ private void addConstructorAndInitializationMethods() {
+ MethodSpec.Builder constructor = componentConstructorBuilder();
+ if (!componentImplementation.isAbstract()) {
+ implementInitializationMethod(constructor, initializationParameters());
+ } else if (componentImplementation.hasInitializations()) {
+ addConfigureInitializationMethod();
+ }
+ componentImplementation.addMethod(CONSTRUCTOR, constructor.build());
+ }
+
+ /** Returns a builder for the component's constructor. */
+ private MethodSpec.Builder componentConstructorBuilder() {
+ return constructorBuilder()
+ .addModifiers(componentImplementation.isAbstract() ? PROTECTED : PRIVATE);
+ }
+
+ /** Adds parameters and code to the given {@code initializationMethod}. */
+ private void implementInitializationMethod(
+ MethodSpec.Builder initializationMethod,
+ ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters) {
+ initializationMethod.addParameters(initializationParameters.values());
+ initializationMethod.addCode(
+ CodeBlocks.concat(componentImplementation.getComponentRequirementInitializations()));
+ componentImplementation
+ .superConfigureInitializationMethod()
+ .ifPresent(
+ superConfigureInitializationMethod ->
+ addSuperConfigureInitializationCall(
+ initializationMethod,
+ initializationParameters,
+ superConfigureInitializationMethod));
+ addInitializeMethods(initializationMethod, initializationParameters.values().asList());
+ }
+
+ /** Creates and adds a {@code configureInitializatoin} method to the component. */
+ private void addConfigureInitializationMethod() {
+ MethodSpec.Builder method = configureInitializationMethodBuilder();
+ ImmutableMap<ComponentRequirement, ParameterSpec> parameters = initializationParameters();
+ implementInitializationMethod(method, parameters);
+ componentImplementation.setConfigureInitializationMethod(
+ ConfigureInitializationMethod.create(method.build(), parameters.keySet()));
+ }
+
+ /** Returns a builder for the component's {@code configureInitialization} method. */
+ private MethodSpec.Builder configureInitializationMethodBuilder() {
+ String methodName = componentImplementation.getUniqueMethodName("configureInitialization");
+ MethodSpec.Builder configureInitialization = methodBuilder(methodName).addModifiers(PROTECTED);
+ if (overridesSuperclassConfigureInitialization(configureInitialization.build())) {
+ configureInitialization.addAnnotation(Override.class);
+ }
+ return configureInitialization;
+ }
+
+ /**
+ * Returns whether or not the given method overrides a configureInitialization method from a
+ * superclass.
+ */
+ private boolean overridesSuperclassConfigureInitialization(MethodSpec method) {
+ for (Optional<ComponentImplementation> currentSuperImplementation =
+ componentImplementation.superclassImplementation();
+ currentSuperImplementation.isPresent();
+ currentSuperImplementation = currentSuperImplementation.get().superclassImplementation()) {
+ Optional<MethodSpec> superConfigureInitializationMethod =
+ currentSuperImplementation.get().configureInitializationMethod().map(m -> m.spec());
+ if (superConfigureInitializationMethod
+ .filter(superMethod -> haveSameSignature(method, superMethod))
+ .isPresent()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** Returns whether or not methods {@code a} and {@code b} have the same signature. */
+ private boolean haveSameSignature(MethodSpec a, MethodSpec b) {
+ return a.name.equals(b.name) && types(a.parameters).equals(types(b.parameters));
+ }
+
+ private ImmutableList<TypeName> types(List<ParameterSpec> parameters) {
+ return parameters.stream().map(parameter -> parameter.type).collect(toImmutableList());
+ }
+
+ /**
+ * Adds a call to the superclass's {@code configureInitialization} method to the given {@code
+ * callingMethod}.
+ */
+ private void addSuperConfigureInitializationCall(
+ MethodSpec.Builder callingMethod,
+ ImmutableMap<ComponentRequirement, ParameterSpec> parameters,
+ ConfigureInitializationMethod superConfigureInitializationMethod) {
+ // This component's constructor may not have all of the parameters that the superclass's
+ // configureInitialization method takes, because the superclass configureInitialization method
+ // necessarily accepts things that it can't know whether will be needed or not. If they aren't
+ // needed (as is the case when the constructor doesn't have a parameter for the module), just
+ // pass null to super.configureInitialization for that parameter; it won't be used.
+ CodeBlock args =
+ superConfigureInitializationMethod.parameters().stream()
+ .map(
+ requirement ->
+ parameters.containsKey(requirement)
+ ? CodeBlock.of("$N", parameters.get(requirement))
+ : CodeBlock.of("null"))
+ .collect(toParametersCodeBlock());
+
+ String qualifier =
+ haveSameSignature(callingMethod.build(), superConfigureInitializationMethod.spec())
+ ? "super."
+ : "";
+ callingMethod.addStatement(
+ qualifier + "$N($L)", superConfigureInitializationMethod.spec(), args);
+ }
+
+ /**
+ * Adds any necessary {@code initialize} methods to the component and adds calls to them to the
+ * given {@code callingMethod}.
+ */
+ private void addInitializeMethods(
+ MethodSpec.Builder callingMethod, ImmutableList<ParameterSpec> parameters) {
+ // TODO(cgdecker): It's not the case that each initialize() method has need for all of the
+ // given parameters. In some cases, those parameters may have already been assigned to fields
+ // which could be referenced instead. In other cases, an initialize method may just not need
+ // some of the parameters because the set of initializations in that partition does not
+ // include any reference to them. Right now, the Dagger code has no way of getting that
+ // information because, among other things, componentImplementation.getImplementations() just
+ // returns a bunch of CodeBlocks with no semantic information. Additionally, we may not know
+ // yet whether a field will end up needing to be created for a specific requirement, and we
+ // don't want to create a field that ends up only being used during initialization.
+ CodeBlock args = parameterNames(parameters);
+ ImmutableList<MethodSpec> methods =
+ createPartitionedMethods(
+ "initialize",
+ makeFinal(parameters),
+ componentImplementation.getInitializations(),
+ methodName ->
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ /* TODO(gak): Strictly speaking, we only need the suppression here if we are
+ * also initializing a raw field in this method, but the structure of this
+ * code makes it awkward to pass that bit through. This will be cleaned up
+ * when we no longer separate fields and initialization as we do now. */
+ .addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED)));
+ for (MethodSpec method : methods) {
+ callingMethod.addStatement("$N($L)", method, args);
+ componentImplementation.addMethod(INITIALIZE_METHOD, method);
+ }
+ }
+
+ /**
+ * Creates one or more methods, all taking the given {@code parameters}, which partition the given
+ * list of {@code statements} among themselves such that no method has more than {@code
+ * STATEMENTS_PER_METHOD} statements in it and such that the returned methods, if called in order,
+ * will execute the {@code statements} in the given order.
+ */
+ private ImmutableList<MethodSpec> createPartitionedMethods(
+ String methodName,
+ Iterable<ParameterSpec> parameters,
+ List<CodeBlock> statements,
+ Function<String, MethodSpec.Builder> methodBuilderCreator) {
+ return Lists.partition(statements, STATEMENTS_PER_METHOD).stream()
+ .map(
+ partition ->
+ methodBuilderCreator
+ .apply(componentImplementation.getUniqueMethodName(methodName))
+ .addParameters(parameters)
+ .addCode(CodeBlocks.concat(partition))
+ .build())
+ .collect(toImmutableList());
+ }
+
+ /** Returns the given parameters with a final modifier added. */
+ private final ImmutableList<ParameterSpec> makeFinal(Collection<ParameterSpec> parameters) {
+ return parameters.stream()
+ .map(param -> param.toBuilder().addModifiers(FINAL).build())
+ .collect(toImmutableList());
+ }
+
+ /**
+ * Returns the parameters for the constructor or {@code configureInitilization} method as a map
+ * from the requirement the parameter fulfills to the spec for the parameter.
+ */
+ private final ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters() {
+ Map<ComponentRequirement, ParameterSpec> parameters;
+ if (componentImplementation.componentDescriptor().hasCreator()) {
+ parameters =
+ Maps.toMap(componentImplementation.requirements(), ComponentRequirement::toParameterSpec);
+ } else if (componentImplementation.isAbstract() && componentImplementation.isNested()) {
+ // If we're generating an abstract inner subcomponent, then we are not implementing module
+ // instance bindings and have no need for factory method parameters.
+ parameters = ImmutableMap.of();
+ } else if (graph.factoryMethod().isPresent()) {
+ parameters = getFactoryMethodParameters(graph);
+ } else if (componentImplementation.isAbstract()) {
+ // If we're generating an abstract base implementation of a subcomponent it's acceptable to
+ // have neither a creator nor factory method.
+ parameters = ImmutableMap.of();
+ } else {
+ throw new AssertionError(
+ "Expected either a component creator or factory method but found neither.");
+ }
+
+ if (componentImplementation.isAbstract()) {
+ parameters = Maps.filterKeys(parameters, in(configureInitializationRequirements()));
+ }
+ return renameParameters(parameters);
+ }
+
+ /**
+ * Returns the set of requirements for the configureInitialization method: the parameters that are
+ * needed either for initializing a component requirement field or for calling the superclass's
+ * {@code configureInitialization} method.
+ */
+ private ImmutableSet<ComponentRequirement> configureInitializationRequirements() {
+ ImmutableSet<ComponentRequirement> initializationParameters =
+ componentImplementation.getComponentRequirementParameters();
+ ImmutableSet<ComponentRequirement> superConfigureInitializationRequirements =
+ componentImplementation
+ .superConfigureInitializationMethod()
+ .map(ConfigureInitializationMethod::parameters)
+ .orElse(ImmutableSet.of());
+ return Sets.union(initializationParameters, superConfigureInitializationRequirements)
+ .immutableCopy();
+ }
+
+ /**
+ * Renames the given parameters to guarantee their names do not conflict with fields in the
+ * component to ensure that a parameter is never referenced where a reference to a field was
+ * intended.
+ */
+ // TODO(cgdecker): This is a bit kludgy; it would be preferable to either qualify the field
+ // references with "this." or "super." when needed to disambiguate between field and parameter,
+ // but that would require more context than is currently available when the code referencing a
+ // field is generated.
+ private ImmutableMap<ComponentRequirement, ParameterSpec> renameParameters(
+ Map<ComponentRequirement, ParameterSpec> parameters) {
+ return ImmutableMap.copyOf(
+ Maps.transformEntries(
+ parameters,
+ (requirement, parameter) ->
+ renameParameter(
+ parameter,
+ componentImplementation.getParameterName(requirement, parameter.name))));
+ }
+
+ private ParameterSpec renameParameter(ParameterSpec parameter, String newName) {
+ return ParameterSpec.builder(parameter.type, newName)
+ .addAnnotations(parameter.annotations)
+ .addModifiers(parameter.modifiers)
+ .build();
+ }
+
+ /** Builds a root component implementation. */
+ static final class RootComponentImplementationBuilder extends ComponentImplementationBuilder {
+ @Inject
+ RootComponentImplementationBuilder(ComponentImplementation componentImplementation) {
+ checkArgument(!componentImplementation.superclassImplementation().isPresent());
+ }
+
+ @Override
+ protected void addCreatorClass(TypeSpec creator) {
+ componentImplementation.addType(COMPONENT_CREATOR, creator);
+ }
+
+ @Override
+ protected void addFactoryMethods() {
+ // Top-level components have a static method that returns a builder or factory for the
+ // component. If the user defined a @Component.Builder or @Component.Factory, an
+ // implementation of their type is returned. Otherwise, an autogenerated Builder type is
+ // returned.
+ // TODO(cgdecker): Replace this abomination with a small class?
+ // Better yet, change things so that an autogenerated builder type has a descriptor of sorts
+ // just like a user-defined creator type.
+ ComponentCreatorKind creatorKind;
+ ClassName creatorType;
+ String factoryMethodName;
+ boolean noArgFactoryMethod;
+ if (creatorDescriptor().isPresent()) {
+ ComponentCreatorDescriptor descriptor = creatorDescriptor().get();
+ creatorKind = descriptor.kind();
+ creatorType = ClassName.get(descriptor.typeElement());
+ factoryMethodName = descriptor.factoryMethod().getSimpleName().toString();
+ noArgFactoryMethod = descriptor.factoryParameters().isEmpty();
+ } else {
+ creatorKind = BUILDER;
+ creatorType = componentCreatorName();
+ factoryMethodName = "build";
+ noArgFactoryMethod = true;
+ }
+
+ MethodSpec creatorFactoryMethod =
+ methodBuilder(creatorKind.methodName())
+ .addModifiers(PUBLIC, STATIC)
+ .returns(creatorType)
+ .addStatement("return new $T()", componentCreatorName())
+ .build();
+ componentImplementation.addMethod(BUILDER_METHOD, creatorFactoryMethod);
+ if (noArgFactoryMethod && canInstantiateAllRequirements()) {
+ componentImplementation.addMethod(
+ BUILDER_METHOD,
+ methodBuilder("create")
+ .returns(ClassName.get(super.graph.componentTypeElement()))
+ .addModifiers(PUBLIC, STATIC)
+ .addStatement(
+ "return new $L().$L()", creatorKind.typeName(), factoryMethodName)
+ .build());
+ }
+ }
+
+ private Optional<ComponentCreatorDescriptor> creatorDescriptor() {
+ return graph.componentDescriptor().creatorDescriptor();
+ }
+
+ /** {@code true} if all of the graph's required dependencies can be automatically constructed */
+ private boolean canInstantiateAllRequirements() {
+ return !Iterables.any(
+ graph.componentRequirements(),
+ dependency -> dependency.requiresAPassedInstance(elements, types));
+ }
+
+ private ClassName componentCreatorName() {
+ return componentImplementation.creatorImplementation().get().name();
+ }
+ }
+
+ /**
+ * Builds a subcomponent implementation. If generating ahead-of-time subcomponents, this may be an
+ * abstract base class implementation, an abstract inner implementation, or a concrete
+ * implementation that extends an abstract base implementation. Otherwise it represents a private,
+ * inner, concrete, final implementation of a subcomponent which extends a user defined type.
+ */
+ static final class SubcomponentImplementationBuilder extends ComponentImplementationBuilder {
+ final Optional<ComponentImplementationBuilder> parent;
+
+ @Inject
+ SubcomponentImplementationBuilder(
+ @ParentComponent Optional<ComponentImplementationBuilder> parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ protected void addCreatorClass(TypeSpec creator) {
+ if (parent.isPresent()) {
+ // In an inner implementation of a subcomponent the creator is a peer class.
+ parent.get().componentImplementation.addType(SUBCOMPONENT, creator);
+ } else {
+ componentImplementation.addType(SUBCOMPONENT, creator);
+ }
+ }
+
+ @Override
+ protected void addFactoryMethods() {
+ // Only construct instances of subcomponents that have concrete implementations.
+ if (!componentImplementation.isAbstract()) {
+ // Use the parent's factory method to create this subcomponent if the
+ // subcomponent was not added via {@link dagger.Module#subcomponents()}.
+ graph.factoryMethod().ifPresent(this::createSubcomponentFactoryMethod);
+ }
+ }
+
+ private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) {
+ checkState(parent.isPresent());
+
+ Collection<ParameterSpec> params = getFactoryMethodParameters(graph).values();
+ MethodSpec.Builder method = MethodSpec.overriding(factoryMethod, parentType(), types);
+ params.forEach(
+ param -> method.addStatement("$T.checkNotNull($N)", Preconditions.class, param));
+ method.addStatement(
+ "return new $T($L)", componentImplementation.name(), parameterNames(params));
+
+ parent.get().componentImplementation.addMethod(COMPONENT_METHOD, method.build());
+ }
+
+ private DeclaredType parentType() {
+ return asDeclared(parent.get().graph.componentTypeElement().asType());
+ }
+
+ @Override
+ protected void addInterfaceMethods() {
+ if (componentImplementation.superclassImplementation().isPresent()) {
+ // Since we're overriding a subcomponent implementation we add to its implementation given
+ // an expanded binding graph.
+
+ ComponentImplementation superclassImplementation =
+ componentImplementation.superclassImplementation().get();
+ for (ModifiableBindingMethod superclassModifiableBindingMethod :
+ superclassImplementation.getModifiableBindingMethods().values()) {
+ bindingExpressions
+ .modifiableBindingExpressions()
+ .possiblyReimplementedMethod(superclassModifiableBindingMethod)
+ .ifPresent(componentImplementation::addImplementedModifiableBindingMethod);
+ }
+ } else {
+ super.addInterfaceMethods();
+ }
+ }
+
+ @Override
+ protected Optional<CodeBlock> cancelParentStatement() {
+ if (!shouldPropagateCancellationToParent()) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ CodeBlock.builder()
+ .addStatement(
+ "$T.this.$N($N)",
+ parent.get().componentImplementation.name(),
+ CANCELLATION_LISTENER_METHOD_NAME,
+ MAY_INTERRUPT_IF_RUNNING)
+ .build());
+ }
+
+ private boolean shouldPropagateCancellationToParent() {
+ return parent.isPresent()
+ && parent
+ .get()
+ .componentImplementation
+ .componentDescriptor()
+ .cancellationPolicy()
+ .map(policy -> policy.fromSubcomponents().equals(PROPAGATE))
+ .orElse(false);
+ }
+ }
+
+ /**
+ * Returns the map of {@link ComponentRequirement}s to {@link ParameterSpec}s for the
+ * given graph's factory method.
+ */
+ private static Map<ComponentRequirement, ParameterSpec> getFactoryMethodParameters(
+ BindingGraph graph) {
+ return Maps.transformValues(graph.factoryMethodParameters(), ParameterSpec::get);
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentImplementationFactory.java b/java/dagger/internal/codegen/ComponentImplementationFactory.java
new file mode 100644
index 0000000..9578ece
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentImplementationFactory.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.ComponentGenerator.componentName;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static javax.tools.Diagnostic.Kind.WARNING;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.serialization.ProtoSerialization.InconsistentSerializedProtoException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.TypeElement;
+
+/** Factory for {@link ComponentImplementation}s. */
+@Singleton
+final class ComponentImplementationFactory implements ClearableCache {
+ private final Map<TypeElement, ComponentImplementation> topLevelComponentCache = new HashMap<>();
+ private final KeyFactory keyFactory;
+ private final CompilerOptions compilerOptions;
+ private final BindingGraphFactory bindingGraphFactory;
+ private final TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder;
+ private final DeserializedComponentImplementationBuilder
+ deserializedComponentImplementationBuilder;
+ private final DaggerElements elements;
+ private final Messager messager;
+
+ @Inject
+ ComponentImplementationFactory(
+ KeyFactory keyFactory,
+ CompilerOptions compilerOptions,
+ BindingGraphFactory bindingGraphFactory,
+ TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder,
+ DeserializedComponentImplementationBuilder deserializedComponentImplementationBuilder,
+ DaggerElements elements,
+ Messager messager) {
+ this.keyFactory = keyFactory;
+ this.compilerOptions = compilerOptions;
+ this.bindingGraphFactory = bindingGraphFactory;
+ this.topLevelImplementationComponentBuilder = topLevelImplementationComponentBuilder;
+ this.deserializedComponentImplementationBuilder = deserializedComponentImplementationBuilder;
+ this.elements = elements;
+ this.messager = messager;
+ }
+
+ /**
+ * Returns a top-level (non-nested) component implementation for a binding graph.
+ *
+ * @throws IllegalStateException if the binding graph is for a subcomponent and
+ * ahead-of-time-subcomponents mode is not enabled
+ */
+ ComponentImplementation createComponentImplementation(BindingGraph bindingGraph) {
+ return reentrantComputeIfAbsent(
+ topLevelComponentCache,
+ bindingGraph.componentTypeElement(),
+ component -> createComponentImplementationUncached(bindingGraph));
+ }
+
+ private ComponentImplementation createComponentImplementationUncached(BindingGraph bindingGraph) {
+ ComponentImplementation componentImplementation =
+ ComponentImplementation.topLevelComponentImplementation(
+ bindingGraph,
+ componentName(bindingGraph.componentTypeElement()),
+ new SubcomponentNames(bindingGraph, keyFactory),
+ compilerOptions);
+
+ // TODO(dpb): explore using optional bindings for the "parent" bindings
+ CurrentImplementationSubcomponent currentImplementationSubcomponent =
+ topLevelImplementationComponentBuilder
+ .topLevelComponent(componentImplementation)
+ .build()
+ .currentImplementationSubcomponentBuilder()
+ .componentImplementation(componentImplementation)
+ .bindingGraph(bindingGraph)
+ .parentBuilder(Optional.empty())
+ .parentBindingExpressions(Optional.empty())
+ .parentRequirementExpressions(Optional.empty())
+ .build();
+
+ if (componentImplementation.isAbstract()) {
+ checkState(
+ compilerOptions.aheadOfTimeSubcomponents(),
+ "Calling 'componentImplementation()' on %s when not generating ahead-of-time "
+ + "subcomponents.",
+ bindingGraph.componentTypeElement());
+ return currentImplementationSubcomponent.subcomponentBuilder().build();
+ } else {
+ return currentImplementationSubcomponent.rootComponentBuilder().build();
+ }
+ }
+
+ /** Returns the superclass of the child nested within a superclass of the parent component. */
+ ComponentImplementation findChildSuperclassImplementation(
+ ComponentDescriptor child, ComponentImplementation parentImplementation) {
+ // If the current component has superclass implementations, a superclass may contain a
+ // reference to the child. Traverse this component's superimplementation hierarchy looking for
+ // the child's implementation. The child superclass implementation may not be present in the
+ // direct superclass implementations if the subcomponent builder was previously a pruned
+ // binding.
+ for (Optional<ComponentImplementation> parent = parentImplementation.superclassImplementation();
+ parent.isPresent();
+ parent = parent.get().superclassImplementation()) {
+ Optional<ComponentImplementation> superclass = parent.get().childImplementation(child);
+ if (superclass.isPresent()) {
+ return superclass.get();
+ }
+ }
+
+ if (compilerOptions.emitModifiableMetadataAnnotations()) {
+ ClassName childSuperclassName = componentName(child.typeElement());
+ TypeElement generatedChildSuperclassImplementation =
+ elements.getTypeElement(childSuperclassName);
+ if (generatedChildSuperclassImplementation != null) {
+ try {
+ return deserializedComponentImplementationBuilder.create(
+ child, generatedChildSuperclassImplementation);
+ } catch (InconsistentSerializedProtoException e) {
+ messager.printMessage(
+ WARNING,
+ String.format(
+ "%s was compiled with a different version of Dagger than the version in this "
+ + "compilation. To ensure the validity of Dagger's generated code, compile "
+ + "all Dagger code with the same version.",
+ child.typeElement().getQualifiedName()));
+ }
+ } else if (compilerOptions.forceUseSerializedComponentImplementations()) {
+ throw new TypeNotPresentException(childSuperclassName.toString(), null);
+ }
+ }
+
+ // Otherwise, the superclass implementation is top-level, so we must recreate the
+ // implementation object for the base implementation of the child by truncating the binding
+ // graph at the child.
+ BindingGraph truncatedBindingGraph = bindingGraphFactory.create(child, false);
+ return createComponentImplementation(truncatedBindingGraph);
+ }
+
+ @Override
+ public void clearCache() {
+ topLevelComponentCache.clear();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java b/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
new file mode 100644
index 0000000..e98d595
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.javapoet.Expression;
+
+/** A binding expression for the instance of the component itself, i.e. {@code this}. */
+final class ComponentInstanceBindingExpression extends SimpleInvocationBindingExpression {
+ private final ClassName componentName;
+ private final ContributionBinding binding;
+
+ ComponentInstanceBindingExpression(ResolvedBindings resolvedBindings, ClassName componentName) {
+ super(resolvedBindings);
+ this.componentName = componentName;
+ this.binding = resolvedBindings.contributionBinding();
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(
+ binding.key().type(),
+ componentName.equals(requestingClass)
+ ? CodeBlock.of("this")
+ : CodeBlock.of("$T.this", componentName));
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentKind.java b/java/dagger/internal/codegen/ComponentKind.java
new file mode 100644
index 0000000..8e5b9ba
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentKind.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static dagger.internal.codegen.DaggerStreams.stream;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.DaggerStreams.valuesOf;
+import static java.util.EnumSet.allOf;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Component;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.lang.model.element.TypeElement;
+
+/** Enumeration of the different kinds of components. */
+enum ComponentKind {
+ /** {@code @Component} */
+ COMPONENT(Component.class, true, false),
+
+ /** {@code @Subcomponent} */
+ SUBCOMPONENT(Subcomponent.class, false, false),
+
+ /** {@code @ProductionComponent} */
+ PRODUCTION_COMPONENT(ProductionComponent.class, true, true),
+
+ /** {@code @ProductionSubcomponent} */
+ PRODUCTION_SUBCOMPONENT(ProductionSubcomponent.class, false, true),
+
+ /**
+ * Kind for a descriptor that was generated from a {@link Module} instead of a component type in
+ * order to validate the module's bindings.
+ */
+ MODULE(Module.class, true, false),
+
+ /**
+ * Kind for a descriptor was generated from a {@link ProducerModule} instead of a component type
+ * in order to validate the module's bindings.
+ */
+ PRODUCER_MODULE(ProducerModule.class, true, true),
+ ;
+
+ private static final ImmutableSet<ComponentKind> ROOT_COMPONENT_KINDS =
+ valuesOf(ComponentKind.class)
+ .filter(kind -> !kind.isForModuleValidation())
+ .filter(kind -> kind.isRoot())
+ .collect(toImmutableSet());
+
+ private static final ImmutableSet<ComponentKind> SUBCOMPONENT_KINDS =
+ valuesOf(ComponentKind.class)
+ .filter(kind -> !kind.isForModuleValidation())
+ .filter(kind -> !kind.isRoot())
+ .collect(toImmutableSet());
+
+ /** Returns the set of kinds for root components. */
+ static ImmutableSet<ComponentKind> rootComponentKinds() {
+ return ROOT_COMPONENT_KINDS;
+ }
+
+ /** Returns the set of kinds for subcomponents. */
+ static ImmutableSet<ComponentKind> subcomponentKinds() {
+ return SUBCOMPONENT_KINDS;
+ }
+
+ /** Returns the annotations for components of the given kinds. */
+ static ImmutableSet<Class<? extends Annotation>> annotationsFor(Iterable<ComponentKind> kinds) {
+ return stream(kinds).map(ComponentKind::annotation).collect(toImmutableSet());
+ }
+
+ /** Returns the set of component kinds the given {@code element} has annotations for. */
+ static ImmutableSet<ComponentKind> getComponentKinds(TypeElement element) {
+ return valuesOf(ComponentKind.class)
+ .filter(kind -> isAnnotationPresent(element, kind.annotation()))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the kind of an annotated element if it is annotated with one of the {@linkplain
+ * #annotation() annotations}.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one of the
+ * annotations
+ */
+ static Optional<ComponentKind> forAnnotatedElement(TypeElement element) {
+ ImmutableSet<ComponentKind> kinds = getComponentKinds(element);
+ if (kinds.size() > 1) {
+ throw new IllegalArgumentException(
+ element + " cannot be annotated with more than one of " + annotationsFor(kinds));
+ }
+ return kinds.stream().findAny();
+ }
+
+ private final Class<? extends Annotation> annotation;
+ private final boolean isRoot;
+ private final boolean production;
+
+ ComponentKind(
+ Class<? extends Annotation> annotation,
+ boolean isRoot,
+ boolean production) {
+ this.annotation = annotation;
+ this.isRoot = isRoot;
+ this.production = production;
+ }
+
+ /** Returns the annotation that marks a component of this kind. */
+ Class<? extends Annotation> annotation() {
+ return annotation;
+ }
+
+ /** Returns the kinds of modules that can be used with a component of this kind. */
+ ImmutableSet<ModuleKind> legalModuleKinds() {
+ return isProducer()
+ ? immutableEnumSet(allOf(ModuleKind.class))
+ : immutableEnumSet(ModuleKind.MODULE);
+ }
+
+ /** Returns the kinds of subcomponents a component of this kind can have. */
+ ImmutableSet<ComponentKind> legalSubcomponentKinds() {
+ return isProducer()
+ ? immutableEnumSet(PRODUCTION_SUBCOMPONENT)
+ : immutableEnumSet(SUBCOMPONENT, PRODUCTION_SUBCOMPONENT);
+ }
+
+ /**
+ * Returns {@code true} if the descriptor is for a root component (not a subcomponent) or is for
+ * {@linkplain #isForModuleValidation() module-validation}.
+ */
+ boolean isRoot() {
+ return isRoot;
+ }
+
+ /** Returns true if this is a production component. */
+ boolean isProducer() {
+ return production;
+ }
+
+ /** Returns {@code true} if the descriptor is for a module in order to validate its bindings. */
+ boolean isForModuleValidation() {
+ switch (this) {
+ case MODULE:
+ case PRODUCER_MODULE:
+ return true;
+ default:
+ // fall through
+ }
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentMethodBindingExpression.java b/java/dagger/internal/codegen/ComponentMethodBindingExpression.java
new file mode 100644
index 0000000..7e7bbd8
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentMethodBindingExpression.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A binding expression that implements and uses a component method.
+ *
+ * <p>Dependents of this binding expression will just call the component method.
+ */
+final class ComponentMethodBindingExpression extends MethodBindingExpression {
+ private final ComponentImplementation componentImplementation;
+ private final ComponentMethodDescriptor componentMethod;
+
+ ComponentMethodBindingExpression(
+ BindingRequest request,
+ ResolvedBindings resolvedBindings,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression,
+ ComponentImplementation componentImplementation,
+ ComponentMethodDescriptor componentMethod,
+ DaggerTypes types) {
+ super(
+ request,
+ resolvedBindings,
+ methodImplementationStrategy,
+ wrappedBindingExpression,
+ componentImplementation,
+ types);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentMethod = checkNotNull(componentMethod);
+ }
+
+ @Override
+ protected CodeBlock getComponentMethodImplementation(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ // There could be several methods on the component for the same request key and kind.
+ // Only one should use the BindingMethodImplementation; the others can delegate that one. So
+ // use methodImplementation.body() only if componentMethod equals the method for this instance.
+
+ // Separately, the method might be defined on a supertype that is also a supertype of some
+ // parent component. In that case, the same ComponentMethodDescriptor will be used to add a CMBE
+ // for the parent and the child. Only the parent's should use the BindingMethodImplementation;
+ // the child's can delegate to the parent. So use methodImplementation.body() only if
+ // componentName equals the component for this instance.
+ return componentMethod.equals(this.componentMethod) && component.equals(componentImplementation)
+ ? methodBodyForComponentMethod(componentMethod)
+ : super.getComponentMethodImplementation(componentMethod, component);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ // If a component method returns a primitive, update the expression's type which might be boxed.
+ Expression expression = super.getDependencyExpression(requestingClass);
+ TypeMirror methodReturnType = componentMethod.methodElement().getReturnType();
+ return methodReturnType.getKind().isPrimitive()
+ ? Expression.create(methodReturnType, expression.codeBlock())
+ : expression;
+ }
+
+ @Override
+ protected void addMethod() {}
+
+ @Override
+ protected String methodName() {
+ return componentMethod.methodElement().getSimpleName().toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentNodeImpl.java b/java/dagger/internal/codegen/ComponentNodeImpl.java
new file mode 100644
index 0000000..3a31ec7
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentNodeImpl.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import dagger.model.Scope;
+
+/** An implementation of {@link ComponentNode} that also exposes the {@link ComponentDescriptor}. */
+@AutoValue
+abstract class ComponentNodeImpl implements ComponentNode {
+ static ComponentNode create(
+ ComponentPath componentPath, ComponentDescriptor componentDescriptor) {
+ return new AutoValue_ComponentNodeImpl(componentPath, componentDescriptor);
+ }
+
+ @Override
+ public final boolean isSubcomponent() {
+ return componentDescriptor().isSubcomponent();
+ }
+
+ @Override
+ public boolean isRealComponent() {
+ return componentDescriptor().isRealComponent();
+ }
+
+ @Override
+ public final ImmutableSet<DependencyRequest> entryPoints() {
+ return componentDescriptor().entryPointMethods().stream()
+ .map(method -> method.dependencyRequest().get())
+ .collect(toImmutableSet());
+ }
+
+ @Override
+ public ImmutableSet<Scope> scopes() {
+ return componentDescriptor().scopes();
+ }
+
+ abstract ComponentDescriptor componentDescriptor();
+
+ @Override
+ public final String toString() {
+ return componentPath().toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentProcessingStep.java b/java/dagger/internal/codegen/ComponentProcessingStep.java
new file mode 100644
index 0000000..645c94f
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentProcessingStep.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Sets.union;
+import static dagger.internal.codegen.ComponentAnnotation.allComponentAnnotations;
+import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotations;
+import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotations;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.allCreatorAnnotations;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.rootComponentCreatorAnnotations;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.ValidationType.NONE;
+import static java.util.Collections.disjoint;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.MoreElements;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A {@link ProcessingStep} that is responsible for dealing with a component or production component
+ * as part of the {@link ComponentProcessor}.
+ */
+final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+ private final Messager messager;
+ private final ComponentValidator componentValidator;
+ private final ComponentCreatorValidator creatorValidator;
+ private final ComponentDescriptorValidator componentDescriptorValidator;
+ private final ComponentDescriptorFactory componentDescriptorFactory;
+ private final BindingGraphFactory bindingGraphFactory;
+ private final SourceFileGenerator<BindingGraph> componentGenerator;
+ private final BindingGraphConverter bindingGraphConverter;
+ private final BindingGraphValidator bindingGraphValidator;
+ private final CompilerOptions compilerOptions;
+ private ImmutableSet<Element> subcomponentElements;
+ private ImmutableSet<Element> subcomponentCreatorElements;
+ private ImmutableMap<Element, ValidationReport<TypeElement>> creatorReportsByComponent;
+ private ImmutableMap<Element, ValidationReport<TypeElement>> creatorReportsBySubcomponent;
+ private ImmutableMap<Element, ValidationReport<TypeElement>> reportsBySubcomponent;
+
+ @Inject
+ ComponentProcessingStep(
+ Messager messager,
+ ComponentValidator componentValidator,
+ ComponentCreatorValidator creatorValidator,
+ ComponentDescriptorValidator componentDescriptorValidator,
+ ComponentDescriptorFactory componentDescriptorFactory,
+ BindingGraphFactory bindingGraphFactory,
+ SourceFileGenerator<BindingGraph> componentGenerator,
+ BindingGraphConverter bindingGraphConverter,
+ BindingGraphValidator bindingGraphValidator,
+ CompilerOptions compilerOptions) {
+ super(MoreElements::asType);
+ this.messager = messager;
+ this.componentValidator = componentValidator;
+ this.creatorValidator = creatorValidator;
+ this.componentDescriptorValidator = componentDescriptorValidator;
+ this.componentDescriptorFactory = componentDescriptorFactory;
+ this.bindingGraphFactory = bindingGraphFactory;
+ this.componentGenerator = componentGenerator;
+ this.bindingGraphConverter = bindingGraphConverter;
+ this.bindingGraphValidator = bindingGraphValidator;
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ public Set<Class<? extends Annotation>> annotations() {
+ return union(allComponentAnnotations(), allCreatorAnnotations());
+ }
+
+ @Override
+ public ImmutableSet<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ subcomponentElements =
+ getElementsFromAnnotations(elementsByAnnotation, subcomponentAnnotations());
+ subcomponentCreatorElements =
+ getElementsFromAnnotations(elementsByAnnotation, subcomponentCreatorAnnotations());
+
+ ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
+
+ creatorReportsByComponent =
+ processCreators(
+ getElementsFromAnnotations(elementsByAnnotation, rootComponentCreatorAnnotations()),
+ rejectedElements);
+ creatorReportsBySubcomponent = processCreators(subcomponentCreatorElements, rejectedElements);
+ reportsBySubcomponent =
+ processSubcomponents(subcomponentElements, subcomponentCreatorElements, rejectedElements);
+
+ return rejectedElements.addAll(super.process(elementsByAnnotation)).build();
+ }
+
+ @Override
+ protected void process(
+ TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
+ if (!disjoint(annotations, rootComponentAnnotations())) {
+ processRootComponent(element);
+ }
+ if (!disjoint(annotations, subcomponentAnnotations())) {
+ processSubcomponent(element);
+ }
+ }
+
+ private void processRootComponent(TypeElement component) {
+ if (!isRootComponentValid(component)) {
+ return;
+ }
+ ComponentDescriptor componentDescriptor =
+ componentDescriptorFactory.rootComponentDescriptor(component);
+ if (!isValid(componentDescriptor)) {
+ return;
+ }
+ if (!isFullBindingGraphValid(componentDescriptor)) {
+ return;
+ }
+ BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor, false);
+ if (isValid(bindingGraph)) {
+ generateComponent(bindingGraph);
+ }
+ }
+
+ private void processSubcomponent(TypeElement subcomponent) {
+ if (!compilerOptions.aheadOfTimeSubcomponents()
+ && compilerOptions.fullBindingGraphValidationType(subcomponent).equals(NONE)) {
+ return;
+ }
+ if (!isSubcomponentValid(subcomponent)) {
+ return;
+ }
+ ComponentDescriptor subcomponentDescriptor =
+ componentDescriptorFactory.subcomponentDescriptor(subcomponent);
+ // TODO(dpb): ComponentDescriptorValidator for subcomponents, as we do for root components.
+ if (!isFullBindingGraphValid(subcomponentDescriptor)) {
+ return;
+ }
+ if (compilerOptions.aheadOfTimeSubcomponents()) {
+ BindingGraph bindingGraph = bindingGraphFactory.create(subcomponentDescriptor, false);
+ if (isValid(bindingGraph)) {
+ generateComponent(bindingGraph);
+ }
+ }
+ }
+
+ private void generateComponent(BindingGraph bindingGraph) {
+ componentGenerator.generate(bindingGraph, messager);
+ }
+
+ static ImmutableSet<Element> getElementsFromAnnotations(
+ final SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation,
+ Set<Class<? extends Annotation>> annotations) {
+ return ImmutableSet.copyOf(
+ Multimaps.filterKeys(elementsByAnnotation, Predicates.in(annotations)).values());
+ }
+
+ private ImmutableMap<Element, ValidationReport<TypeElement>> processCreators(
+ Set<? extends Element> builderElements, ImmutableSet.Builder<Element> rejectedElements) {
+ // Can't use an ImmutableMap.Builder here because a component may have (invalidly) more than one
+ // builder type, and that would make ImmutableMap.Builder throw.
+ Map<Element, ValidationReport<TypeElement>> reports = new HashMap<>();
+ for (Element element : builderElements) {
+ try {
+ ValidationReport<TypeElement> report =
+ creatorValidator.validate(MoreElements.asType(element));
+ report.printMessagesTo(messager);
+ reports.put(element.getEnclosingElement(), report);
+ } catch (TypeNotPresentException e) {
+ rejectedElements.add(element);
+ }
+ }
+ return ImmutableMap.copyOf(reports);
+ }
+
+ private ImmutableMap<Element, ValidationReport<TypeElement>> processSubcomponents(
+ Set<? extends Element> subcomponentElements,
+ Set<? extends Element> subcomponentBuilderElements,
+ ImmutableSet.Builder<Element> rejectedElements) {
+ ImmutableMap.Builder<Element, ValidationReport<TypeElement>> reports = ImmutableMap.builder();
+ for (Element element : subcomponentElements) {
+ try {
+ ComponentValidationReport report =
+ componentValidator.validate(
+ MoreElements.asType(element), subcomponentElements, subcomponentBuilderElements);
+ report.report().printMessagesTo(messager);
+ reports.put(element, report.report());
+ } catch (TypeNotPresentException e) {
+ rejectedElements.add(element);
+ }
+ }
+ return reports.build();
+ }
+
+ private boolean isRootComponentValid(TypeElement rootComponent) {
+ ComponentValidationReport validationReport =
+ componentValidator.validate(
+ rootComponent, subcomponentElements, subcomponentCreatorElements);
+ validationReport.report().printMessagesTo(messager);
+ return isClean(validationReport);
+ }
+
+ // TODO(dpb): Clean up generics so this can take TypeElement.
+ private boolean isSubcomponentValid(Element subcomponentElement) {
+ ValidationReport<?> subcomponentCreatorReport =
+ creatorReportsBySubcomponent.get(subcomponentElement);
+ if (subcomponentCreatorReport != null && !subcomponentCreatorReport.isClean()) {
+ return false;
+ }
+ ValidationReport<?> subcomponentReport = reportsBySubcomponent.get(subcomponentElement);
+ return subcomponentReport == null || subcomponentReport.isClean();
+ }
+
+ private boolean isFullBindingGraphValid(ComponentDescriptor componentDescriptor) {
+ if (compilerOptions
+ .fullBindingGraphValidationType(componentDescriptor.typeElement())
+ .equals(NONE)) {
+ return true;
+ }
+ BindingGraph fullBindingGraph = bindingGraphFactory.create(componentDescriptor, true);
+ return isValid(fullBindingGraph);
+ }
+
+ private boolean isValid(ComponentDescriptor componentDescriptor) {
+ ValidationReport<TypeElement> componentDescriptorReport =
+ componentDescriptorValidator.validate(componentDescriptor);
+ componentDescriptorReport.printMessagesTo(messager);
+ return componentDescriptorReport.isClean();
+ }
+
+ private boolean isValid(BindingGraph bindingGraph) {
+ return bindingGraphValidator.isValid(bindingGraphConverter.convert(bindingGraph));
+ }
+
+ /**
+ * Returns true if the component's report is clean, its builder report is clean, and all
+ * referenced subcomponent reports and subcomponent builder reports are clean.
+ */
+ private boolean isClean(ComponentValidationReport report) {
+ Element component = report.report().subject();
+ ValidationReport<?> componentReport = report.report();
+ if (!componentReport.isClean()) {
+ return false;
+ }
+ ValidationReport<?> builderReport = creatorReportsByComponent.get(component);
+ if (builderReport != null && !builderReport.isClean()) {
+ return false;
+ }
+ for (Element element : report.referencedSubcomponents()) {
+ if (!isSubcomponentValid(element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentProcessor.java b/java/dagger/internal/codegen/ComponentProcessor.java
new file mode 100644
index 0000000..541a4ab
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentProcessor.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.DYNAMIC;
+
+import com.google.auto.common.BasicAnnotationProcessor;
+import com.google.auto.service.AutoService;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.SpiModule.TestingPlugins;
+import dagger.spi.BindingGraphPlugin;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.SourceVersion;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/**
+ * The annotation processor responsible for generating the classes that drive the Dagger 2.0
+ * implementation.
+ *
+ * <p>TODO(gak): give this some better documentation
+ */
+@IncrementalAnnotationProcessor(DYNAMIC)
+@AutoService(Processor.class)
+public class ComponentProcessor extends BasicAnnotationProcessor {
+ private final Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins;
+
+ @Inject InjectBindingRegistry injectBindingRegistry;
+ @Inject SourceFileGenerator<ProvisionBinding> factoryGenerator;
+ @Inject SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator;
+ @Inject ImmutableList<ProcessingStep> processingSteps;
+ @Inject BindingGraphPlugins bindingGraphPlugins;
+ @Inject CompilerOptions compilerOptions;
+ @Inject DaggerStatisticsCollector statisticsCollector;
+ @Inject Set<ClearableCache> clearableCaches;
+
+ public ComponentProcessor() {
+ this.testingPlugins = Optional.empty();
+ }
+
+ private ComponentProcessor(Iterable<BindingGraphPlugin> testingPlugins) {
+ this.testingPlugins = Optional.of(ImmutableSet.copyOf(testingPlugins));
+ }
+
+ /**
+ * Creates a component processor that uses given {@link BindingGraphPlugin}s instead of loading
+ * them from a {@link java.util.ServiceLoader}.
+ */
+ @VisibleForTesting
+ public static ComponentProcessor forTesting(BindingGraphPlugin... testingPlugins) {
+ return forTesting(Arrays.asList(testingPlugins));
+ }
+
+ /**
+ * Creates a component processor that uses given {@link BindingGraphPlugin}s instead of loading
+ * them from a {@link java.util.ServiceLoader}.
+ */
+ @VisibleForTesting
+ public static ComponentProcessor forTesting(Iterable<BindingGraphPlugin> testingPlugins) {
+ return new ComponentProcessor(testingPlugins);
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ ImmutableSet.Builder<String> options = ImmutableSet.builder();
+ options.addAll(ProcessingEnvironmentCompilerOptions.supportedOptions());
+ options.addAll(bindingGraphPlugins.allSupportedOptions());
+ if (compilerOptions.useGradleIncrementalProcessing()) {
+ options.add("org.gradle.annotation.processing.isolating");
+ }
+ return options.build();
+ }
+
+ @Override
+ protected Iterable<? extends ProcessingStep> initSteps() {
+ ProcessorComponent.builder()
+ .processingEnvironmentModule(new ProcessingEnvironmentModule(processingEnv))
+ .testingPlugins(testingPlugins)
+ .build()
+ .inject(this);
+
+ statisticsCollector.processingStarted();
+ bindingGraphPlugins.initializePlugins();
+ return Iterables.transform(
+ processingSteps,
+ step -> new DaggerStatisticsCollectingProcessingStep(step, statisticsCollector));
+ }
+
+ @Singleton
+ @Component(
+ modules = {
+ BindingGraphValidationModule.class,
+ BindingMethodValidatorsModule.class,
+ InjectBindingRegistryModule.class,
+ ProcessingEnvironmentModule.class,
+ ProcessingRoundCacheModule.class,
+ ProcessingStepsModule.class,
+ SourceFileGeneratorsModule.class,
+ SpiModule.class,
+ SystemComponentsModule.class,
+ TopLevelImplementationComponent.InstallationModule.class,
+ })
+ interface ProcessorComponent {
+ void inject(ComponentProcessor processor);
+
+ static Builder builder() {
+ return DaggerComponentProcessor_ProcessorComponent.builder();
+ }
+
+ @CanIgnoreReturnValue
+ @Component.Builder
+ interface Builder {
+ Builder processingEnvironmentModule(ProcessingEnvironmentModule module);
+
+ @BindsInstance
+ Builder testingPlugins(
+ @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins);
+
+ @CheckReturnValue ProcessorComponent build();
+ }
+ }
+
+ @Module
+ interface ProcessingStepsModule {
+ @Provides
+ static ImmutableList<ProcessingStep> processingSteps(
+ MapKeyProcessingStep mapKeyProcessingStep,
+ InjectProcessingStep injectProcessingStep,
+ MonitoringModuleProcessingStep monitoringModuleProcessingStep,
+ MultibindingAnnotationsProcessingStep multibindingAnnotationsProcessingStep,
+ BindsInstanceProcessingStep bindsInstanceProcessingStep,
+ ModuleProcessingStep moduleProcessingStep,
+ ComponentProcessingStep componentProcessingStep,
+ ComponentHjarProcessingStep componentHjarProcessingStep,
+ BindingMethodProcessingStep bindingMethodProcessingStep,
+ CompilerOptions compilerOptions) {
+ return ImmutableList.of(
+ mapKeyProcessingStep,
+ injectProcessingStep,
+ monitoringModuleProcessingStep,
+ multibindingAnnotationsProcessingStep,
+ bindsInstanceProcessingStep,
+ moduleProcessingStep,
+ compilerOptions.headerCompilation()
+ // Ahead Of Time subcomponents use the regular hjar filtering in
+ // HjarSourceFileGenerator since they must retain protected implementation methods
+ // between subcomponents
+ && !compilerOptions.aheadOfTimeSubcomponents()
+ ? componentHjarProcessingStep
+ : componentProcessingStep,
+ bindingMethodProcessingStep);
+ }
+ }
+
+ @Override
+ protected void postRound(RoundEnvironment roundEnv) {
+ statisticsCollector.roundFinished();
+ if (roundEnv.processingOver()) {
+ statisticsCollector.processingStopped();
+ } else {
+ try {
+ injectBindingRegistry.generateSourcesForRequiredBindings(
+ factoryGenerator, membersInjectorGenerator);
+ } catch (SourceFileGenerationException e) {
+ e.printMessageTo(processingEnv.getMessager());
+ }
+ }
+ clearableCaches.forEach(ClearableCache::clearCache);
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java b/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java
new file mode 100644
index 0000000..b8c6049
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.javapoet.Expression;
+
+/** A binding expression for component provision methods. */
+final class ComponentProvisionBindingExpression extends SimpleInvocationBindingExpression {
+ private final ProvisionBinding binding;
+ private final BindingGraph bindingGraph;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final CompilerOptions compilerOptions;
+
+ ComponentProvisionBindingExpression(
+ ResolvedBindings resolvedBindings,
+ BindingGraph bindingGraph,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ CompilerOptions compilerOptions) {
+ super(resolvedBindings);
+ this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
+ this.bindingGraph = checkNotNull(bindingGraph);
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.compilerOptions = checkNotNull(compilerOptions);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ CodeBlock invocation =
+ CodeBlock.of(
+ "$L.$L()",
+ componentRequirementExpressions.getExpression(componentRequirement(), requestingClass),
+ binding.bindingElement().get().getSimpleName());
+ return Expression.create(
+ binding.contributedPrimitiveType().orElse(binding.key().type()),
+ maybeCheckForNull(binding, compilerOptions, invocation));
+ }
+
+ private ComponentRequirement componentRequirement() {
+ return bindingGraph
+ .componentDescriptor()
+ .getDependencyThatDefinesMethod(binding.bindingElement().get());
+ }
+
+ static CodeBlock maybeCheckForNull(
+ ProvisionBinding binding, CompilerOptions compilerOptions, CodeBlock invocation) {
+ return binding.shouldCheckForNull(compilerOptions)
+ ? CodeBlock.of(
+ "$T.checkNotNull($L, $S)",
+ Preconditions.class,
+ invocation,
+ "Cannot return null from a non-@Nullable component method")
+ : invocation;
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentRequirement.java b/java/dagger/internal/codegen/ComponentRequirement.java
new file mode 100644
index 0000000..3bed5da
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentRequirement.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.SourceFiles.simpleVariableName;
+import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.ComponentRequirementProto;
+import dagger.internal.codegen.serialization.ComponentRequirementProto.BoundInstanceRequirement;
+import dagger.model.BindingKind;
+import dagger.model.Key;
+import dagger.multibindings.Multibinds;
+import dagger.producers.Produces;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A type that a component needs an instance of. */
+@AutoValue
+abstract class ComponentRequirement {
+ enum Kind {
+ /** A type listed in the component's {@code dependencies} attribute. */
+ DEPENDENCY,
+
+ /** A type listed in the component or subcomponent's {@code modules} attribute. */
+ MODULE,
+
+ /**
+ * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method.
+ */
+ BOUND_INSTANCE,
+ ;
+
+ boolean isBoundInstance() {
+ return equals(BOUND_INSTANCE);
+ }
+
+ boolean isModule() {
+ return equals(MODULE);
+ }
+ }
+
+ /** The kind of requirement. */
+ abstract Kind kind();
+
+ /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */
+ // TODO(ronshapiro): consider removing this and inlining the usages
+ final boolean isBoundInstance() {
+ return kind().isBoundInstance();
+ }
+
+ /**
+ * The type of the instance the component must have, wrapped so that requirements can be used as
+ * value types.
+ */
+ abstract Equivalence.Wrapper<TypeMirror> wrappedType();
+
+ /** The type of the instance the component must have. */
+ TypeMirror type() {
+ return wrappedType().get();
+ }
+
+ /** The element associated with the type of this requirement. */
+ TypeElement typeElement() {
+ return MoreTypes.asTypeElement(type());
+ }
+
+ /** The action a component builder should take if it {@code null} is passed. */
+ enum NullPolicy {
+ /** Make a new instance. */
+ NEW,
+ /** Throw an exception. */
+ THROW,
+ /** Allow use of null values. */
+ ALLOW,
+ }
+
+ /**
+ * An override for the requirement's null policy. If set, this is used as the null policy instead
+ * of the default behavior in {@link #nullPolicy}.
+ *
+ * <p>Some implementations' null policy can be determined upon construction (e.g., for binding
+ * instances), but others' require Elements and Types, which must wait until {@link #nullPolicy}
+ * is called.
+ */
+ abstract Optional<NullPolicy> overrideNullPolicy();
+
+ /** The requirement's null policy. */
+ NullPolicy nullPolicy(DaggerElements elements, DaggerTypes types) {
+ if (overrideNullPolicy().isPresent()) {
+ return overrideNullPolicy().get();
+ }
+ switch (kind()) {
+ case MODULE:
+ return componentCanMakeNewInstances(typeElement())
+ ? NullPolicy.NEW
+ : requiresAPassedInstance(elements, types) ? NullPolicy.THROW : NullPolicy.ALLOW;
+ case DEPENDENCY:
+ case BOUND_INSTANCE:
+ return NullPolicy.THROW;
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns true if the passed {@link ComponentRequirement} requires a passed instance in order to
+ * be used within a component.
+ */
+ boolean requiresAPassedInstance(DaggerElements elements, DaggerTypes types) {
+ if (!kind().isModule()) {
+ // Bound instances and dependencies always require the user to provide an instance.
+ return true;
+ }
+ return requiresModuleInstance(elements, types) && !componentCanMakeNewInstances(typeElement());
+ }
+
+ /**
+ * Returns {@code true} if an instance is needed for this (module) requirement.
+ *
+ * <p>An instance is only needed if there is a binding method on the module that is neither {@code
+ * abstract} nor {@code static}; if all bindings are one of those, then there should be no
+ * possible dependency on instance state in the module's bindings.
+ */
+ private boolean requiresModuleInstance(DaggerElements elements, DaggerTypes types) {
+ ImmutableSet<ExecutableElement> methods =
+ getLocalAndInheritedMethods(typeElement(), types, elements);
+ return methods.stream()
+ .filter(this::isBindingMethod)
+ .map(ExecutableElement::getModifiers)
+ .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
+ }
+
+ private boolean isBindingMethod(ExecutableElement method) {
+ // TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff
+ // in one place; listing individual annotations all over the place is brittle.
+ return isAnyAnnotationPresent(
+ method,
+ Provides.class,
+ Produces.class,
+ // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe
+ // these, like @AbstractBindingMethod
+ Binds.class,
+ Multibinds.class,
+ BindsOptionalOf.class);
+ }
+
+ /** The key for this requirement, if one is available. */
+ abstract Optional<Key> key();
+
+ /** Returns the name for this requirement that could be used as a variable. */
+ abstract String variableName();
+
+ /** Returns a parameter spec for this requirement. */
+ ParameterSpec toParameterSpec() {
+ return ParameterSpec.builder(TypeName.get(type()), variableName()).build();
+ }
+
+ /** Creates a proto representation of this requirement. */
+ ComponentRequirementProto toProto() {
+ switch (kind()) {
+ case DEPENDENCY:
+ return ComponentRequirementProto.newBuilder()
+ .setDependency(TypeProtoConverter.toProto(type()))
+ .build();
+ case MODULE:
+ return ComponentRequirementProto.newBuilder()
+ .setModule(TypeProtoConverter.toProto(type()))
+ .build();
+ case BOUND_INSTANCE:
+ return ComponentRequirementProto.newBuilder()
+ .setBoundInstance(
+ BoundInstanceRequirement.newBuilder()
+ .setKey(KeyFactory.toProto(key().get()))
+ .setNullable(overrideNullPolicy().equals(Optional.of(NullPolicy.ALLOW)))
+ .setVariableName(variableName()))
+ .build();
+ }
+ throw new AssertionError(this);
+ }
+
+ static ComponentRequirement forDependency(TypeMirror type) {
+ return new AutoValue_ComponentRequirement(
+ Kind.DEPENDENCY,
+ MoreTypes.equivalence().wrap(checkNotNull(type)),
+ Optional.empty(),
+ Optional.empty(),
+ simpleVariableName(MoreTypes.asTypeElement(type)));
+ }
+
+ static ComponentRequirement forModule(TypeMirror type) {
+ return new AutoValue_ComponentRequirement(
+ Kind.MODULE,
+ MoreTypes.equivalence().wrap(checkNotNull(type)),
+ Optional.empty(),
+ Optional.empty(),
+ simpleVariableName(MoreTypes.asTypeElement(type)));
+ }
+
+ static ComponentRequirement forBoundInstance(Key key, boolean nullable, String variableName) {
+ return new AutoValue_ComponentRequirement(
+ Kind.BOUND_INSTANCE,
+ MoreTypes.equivalence().wrap(key.type()),
+ nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(),
+ Optional.of(key),
+ variableName);
+ }
+
+ static ComponentRequirement forBoundInstance(ContributionBinding binding) {
+ checkArgument(binding.kind().equals(BindingKind.BOUND_INSTANCE));
+ return forBoundInstance(
+ binding.key(),
+ binding.nullableType().isPresent(),
+ binding.bindingElement().get().getSimpleName().toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java b/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java
new file mode 100644
index 0000000..d6aa053
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.Expression;
+
+/**
+ * A binding expression for instances bound with {@link dagger.BindsInstance} and instances of
+ * {@linkplain dagger.Component#dependencies() component} and {@linkplain
+ * dagger.producers.ProductionComponent#dependencies() production component dependencies}.
+ */
+final class ComponentRequirementBindingExpression extends SimpleInvocationBindingExpression {
+ private final ComponentRequirement componentRequirement;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+
+ ComponentRequirementBindingExpression(
+ ResolvedBindings resolvedBindings,
+ ComponentRequirement componentRequirement,
+ ComponentRequirementExpressions componentRequirementExpressions) {
+ super(resolvedBindings);
+ this.componentRequirement = componentRequirement;
+ this.componentRequirementExpressions = componentRequirementExpressions;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(
+ componentRequirement.type(),
+ componentRequirementExpressions.getExpression(componentRequirement, requestingClass));
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentRequirementExpression.java b/java/dagger/internal/codegen/ComponentRequirementExpression.java
new file mode 100644
index 0000000..b25c01b
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentRequirementExpression.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+
+/**
+ * A factory for expressions of {@link ComponentRequirement}s in the generated component. This is
+ * <em>not</em> a {@link BindingExpression}, since {@link ComponentRequirement}s do not have a
+ * {@link dagger.model.Key}. See {@link ComponentRequirementBindingExpression} for binding
+ * expressions that are themselves a component requirement.
+ */
+interface ComponentRequirementExpression {
+ /**
+ * Returns an expression for the {@link ComponentRequirement} to be used when implementing a
+ * component method. This may add a field or method to the component in order to reference the
+ * component requirement outside of the {@code initialize()} methods.
+ */
+ CodeBlock getExpression(ClassName requestingClass);
+
+ /**
+ * Returns an expression for the {@link ComponentRequirement} to be used only within {@code
+ * initialize()} methods, where the constructor parameters are available.
+ *
+ * <p>When accessing this expression from a subcomponent, this may cause a field to be initialized
+ * or a method to be added in the component that owns this {@link ComponentRequirement}.
+ */
+ default CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
+ return getExpression(requestingClass);
+ }
+
+ /**
+ * Returns the expression for the {@link ComponentRequirement} to be used when reimplementing a
+ * modifiable module method.
+ */
+ default CodeBlock getModifiableModuleMethodExpression(ClassName requestingClass) {
+ return CodeBlock.of("return $L", getExpression(requestingClass));
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentRequirementExpressions.java b/java/dagger/internal/codegen/ComponentRequirementExpressions.java
new file mode 100644
index 0000000..4ab58c9
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentRequirementExpressions.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Suppliers.memoize;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.COMPONENT_REQUIREMENT_FIELD;
+import static dagger.internal.codegen.ModuleProxies.newModuleInstance;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PROTECTED;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A central repository of expressions used to access any {@link ComponentRequirement} available to
+ * a component.
+ */
+@PerComponentImplementation
+final class ComponentRequirementExpressions {
+
+ // TODO(dpb,ronshapiro): refactor this and ComponentBindingExpressions into a
+ // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
+ // parents? If so, maybe make ComponentRequirementExpression.Factory create it.
+
+ private final Optional<ComponentRequirementExpressions> parent;
+ private final Map<ComponentRequirement, ComponentRequirementExpression>
+ componentRequirementExpressions = new HashMap<>();
+ private final BindingGraph graph;
+ private final ComponentImplementation componentImplementation;
+ private final CompilerOptions compilerOptions;
+ private final DaggerElements elements;
+
+ // TODO(ronshapiro): give ComponentImplementation a graph() method
+ @Inject
+ ComponentRequirementExpressions(
+ @ParentComponent Optional<ComponentRequirementExpressions> parent,
+ BindingGraph graph,
+ ComponentImplementation componentImplementation,
+ CompilerOptions compilerOptions,
+ DaggerElements elements) {
+ this.parent = parent;
+ this.graph = graph;
+ this.componentImplementation = componentImplementation;
+ this.compilerOptions = compilerOptions;
+ this.elements = elements;
+ }
+
+ /**
+ * Returns an expression for the {@code componentRequirement} to be used when implementing a
+ * component method. This may add a field or method to the component in order to reference the
+ * component requirement outside of the {@code initialize()} methods.
+ */
+ CodeBlock getExpression(ComponentRequirement componentRequirement, ClassName requestingClass) {
+ return getExpression(componentRequirement).getExpression(requestingClass);
+ }
+
+ /**
+ * Returns an expression for the {@code componentRequirement} to be used only within {@code
+ * initialize()} methods, where the component constructor parameters are available.
+ *
+ * <p>When accessing this expression from a subcomponent, this may cause a field to be initialized
+ * or a method to be added in the component that owns this {@link ComponentRequirement}.
+ */
+ CodeBlock getExpressionDuringInitialization(
+ ComponentRequirement componentRequirement, ClassName requestingClass) {
+ return getExpression(componentRequirement).getExpressionDuringInitialization(requestingClass);
+ }
+
+ ComponentRequirementExpression getExpression(ComponentRequirement componentRequirement) {
+ if (graph.componentRequirements().contains(componentRequirement)) {
+ return componentRequirementExpressions.computeIfAbsent(
+ componentRequirement, this::createMethodOrField);
+ }
+ if (parent.isPresent()) {
+ return parent.get().getExpression(componentRequirement);
+ }
+
+ if (componentRequirement.kind().isModule() && compilerOptions.aheadOfTimeSubcomponents()) {
+ return new PrunedModifiableModule(componentRequirement);
+ }
+
+ throw new IllegalStateException(
+ "no component requirement expression found for " + componentRequirement);
+ }
+
+ /**
+ * If {@code requirement} is a module that may be owned by a future ancestor component, returns a
+ * modifiable module method. Otherwise, returns a field for {@code requirement}.
+ */
+ private ComponentRequirementExpression createMethodOrField(ComponentRequirement requirement) {
+ if (componentImplementation.isAbstract() && requirement.kind().isModule()) {
+ return new ModifiableModule(requirement);
+ }
+ return createField(requirement);
+ }
+
+ /** Returns a field for a {@link ComponentRequirement}. */
+ private ComponentRequirementExpression createField(ComponentRequirement requirement) {
+ if (componentImplementation.componentDescriptor().hasCreator()) {
+ return new ComponentParameterField(requirement, componentImplementation, Optional.empty());
+ } else if (graph.factoryMethod().isPresent()
+ && graph.factoryMethodParameters().containsKey(requirement)) {
+ String parameterName =
+ graph.factoryMethodParameters().get(requirement).getSimpleName().toString();
+ return new ComponentParameterField(
+ requirement, componentImplementation, Optional.of(parameterName));
+ } else if (requirement.kind().isModule()) {
+ return new InstantiableModuleField(requirement, componentImplementation);
+ } else {
+ throw new AssertionError(
+ String.format("Can't create %s in %s", requirement, componentImplementation.name()));
+ }
+ }
+
+ private abstract static class AbstractField implements ComponentRequirementExpression {
+ final ComponentRequirement componentRequirement;
+ final ComponentImplementation componentImplementation;
+ final String fieldName;
+ private final Supplier<MemberSelect> field = memoize(this::addField);
+
+ private AbstractField(
+ ComponentRequirement componentRequirement,
+ ComponentImplementation componentImplementation) {
+ this.componentRequirement = checkNotNull(componentRequirement);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ // Note: The field name is being claimed eagerly here even though we don't know at this point
+ // whether or not the requirement will even need a field. This is done because:
+ // A) ComponentParameterField wants to ensure that it doesn't give the parameter the same name
+ // as any field in the component, which requires that it claim a "field name" for itself
+ // when naming the parameter.
+ // B) The parameter name may be needed before the field name is.
+ // C) We want to prefer giving the best name to the field rather than the parameter given its
+ // wider scope.
+ this.fieldName =
+ componentImplementation.getUniqueFieldName(componentRequirement.variableName());
+ }
+
+ @Override
+ public CodeBlock getExpression(ClassName requestingClass) {
+ return field.get().getExpressionFor(requestingClass);
+ }
+
+ private MemberSelect addField() {
+ FieldSpec field = createField();
+ componentImplementation.addField(COMPONENT_REQUIREMENT_FIELD, field);
+ componentImplementation.addComponentRequirementInitialization(fieldInitialization(field));
+ return MemberSelect.localField(componentImplementation.name(), fieldName);
+ }
+
+ private FieldSpec createField() {
+ FieldSpec.Builder field =
+ FieldSpec.builder(TypeName.get(componentRequirement.type()), fieldName, PRIVATE);
+ if (!componentImplementation.isAbstract()) {
+ field.addModifiers(FINAL);
+ }
+ return field.build();
+ }
+
+ /** Returns the {@link CodeBlock} that initializes the component field during construction. */
+ abstract CodeBlock fieldInitialization(FieldSpec componentField);
+ }
+
+ /**
+ * A {@link ComponentRequirementExpression} for {@link ComponentRequirement}s that can be
+ * instantiated by the component (i.e. a static class with a no-arg constructor).
+ */
+ private final class InstantiableModuleField extends AbstractField {
+ private final TypeElement moduleElement;
+
+ private InstantiableModuleField(
+ ComponentRequirement module, ComponentImplementation componentImplementation) {
+ super(module, componentImplementation);
+ checkArgument(module.kind().isModule());
+ this.moduleElement = module.typeElement();
+ }
+
+ @Override
+ CodeBlock fieldInitialization(FieldSpec componentField) {
+ return CodeBlock.of(
+ "this.$N = $L;",
+ componentField,
+ newModuleInstance(moduleElement, componentImplementation.name(), elements));
+ }
+ }
+
+ /**
+ * A {@link ComponentRequirementExpression} for {@link ComponentRequirement}s that are passed in
+ * as parameters to the component's constructor.
+ */
+ private static final class ComponentParameterField extends AbstractField {
+ private final String parameterName;
+
+ private ComponentParameterField(
+ ComponentRequirement componentRequirement,
+ ComponentImplementation componentImplementation,
+ Optional<String> name) {
+ super(componentRequirement, componentImplementation);
+ componentImplementation.addComponentRequirementParameter(componentRequirement);
+ // Get the name that the component implementation will use for its parameter for the
+ // requirement. If the given name is different than the name of the field created for the
+ // requirement (as may be the case when the parameter name is derived from a user-written
+ // factory method parameter), just use that as the base name for the parameter. Otherwise,
+ // append "Param" to the end of the name to differentiate.
+ // In either case, componentImplementation.getParameterName() will ensure that the final name
+ // that is used is not the same name as any field in the component even if there's something
+ // weird where the component actually has fields named, say, "foo" and "fooParam".
+ String baseName = name.filter(n -> !n.equals(fieldName)).orElse(fieldName + "Param");
+ this.parameterName = componentImplementation.getParameterName(componentRequirement, baseName);
+ }
+
+ @Override
+ public CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
+ if (componentImplementation.name().equals(requestingClass)) {
+ return CodeBlock.of("$L", parameterName);
+ } else {
+ // requesting this component requirement during initialization of a child component requires
+ // it to be accessed from a field and not the parameter (since it is no longer available)
+ return getExpression(requestingClass);
+ }
+ }
+
+ @Override
+ CodeBlock fieldInitialization(FieldSpec componentField) {
+ // Don't checkNotNull here because the parameter may be nullable; if it isn't, the caller
+ // should handle checking that before passing the parameter.
+ return CodeBlock.of("this.$N = $L;", componentField, parameterName);
+ }
+ }
+
+ private final class ModifiableModule implements ComponentRequirementExpression {
+ private final ComponentRequirement module;
+ private final Supplier<MemberSelect> method = Suppliers.memoize(this::methodSelect);
+
+ private ModifiableModule(ComponentRequirement module) {
+ checkArgument(module.kind().isModule());
+ this.module = module;
+ }
+
+ @Override
+ public CodeBlock getExpression(ClassName requestingClass) {
+ return method.get().getExpressionFor(requestingClass);
+ }
+
+ private MemberSelect methodSelect() {
+ String methodName =
+ componentImplementation
+ .supertypeModifiableModuleMethodName(module)
+ .orElseGet(this::createMethod);
+ return MemberSelect.localMethod(componentImplementation.name(), methodName);
+ }
+
+ private String createMethod() {
+ String methodName =
+ UPPER_CAMEL.to(
+ LOWER_CAMEL,
+ componentImplementation.getUniqueMethodName(
+ module.typeElement().getSimpleName().toString()));
+ MethodSpec.Builder methodBuilder =
+ methodBuilder(methodName)
+ .addModifiers(PROTECTED)
+ .returns(TypeName.get(module.type()));
+ // TODO(b/117833324): if the module is instantiable, we could provide an implementation here
+ // too. Then, if no ancestor ever repeats the module, there's nothing to do in subclasses.
+ if (graph.componentDescriptor().creatorDescriptor().isPresent()) {
+ methodBuilder.addStatement(
+ "return $L",
+ createField(module).getExpression(componentImplementation.name()));
+ } else {
+ methodBuilder.addModifiers(ABSTRACT);
+ }
+ componentImplementation.addModifiableModuleMethod(module, methodBuilder.build());
+ return methodName;
+ }
+ }
+
+ private static final class PrunedModifiableModule implements ComponentRequirementExpression {
+ private final ComponentRequirement module;
+
+ private PrunedModifiableModule(ComponentRequirement module) {
+ checkArgument(module.kind().isModule());
+ this.module = module;
+ }
+
+ @Override
+ public CodeBlock getExpression(ClassName requestingClass) {
+ throw new UnsupportedOperationException(module + " is pruned - it cannot be requested");
+ }
+
+ @Override
+ public CodeBlock getModifiableModuleMethodExpression(ClassName requestingClass) {
+ return CodeBlock.builder()
+ .add(
+ "// $L has been pruned from the final resolved binding graph. The result of this "
+ + "method should never be used, but it may be called in an initialize() method "
+ + "when creating a framework instance of a now-pruned binding. Those framework "
+ + "instances should never be used.\n",
+ module.typeElement())
+ .add("return null")
+ .build();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentTreeTraverser.java b/java/dagger/internal/codegen/ComponentTreeTraverser.java
new file mode 100644
index 0000000..cc1efd2
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentTreeTraverser.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * An object that traverses the entire component hierarchy, starting from the root component.
+ *
+ * <p>Subclasses can override {@link #visitComponent(BindingGraph)} to perform custom logic at each
+ * component in the tree, and {@link #visitSubcomponentFactoryMethod(BindingGraph, BindingGraph,
+ * ExecutableElement)} to perform custom logic at each subcomponent factory method.
+ */
+public class ComponentTreeTraverser {
+
+ /** The path from the root graph to the currently visited graph. */
+ private final Deque<BindingGraph> bindingGraphPath = new ArrayDeque<>();
+
+ /** The {@link ComponentPath} for each component in {@link #bindingGraphPath}. */
+ private final Deque<ComponentPath> componentPaths = new ArrayDeque<>();
+
+ /** Constructs a traverser for a root (component, not subcomponent) binding graph. */
+ public ComponentTreeTraverser(BindingGraph rootGraph) {
+ bindingGraphPath.add(rootGraph);
+ componentPaths.add(ComponentPath.create(ImmutableList.of(rootGraph.componentTypeElement())));
+ }
+
+ /**
+ * Calls {@link #visitComponent(BindingGraph)} for the root component.
+ *
+ * @throws IllegalStateException if a traversal is in progress
+ */
+ public final void traverseComponents() {
+ checkState(bindingGraphPath.size() == 1);
+ checkState(componentPaths.size() == 1);
+ visitComponent(bindingGraphPath.getFirst());
+ }
+
+ /**
+ * Called once for each component in a component hierarchy.
+ *
+ * <p>Subclasses can override this method to perform whatever logic is required per component.
+ * They should call the {@code super} implementation if they want to continue the traversal in the
+ * standard order.
+ *
+ * <p>This implementation does the following:
+ *
+ * <ol>
+ * <li>If this component is installed in its parent by a subcomponent factory method, calls
+ * {@link #visitSubcomponentFactoryMethod(BindingGraph, BindingGraph, ExecutableElement)}.
+ * <li>For each entry point in the component, calls {@link #visitEntryPoint(DependencyRequest,
+ * BindingGraph)}.
+ * <li>For each child component, calls {@link #visitComponent(BindingGraph)}, updating the
+ * traversal state.
+ * </ol>
+ *
+ * @param graph the currently visited graph
+ */
+ protected void visitComponent(BindingGraph graph) {
+ if (bindingGraphPath.size() > 1) {
+ BindingGraph parent = Iterators.get(bindingGraphPath.descendingIterator(), 1);
+ parent
+ .componentDescriptor()
+ .getFactoryMethodForChildComponent(graph.componentDescriptor())
+ .ifPresent(
+ childFactoryMethod ->
+ visitSubcomponentFactoryMethod(
+ graph, parent, childFactoryMethod.methodElement()));
+ }
+
+ for (ComponentMethodDescriptor entryPointMethod :
+ graph.componentDescriptor().entryPointMethods()) {
+ visitEntryPoint(entryPointMethod.dependencyRequest().get(), graph);
+ }
+
+ for (BindingGraph child : graph.subgraphs()) {
+ bindingGraphPath.addLast(child);
+ ComponentPath childPath =
+ ComponentPath.create(
+ bindingGraphPath.stream()
+ .map(BindingGraph::componentTypeElement)
+ .collect(toImmutableList()));
+ componentPaths.addLast(childPath);
+ try {
+ visitComponent(child);
+ } finally {
+ verify(bindingGraphPath.removeLast().equals(child));
+ verify(componentPaths.removeLast().equals(childPath));
+ }
+ }
+ }
+
+ /**
+ * Called if this component was installed in its parent by a subcomponent factory method.
+ *
+ * <p>This implementation does nothing.
+ *
+ * @param graph the currently visited graph
+ * @param parent the parent graph
+ * @param factoryMethod the factory method in the parent component that declares that the current
+ * component is a child
+ */
+ protected void visitSubcomponentFactoryMethod(
+ BindingGraph graph, BindingGraph parent, ExecutableElement factoryMethod) {}
+
+ /**
+ * Called once for each entry point in a component.
+ *
+ * <p>Subclasses can override this method to perform whatever logic is required per entry point.
+ * They should call the {@code super} implementation if they want to continue the traversal in the
+ * standard order.
+ *
+ * <p>This implementation does nothing.
+ *
+ * @param graph the graph for the component that contains the entry point
+ */
+ protected void visitEntryPoint(DependencyRequest entryPoint, BindingGraph graph) {}
+
+ /**
+ * Returns an immutable snapshot of the path from the root component to the currently visited
+ * component.
+ */
+ protected final ComponentPath componentPath() {
+ return componentPaths.getLast();
+ }
+
+ /**
+ * Returns the subpath from the root component to the matching {@code ancestor} of the current
+ * component.
+ */
+ protected final ComponentPath pathFromRootToAncestor(TypeElement ancestor) {
+ for (ComponentPath componentPath : componentPaths) {
+ if (componentPath.currentComponent().equals(ancestor)) {
+ return componentPath;
+ }
+ }
+ throw new IllegalArgumentException(
+ String.format("%s is not in the current path: %s", ancestor.getQualifiedName(), this));
+ }
+
+ /**
+ * Returns the BindingGraph for {@code ancestor}, where {@code ancestor} is in the component path
+ * of the current traversal.
+ */
+ protected final BindingGraph graphForAncestor(TypeElement ancestor) {
+ for (BindingGraph graph : bindingGraphPath) {
+ if (graph.componentTypeElement().equals(ancestor)) {
+ return graph;
+ }
+ }
+ throw new IllegalArgumentException(
+ String.format("%s is not in the current path: %s", ancestor.getQualifiedName(), this));
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentValidator.java b/java/dagger/internal/codegen/ComponentValidator.java
new file mode 100644
index 0000000..7739915
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentValidator.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.Multimaps.asMap;
+import static com.google.common.collect.Sets.intersection;
+import static dagger.internal.codegen.ComponentAnnotation.anyComponentAnnotation;
+import static dagger.internal.codegen.ComponentAnnotation.componentAnnotation;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.creatorAnnotationsFor;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.productionCreatorAnnotations;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.ComponentKind.annotationsFor;
+import static dagger.internal.codegen.ConfigurationAnnotations.enclosedAnnotatedTypes;
+import static dagger.internal.codegen.ConfigurationAnnotations.getTransitiveModules;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.builderMethodRequiresNoArgs;
+import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
+import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
+import static java.util.Comparator.comparing;
+import static javax.lang.model.element.ElementKind.CLASS;
+import static javax.lang.model.element.ElementKind.INTERFACE;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.type.TypeKind.VOID;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import dagger.Component;
+import dagger.Reusable;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.producers.CancellationPolicy;
+import dagger.producers.ProductionComponent;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor6;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Performs superficial validation of the contract of the {@link Component} and {@link
+ * ProductionComponent} annotations.
+ */
+final class ComponentValidator {
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final ModuleValidator moduleValidator;
+ private final ComponentCreatorValidator creatorValidator;
+ private final DependencyRequestValidator dependencyRequestValidator;
+ private final MembersInjectionValidator membersInjectionValidator;
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final DependencyRequestFactory dependencyRequestFactory;
+
+ @Inject
+ ComponentValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ ModuleValidator moduleValidator,
+ ComponentCreatorValidator creatorValidator,
+ DependencyRequestValidator dependencyRequestValidator,
+ MembersInjectionValidator membersInjectionValidator,
+ MethodSignatureFormatter methodSignatureFormatter,
+ DependencyRequestFactory dependencyRequestFactory) {
+ this.elements = elements;
+ this.types = types;
+ this.moduleValidator = moduleValidator;
+ this.creatorValidator = creatorValidator;
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ this.membersInjectionValidator = membersInjectionValidator;
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ }
+
+ @AutoValue
+ abstract static class ComponentValidationReport {
+ abstract ImmutableSet<Element> referencedSubcomponents();
+
+ abstract ValidationReport<TypeElement> report();
+ }
+
+ /**
+ * Validates the given component subject. Also validates any referenced subcomponents that aren't
+ * already included in the {@code validatedSubcomponents} set.
+ */
+ public ComponentValidationReport validate(
+ TypeElement subject,
+ Set<? extends Element> validatedSubcomponents,
+ Set<? extends Element> validatedSubcomponentCreators) {
+ ValidationReport.Builder<TypeElement> report = ValidationReport.about(subject);
+
+ ImmutableSet<ComponentKind> componentKinds = ComponentKind.getComponentKinds(subject);
+ ImmutableSet<Element> allSubcomponents;
+ if (componentKinds.size() > 1) {
+ String error =
+ "Components may not be annotated with more than one component annotation: found "
+ + annotationsFor(componentKinds);
+ report.addError(error, subject);
+ allSubcomponents = ImmutableSet.of();
+ } else {
+ ComponentKind componentKind = getOnlyElement(componentKinds);
+ ComponentAnnotation componentAnnotation = anyComponentAnnotation(subject).get();
+ allSubcomponents =
+ validate(
+ subject,
+ componentAnnotation,
+ componentKind,
+ validatedSubcomponents,
+ validatedSubcomponentCreators,
+ report);
+ }
+
+ return new AutoValue_ComponentValidator_ComponentValidationReport(
+ allSubcomponents, report.build());
+ }
+
+ private ImmutableSet<Element> validate(
+ TypeElement subject,
+ ComponentAnnotation componentAnnotation,
+ ComponentKind componentKind,
+ Set<? extends Element> validatedSubcomponents,
+ Set<? extends Element> validatedSubcomponentCreators,
+ ValidationReport.Builder<TypeElement> report) {
+ if (isAnnotationPresent(subject, CancellationPolicy.class) && !componentKind.isProducer()) {
+ report.addError(
+ "@CancellationPolicy may only be applied to production components and subcomponents",
+ subject);
+ }
+
+ if (!subject.getKind().equals(INTERFACE)
+ && !(subject.getKind().equals(CLASS) && subject.getModifiers().contains(ABSTRACT))) {
+ report.addError(
+ String.format(
+ "@%s may only be applied to an interface or abstract class",
+ componentKind.annotation().getSimpleName()),
+ subject);
+ }
+
+ ImmutableList<DeclaredType> creators =
+ creatorAnnotationsFor(componentAnnotation).stream()
+ .flatMap(annotation -> enclosedAnnotatedTypes(subject, annotation).stream())
+ .collect(toImmutableList());
+ if (creators.size() > 1) {
+ report.addError(
+ String.format(ErrorMessages.componentMessagesFor(componentKind).moreThanOne(), creators),
+ subject);
+ }
+
+ Optional<AnnotationMirror> reusableAnnotation = getAnnotationMirror(subject, Reusable.class);
+ if (reusableAnnotation.isPresent()) {
+ report.addError(
+ "@Reusable cannot be applied to components or subcomponents",
+ subject,
+ reusableAnnotation.get());
+ }
+
+ DeclaredType subjectType = MoreTypes.asDeclared(subject.asType());
+
+ SetMultimap<Element, ExecutableElement> referencedSubcomponents = LinkedHashMultimap.create();
+ getLocalAndInheritedMethods(subject, types, elements).stream()
+ .filter(method -> method.getModifiers().contains(ABSTRACT))
+ .forEachOrdered(
+ method -> {
+ ExecutableType resolvedMethod = asExecutable(types.asMemberOf(subjectType, method));
+ List<? extends TypeMirror> parameterTypes = resolvedMethod.getParameterTypes();
+ List<? extends VariableElement> parameters = method.getParameters();
+ TypeMirror returnType = resolvedMethod.getReturnType();
+
+ if (!resolvedMethod.getTypeVariables().isEmpty()) {
+ report.addError("Component methods cannot have type variables", method);
+ }
+
+ // abstract methods are ones we have to implement, so they each need to be validated
+ // first, check the return type. if it's a subcomponent, validate that method as such.
+ Optional<AnnotationMirror> subcomponentAnnotation =
+ checkForAnnotations(
+ returnType,
+ componentKind.legalSubcomponentKinds().stream()
+ .map(ComponentKind::annotation)
+ .collect(toImmutableSet()));
+ Optional<AnnotationMirror> subcomponentCreatorAnnotation =
+ checkForAnnotations(
+ returnType,
+ componentAnnotation.isProduction()
+ ? intersection(
+ subcomponentCreatorAnnotations(), productionCreatorAnnotations())
+ : subcomponentCreatorAnnotations());
+ if (subcomponentAnnotation.isPresent()) {
+ referencedSubcomponents.put(MoreTypes.asElement(returnType), method);
+ validateSubcomponentMethod(
+ report,
+ ComponentKind.forAnnotatedElement(MoreTypes.asTypeElement(returnType)).get(),
+ method,
+ parameters,
+ parameterTypes,
+ returnType,
+ subcomponentAnnotation);
+ } else if (subcomponentCreatorAnnotation.isPresent()) {
+ referencedSubcomponents.put(
+ MoreTypes.asElement(returnType).getEnclosingElement(), method);
+ validateSubcomponentCreatorMethod(
+ report, method, parameters, returnType, validatedSubcomponentCreators);
+ } else {
+ // if it's not a subcomponent...
+ switch (parameters.size()) {
+ case 0:
+ // no parameters means that it is a provision method
+ dependencyRequestValidator.validateDependencyRequest(
+ report, method, returnType);
+ break;
+ case 1:
+ // one parameter means that it's a members injection method
+ TypeMirror parameterType = Iterables.getOnlyElement(parameterTypes);
+ report.addSubreport(
+ membersInjectionValidator.validateMembersInjectionMethod(
+ method, parameterType));
+ if (!(returnType.getKind().equals(VOID)
+ || types.isSameType(returnType, parameterType))) {
+ report.addError(
+ "Members injection methods may only return the injected type or void.",
+ method);
+ }
+ break;
+ default:
+ // this isn't any method that we know how to implement...
+ report.addError(
+ "This method isn't a valid provision method, members injection method or "
+ + "subcomponent factory method. Dagger cannot implement this method",
+ method);
+ break;
+ }
+ }
+ });
+
+ checkConflictingEntryPoints(report);
+
+ Maps.filterValues(referencedSubcomponents.asMap(), methods -> methods.size() > 1)
+ .forEach(
+ (subcomponent, methods) ->
+ report.addError(
+ String.format(moreThanOneRefToSubcomponent(), subcomponent, methods), subject));
+
+ validateComponentDependencies(report, componentAnnotation.dependencyTypes());
+ report.addSubreport(
+ moduleValidator.validateReferencedModules(
+ subject,
+ componentAnnotation.annotation(),
+ componentKind.legalModuleKinds(),
+ new HashSet<>()));
+
+ // Make sure we validate any subcomponents we're referencing, unless we know we validated
+ // them already in this pass.
+ // TODO(sameb): If subcomponents refer to each other and both aren't in
+ // 'validatedSubcomponents' (e.g, both aren't compiled in this pass),
+ // then this can loop forever.
+ ImmutableSet.Builder<Element> allSubcomponents =
+ ImmutableSet.<Element>builder().addAll(referencedSubcomponents.keySet());
+ for (Element subcomponent :
+ Sets.difference(referencedSubcomponents.keySet(), validatedSubcomponents)) {
+ ComponentValidationReport subreport =
+ validate(asType(subcomponent), validatedSubcomponents, validatedSubcomponentCreators);
+ report.addItems(subreport.report().items());
+ allSubcomponents.addAll(subreport.referencedSubcomponents());
+ }
+ return allSubcomponents.build();
+ }
+
+ private void checkConflictingEntryPoints(ValidationReport.Builder<TypeElement> report) {
+ DeclaredType componentType = asDeclared(report.getSubject().asType());
+
+ // Collect entry point methods that are not overridden by others. If the "same" method is
+ // inherited from more than one supertype, each will be in the multimap.
+ SetMultimap<String, ExecutableElement> entryPointMethods = HashMultimap.create();
+
+ methodsIn(elements.getAllMembers(report.getSubject()))
+ .stream()
+ .filter(
+ method -> isEntryPoint(method, asExecutable(types.asMemberOf(componentType, method))))
+ .forEach(
+ method ->
+ addMethodUnlessOverridden(
+ method, entryPointMethods.get(method.getSimpleName().toString())));
+
+ for (Set<ExecutableElement> methods : asMap(entryPointMethods).values()) {
+ if (distinctKeys(methods, report.getSubject()).size() > 1) {
+ reportConflictingEntryPoints(methods, report);
+ }
+ }
+ }
+
+ private boolean isEntryPoint(ExecutableElement method, ExecutableType methodType) {
+ return method.getModifiers().contains(ABSTRACT)
+ && method.getParameters().isEmpty()
+ && !methodType.getReturnType().getKind().equals(VOID)
+ && methodType.getTypeVariables().isEmpty();
+ }
+
+ private ImmutableSet<Key> distinctKeys(Set<ExecutableElement> methods, TypeElement component) {
+ return methods
+ .stream()
+ .map(method -> dependencyRequest(method, component))
+ .map(DependencyRequest::key)
+ .collect(toImmutableSet());
+ }
+
+ private DependencyRequest dependencyRequest(ExecutableElement method, TypeElement component) {
+ ExecutableType methodType =
+ asExecutable(types.asMemberOf(asDeclared(component.asType()), method));
+ return ComponentKind.forAnnotatedElement(component).get().isProducer()
+ ? dependencyRequestFactory.forComponentProductionMethod(method, methodType)
+ : dependencyRequestFactory.forComponentProvisionMethod(method, methodType);
+ }
+
+ private void addMethodUnlessOverridden(ExecutableElement method, Set<ExecutableElement> methods) {
+ if (methods.stream().noneMatch(existingMethod -> overridesAsDeclared(existingMethod, method))) {
+ methods.removeIf(existingMethod -> overridesAsDeclared(method, existingMethod));
+ methods.add(method);
+ }
+ }
+
+ /**
+ * Returns {@code true} if {@code overrider} overrides {@code overridden} considered from within
+ * the type that declares {@code overrider}.
+ */
+ // TODO(dpb): Does this break for ECJ?
+ private boolean overridesAsDeclared(ExecutableElement overridder, ExecutableElement overridden) {
+ return elements.overrides(overridder, overridden, asType(overridder.getEnclosingElement()));
+ }
+
+ private void reportConflictingEntryPoints(
+ Collection<ExecutableElement> methods, ValidationReport.Builder<TypeElement> report) {
+ verify(
+ methods.stream().map(ExecutableElement::getEnclosingElement).distinct().count()
+ == methods.size(),
+ "expected each method to be declared on a different type: %s",
+ methods);
+ StringBuilder message = new StringBuilder("conflicting entry point declarations:");
+ methodSignatureFormatter
+ .typedFormatter(asDeclared(report.getSubject().asType()))
+ .formatIndentedList(
+ message,
+ ImmutableList.sortedCopyOf(
+ comparing(
+ method -> asType(method.getEnclosingElement()).getQualifiedName().toString()),
+ methods),
+ 1);
+ report.addError(message.toString());
+ }
+
+ private void validateSubcomponentMethod(
+ final ValidationReport.Builder<TypeElement> report,
+ final ComponentKind subcomponentKind,
+ ExecutableElement method,
+ List<? extends VariableElement> parameters,
+ List<? extends TypeMirror> parameterTypes,
+ TypeMirror returnType,
+ Optional<AnnotationMirror> subcomponentAnnotation) {
+ ImmutableSet<TypeElement> moduleTypes =
+ componentAnnotation(subcomponentAnnotation.get()).modules();
+
+ // TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse
+ // subcomponents and their modules separately from how it is done in ComponentDescriptor and
+ // ModuleDescriptor
+ @SuppressWarnings("deprecation")
+ ImmutableSet<TypeElement> transitiveModules =
+ getTransitiveModules(types, elements, moduleTypes);
+
+ Set<TypeElement> variableTypes = Sets.newHashSet();
+
+ for (int i = 0; i < parameterTypes.size(); i++) {
+ VariableElement parameter = parameters.get(i);
+ TypeMirror parameterType = parameterTypes.get(i);
+ Optional<TypeElement> moduleType =
+ parameterType.accept(
+ new SimpleTypeVisitor6<Optional<TypeElement>, Void>() {
+ @Override
+ protected Optional<TypeElement> defaultAction(TypeMirror e, Void p) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<TypeElement> visitDeclared(DeclaredType t, Void p) {
+ for (ModuleKind moduleKind : subcomponentKind.legalModuleKinds()) {
+ if (isAnnotationPresent(t.asElement(), moduleKind.annotation())) {
+ return Optional.of(MoreTypes.asTypeElement(t));
+ }
+ }
+ return Optional.empty();
+ }
+ },
+ null);
+ if (moduleType.isPresent()) {
+ if (variableTypes.contains(moduleType.get())) {
+ report.addError(
+ String.format(
+ "A module may only occur once an an argument in a Subcomponent factory "
+ + "method, but %s was already passed.",
+ moduleType.get().getQualifiedName()),
+ parameter);
+ }
+ if (!transitiveModules.contains(moduleType.get())) {
+ report.addError(
+ String.format(
+ "%s is present as an argument to the %s factory method, but is not one of the"
+ + " modules used to implement the subcomponent.",
+ moduleType.get().getQualifiedName(),
+ MoreTypes.asTypeElement(returnType).getQualifiedName()),
+ method);
+ }
+ variableTypes.add(moduleType.get());
+ } else {
+ report.addError(
+ String.format(
+ "Subcomponent factory methods may only accept modules, but %s is not.",
+ parameterType),
+ parameter);
+ }
+ }
+ }
+
+ private void validateSubcomponentCreatorMethod(
+ ValidationReport.Builder<TypeElement> report,
+ ExecutableElement method,
+ List<? extends VariableElement> parameters,
+ TypeMirror returnType,
+ Set<? extends Element> validatedSubcomponentCreators) {
+ if (!parameters.isEmpty()) {
+ report.addError(builderMethodRequiresNoArgs(), method);
+ }
+
+ // If we haven't already validated the subcomponent creator itself, validate it now.
+ TypeElement creatorElement = MoreTypes.asTypeElement(returnType);
+ if (!validatedSubcomponentCreators.contains(creatorElement)) {
+ // TODO(sameb): The creator validator right now assumes the element is being compiled
+ // in this pass, which isn't true here. We should change error messages to spit out
+ // this method as the subject and add the original subject to the message output.
+ report.addItems(creatorValidator.validate(creatorElement).items());
+ }
+ }
+
+ private static <T extends Element> void validateComponentDependencies(
+ ValidationReport.Builder<T> report, Iterable<TypeMirror> types) {
+ for (TypeMirror type : types) {
+ type.accept(CHECK_DEPENDENCY_TYPES, report);
+ }
+ }
+
+ private static final TypeVisitor<Void, ValidationReport.Builder<?>> CHECK_DEPENDENCY_TYPES =
+ new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() {
+ @Override
+ protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) {
+ report.addError(type + " is not a valid component dependency type");
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) {
+ if (moduleAnnotation(MoreTypes.asTypeElement(type)).isPresent()) {
+ report.addError(type + " is a module, which cannot be a component dependency");
+ }
+ return null;
+ }
+ };
+
+ private static Optional<AnnotationMirror> checkForAnnotations(
+ TypeMirror type, final Set<? extends Class<? extends Annotation>> annotations) {
+ return type.accept(
+ new SimpleTypeVisitor6<Optional<AnnotationMirror>, Void>(Optional.empty()) {
+ @Override
+ public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void p) {
+ return getAnyAnnotation(t.asElement(), annotations);
+ }
+ },
+ null);
+ }
+}
diff --git a/java/dagger/internal/codegen/ConfigurationAnnotations.java b/java/dagger/internal/codegen/ConfigurationAnnotations.java
new file mode 100644
index 0000000..a9bba29
--- /dev/null
+++ b/java/dagger/internal/codegen/ConfigurationAnnotations.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.consumingIterable;
+import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.MoreAnnotationMirrors.getTypeListValue;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import dagger.Component;
+import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.lang.annotation.Annotation;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Utility methods related to dagger configuration annotations (e.g.: {@link Component}
+ * and {@link Module}).
+ */
+final class ConfigurationAnnotations {
+
+ static Optional<TypeElement> getSubcomponentCreator(TypeElement subcomponent) {
+ checkArgument(subcomponentAnnotation(subcomponent).isPresent());
+ for (TypeElement nestedType : typesIn(subcomponent.getEnclosedElements())) {
+ if (isSubcomponentCreator(nestedType)) {
+ return Optional.of(nestedType);
+ }
+ }
+ return Optional.empty();
+ }
+
+ static boolean isSubcomponentCreator(Element element) {
+ return isAnyAnnotationPresent(element, subcomponentCreatorAnnotations());
+ }
+
+ // Dagger 1 support.
+ static ImmutableList<TypeMirror> getModuleInjects(AnnotationMirror moduleAnnotation) {
+ checkNotNull(moduleAnnotation);
+ return getTypeListValue(moduleAnnotation, "injects");
+ }
+
+ /** Returns the first type that specifies this' nullability, or empty if none. */
+ static Optional<DeclaredType> getNullableType(Element element) {
+ List<? extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
+ for (AnnotationMirror mirror : mirrors) {
+ if (mirror.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable")) {
+ return Optional.of(mirror.getAnnotationType());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns the full set of modules transitively {@linkplain Module#includes included} from the
+ * given seed modules. If a module is malformed and a type listed in {@link Module#includes} is
+ * not annotated with {@link Module}, it is ignored.
+ *
+ * @deprecated Use {@link ComponentDescriptor#modules()}.
+ */
+ @Deprecated
+ static ImmutableSet<TypeElement> getTransitiveModules(
+ DaggerTypes types, DaggerElements elements, Iterable<TypeElement> seedModules) {
+ TypeMirror objectType = elements.getTypeElement(Object.class).asType();
+ Queue<TypeElement> moduleQueue = new ArrayDeque<>();
+ Iterables.addAll(moduleQueue, seedModules);
+ Set<TypeElement> moduleElements = Sets.newLinkedHashSet();
+ for (TypeElement moduleElement : consumingIterable(moduleQueue)) {
+ moduleAnnotation(moduleElement)
+ .ifPresent(
+ moduleAnnotation -> {
+ ImmutableSet.Builder<TypeElement> moduleDependenciesBuilder =
+ ImmutableSet.builder();
+ moduleDependenciesBuilder.addAll(moduleAnnotation.includes());
+ // We don't recur on the parent class because we don't want the parent class as a
+ // root that the component depends on, and also because we want the dependencies
+ // rooted against this element, not the parent.
+ addIncludesFromSuperclasses(
+ types, moduleElement, moduleDependenciesBuilder, objectType);
+ ImmutableSet<TypeElement> moduleDependencies = moduleDependenciesBuilder.build();
+ moduleElements.add(moduleElement);
+ for (TypeElement dependencyType : moduleDependencies) {
+ if (!moduleElements.contains(dependencyType)) {
+ moduleQueue.add(dependencyType);
+ }
+ }
+ });
+ }
+ return ImmutableSet.copyOf(moduleElements);
+ }
+
+ /** Returns the enclosed types annotated with the given annotation. */
+ static ImmutableList<DeclaredType> enclosedAnnotatedTypes(
+ TypeElement typeElement, Class<? extends Annotation> annotation) {
+ final ImmutableList.Builder<DeclaredType> builders = ImmutableList.builder();
+ for (TypeElement element : typesIn(typeElement.getEnclosedElements())) {
+ if (MoreElements.isAnnotationPresent(element, annotation)) {
+ builders.add(MoreTypes.asDeclared(element.asType()));
+ }
+ }
+ return builders.build();
+ }
+
+ /** Traverses includes from superclasses and adds them into the builder. */
+ private static void addIncludesFromSuperclasses(
+ DaggerTypes types,
+ TypeElement element,
+ ImmutableSet.Builder<TypeElement> builder,
+ TypeMirror objectType) {
+ // Also add the superclass to the queue, in case any @Module definitions were on that.
+ TypeMirror superclass = element.getSuperclass();
+ while (!types.isSameType(objectType, superclass)
+ && superclass.getKind().equals(TypeKind.DECLARED)) {
+ element = MoreElements.asType(types.asElement(superclass));
+ moduleAnnotation(element)
+ .ifPresent(moduleAnnotation -> builder.addAll(moduleAnnotation.includes()));
+ superclass = element.getSuperclass();
+ }
+ }
+
+ private ConfigurationAnnotations() {}
+}
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
new file mode 100644
index 0000000..0958bc8
--- /dev/null
+++ b/java/dagger/internal/codegen/ContributionBinding.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.CLASS_CONSTRUCTOR;
+import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.DELEGATE;
+import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static dagger.internal.codegen.MapKeys.unwrapValue;
+import static dagger.internal.codegen.MoreAnnotationMirrors.unwrapOptionalEquivalence;
+import static java.util.Arrays.asList;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.base.Equivalence;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.internal.codegen.ContributionType.HasContributionType;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * An abstract class for a value object representing the mechanism by which a {@link Key} can be
+ * contributed to a dependency graph.
+ */
+abstract class ContributionBinding extends Binding implements HasContributionType {
+
+ /** Returns the type that specifies this' nullability, absent if not nullable. */
+ abstract Optional<DeclaredType> nullableType();
+
+ abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation();
+
+ final Optional<AnnotationMirror> mapKeyAnnotation() {
+ return unwrapOptionalEquivalence(wrappedMapKeyAnnotation());
+ }
+
+ /**
+ * If this is a map contribution, returns the key of its map entry.
+ *
+ * @throws IllegalStateException if {@link #mapKeyAnnotation()} returns an empty value.
+ */
+ final Object mapKey() {
+ checkState(mapKeyAnnotation().isPresent());
+ AnnotationMirror mapKeyAnnotation = mapKeyAnnotation().get();
+ return unwrapValue(mapKeyAnnotation).map(AnnotationValue::getValue).orElse(mapKeyAnnotation);
+ }
+
+ /** If {@link #bindingElement()} is a method that returns a primitive type, returns that type. */
+ final Optional<TypeMirror> contributedPrimitiveType() {
+ return bindingElement()
+ .filter(bindingElement -> bindingElement instanceof ExecutableElement)
+ .map(bindingElement -> MoreElements.asExecutable(bindingElement).getReturnType())
+ .filter(type -> type.getKind().isPrimitive());
+ }
+
+ @Override
+ public final boolean isNullable() {
+ return nullableType().isPresent();
+ }
+
+ /**
+ * The strategy for getting an instance of a factory for a {@link ContributionBinding}.
+ */
+ enum FactoryCreationStrategy {
+ /** The factory class is a single instance. */
+ SINGLETON_INSTANCE,
+ /** The factory must be created by calling the constructor. */
+ CLASS_CONSTRUCTOR,
+ /** The factory is simply delegated to another. */
+ DELEGATE,
+ }
+
+ /**
+ * Returns the {@link FactoryCreationStrategy} appropriate for a binding.
+ *
+ * <p>Delegate bindings use the {@link FactoryCreationStrategy#DELEGATE} strategy.
+ *
+ * <p>Bindings without dependencies that don't require a module instance use the {@link
+ * FactoryCreationStrategy#SINGLETON_INSTANCE} strategy.
+ *
+ * <p>All other bindings use the {@link FactoryCreationStrategy#CLASS_CONSTRUCTOR} strategy.
+ */
+ final FactoryCreationStrategy factoryCreationStrategy() {
+ switch (kind()) {
+ case DELEGATE:
+ return DELEGATE;
+ case PROVISION:
+ return dependencies().isEmpty() && !requiresModuleInstance()
+ ? SINGLETON_INSTANCE
+ : CLASS_CONSTRUCTOR;
+ case INJECTION:
+ case MULTIBOUND_SET:
+ case MULTIBOUND_MAP:
+ return dependencies().isEmpty() ? SINGLETON_INSTANCE : CLASS_CONSTRUCTOR;
+ default:
+ return CLASS_CONSTRUCTOR;
+ }
+ }
+
+ /**
+ * The {@link TypeMirror type} for the {@code Factory<T>} or {@code Producer<T>} which is created
+ * for this binding. Uses the binding's key, V in the case of {@code Map<K, FrameworkClass<V>>>},
+ * and E {@code Set<E>} for {@link dagger.multibindings.IntoSet @IntoSet} methods.
+ */
+ final TypeMirror contributedType() {
+ switch (contributionType()) {
+ case MAP:
+ return MapType.from(key()).unwrappedFrameworkValueType();
+ case SET:
+ return SetType.from(key()).elementType();
+ case SET_VALUES:
+ case UNIQUE:
+ return key().type();
+ }
+ throw new AssertionError();
+ }
+
+ final boolean isSyntheticMultibinding() {
+ switch (kind()) {
+ case MULTIBOUND_SET:
+ case MULTIBOUND_MAP:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** Whether the bound type has a generated implementation. */
+ final boolean requiresGeneratedInstance() {
+ switch (kind()) {
+ case COMPONENT:
+ case SUBCOMPONENT_CREATOR:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Returns {@link BindingKind#MULTIBOUND_SET} or {@link
+ * BindingKind#MULTIBOUND_MAP} if the key is a set or map.
+ *
+ * @throws IllegalArgumentException if {@code key} is neither a set nor a map
+ */
+ static BindingKind bindingKindForMultibindingKey(Key key) {
+ if (SetType.isSet(key)) {
+ return BindingKind.MULTIBOUND_SET;
+ } else if (MapType.isMap(key)) {
+ return BindingKind.MULTIBOUND_MAP;
+ } else {
+ throw new IllegalArgumentException(String.format("key is not for a set or map: %s", key));
+ }
+ }
+
+ /**
+ * Base builder for {@link com.google.auto.value.AutoValue @AutoValue} subclasses of {@link
+ * ContributionBinding}.
+ */
+ @CanIgnoreReturnValue
+ abstract static class Builder<C extends ContributionBinding, B extends Builder<C, B>> {
+ abstract B dependencies(Iterable<DependencyRequest> dependencies);
+
+ B dependencies(DependencyRequest... dependencies) {
+ return dependencies(asList(dependencies));
+ }
+
+ abstract B unresolved(C unresolved);
+
+ abstract B contributionType(ContributionType contributionType);
+
+ abstract B bindingElement(Element bindingElement);
+
+ abstract B contributingModule(TypeElement contributingModule);
+
+ abstract B key(Key key);
+
+ abstract B nullableType(Optional<DeclaredType> nullableType);
+
+ abstract B wrappedMapKeyAnnotation(
+ Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation);
+
+ abstract B kind(BindingKind kind);
+
+ @CheckReturnValue
+ abstract C build();
+ }
+}
diff --git a/java/dagger/internal/codegen/ContributionType.java b/java/dagger/internal/codegen/ContributionType.java
new file mode 100644
index 0000000..66b5289
--- /dev/null
+++ b/java/dagger/internal/codegen/ContributionType.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+
+import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import javax.lang.model.element.Element;
+
+/** Whether a binding or declaration is for a unique contribution or a map or set multibinding. */
+enum ContributionType {
+ /** Represents map bindings. */
+ MAP,
+ /** Represents set bindings. */
+ SET,
+ /** Represents set values bindings. */
+ SET_VALUES,
+ /** Represents a valid non-collection binding. */
+ UNIQUE,
+ ;
+
+ /** An object that is associated with a {@link ContributionType}. */
+ interface HasContributionType {
+
+ /** The contribution type of this object. */
+ ContributionType contributionType();
+ }
+
+ /** {@code true} if this is for a multibinding. */
+ boolean isMultibinding() {
+ return !this.equals(UNIQUE);
+ }
+
+ /**
+ * The contribution type from a binding element's annotations. Presumes a well-formed binding
+ * element (at most one of @IntoSet, @IntoMap, @ElementsIntoSet and @Provides.type). {@link
+ * BindingMethodValidator} and {@link BindsInstanceProcessingStep} validate correctness on their
+ * own.
+ */
+ static ContributionType fromBindingElement(Element element) {
+ if (isAnnotationPresent(element, IntoMap.class)) {
+ return ContributionType.MAP;
+ } else if (isAnnotationPresent(element, IntoSet.class)) {
+ return ContributionType.SET;
+ } else if (isAnnotationPresent(element, ElementsIntoSet.class)) {
+ return ContributionType.SET_VALUES;
+ }
+ return ContributionType.UNIQUE;
+ }
+}
diff --git a/java/dagger/internal/codegen/CurrentImplementationSubcomponent.java b/java/dagger/internal/codegen/CurrentImplementationSubcomponent.java
new file mode 100644
index 0000000..c78d60d
--- /dev/null
+++ b/java/dagger/internal/codegen/CurrentImplementationSubcomponent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+import dagger.internal.codegen.ComponentImplementationBuilder.RootComponentImplementationBuilder;
+import dagger.internal.codegen.ComponentImplementationBuilder.SubcomponentImplementationBuilder;
+import java.util.Optional;
+
+/**
+ * A subcomponent that injects all objects that are responsible for creating a single {@link
+ * ComponentImplementation} instance. Each child {@link ComponentImplementation} will have its own
+ * instance of {@link CurrentImplementationSubcomponent}.
+ */
+@Subcomponent(modules = GenerationOptionsModule.class)
+@PerComponentImplementation
+interface CurrentImplementationSubcomponent {
+ RootComponentImplementationBuilder rootComponentBuilder();
+
+ SubcomponentImplementationBuilder subcomponentBuilder();
+
+ @Subcomponent.Builder
+ interface Builder {
+ @BindsInstance
+ Builder componentImplementation(ComponentImplementation componentImplementation);
+
+ @BindsInstance
+ Builder bindingGraph(BindingGraph bindingGraph);
+
+ @BindsInstance
+ Builder parentBuilder(@ParentComponent Optional<ComponentImplementationBuilder> parentBuilder);
+
+ @BindsInstance
+ Builder parentBindingExpressions(
+ @ParentComponent Optional<ComponentBindingExpressions> parentBindingExpressions);
+
+ @BindsInstance
+ Builder parentRequirementExpressions(
+ @ParentComponent Optional<ComponentRequirementExpressions> parentRequirementExpressions);
+
+ CurrentImplementationSubcomponent build();
+ }
+}
diff --git a/java/dagger/internal/codegen/DaggerGraphs.java b/java/dagger/internal/codegen/DaggerGraphs.java
new file mode 100644
index 0000000..dda6c11
--- /dev/null
+++ b/java/dagger/internal/codegen/DaggerGraphs.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Sets.difference;
+import static com.google.common.graph.Graphs.reachableNodes;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.Graph;
+import com.google.common.graph.SuccessorsFunction;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+/** Utility methods for {@link com.google.common.graph} types. */
+public final class DaggerGraphs {
+ /**
+ * Returns a shortest path from {@code nodeU} to {@code nodeV} in {@code graph} as a list of the
+ * nodes visited in sequence, including both {@code nodeU} and {@code nodeV}. (Note that there may
+ * be many possible shortest paths.)
+ *
+ * <p>If {@code nodeV} is not {@link
+ * com.google.common.graph.Graphs#reachableNodes(com.google.common.graph.Graph, Object) reachable}
+ * from {@code nodeU}, the list returned is empty.
+ *
+ * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not present in {@code
+ * graph}
+ */
+ public static <N> ImmutableList<N> shortestPath(SuccessorsFunction<N> graph, N nodeU, N nodeV) {
+ if (nodeU.equals(nodeV)) {
+ return ImmutableList.of(nodeU);
+ }
+ Set<N> successors = ImmutableSet.copyOf(graph.successors(nodeU));
+ if (successors.contains(nodeV)) {
+ return ImmutableList.of(nodeU, nodeV);
+ }
+
+ Map<N, N> visitedNodeToPathPredecessor = new HashMap<>(); // encodes shortest path tree
+ for (N node : successors) {
+ visitedNodeToPathPredecessor.put(node, nodeU);
+ }
+ Queue<N> currentNodes = new ArrayDeque<N>(successors);
+ Queue<N> nextNodes = new ArrayDeque<N>();
+
+ // Perform a breadth-first traversal starting with the successors of nodeU.
+ while (!currentNodes.isEmpty()) {
+ while (!currentNodes.isEmpty()) {
+ N currentNode = currentNodes.remove();
+ for (N nextNode : graph.successors(currentNode)) {
+ if (visitedNodeToPathPredecessor.containsKey(nextNode)) {
+ continue; // we already have a shortest path to nextNode
+ }
+ visitedNodeToPathPredecessor.put(nextNode, currentNode);
+ if (nextNode.equals(nodeV)) {
+ ImmutableList.Builder<N> builder = ImmutableList.builder();
+ N node = nodeV;
+ builder.add(node);
+ while (!node.equals(nodeU)) {
+ node = visitedNodeToPathPredecessor.get(node);
+ builder.add(node);
+ }
+ return builder.build().reverse();
+ }
+ nextNodes.add(nextNode);
+ }
+ }
+ Queue<N> emptyQueue = currentNodes;
+ currentNodes = nextNodes;
+ nextNodes = emptyQueue; // reusing empty queue faster than allocating new one
+ }
+
+ return ImmutableList.of();
+ }
+
+ /** Returns the nodes in a graph that are not reachable from a node. */
+ public static <N> ImmutableSet<N> unreachableNodes(Graph<N> graph, N node) {
+ return ImmutableSet.copyOf(difference(graph.nodes(), reachableNodes(graph, node)));
+ }
+
+ private DaggerGraphs() {}
+}
diff --git a/java/dagger/internal/codegen/DaggerKythePlugin.java b/java/dagger/internal/codegen/DaggerKythePlugin.java
new file mode 100644
index 0000000..5a685ef
--- /dev/null
+++ b/java/dagger/internal/codegen/DaggerKythePlugin.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This must be in the dagger.internal.codegen package since Dagger doesn't expose its APIs publicly
+// https://github.com/google/dagger/issues/773 could present an opportunity to put this somewhere in
+// the regular kythe/java tree.
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Iterables;
+import com.google.devtools.kythe.analyzers.base.EntrySet;
+import com.google.devtools.kythe.analyzers.base.FactEmitter;
+import com.google.devtools.kythe.analyzers.base.KytheEntrySets;
+import com.google.devtools.kythe.analyzers.java.Plugin;
+import com.google.devtools.kythe.proto.Storage.VName;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.util.Context;
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.producers.ProductionComponent;
+import java.util.Optional;
+import java.util.logging.Logger;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.Element;
+
+/**
+ * A plugin which emits nodes and edges for <a href="https://github.com/google/dagger">Dagger</a>
+ * specific code.
+ */
+@AutoService(Plugin.class)
+public class DaggerKythePlugin extends Plugin.Scanner<Void, Void> {
+ // TODO(ronshapiro): use flogger
+ private static final Logger logger = Logger.getLogger(DaggerKythePlugin.class.getCanonicalName());
+ private FactEmitter emitter;
+ @Inject ComponentDescriptorFactory componentDescriptorFactory;
+ @Inject BindingGraphFactory bindingGraphFactory;
+
+ @Override
+ public Void visitClassDef(JCClassDecl tree, Void p) {
+ if (tree.sym != null
+ && isAnyAnnotationPresent(tree.sym, Component.class, ProductionComponent.class)) {
+ addNodesForGraph(
+ bindingGraphFactory.create(
+ componentDescriptorFactory.rootComponentDescriptor(tree.sym), false));
+ }
+ return super.visitClassDef(tree, p);
+ }
+
+ private void addNodesForGraph(BindingGraph graph) {
+ addDependencyEdges(graph);
+ addModuleEdges(graph);
+ addChildComponentEdges(graph);
+
+ graph.subgraphs().forEach(this::addNodesForGraph);
+ }
+
+ private void addDependencyEdges(BindingGraph graph) {
+ for (ResolvedBindings resolvedBinding : graph.resolvedBindings()) {
+ for (Binding binding : resolvedBinding.bindings()) {
+ for (DependencyRequest dependency : binding.explicitDependencies()) {
+ addEdgesForDependencyRequest(dependency, dependency.key(), graph);
+ }
+ }
+ }
+
+ for (ComponentDescriptor.ComponentMethodDescriptor componentMethod :
+ graph.componentDescriptor().componentMethods()) {
+ componentMethod
+ .dependencyRequest()
+ .ifPresent(request -> addEdgesForDependencyRequest(request, request.key(), graph));
+ }
+ }
+
+ /**
+ * Add {@code /inject/satisfiedby} edges from {@code dependency}'s {@link
+ * DependencyRequest#requestElement()} to any {@link BindingDeclaration#bindingElement() binding
+ * elements} that satisfy the request.
+ *
+ * <p>This collapses requests for synthetic bindings so that a request for a multibound key
+ * points to all of the contributions for the multibound object. It does so by recursively calling
+ * this method, with each dependency's key as the {@code targetKey}.
+ */
+ private void addEdgesForDependencyRequest(
+ DependencyRequest dependency, Key targetKey, BindingGraph graph) {
+ if (!dependency.requestElement().isPresent()) {
+ return;
+ }
+ BindingRequest request = bindingRequest(targetKey, dependency.kind());
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
+ for (Binding binding : resolvedBindings.bindings()) {
+ if (binding.bindingElement().isPresent()) {
+ addDependencyEdge(dependency, binding);
+ } else {
+ for (DependencyRequest subsequentDependency : binding.explicitDependencies()) {
+ addEdgesForDependencyRequest(dependency, subsequentDependency.key(), graph);
+ }
+ }
+ }
+ for (BindingDeclaration bindingDeclaration :
+ Iterables.concat(
+ resolvedBindings.multibindingDeclarations(),
+ resolvedBindings.optionalBindingDeclarations())) {
+ addDependencyEdge(dependency, bindingDeclaration);
+ }
+ }
+
+ private void addDependencyEdge(
+ DependencyRequest dependency, BindingDeclaration bindingDeclaration) {
+ Element requestElement = dependency.requestElement().get();
+ Element bindingElement = bindingDeclaration.bindingElement().get();
+ Optional<VName> requestElementNode = jvmNode(requestElement, "request element");
+ Optional<VName> bindingElementNode = jvmNode(bindingElement, "binding element");
+ emitEdge(requestElementNode, "/inject/satisfiedby", bindingElementNode);
+ // TODO(ronshapiro): emit facts about the component that satisfies the edge
+ }
+
+ private void addModuleEdges(BindingGraph graph) {
+ Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
+ for (ModuleDescriptor module : graph.componentDescriptor().modules()) {
+ Optional<VName> moduleNode = jvmNode(module.moduleElement(), "module");
+ emitEdge(componentNode, "/inject/installsmodule", moduleNode);
+ }
+ }
+
+ private void addChildComponentEdges(BindingGraph graph) {
+ Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
+ for (BindingGraph subgraph : graph.subgraphs()) {
+ Optional<VName> subcomponentNode =
+ jvmNode(subgraph.componentTypeElement(), "child component");
+ emitEdge(componentNode, "/inject/childcomponent", subcomponentNode);
+ }
+ }
+
+ private Optional<VName> jvmNode(Element element, String name) {
+ Optional<VName> jvmNode = kytheGraph.getJvmNode((Symbol) element).map(KytheNode::getVName);
+ if (!jvmNode.isPresent()) {
+ logger.warning(String.format("Missing JVM node for %s: %s", name, element));
+ }
+ return jvmNode;
+ }
+
+ private void emitEdge(Optional<VName> source, String edgeName, Optional<VName> target) {
+ source.ifPresent(
+ s -> target.ifPresent(t -> new EntrySet.Builder(s, edgeName, t).build().emit(emitter)));
+ }
+
+ @Override
+ public void run(
+ JCCompilationUnit compilationUnit, KytheEntrySets entrySets, KytheGraph kytheGraph) {
+ if (bindingGraphFactory == null) {
+ emitter = entrySets.getEmitter();
+ DaggerDaggerKythePlugin_PluginComponent.builder()
+ .context(kytheGraph.getJavaContext())
+ .build()
+ .inject(this);
+ }
+ super.run(compilationUnit, entrySets, kytheGraph);
+ }
+
+ @Singleton
+ @Component(modules = JavacPluginModule.class)
+ interface PluginComponent {
+ void inject(DaggerKythePlugin plugin);
+
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ Builder context(Context context);
+
+ PluginComponent build();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DaggerStatistics.java b/java/dagger/internal/codegen/DaggerStatistics.java
new file mode 100644
index 0000000..790ec76
--- /dev/null
+++ b/java/dagger/internal/codegen/DaggerStatistics.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.time.Duration;
+
+/** Statistics collected over the course of Dagger annotation processing. */
+@AutoValue
+abstract class DaggerStatistics {
+ /** Returns a new {@link Builder}. */
+ static Builder builder() {
+ return new AutoValue_DaggerStatistics.Builder();
+ }
+
+ /** Returns a new {@link RoundStatistics} builder. */
+ static RoundStatistics.Builder roundBuilder() {
+ return new AutoValue_DaggerStatistics_RoundStatistics.Builder();
+ }
+
+ /** Total time spent in Dagger annotation processing. */
+ abstract Duration totalProcessingTime();
+
+ /** List of statistics for processing rounds that the Dagger processor handled. */
+ abstract ImmutableList<RoundStatistics> rounds();
+
+ /** Builder for {@link DaggerStatistics}. */
+ @AutoValue.Builder
+ abstract static class Builder {
+ /** Sets the given duration for the total time spent in Dagger processing. */
+ @CanIgnoreReturnValue
+ abstract Builder setTotalProcessingTime(Duration totalProcessingTime);
+
+ /** Returns a builder for adding processing round statistics. */
+ abstract ImmutableList.Builder<RoundStatistics> roundsBuilder();
+
+ /** Adds the given {@code round} statistics. */
+ @CanIgnoreReturnValue
+ final Builder addRound(RoundStatistics round) {
+ roundsBuilder().add(round);
+ return this;
+ }
+
+ /** Creates a new {@link DaggerStatistics} instance. */
+ abstract DaggerStatistics build();
+ }
+
+ /** Statistics for each processing step in a single processing round. */
+ @AutoValue
+ abstract static class RoundStatistics {
+ /** Map of processing step class to duration of that step for this round. */
+ abstract ImmutableMap<Class<? extends ProcessingStep>, Duration> stepDurations();
+
+ /** Builder for {@link RoundStatistics}. */
+ @AutoValue.Builder
+ abstract static class Builder {
+ /** Returns a builder for adding durations for each processing step for the round. */
+ abstract ImmutableMap.Builder<Class<? extends ProcessingStep>, Duration>
+ stepDurationsBuilder();
+
+ /** Adds the given {@code duration} for the given {@code step}. */
+ @CanIgnoreReturnValue
+ final Builder addStepDuration(ProcessingStep step, Duration duration) {
+ stepDurationsBuilder().put(step.getClass(), duration);
+ return this;
+ }
+
+ /** Creates a new {@link RoundStatistics} instance. */
+ abstract RoundStatistics build();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DaggerStatisticsCollectingProcessingStep.java b/java/dagger/internal/codegen/DaggerStatisticsCollectingProcessingStep.java
new file mode 100644
index 0000000..51f6fc3
--- /dev/null
+++ b/java/dagger/internal/codegen/DaggerStatisticsCollectingProcessingStep.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.common.collect.SetMultimap;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.lang.model.element.Element;
+
+/**
+ * {@link ProcessingStep} that delegates to another {@code ProcessingStep} and collects timing
+ * statistics for each processing round for that step.
+ */
+final class DaggerStatisticsCollectingProcessingStep implements ProcessingStep {
+
+ private final ProcessingStep delegate;
+ private final DaggerStatisticsCollector statisticsCollector;
+
+ DaggerStatisticsCollectingProcessingStep(
+ ProcessingStep delegate, DaggerStatisticsCollector statisticsCollector) {
+ this.delegate = checkNotNull(delegate);
+ this.statisticsCollector = checkNotNull(statisticsCollector);
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return delegate.annotations();
+ }
+
+ @Override
+ public Set<? extends Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ statisticsCollector.stepStarted(delegate);
+ try {
+ return delegate.process(elementsByAnnotation);
+ } finally {
+ statisticsCollector.stepFinished(delegate);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DaggerStatisticsCollector.java b/java/dagger/internal/codegen/DaggerStatisticsCollector.java
new file mode 100644
index 0000000..e14fbb7
--- /dev/null
+++ b/java/dagger/internal/codegen/DaggerStatisticsCollector.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Ticker;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Collects {@link DaggerStatistics} over the course of Dagger annotation processing. */
+@Singleton // for state sharing
+final class DaggerStatisticsCollector {
+
+ private final Ticker ticker;
+ private final Stopwatch totalRuntimeStopwatch;
+ private final Map<ProcessingStep, Stopwatch> stepStopwatches = new HashMap<>();
+
+ private final DaggerStatistics.Builder statisticsBuilder = DaggerStatistics.builder();
+ private DaggerStatistics.RoundStatistics.Builder roundBuilder = DaggerStatistics.roundBuilder();
+
+ private final Optional<DaggerStatisticsRecorder> statisticsRecorder;
+
+ @Inject
+ DaggerStatisticsCollector(Ticker ticker, Optional<DaggerStatisticsRecorder> statisticsRecorder) {
+ this.ticker = ticker;
+ this.totalRuntimeStopwatch = Stopwatch.createUnstarted(ticker);
+ this.statisticsRecorder = statisticsRecorder;
+ }
+
+ /** Called when Dagger annotation processing starts. */
+ void processingStarted() {
+ checkState(!totalRuntimeStopwatch.isRunning());
+ totalRuntimeStopwatch.start();
+ }
+
+ /** Called when the given {@code step} starts processing for a round. */
+ void stepStarted(ProcessingStep step) {
+ Stopwatch stopwatch =
+ stepStopwatches.computeIfAbsent(step, unused -> Stopwatch.createUnstarted(ticker));
+ stopwatch.start();
+ }
+
+ /** Called when the given {@code step} finishes processing for a round. */
+ void stepFinished(ProcessingStep step) {
+ Stopwatch stopwatch = stepStopwatches.get(step);
+ roundBuilder.addStepDuration(step, elapsedTime(stopwatch));
+ stopwatch.reset();
+ }
+
+ /** Called when Dagger finishes a processing round. */
+ void roundFinished() {
+ statisticsBuilder.addRound(roundBuilder.build());
+ roundBuilder = DaggerStatistics.roundBuilder();
+ }
+
+ /** Called when Dagger annotation processing completes. */
+ void processingStopped() {
+ checkState(totalRuntimeStopwatch.isRunning());
+ totalRuntimeStopwatch.stop();
+ statisticsBuilder.setTotalProcessingTime(elapsedTime(totalRuntimeStopwatch));
+
+ statisticsRecorder.ifPresent(
+ recorder -> recorder.recordStatistics(statisticsBuilder.build()));
+ }
+
+ @SuppressWarnings("StopwatchNanosToDuration") // intentional
+ private Duration elapsedTime(Stopwatch stopwatch) {
+ // Using the java 7 method here as opposed to the Duration-returning version to avoid issues
+ // when other annotation processors rely on java 7's guava
+ return Duration.ofNanos(stopwatch.elapsed(NANOSECONDS));
+ }
+}
diff --git a/java/dagger/internal/codegen/DaggerStatisticsRecorder.java b/java/dagger/internal/codegen/DaggerStatisticsRecorder.java
new file mode 100644
index 0000000..66f41d1
--- /dev/null
+++ b/java/dagger/internal/codegen/DaggerStatisticsRecorder.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+/** Records collected {@link DaggerStatistics}. */
+interface DaggerStatisticsRecorder {
+ /** Records the given {@code statistics}. */
+ void recordStatistics(DaggerStatistics statistics);
+}
diff --git a/java/dagger/internal/codegen/DaggerStreams.java b/java/dagger/internal/codegen/DaggerStreams.java
new file mode 100644
index 0000000..50d17a6
--- /dev/null
+++ b/java/dagger/internal/codegen/DaggerStreams.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+/** Utilities for streams. */
+public final class DaggerStreams {
+
+ /**
+ * Returns a {@link Collector} that accumulates the input elements into a new {@link
+ * ImmutableList}, in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableList.toImmutableList().
+ public static <T> Collector<T, ?, ImmutableList<T>> toImmutableList() {
+ return collectingAndThen(toList(), ImmutableList::copyOf);
+ }
+
+ /**
+ * Returns a {@link Collector} that accumulates the input elements into a new {@link
+ * ImmutableSet}, in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableSet.toImmutableSet().
+ public static <T> Collector<T, ?, ImmutableSet<T>> toImmutableSet() {
+ return collectingAndThen(toList(), ImmutableSet::copyOf);
+ }
+
+ /**
+ * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys
+ * and values are the result of applying the provided mapping functions to the input elements.
+ * Entries appear in the result {@code ImmutableMap} in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableMap.toImmutableMap().
+ public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
+ Function<? super T, K> keyMapper, Function<? super T, V> valueMapper) {
+ return Collectors.mapping(
+ value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
+ Collector.of(
+ ImmutableMap::builder,
+ (ImmutableMap.Builder<K, V> builder, Map.Entry<K, V> entry) -> builder.put(entry),
+ (left, right) -> left.putAll(right.build()),
+ ImmutableMap.Builder::build));
+ }
+
+ /**
+ * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap}
+ * whose keys and values are the result of applying the provided mapping functions to the input
+ * elements. Entries appear in the result {@code ImmutableSetMultimap} in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableSetMultimap.toImmutableSetMultimap().
+ public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
+ Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
+ return Collectors.mapping(
+ value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
+ Collector.of(
+ ImmutableSetMultimap::builder,
+ (ImmutableSetMultimap.Builder<K, V> builder, Map.Entry<K, V> entry) ->
+ builder.put(entry),
+ (left, right) -> left.putAll(right.build()),
+ ImmutableSetMultimap.Builder::build));
+ }
+
+ /**
+ * Returns a function from {@link Object} to {@code Stream<T>}, which returns a stream containing
+ * its input if its input is an instance of {@code T}.
+ *
+ * <p>Use as an argument to {@link Stream#flatMap(Function)}:
+ *
+ * <pre>{@code Stream<Bar>} barStream = fooStream.flatMap(instancesOf(Bar.class));</pre>
+ */
+ public static <T> Function<Object, Stream<T>> instancesOf(Class<T> to) {
+ return f -> to.isInstance(f) ? Stream.of(to.cast(f)) : Stream.empty();
+ }
+
+ /** Returns a stream of all values of the given {@code enumType}. */
+ public static <E extends Enum<E>> Stream<E> valuesOf(Class<E> enumType) {
+ return EnumSet.allOf(enumType).stream();
+ }
+
+ /**
+ * A function that you can use to extract the present values from a stream of {@link Optional}s.
+ *
+ * <pre>{@code
+ * Set<Foo> foos =
+ * optionalFoos()
+ * .flatMap(DaggerStreams.presentValues())
+ * .collect(toSet());
+ * }</pre>
+ */
+ public static <T> Function<Optional<T>, Stream<T>> presentValues() {
+ return optional -> optional.map(Stream::of).orElse(Stream.empty());
+ }
+
+ /**
+ * Returns a sequential {@link Stream} of the contents of {@code iterable}, delegating to {@link
+ * Collection#stream} if possible.
+ */
+ public static <T> Stream<T> stream(Iterable<T> iterable) {
+ return (iterable instanceof Collection)
+ ? ((Collection<T>) iterable).stream()
+ : StreamSupport.stream(iterable.spliterator(), false);
+ }
+
+ private DaggerStreams() {}
+}
diff --git a/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java b/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java
new file mode 100644
index 0000000..c41cf2c
--- /dev/null
+++ b/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A {@link ModifiableAbstractMethodBindingExpression} for a binding that exists but is not ready to
+ * be expressed in this compilation unit and should be deferred until a future compilation.
+ * Generates a method that will be implemented in the future compilation.
+ *
+ * <p>A deferred modifiable binding expression is used when:
+ *
+ * <ul>
+ * <li>The generated code for a binding requires an instance of a type that is generated in the
+ * root component compilation unit.
+ * <li>A {@linkplain ModifiableBindingType#BINDS_METHOD_WITH_MISSING_DEPENDENCY {@code @Binds}
+ * method's dependency is missing} in a subcomponent.
+ * </ul>
+ */
+final class DeferredModifiableBindingExpression extends ModifiableAbstractMethodBindingExpression {
+ private final ComponentImplementation componentImplementation;
+ private final ContributionBinding binding;
+ private final BindingRequest request;
+
+ DeferredModifiableBindingExpression(
+ ComponentImplementation componentImplementation,
+ ModifiableBindingType modifiableBindingType,
+ ContributionBinding binding,
+ BindingRequest request,
+ Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
+ Optional<ComponentMethodDescriptor> matchingComponentMethod,
+ DaggerTypes types) {
+ super(
+ componentImplementation,
+ modifiableBindingType,
+ request,
+ matchingModifiableBindingMethod,
+ matchingComponentMethod,
+ types);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.binding = checkNotNull(binding);
+ this.request = checkNotNull(request);
+ }
+
+ @Override
+ String chooseMethodName() {
+ return componentImplementation.getUniqueMethodName(request);
+ }
+
+ @Override
+ protected TypeMirror contributedType() {
+ return binding.contributedType();
+ }
+}
diff --git a/java/dagger/internal/codegen/DelegateBindingExpression.java b/java/dagger/internal/codegen/DelegateBindingExpression.java
new file mode 100644
index 0000000..8cdf6d1
--- /dev/null
+++ b/java/dagger/internal/codegen/DelegateBindingExpression.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.RequestKinds.requestType;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.model.BindingKind.DELEGATE;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.RequestKind;
+import javax.lang.model.type.TypeMirror;
+
+/** A {@link BindingExpression} for {@code @Binds} methods. */
+final class DelegateBindingExpression extends BindingExpression {
+ private final ContributionBinding binding;
+ private final RequestKind requestKind;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final BindsTypeChecker bindsTypeChecker;
+
+ DelegateBindingExpression(
+ ResolvedBindings resolvedBindings,
+ RequestKind requestKind,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ DaggerElements elements) {
+ this.binding = checkNotNull(resolvedBindings.contributionBinding());
+ this.requestKind = checkNotNull(requestKind);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.types = checkNotNull(types);
+ this.bindsTypeChecker = new BindsTypeChecker(types, elements);
+ }
+
+ /**
+ * Returns {@code true} if the {@code @Binds} binding's scope is stronger than the scope of the
+ * binding it depends on.
+ */
+ static boolean isBindsScopeStrongerThanDependencyScope(
+ ResolvedBindings resolvedBindings, BindingGraph graph) {
+ ContributionBinding bindsBinding = resolvedBindings.contributionBinding();
+ checkArgument(bindsBinding.kind().equals(DELEGATE));
+ Binding dependencyBinding =
+ graph
+ .contributionBindings()
+ .get(getOnlyElement(bindsBinding.dependencies()).key())
+ .binding();
+ ScopeKind bindsScope = ScopeKind.get(bindsBinding, graph);
+ ScopeKind dependencyScope = ScopeKind.get(dependencyBinding, graph);
+ return bindsScope.isStrongerScopeThan(dependencyScope);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ Expression delegateExpression =
+ componentBindingExpressions.getDependencyExpression(
+ bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
+ requestingClass);
+
+ TypeMirror contributedType = binding.contributedType();
+ switch (requestKind) {
+ case INSTANCE:
+ return instanceRequiresCast(delegateExpression, requestingClass)
+ ? delegateExpression.castTo(contributedType)
+ : delegateExpression;
+ default:
+ return castToRawTypeIfNecessary(
+ delegateExpression, requestType(requestKind, contributedType, types));
+ }
+ }
+
+ private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
+ // delegateExpression.type() could be Object if expression is satisfied with a raw
+ // Provider's get() method.
+ return !bindsTypeChecker.isAssignable(
+ delegateExpression.type(), binding.contributedType(), binding.contributionType())
+ && isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
+ }
+
+ /**
+ * If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
+ * delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
+ * type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
+ * returns a {@link Expression#castTo(TypeMirror) casted} version of {@code delegateExpression}
+ * to the raw type of {@code desiredType}.
+ */
+ // TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
+ private Expression castToRawTypeIfNecessary(
+ Expression delegateExpression, TypeMirror desiredType) {
+ if (types.isAssignable(delegateExpression.type(), desiredType)) {
+ return delegateExpression;
+ }
+ return delegateExpression.castTo(types.erasure(desiredType));
+ }
+
+ private enum ScopeKind {
+ UNSCOPED,
+ SINGLE_CHECK,
+ DOUBLE_CHECK,
+ ;
+
+ static ScopeKind get(Binding binding, BindingGraph graph) {
+ return binding
+ .scope()
+ .map(scope -> scope.isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
+ .orElse(UNSCOPED);
+ }
+
+ boolean isStrongerScopeThan(ScopeKind other) {
+ return this.ordinal() > other.ordinal();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DelegateDeclaration.java b/java/dagger/internal/codegen/DelegateDeclaration.java
new file mode 100644
index 0000000..67991de
--- /dev/null
+++ b/java/dagger/internal/codegen/DelegateDeclaration.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.MapKeys.getMapKey;
+import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.Iterables;
+import dagger.Binds;
+import dagger.internal.codegen.ContributionType.HasContributionType;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ExecutableType;
+
+/**
+ * The declaration for a delegate binding established by a {@link Binds} method.
+ */
+@AutoValue
+abstract class DelegateDeclaration extends BindingDeclaration implements HasContributionType {
+ abstract DependencyRequest delegateRequest();
+
+ abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKey();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ static final class Factory {
+ private final DaggerTypes types;
+ private final KeyFactory keyFactory;
+ private final DependencyRequestFactory dependencyRequestFactory;
+
+ @Inject
+ Factory(
+ DaggerTypes types,
+ KeyFactory keyFactory,
+ DependencyRequestFactory dependencyRequestFactory) {
+ this.types = types;
+ this.keyFactory = keyFactory;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ }
+
+ DelegateDeclaration create(
+ ExecutableElement bindsMethod, TypeElement contributingModule) {
+ checkArgument(MoreElements.isAnnotationPresent(bindsMethod, Binds.class));
+ ExecutableType resolvedMethod =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), bindsMethod));
+ DependencyRequest delegateRequest =
+ dependencyRequestFactory.forRequiredResolvedVariable(
+ Iterables.getOnlyElement(bindsMethod.getParameters()),
+ Iterables.getOnlyElement(resolvedMethod.getParameterTypes()));
+ return new AutoValue_DelegateDeclaration(
+ ContributionType.fromBindingElement(bindsMethod),
+ keyFactory.forBindsMethod(bindsMethod, contributingModule),
+ Optional.<Element>of(bindsMethod),
+ Optional.of(contributingModule),
+ delegateRequest,
+ wrapOptionalInEquivalence(getMapKey(bindsMethod)));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java b/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java
new file mode 100644
index 0000000..7524cdf
--- /dev/null
+++ b/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.model.DependencyRequest;
+
+/** A framework instance creation expression for a {@link dagger.Binds @Binds} binding. */
+final class DelegatingFrameworkInstanceCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ DelegatingFrameworkInstanceCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ DependencyRequest dependency = getOnlyElement(binding.dependencies());
+ return CodeBlocks.cast(
+ componentBindingExpressions
+ .getDependencyExpression(
+ bindingRequest(dependency.key(), binding.frameworkType()),
+ componentImplementation.name())
+ .codeBlock(),
+ binding.frameworkType().frameworkClass());
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyCycleValidator.java b/java/dagger/internal/codegen/DependencyCycleValidator.java
new file mode 100644
index 0000000..d5d4b62
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyCycleValidator.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.limit;
+import static com.google.common.collect.Iterables.skip;
+import static com.google.common.collect.Sets.newHashSetWithExpectedSize;
+import static dagger.internal.codegen.DaggerGraphs.shortestPath;
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.RequestKinds.getRequestKind;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.graph.EndpointPair;
+import com.google.common.graph.ImmutableNetwork;
+import com.google.common.graph.MutableNetwork;
+import com.google.common.graph.NetworkBuilder;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Node;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** Reports errors for dependency cycles. */
+final class DependencyCycleValidator implements BindingGraphPlugin {
+
+ private final DependencyRequestFormatter dependencyRequestFormatter;
+
+ @Inject
+ DependencyCycleValidator(DependencyRequestFormatter dependencyRequestFormatter) {
+ this.dependencyRequestFormatter = dependencyRequestFormatter;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/DependencyCycle";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ ImmutableNetwork<Node, DependencyEdge> dependencyGraph =
+ nonCycleBreakingDependencyGraph(bindingGraph);
+ // Check each endpoint pair only once, no matter how many parallel edges connect them.
+ Set<EndpointPair<Node>> dependencyEndpointPairs = dependencyGraph.asGraph().edges();
+ Set<EndpointPair<Node>> visited = newHashSetWithExpectedSize(dependencyEndpointPairs.size());
+ for (EndpointPair<Node> endpointPair : dependencyEndpointPairs) {
+ cycleContainingEndpointPair(endpointPair, dependencyGraph, visited)
+ .ifPresent(cycle -> reportCycle(cycle, bindingGraph, diagnosticReporter));
+ }
+ }
+
+ private Optional<Cycle<Node>> cycleContainingEndpointPair(
+ EndpointPair<Node> endpoints,
+ ImmutableNetwork<Node, DependencyEdge> dependencyGraph,
+ Set<EndpointPair<Node>> visited) {
+ if (!visited.add(endpoints)) {
+ // don't recheck endpoints we already know are part of a cycle
+ return Optional.empty();
+ }
+
+ // If there's a path from the target back to the source, there's a cycle.
+ ImmutableList<Node> cycleNodes =
+ shortestPath(dependencyGraph, endpoints.target(), endpoints.source());
+ if (cycleNodes.isEmpty()) {
+ return Optional.empty();
+ }
+
+ Cycle<Node> cycle = Cycle.fromPath(cycleNodes);
+ visited.addAll(cycle.endpointPairs()); // no need to check any edge in this cycle again
+ return Optional.of(cycle);
+ }
+
+ /**
+ * Reports a dependency cycle at the dependency into the cycle that is closest to an entry point.
+ *
+ * <p>For cycles found in reachable binding graphs, looks for the shortest path from the component
+ * that contains the cycle (all bindings in a cycle must be in the same component; see below) to
+ * some binding in the cycle. Then looks for the last dependency in that path that is not in the
+ * cycle; that is the dependency that will be reported, so that the dependency trace will end just
+ * before the cycle.
+ *
+ * <p>For cycles found during full binding graph validation, just reports the component that
+ * contains the cycle.
+ *
+ * <p>Proof (by counterexample) that all bindings in a cycle must be in the same component: Assume
+ * one binding in the cycle is in a parent component. Bindings cannot depend on bindings in child
+ * components, so that binding cannot depend on the next binding in the cycle.
+ */
+ private void reportCycle(
+ Cycle<Node> cycle, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ if (bindingGraph.isFullBindingGraph()) {
+ diagnosticReporter.reportComponent(
+ ERROR,
+ bindingGraph.componentNode(cycle.nodes().asList().get(0).componentPath()).get(),
+ errorMessage(cycle, bindingGraph));
+ return;
+ }
+
+ ImmutableList<Node> path = shortestPathToCycleFromAnEntryPoint(cycle, bindingGraph);
+ Node cycleStartNode = path.get(path.size() - 1);
+ Node previousNode = path.get(path.size() - 2);
+ DependencyEdge dependencyToReport =
+ chooseDependencyEdgeConnecting(previousNode, cycleStartNode, bindingGraph);
+ diagnosticReporter.reportDependency(
+ ERROR, dependencyToReport, errorMessage(cycle.shift(cycleStartNode), bindingGraph));
+ }
+
+ private ImmutableList<Node> shortestPathToCycleFromAnEntryPoint(
+ Cycle<Node> cycle, BindingGraph bindingGraph) {
+ Node someCycleNode = cycle.nodes().asList().get(0);
+ ComponentNode componentContainingCycle =
+ bindingGraph.componentNode(someCycleNode.componentPath()).get();
+ ImmutableList<Node> pathToCycle =
+ shortestPath(bindingGraph.network(), componentContainingCycle, someCycleNode);
+ return subpathToCycle(pathToCycle, cycle);
+ }
+
+ /**
+ * Returns the subpath from the head of {@code path} to the first node in {@code path} that's in
+ * the cycle.
+ */
+ private ImmutableList<Node> subpathToCycle(ImmutableList<Node> path, Cycle<Node> cycle) {
+ ImmutableList.Builder<Node> subpath = ImmutableList.builder();
+ for (Node node : path) {
+ subpath.add(node);
+ if (cycle.nodes().contains(node)) {
+ return subpath.build();
+ }
+ }
+ throw new IllegalArgumentException(
+ "path " + path + " doesn't contain any nodes in cycle " + cycle);
+ }
+
+ private String errorMessage(Cycle<Node> cycle, BindingGraph graph) {
+ StringBuilder message = new StringBuilder("Found a dependency cycle:");
+ ImmutableList<DependencyRequest> cycleRequests =
+ cycle.endpointPairs().stream()
+ // TODO(dpb): Would be nice to take the dependency graph here.
+ .map(endpointPair -> nonCycleBreakingEdge(endpointPair, graph))
+ .map(DependencyEdge::dependencyRequest)
+ .collect(toImmutableList())
+ .reverse();
+ dependencyRequestFormatter.formatIndentedList(message, cycleRequests, 0);
+ return message.toString();
+ }
+
+ /**
+ * Returns one of the edges between two nodes that doesn't {@linkplain
+ * #breaksCycle(DependencyEdge, BindingGraph) break} a cycle.
+ */
+ private DependencyEdge nonCycleBreakingEdge(EndpointPair<Node> endpointPair, BindingGraph graph) {
+ return graph.network().edgesConnecting(endpointPair.source(), endpointPair.target()).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .filter(edge -> !breaksCycle(edge, graph))
+ .findFirst()
+ .get();
+ }
+
+ private boolean breaksCycle(DependencyEdge edge, BindingGraph graph) {
+ if (edge.dependencyRequest().key().multibindingContributionIdentifier().isPresent()) {
+ return false;
+ }
+ if (breaksCycle(edge.dependencyRequest().key().type(), edge.dependencyRequest().kind())) {
+ return true;
+ }
+ Node target = graph.network().incidentNodes(edge).target();
+ if (target instanceof dagger.model.Binding
+ && ((dagger.model.Binding) target).kind().equals(BindingKind.OPTIONAL)) {
+ /* For @BindsOptionalOf bindings, unwrap the type inside the Optional. If the unwrapped type
+ * breaks the cycle, so does the optional binding. */
+ TypeMirror optionalValueType = OptionalType.from(edge.dependencyRequest().key()).valueType();
+ RequestKind requestKind = getRequestKind(optionalValueType);
+ return breaksCycle(extractKeyType(requestKind, optionalValueType), requestKind);
+ }
+ return false;
+ }
+
+ private boolean breaksCycle(TypeMirror requestedType, RequestKind requestKind) {
+ switch (requestKind) {
+ case PROVIDER:
+ case LAZY:
+ case PROVIDER_OF_LAZY:
+ return true;
+
+ case INSTANCE:
+ if (MapType.isMap(requestedType)) {
+ MapType mapType = MapType.from(requestedType);
+ return !mapType.isRawType() && mapType.valuesAreTypeOf(Provider.class);
+ }
+ // fall through
+
+ default:
+ return false;
+ }
+ }
+
+ private DependencyEdge chooseDependencyEdgeConnecting(
+ Node source, Node target, BindingGraph bindingGraph) {
+ return bindingGraph.network().edgesConnecting(source, target).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .findFirst()
+ .get();
+ }
+
+ /** Returns the subgraph containing only {@link DependencyEdge}s that would not break a cycle. */
+ // TODO(dpb): Return a network containing only Binding nodes.
+ private ImmutableNetwork<Node, DependencyEdge> nonCycleBreakingDependencyGraph(
+ BindingGraph bindingGraph) {
+ MutableNetwork<Node, DependencyEdge> dependencyNetwork =
+ NetworkBuilder.from(bindingGraph.network())
+ .expectedNodeCount(bindingGraph.network().nodes().size())
+ .expectedEdgeCount(bindingGraph.dependencyEdges().size())
+ .build();
+ bindingGraph.dependencyEdges().stream()
+ .filter(edge -> !breaksCycle(edge, bindingGraph))
+ .forEach(
+ edge -> {
+ EndpointPair<Node> endpoints = bindingGraph.network().incidentNodes(edge);
+ dependencyNetwork.addEdge(endpoints.source(), endpoints.target(), edge);
+ });
+ return ImmutableNetwork.copyOf(dependencyNetwork);
+ }
+
+ /**
+ * An ordered set of endpoint pairs representing the edges in the cycle. The target of each pair
+ * is the source of the next pair. The target of the last pair is the source of the first pair.
+ */
+ @AutoValue
+ abstract static class Cycle<N> {
+ /**
+ * The ordered set of endpoint pairs representing the edges in the cycle. The target of each
+ * pair is the source of the next pair. The target of the last pair is the source of the first
+ * pair.
+ */
+ abstract ImmutableSet<EndpointPair<N>> endpointPairs();
+
+ /** Returns the nodes that participate in the cycle. */
+ ImmutableSet<N> nodes() {
+ return endpointPairs().stream()
+ .flatMap(pair -> Stream.of(pair.source(), pair.target()))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the number of edges in the cycle. */
+ int size() {
+ return endpointPairs().size();
+ }
+
+ /**
+ * Shifts this cycle so that it starts with a specific node.
+ *
+ * @return a cycle equivalent to this one but whose first pair starts with {@code startNode}
+ */
+ Cycle<N> shift(N startNode) {
+ int startIndex = Iterables.indexOf(endpointPairs(), pair -> pair.source().equals(startNode));
+ checkArgument(
+ startIndex >= 0, "startNode (%s) is not part of this cycle: %s", startNode, this);
+ if (startIndex == 0) {
+ return this;
+ }
+ ImmutableSet.Builder<EndpointPair<N>> shifted = ImmutableSet.builder();
+ shifted.addAll(skip(endpointPairs(), startIndex));
+ shifted.addAll(limit(endpointPairs(), size() - startIndex));
+ return new AutoValue_DependencyCycleValidator_Cycle<>(shifted.build());
+ }
+
+ @Override
+ public final String toString() {
+ return endpointPairs().toString();
+ }
+
+ /**
+ * Creates a {@link Cycle} from a nonempty list of nodes, assuming there is an edge between each
+ * pair of nodes as well as an edge from the last node to the first.
+ */
+ static <N> Cycle<N> fromPath(List<N> nodes) {
+ checkArgument(!nodes.isEmpty());
+ ImmutableSet.Builder<EndpointPair<N>> cycle = ImmutableSet.builder();
+ cycle.add(EndpointPair.ordered(getLast(nodes), nodes.get(0)));
+ for (int i = 0; i < nodes.size() - 1; i++) {
+ cycle.add(EndpointPair.ordered(nodes.get(i), nodes.get(i + 1)));
+ }
+ return new AutoValue_DependencyCycleValidator_Cycle<>(cycle.build());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyEdgeImpl.java b/java/dagger/internal/codegen/DependencyEdgeImpl.java
new file mode 100644
index 0000000..64b0845
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyEdgeImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.DependencyRequest;
+
+/** An implementation of {@link DependencyEdge}. */
+final class DependencyEdgeImpl implements DependencyEdge {
+
+ private final DependencyRequest dependencyRequest;
+ private final boolean entryPoint;
+
+ DependencyEdgeImpl(DependencyRequest dependencyRequest, boolean entryPoint) {
+ this.dependencyRequest = dependencyRequest;
+ this.entryPoint = entryPoint;
+ }
+
+ @Override
+ public DependencyRequest dependencyRequest() {
+ return dependencyRequest;
+ }
+
+ @Override
+ public boolean isEntryPoint() {
+ return entryPoint;
+ }
+
+ @Override
+ public String toString() {
+ String string =
+ dependencyRequest
+ .requestElement()
+ .map(ElementFormatter::elementToString)
+ .orElseGet(
+ () ->
+ "synthetic request for "
+ + dependencyRequest.kind().format(dependencyRequest.key()));
+ return entryPoint ? string + " (entry point)" : string;
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java b/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java
new file mode 100644
index 0000000..b42f9c4
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
+import static dagger.internal.codegen.javapoet.TypeNames.dependencyMethodProducerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/**
+ * A {@link dagger.producers.Producer} creation expression for a production method on a production
+ * component's {@linkplain dagger.producers.ProductionComponent#dependencies()} dependency} that
+ * returns a {@link com.google.common.util.concurrent.ListenableFuture}.
+ */
+// TODO(dpb): Resolve with DependencyMethodProviderCreationExpression.
+final class DependencyMethodProducerCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final BindingGraph graph;
+
+ DependencyMethodProducerCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ BindingGraph graph) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.graph = checkNotNull(graph);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ ComponentRequirement dependency =
+ graph.componentDescriptor().getDependencyThatDefinesMethod(binding.bindingElement().get());
+ FieldSpec dependencyField =
+ FieldSpec.builder(
+ ClassName.get(dependency.typeElement()), dependency.variableName(), PRIVATE, FINAL)
+ .initializer(
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ dependency,
+ // This isn't a real class name, but we want the requesting class for the
+ // expression to *not* be the same class as the component implementation,
+ // because it isn't... it's an anonymous inner class.
+ // TODO(cgdecker): If we didn't use an anonymous inner class here but instead
+ // generated a named nested class as with
+ // DependencyMethodProviderCreationExpression, we wouldn't need to deal with
+ // this and might be able to avoid potentially creating an extra field in the
+ // component?
+ componentImplementation.name().nestedClass("Anonymous")))
+ .build();
+ // TODO(b/70395982): Explore using a private static type instead of an anonymous class.
+ TypeName keyType = TypeName.get(binding.key().type());
+ return CodeBlock.of(
+ "$L",
+ anonymousClassBuilder("")
+ .superclass(dependencyMethodProducerOf(keyType))
+ .addField(dependencyField)
+ .addMethod(
+ methodBuilder("callDependencyMethod")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(listenableFutureOf(keyType))
+ .addStatement(
+ "return $N.$L()",
+ dependencyField,
+ binding.bindingElement().get().getSimpleName())
+ .build())
+ .build());
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java b/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java
new file mode 100644
index 0000000..8532481
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import javax.lang.model.element.Element;
+
+/**
+ * A {@link javax.inject.Provider} creation expression for a provision method on a component's
+ * {@linkplain dagger.Component#dependencies()} dependency}.
+ */
+// TODO(dpb): Resolve with DependencyMethodProducerCreationExpression.
+final class DependencyMethodProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ComponentImplementation componentImplementation;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final CompilerOptions compilerOptions;
+ private final BindingGraph graph;
+ private final ContributionBinding binding;
+
+ DependencyMethodProviderCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ CompilerOptions compilerOptions,
+ BindingGraph graph) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.compilerOptions = checkNotNull(compilerOptions);
+ this.graph = checkNotNull(graph);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ // TODO(sameb): The Provider.get() throws a very vague NPE. The stack trace doesn't
+ // help to figure out what the method or return type is. If we include a string
+ // of the return type or method name in the error message, that can defeat obfuscation.
+ // We can easily include the raw type (no generics) + annotation type (no values),
+ // using .class & String.format -- but that wouldn't be the whole story.
+ // What should we do?
+ CodeBlock invocation =
+ ComponentProvisionBindingExpression.maybeCheckForNull(
+ (ProvisionBinding) binding,
+ compilerOptions,
+ CodeBlock.of(
+ "$N.$N()", dependency().variableName(), provisionMethod().getSimpleName()));
+ ClassName dependencyClassName = ClassName.get(dependency().typeElement());
+ TypeName keyType = TypeName.get(binding.key().type());
+ MethodSpec.Builder getMethod =
+ methodBuilder("get")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(keyType)
+ .addStatement("return $L", invocation);
+ if (binding.nullableType().isPresent()) {
+ getMethod.addAnnotation(ClassName.get(MoreTypes.asTypeElement(binding.nullableType().get())));
+ }
+ componentImplementation.addType(
+ COMPONENT_PROVISION_FACTORY,
+ classBuilder(factoryClassName())
+ .addSuperinterface(providerOf(keyType))
+ .addModifiers(PRIVATE, STATIC)
+ .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL)
+ .addMethod(
+ constructorBuilder()
+ .addParameter(dependencyClassName, dependency().variableName())
+ .addStatement("this.$1L = $1L", dependency().variableName())
+ .build())
+ .addMethod(getMethod.build())
+ .build());
+ return CodeBlock.of(
+ "new $T($L)",
+ factoryClassName(),
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ dependency(), componentImplementation.name()));
+ }
+
+ private ClassName factoryClassName() {
+ String factoryName =
+ ClassName.get(dependency().typeElement()).toString().replace('.', '_')
+ + "_"
+ + binding.bindingElement().get().getSimpleName();
+ return componentImplementation.name().nestedClass(factoryName);
+ }
+
+ private ComponentRequirement dependency() {
+ return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod());
+ }
+
+ private Element provisionMethod() {
+ return binding.bindingElement().get();
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyRequestFactory.java b/java/dagger/internal/codegen/DependencyRequestFactory.java
new file mode 100644
index 0000000..3ad12e2
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyRequestFactory.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.RequestKinds.frameworkClass;
+import static dagger.internal.codegen.RequestKinds.getRequestKind;
+import static dagger.model.RequestKind.FUTURE;
+import static dagger.model.RequestKind.INSTANCE;
+import static dagger.model.RequestKind.MEMBERS_INJECTION;
+import static dagger.model.RequestKind.PRODUCER;
+import static dagger.model.RequestKind.PROVIDER;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Lazy;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Factory for {@link DependencyRequest}s.
+ *
+ * <p>Any factory method may throw {@link TypeNotPresentException} if a type is not available, which
+ * may mean that the type will be generated in a later round of processing.
+ */
+final class DependencyRequestFactory {
+ private final KeyFactory keyFactory;
+ private final DaggerTypes types;
+
+ @Inject
+ DependencyRequestFactory(KeyFactory keyFactory, DaggerTypes types) {
+ this.keyFactory = keyFactory;
+ this.types = types;
+ }
+
+ ImmutableSet<DependencyRequest> forRequiredResolvedVariables(
+ List<? extends VariableElement> variables, List<? extends TypeMirror> resolvedTypes) {
+ checkState(resolvedTypes.size() == variables.size());
+ ImmutableSet.Builder<DependencyRequest> builder = ImmutableSet.builder();
+ for (int i = 0; i < variables.size(); i++) {
+ builder.add(forRequiredResolvedVariable(variables.get(i), resolvedTypes.get(i)));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Creates synthetic dependency requests for each individual multibinding contribution in {@code
+ * multibindingContributions}.
+ */
+ ImmutableSet<DependencyRequest> forMultibindingContributions(
+ Key multibindingKey, Iterable<ContributionBinding> multibindingContributions) {
+ ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
+ for (ContributionBinding multibindingContribution : multibindingContributions) {
+ requests.add(forMultibindingContribution(multibindingKey, multibindingContribution));
+ }
+ return requests.build();
+ }
+
+ /** Creates a synthetic dependency request for one individual {@code multibindingContribution}. */
+ private DependencyRequest forMultibindingContribution(
+ Key multibindingKey, ContributionBinding multibindingContribution) {
+ checkArgument(
+ multibindingContribution.key().multibindingContributionIdentifier().isPresent(),
+ "multibindingContribution's key must have a multibinding contribution identifier: %s",
+ multibindingContribution);
+ return DependencyRequest.builder()
+ .kind(multibindingContributionRequestKind(multibindingKey, multibindingContribution))
+ .key(multibindingContribution.key())
+ .build();
+ }
+
+ // TODO(b/28555349): support PROVIDER_OF_LAZY here too
+ private static final ImmutableSet<RequestKind> WRAPPING_MAP_VALUE_FRAMEWORK_TYPES =
+ ImmutableSet.of(PROVIDER, PRODUCER);
+
+ private RequestKind multibindingContributionRequestKind(
+ Key multibindingKey, ContributionBinding multibindingContribution) {
+ switch (multibindingContribution.contributionType()) {
+ case MAP:
+ MapType mapType = MapType.from(multibindingKey);
+ for (RequestKind kind : WRAPPING_MAP_VALUE_FRAMEWORK_TYPES) {
+ if (mapType.valuesAreTypeOf(frameworkClass(kind))) {
+ return kind;
+ }
+ }
+ // fall through
+ case SET:
+ case SET_VALUES:
+ return INSTANCE;
+ case UNIQUE:
+ throw new IllegalArgumentException(
+ "multibindingContribution must be a multibinding: " + multibindingContribution);
+ }
+ throw new AssertionError(multibindingContribution.toString());
+ }
+
+ DependencyRequest forRequiredResolvedVariable(
+ VariableElement variableElement, TypeMirror resolvedType) {
+ checkNotNull(variableElement);
+ checkNotNull(resolvedType);
+ Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement);
+ return newDependencyRequest(variableElement, resolvedType, qualifier);
+ }
+
+ DependencyRequest forComponentProvisionMethod(
+ ExecutableElement provisionMethod, ExecutableType provisionMethodType) {
+ checkNotNull(provisionMethod);
+ checkNotNull(provisionMethodType);
+ checkArgument(
+ provisionMethod.getParameters().isEmpty(),
+ "Component provision methods must be empty: %s",
+ provisionMethod);
+ Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(provisionMethod);
+ return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier);
+ }
+
+ DependencyRequest forComponentProductionMethod(
+ ExecutableElement productionMethod, ExecutableType productionMethodType) {
+ checkNotNull(productionMethod);
+ checkNotNull(productionMethodType);
+ checkArgument(
+ productionMethod.getParameters().isEmpty(),
+ "Component production methods must be empty: %s",
+ productionMethod);
+ TypeMirror type = productionMethodType.getReturnType();
+ Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(productionMethod);
+ // Only a component production method can be a request for a ListenableFuture, so we
+ // special-case it here.
+ if (isTypeOf(ListenableFuture.class, type)) {
+ return DependencyRequest.builder()
+ .kind(FUTURE)
+ .key(keyFactory.forQualifiedType(qualifier, types.unwrapType(type)))
+ .requestElement(productionMethod)
+ .build();
+ } else {
+ return newDependencyRequest(productionMethod, type, qualifier);
+ }
+ }
+
+ DependencyRequest forComponentMembersInjectionMethod(
+ ExecutableElement membersInjectionMethod, ExecutableType membersInjectionMethodType) {
+ checkNotNull(membersInjectionMethod);
+ checkNotNull(membersInjectionMethodType);
+ Optional<AnnotationMirror> qualifier =
+ InjectionAnnotations.getQualifier(membersInjectionMethod);
+ checkArgument(!qualifier.isPresent());
+ TypeMirror membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes());
+ return DependencyRequest.builder()
+ .kind(MEMBERS_INJECTION)
+ .key(keyFactory.forMembersInjectedType(membersInjectedType))
+ .requestElement(membersInjectionMethod)
+ .build();
+ }
+
+ DependencyRequest forProductionImplementationExecutor() {
+ return DependencyRequest.builder()
+ .kind(PROVIDER)
+ .key(keyFactory.forProductionImplementationExecutor())
+ .build();
+ }
+
+ DependencyRequest forProductionComponentMonitor() {
+ return DependencyRequest.builder()
+ .kind(PROVIDER)
+ .key(keyFactory.forProductionComponentMonitor())
+ .build();
+ }
+
+ /**
+ * Returns a synthetic request for the present value of an optional binding generated from a
+ * {@link dagger.BindsOptionalOf} declaration.
+ */
+ DependencyRequest forSyntheticPresentOptionalBinding(Key requestKey, RequestKind kind) {
+ Optional<Key> key = keyFactory.unwrapOptional(requestKey);
+ checkArgument(key.isPresent(), "not a request for optional: %s", requestKey);
+ return DependencyRequest.builder()
+ .kind(kind)
+ .key(key.get())
+ .isNullable(
+ allowsNull(getRequestKind(OptionalType.from(requestKey).valueType()), Optional.empty()))
+ .build();
+ }
+
+ private DependencyRequest newDependencyRequest(
+ Element requestElement, TypeMirror type, Optional<AnnotationMirror> qualifier) {
+ RequestKind requestKind = getRequestKind(type);
+ return DependencyRequest.builder()
+ .kind(requestKind)
+ .key(keyFactory.forQualifiedType(qualifier, extractKeyType(requestKind, type)))
+ .requestElement(requestElement)
+ .isNullable(allowsNull(requestKind, getNullableType(requestElement)))
+ .build();
+ }
+
+ /**
+ * Returns {@code true} if a given request element allows null values. {@link
+ * RequestKind#INSTANCE} requests must be annotated with {@code @Nullable} in order to allow null
+ * values. All other request kinds implicitly allow null values because they are are wrapped
+ * inside {@link Provider}, {@link Lazy}, etc.
+ */
+ private boolean allowsNull(RequestKind kind, Optional<DeclaredType> nullableType) {
+ return nullableType.isPresent() || !kind.equals(INSTANCE);
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyRequestFormatter.java b/java/dagger/internal/codegen/DependencyRequestFormatter.java
new file mode 100644
index 0000000..25becaf
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyRequestFormatter.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.ElementFormatter.elementToString;
+import static dagger.internal.codegen.RequestKinds.requestType;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.producers.Produces;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing a
+ * chain of dependencies.
+ *
+ * <dl>
+ * <dt>For component provision methods
+ * <dd>{@code @Qualifier SomeType is provided at\n ComponentType.method()}
+ * <dt>For component injection methods
+ * <dd>{@code SomeType is injected at\n ComponentType.method(foo)}
+ * <dt>For parameters to {@link Provides @Provides}, {@link Produces @Produces}, or {@link
+ * Inject @Inject} methods:
+ * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.method([…, ]param[, …])}
+ * <dt>For parameters to {@link Inject @Inject} constructors:
+ * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType([…, ]param[, …])}
+ * <dt>For {@link Inject @Inject} fields:
+ * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.field}
+ * </dl>
+ */
+final class DependencyRequestFormatter extends Formatter<DependencyRequest> {
+
+ private final DaggerTypes types;
+
+ @Inject
+ DependencyRequestFormatter(DaggerTypes types) {
+ this.types = types;
+ }
+
+ @Override
+ public String format(DependencyRequest request) {
+ return request
+ .requestElement()
+ .map(element -> element.accept(formatVisitor, request))
+ .orElse("");
+ }
+
+ /**
+ * Appends a newline and the formatted dependency request unless {@link
+ * #format(DependencyRequest)} returns the empty string.
+ */
+ @CanIgnoreReturnValue
+ StringBuilder appendFormatLine(StringBuilder builder, DependencyRequest dependencyRequest) {
+ String formatted = format(dependencyRequest);
+ if (!formatted.isEmpty()) {
+ builder.append('\n').append(formatted);
+ }
+ return builder;
+ }
+
+ private final ElementVisitor<String, DependencyRequest> formatVisitor =
+ new ElementKindVisitor8<String, DependencyRequest>() {
+
+ @Override
+ public String visitExecutableAsMethod(ExecutableElement method, DependencyRequest request) {
+ return INDENT
+ + request.key()
+ + " is "
+ + componentMethodRequestVerb(request)
+ + " at\n"
+ + DOUBLE_INDENT
+ + elementToString(method);
+ }
+
+ @Override
+ public String visitVariable(VariableElement variable, DependencyRequest request) {
+ TypeMirror requestedType = requestType(request.kind(), request.key().type(), types);
+ return INDENT
+ + formatQualifier(request.key().qualifier())
+ + requestedType
+ + " is injected at\n"
+ + DOUBLE_INDENT
+ + elementToString(variable);
+ }
+
+ @Override
+ public String visitType(TypeElement e, DependencyRequest request) {
+ return ""; // types by themselves provide no useful information.
+ }
+
+ @Override
+ protected String defaultAction(Element element, DependencyRequest request) {
+ throw new IllegalStateException(
+ "Invalid request " + element.getKind() + " element " + element);
+ }
+ };
+
+ private String formatQualifier(Optional<AnnotationMirror> maybeQualifier) {
+ return maybeQualifier.map(qualifier -> qualifier + " ").orElse("");
+ }
+
+ /**
+ * Returns the verb for a component method dependency request. Returns "produced", "provided", or
+ * "injected", depending on the kind of request.
+ */
+ private String componentMethodRequestVerb(DependencyRequest request) {
+ switch (request.kind()) {
+ case FUTURE:
+ case PRODUCER:
+ return "produced";
+
+ case INSTANCE:
+ case LAZY:
+ case PROVIDER:
+ case PROVIDER_OF_LAZY:
+ return "provided";
+
+ case MEMBERS_INJECTION:
+ return "injected";
+
+ case PRODUCED:
+ break;
+ }
+ throw new AssertionError("illegal request kind for method: " + request);
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyRequestValidator.java b/java/dagger/internal/codegen/DependencyRequestValidator.java
new file mode 100644
index 0000000..1a99818
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyRequestValidator.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
+import static dagger.internal.codegen.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.RequestKinds.getRequestKind;
+import static javax.lang.model.type.TypeKind.WILDCARD;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.MembersInjector;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** Validation for dependency requests. */
+final class DependencyRequestValidator {
+ private final MembersInjectionValidator membersInjectionValidator;
+
+ @Inject
+ DependencyRequestValidator(MembersInjectionValidator membersInjectionValidator) {
+ this.membersInjectionValidator = membersInjectionValidator;
+ }
+
+ /**
+ * Adds an error if the given dependency request has more than one qualifier annotation or is a
+ * non-instance request with a wildcard type.
+ */
+ void validateDependencyRequest(
+ ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
+ ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(requestElement);
+ if (qualifiers.size() > 1) {
+ for (AnnotationMirror qualifier : qualifiers) {
+ report.addError(
+ "A single dependency request may not use more than one @Qualifier",
+ requestElement,
+ qualifier);
+ }
+ }
+
+ TypeMirror keyType = extractKeyType(getRequestKind(requestType), requestType);
+ if (keyType.getKind().equals(WILDCARD)) {
+ // TODO(ronshapiro): Explore creating this message using RequestKinds.
+ report.addError(
+ "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
+ + "or Produced<T> when T is a wildcard type such as "
+ + keyType,
+ requestElement);
+ }
+ if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) {
+ DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType);
+ if (membersInjectorType.getTypeArguments().isEmpty()) {
+ report.addError("Cannot inject a raw MembersInjector", requestElement);
+ } else {
+ report.addSubreport(
+ membersInjectionValidator.validateMembersInjectionRequest(
+ requestElement, membersInjectorType.getTypeArguments().get(0)));
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the given dependency request is for a {@link dagger.producers.Producer} or
+ * {@link dagger.producers.Produced}.
+ *
+ * <p>Only call this when processing a provision binding.
+ */
+ // TODO(dpb): Should we disallow Producer entry points in non-production components?
+ void checkNotProducer(ValidationReport.Builder<?> report, VariableElement requestElement) {
+ TypeMirror requestType = requestElement.asType();
+ if (FrameworkTypes.isProducerType(requestType)) {
+ report.addError(
+ String.format(
+ "%s may only be injected in @Produces methods",
+ MoreTypes.asTypeElement(requestType).getSimpleName()),
+ requestElement);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyVariableNamer.java b/java/dagger/internal/codegen/DependencyVariableNamer.java
new file mode 100644
index 0000000..21f2d32
--- /dev/null
+++ b/java/dagger/internal/codegen/DependencyVariableNamer.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.SourceFiles.simpleVariableName;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Ascii;
+import com.google.common.base.CaseFormat;
+import dagger.Lazy;
+import dagger.model.DependencyRequest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.inject.Provider;
+
+/**
+ * Picks a reasonable name for what we think is being provided from the variable name associated
+ * with the {@link DependencyRequest}. I.e. strips out words like "lazy" and "provider" if we
+ * believe that those refer to {@link Lazy} and {@link Provider} rather than the type being
+ * provided.
+ */
+//TODO(gak): develop the heuristics to get better names
+final class DependencyVariableNamer {
+ private static final Pattern LAZY_PROVIDER_PATTERN = Pattern.compile("lazy(\\w+)Provider");
+
+ static String name(DependencyRequest dependency) {
+ if (!dependency.requestElement().isPresent()) {
+ return simpleVariableName(MoreTypes.asTypeElement(dependency.key().type()));
+ }
+
+ String variableName = dependency.requestElement().get().getSimpleName().toString();
+ if (Ascii.isUpperCase(variableName.charAt(0))) {
+ variableName = toLowerCamel(variableName);
+ }
+ switch (dependency.kind()) {
+ case INSTANCE:
+ return variableName;
+ case LAZY:
+ return variableName.startsWith("lazy") && !variableName.equals("lazy")
+ ? toLowerCamel(variableName.substring(4))
+ : variableName;
+ case PROVIDER_OF_LAZY:
+ Matcher matcher = LAZY_PROVIDER_PATTERN.matcher(variableName);
+ if (matcher.matches()) {
+ return toLowerCamel(matcher.group(1));
+ }
+ // fall through
+ case PROVIDER:
+ return variableName.endsWith("Provider") && !variableName.equals("Provider")
+ ? variableName.substring(0, variableName.length() - 8)
+ : variableName;
+ case PRODUCED:
+ return variableName.startsWith("produced") && !variableName.equals("produced")
+ ? toLowerCamel(variableName.substring(8))
+ : variableName;
+ case PRODUCER:
+ return variableName.endsWith("Producer") && !variableName.equals("Producer")
+ ? variableName.substring(0, variableName.length() - 8)
+ : variableName;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ private static String toLowerCamel(String name) {
+ return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name);
+ }
+}
diff --git a/java/dagger/internal/codegen/DependsOnProductionExecutorValidator.java b/java/dagger/internal/codegen/DependsOnProductionExecutorValidator.java
new file mode 100644
index 0000000..e611d22
--- /dev/null
+++ b/java/dagger/internal/codegen/DependsOnProductionExecutorValidator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.MaybeBinding;
+import dagger.model.Key;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+
+/**
+ * Reports an error on all bindings that depend explicitly on the {@code @Production Executor} key.
+ */
+// TODO(dpb,beder): Validate this during @Inject/@Provides/@Produces validation.
+final class DependsOnProductionExecutorValidator implements BindingGraphPlugin {
+ private final CompilerOptions compilerOptions;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ DependsOnProductionExecutorValidator(CompilerOptions compilerOptions, KeyFactory keyFactory) {
+ this.compilerOptions = compilerOptions;
+ this.keyFactory = keyFactory;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/DependsOnProductionExecutor";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ if (!compilerOptions.usesProducers()) {
+ return;
+ }
+
+ Key productionImplementationExecutorKey = keyFactory.forProductionImplementationExecutor();
+ Key productionExecutorKey = keyFactory.forProductionExecutor();
+
+ bindingGraph.network().nodes().stream()
+ .flatMap(instancesOf(MaybeBinding.class))
+ .filter(node -> node.key().equals(productionExecutorKey))
+ .flatMap(productionExecutor -> bindingGraph.requestingBindings(productionExecutor).stream())
+ .filter(binding -> !binding.key().equals(productionImplementationExecutorKey))
+ .forEach(binding -> reportError(diagnosticReporter, binding));
+ }
+
+ private void reportError(DiagnosticReporter diagnosticReporter, dagger.model.Binding binding) {
+ diagnosticReporter.reportBinding(
+ ERROR, binding, "%s may not depend on the production executor", binding.key());
+ }
+}
diff --git a/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java
new file mode 100644
index 0000000..4f2f622
--- /dev/null
+++ b/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression that depends on a framework instance. */
+final class DerivedFromFrameworkInstanceBindingExpression extends BindingExpression {
+
+ private final BindingRequest frameworkRequest;
+ private final RequestKind requestKind;
+ private final FrameworkType frameworkType;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+
+ DerivedFromFrameworkInstanceBindingExpression(
+ Key key,
+ FrameworkType frameworkType,
+ RequestKind requestKind,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types) {
+ this.frameworkRequest = bindingRequest(key, frameworkType);
+ this.requestKind = checkNotNull(requestKind);
+ this.frameworkType = checkNotNull(frameworkType);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.types = checkNotNull(types);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return frameworkType.to(
+ requestKind,
+ componentBindingExpressions.getDependencyExpression(frameworkRequest, requestingClass),
+ types);
+ }
+
+ @Override
+ Expression getDependencyExpressionForComponentMethod(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ Expression frameworkInstance =
+ componentBindingExpressions.getDependencyExpressionForComponentMethod(
+ frameworkRequest, componentMethod, component);
+ Expression forRequestKind = frameworkType.to(requestKind, frameworkInstance, types);
+ TypeMirror rawReturnType = types.erasure(componentMethod.resolvedReturnType(types));
+ if (!types.isAssignable(forRequestKind.type(), rawReturnType)) {
+ checkState(
+ component.isAbstract(),
+ "FrameworkType.to() should always return an accessible type unless we're in "
+ + "ahead-of-time mode, where the framework instance type is erased since it's not "
+ + "publicly accessible, but the return type is accessible to the package. "
+ + "\n Component: %s, method: %s",
+ component.name(),
+ componentMethod);
+ return forRequestKind.castTo(rawReturnType);
+ }
+ return forRequestKind;
+ }
+}
diff --git a/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java b/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java
new file mode 100644
index 0000000..45e99a8
--- /dev/null
+++ b/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreElements.getAnnotationMirror;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.serialization.ProtoSerialization.fromAnnotationValue;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.ComponentDefinitionType;
+import dagger.internal.ConfigureInitializationParameters;
+import dagger.internal.ModifiableBinding;
+import dagger.internal.ModifiableModule;
+import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod;
+import dagger.internal.codegen.serialization.BindingRequestProto;
+import dagger.internal.codegen.serialization.ComponentRequirementProto;
+import dagger.internal.codegen.serialization.FrameworkTypeWrapper;
+import dagger.internal.codegen.serialization.KeyProto;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Reconstructs {@link ComponentImplementation}s that have already been compiled. Uses metadata
+ * annotations on the generated type and it's methods to reconstitute the equivalent {@link
+ * ComponentImplementation} state.
+ */
+final class DeserializedComponentImplementationBuilder {
+ private final CompilerOptions compilerOptions;
+ private final ComponentCreatorImplementationFactory componentCreatorImplementationFactory;
+ private final TypeProtoConverter typeProtoConverter;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ DeserializedComponentImplementationBuilder(
+ CompilerOptions compilerOptions,
+ ComponentCreatorImplementationFactory componentCreatorImplementationFactory,
+ TypeProtoConverter typeProtoConverter,
+ KeyFactory keyFactory) {
+ this.compilerOptions = compilerOptions;
+ this.componentCreatorImplementationFactory = componentCreatorImplementationFactory;
+ this.typeProtoConverter = typeProtoConverter;
+ this.keyFactory = keyFactory;
+ }
+
+ /** Creates a new {@link ComponentImplementation} from a compiled component. */
+ ComponentImplementation create(ComponentDescriptor component, TypeElement generatedComponent) {
+ Optional<ComponentImplementation> superclassImplementation =
+ deserializedSuperclassImplementation(
+ component, MoreTypes.asTypeElement(generatedComponent.getSuperclass()));
+
+ ComponentImplementation componentImplementation =
+ ComponentImplementation.forDeserializedComponent(
+ component,
+ ClassName.get(generatedComponent),
+ generatedComponent.getNestingKind(),
+ superclassImplementation,
+ compilerOptions);
+
+ componentImplementation.setCreatorImplementation(
+ superclassImplementation.isPresent()
+ ? Optional.empty()
+ : componentCreatorImplementationFactory.create(
+ componentImplementation, Optional.empty()));
+
+ // TODO(b/117833324): Consider omitting superclass implementations, so that only one instance of
+ // ComponentImplementation needs to be created (in most cases, we don't care about nested levels
+ // of superclass implementations, except for the base implementation). If that's possible, use
+ // getLocalAndInheritedMethods instead of getEnclosedElements() here.
+ for (ExecutableElement method : methodsIn(generatedComponent.getEnclosedElements())) {
+ getAnnotationMirror(method, ModifiableBinding.class)
+ .asSet()
+ .forEach(
+ annotation ->
+ addModifiableBindingMethod(componentImplementation, method, annotation));
+
+ getAnnotationMirror(method, ModifiableModule.class)
+ .asSet()
+ .forEach(
+ annotation -> addModifiableModuleMethod(componentImplementation, method, annotation));
+
+ getAnnotationMirror(method, ConfigureInitializationParameters.class)
+ .asSet()
+ .forEach(
+ annotation ->
+ setConfigureInitializationMethod(componentImplementation, method, annotation));
+ }
+
+ for (TypeElement nestedType : typesIn(generatedComponent.getEnclosedElements())) {
+ addChildImplementation(component, componentImplementation, nestedType);
+ }
+
+ return componentImplementation;
+ }
+
+ private Optional<ComponentImplementation> deserializedSuperclassImplementation(
+ ComponentDescriptor component, TypeElement superclassElement) {
+ return isAnnotationPresent(superclassElement, ComponentDefinitionType.class)
+ ? Optional.of(create(component, superclassElement))
+ : Optional.empty();
+ }
+
+ private void addModifiableBindingMethod(
+ ComponentImplementation componentImplementation,
+ ExecutableElement method,
+ AnnotationMirror metadataAnnotation) {
+ ModifiableBindingType modifiableBindingType =
+ ModifiableBindingType.valueOf(
+ getAnnotationValue(metadataAnnotation, "modifiableBindingType").getValue().toString());
+
+ BindingRequest request =
+ parseBindingRequest(getAnnotationValue(metadataAnnotation, "bindingRequest"));
+
+ ImmutableList<Key> multibindingContributions =
+ asAnnotationValues(getAnnotationValue(metadataAnnotation, "multibindingContributions"))
+ .stream()
+ .map(this::parseKey)
+ .collect(toImmutableList());
+
+ componentImplementation.addModifiableBindingMethod(
+ modifiableBindingType,
+ request,
+ method.getReturnType(),
+ methodDeclaration(method),
+ method.getModifiers().contains(FINAL));
+ componentImplementation.registerImplementedMultibindingKeys(request, multibindingContributions);
+ }
+
+ private BindingRequest fromProto(BindingRequestProto bindingRequest) {
+ Key key = keyFactory.fromProto(bindingRequest.getKey());
+ return bindingRequest.getFrameworkType().equals(FrameworkTypeWrapper.FrameworkType.UNKNOWN)
+ ? bindingRequest(key, RequestKind.valueOf(bindingRequest.getRequestKind().name()))
+ : bindingRequest(key, FrameworkType.valueOf(bindingRequest.getFrameworkType().name()));
+ }
+
+ /**
+ * Returns a {@link MethodSpec} for a {@link
+ * dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod}. The method contents
+ * are not relevant since this represents a method that has already been compiled.
+ *
+ * <p>Ideally this could be {@code MethodSpec.overriding(method).build()}, but that doesn't work
+ * for {@code final} methods
+ */
+ private MethodSpec methodDeclaration(ExecutableElement method) {
+ return methodBuilder(method.getSimpleName().toString())
+ .addModifiers(method.getModifiers())
+ .returns(TypeName.get(method.getReturnType()))
+ .build();
+ }
+
+ private void addModifiableModuleMethod(
+ ComponentImplementation componentImplementation,
+ ExecutableElement method,
+ AnnotationMirror metadataAnnotation) {
+ ComponentRequirement moduleRequirement =
+ parseComponentRequirement(getAnnotationValue(metadataAnnotation, "value"));
+ componentImplementation.registerModifiableModuleMethod(
+ moduleRequirement, method.getSimpleName().toString());
+ }
+
+ private void setConfigureInitializationMethod(
+ ComponentImplementation componentImplementation,
+ ExecutableElement method,
+ AnnotationMirror metadataAnnotation) {
+ ImmutableSet<ComponentRequirement> parameters =
+ asAnnotationValues(getAnnotationValue(metadataAnnotation, "value")).stream()
+ .map(this::parseComponentRequirement)
+ .collect(toImmutableSet());
+
+ componentImplementation.setConfigureInitializationMethod(
+ ConfigureInitializationMethod.create(MethodSpec.overriding(method).build(), parameters));
+ }
+
+ private void addChildImplementation(
+ ComponentDescriptor component,
+ ComponentImplementation componentImplementation,
+ TypeElement nestedType) {
+ getAnnotationMirror(nestedType, ComponentDefinitionType.class)
+ .transform(annotation -> (TypeMirror) getAnnotationValue(annotation, "value").getValue())
+ .transform(MoreTypes::asTypeElement)
+ .asSet()
+ .forEach(
+ componentDefinitionType -> {
+ ComponentDescriptor child =
+ component.childComponentsByElement().get(componentDefinitionType);
+ componentImplementation.addChild(child, create(child, nestedType));
+ });
+ }
+
+ private Key parseKey(AnnotationValue annotationValue) {
+ return keyFactory.fromProto(
+ fromAnnotationValue(annotationValue, KeyProto.getDefaultInstance()));
+ }
+
+ private BindingRequest parseBindingRequest(AnnotationValue annotationValue) {
+ return fromProto(
+ fromAnnotationValue(annotationValue, BindingRequestProto.getDefaultInstance()));
+ }
+
+ private ComponentRequirement parseComponentRequirement(AnnotationValue annotationValue) {
+ return fromProto(
+ fromAnnotationValue(annotationValue, ComponentRequirementProto.getDefaultInstance()));
+ }
+
+ private ComponentRequirement fromProto(ComponentRequirementProto proto) {
+ switch (proto.getRequirementCase()) {
+ case MODULE:
+ return ComponentRequirement.forModule(typeProtoConverter.fromProto(proto.getModule()));
+ case DEPENDENCY:
+ return ComponentRequirement.forDependency(
+ typeProtoConverter.fromProto(proto.getDependency()));
+ case BOUND_INSTANCE:
+ return ComponentRequirement.forBoundInstance(
+ keyFactory.fromProto(proto.getBoundInstance().getKey()),
+ proto.getBoundInstance().getNullable(),
+ proto.getBoundInstance().getVariableName());
+ case REQUIREMENT_NOT_SET:
+ // fall through
+ }
+ throw new AssertionError(proto);
+ }
+}
diff --git a/java/dagger/internal/codegen/DiagnosticFormatting.java b/java/dagger/internal/codegen/DiagnosticFormatting.java
new file mode 100644
index 0000000..8502ecb
--- /dev/null
+++ b/java/dagger/internal/codegen/DiagnosticFormatting.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utility methods for formatting diagnostics to the {@link javax.annotation.processing.Messager}.
+ */
+final class DiagnosticFormatting {
+
+ /**
+ * A regular expression to match a small list of specific packages deemed to be unhelpful to
+ * display in fully qualified types in error messages.
+ *
+ * <p>Note: This should never be applied to messages themselves.
+ */
+ private static final Pattern COMMON_PACKAGE_PATTERN =
+ Pattern.compile(
+ "(?:^|[^.a-z_])" // What we want to match on but not capture.
+ + "((?:" // Start a group with a non-capturing or part
+ + "java[.]lang"
+ + "|java[.]util"
+ + "|javax[.]inject"
+ + "|dagger"
+ + "|com[.]google[.]common[.]base"
+ + "|com[.]google[.]common[.]collect"
+ + ")[.])" // Always end with a literal .
+ + "[A-Z]"); // What we want to match on but not capture.
+
+ /**
+ * A method to strip out common packages and a few rare type prefixes from types' string
+ * representation before being used in error messages.
+ *
+ * <p>This type assumes a String value that is a valid fully qualified (and possibly
+ * parameterized) type, and should NOT be used with arbitrary text, especially prose error
+ * messages.
+ *
+ * <p>TODO(cgruber): Tighten these to take type representations (mirrors and elements) to avoid
+ * accidental mis-use by running errors through this method.
+ */
+ static String stripCommonTypePrefixes(String type) {
+ // Do regex magic to remove common packages we care to shorten.
+ Matcher matcher = COMMON_PACKAGE_PATTERN.matcher(type);
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+ while (matcher.find()) {
+ result.append(type.subSequence(index, matcher.start(1)));
+ index = matcher.end(1); // Skip the matched pattern content.
+ }
+ result.append(type.subSequence(index, type.length()));
+ return result.toString();
+ }
+
+ private DiagnosticFormatting() {}
+}
diff --git a/java/dagger/internal/codegen/DiagnosticReporterFactory.java b/java/dagger/internal/codegen/DiagnosticReporterFactory.java
new file mode 100644
index 0000000..f657cee
--- /dev/null
+++ b/java/dagger/internal/codegen/DiagnosticReporterFactory.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.indexOf;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Lists.asList;
+import static dagger.internal.codegen.DaggerGraphs.shortestPath;
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.DaggerStreams.presentValues;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
+import static dagger.internal.codegen.ValidationType.NONE;
+import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static dagger.internal.codegen.langmodel.DaggerElements.elementEncloses;
+import static java.util.Collections.min;
+import static java.util.Comparator.comparing;
+import static java.util.Comparator.comparingInt;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Table;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.MaybeBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.ComponentPath;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.function.Function;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/** A factory for {@link DiagnosticReporter}s. */
+// TODO(ronshapiro): If multiple plugins print errors on the same node/edge, should we condense the
+// messages and only print the dependency trace once?
+final class DiagnosticReporterFactory {
+ private final DaggerTypes types;
+ private final Messager messager;
+ private final DependencyRequestFormatter dependencyRequestFormatter;
+ private final ElementFormatter elementFormatter;
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ DiagnosticReporterFactory(
+ DaggerTypes types,
+ Messager messager,
+ DependencyRequestFormatter dependencyRequestFormatter,
+ ElementFormatter elementFormatter,
+ CompilerOptions compilerOptions) {
+ this.types = types;
+ this.messager = messager;
+ this.dependencyRequestFormatter = dependencyRequestFormatter;
+ this.elementFormatter = elementFormatter;
+ this.compilerOptions = compilerOptions;
+ }
+
+ /** Creates a reporter for a binding graph and a plugin. */
+ DiagnosticReporterImpl reporter(BindingGraph graph, BindingGraphPlugin plugin) {
+ return new DiagnosticReporterImpl(graph, plugin.pluginName());
+ }
+
+ private static <K, V> Function<K, V> memoize(Function<K, V> uncached) {
+ // If Android Guava is on the processor path, then c.g.c.b.Function (which LoadingCache
+ // implements) does not extend j.u.f.Function.
+
+ // First, explicitly convert uncached to c.g.c.b.Function because CacheLoader.from() expects
+ // one.
+ com.google.common.base.Function<K, V> uncachedAsBaseFunction = uncached::apply;
+
+ LoadingCache<K, V> cache =
+ CacheBuilder.newBuilder().build(CacheLoader.from(uncachedAsBaseFunction));
+
+ // Second, explicitly convert LoadingCache to j.u.f.Function.
+ @SuppressWarnings("deprecation") // uncachedAsBaseFunction throws only unchecked exceptions
+ Function<K, V> memoized = cache::apply;
+
+ return memoized;
+ }
+
+ /**
+ * A {@link DiagnosticReporter} that keeps track of which {@linkplain Diagnostic.Kind kinds} of
+ * diagnostics were reported.
+ */
+ final class DiagnosticReporterImpl implements DiagnosticReporter {
+
+ /** A cached function from type to all of its supertypes in breadth-first order. */
+ private final Function<TypeElement, Iterable<TypeElement>> supertypes =
+ memoize(
+ component ->
+ transform(types.supertypes(component.asType()), type -> asTypeElement(type)));
+
+ /** The shortest path (value) from an entry point (column) to a binding (row). */
+ private final Table<MaybeBinding, DependencyEdge, ImmutableList<Node>> shortestPaths =
+ HashBasedTable.create();
+
+ private final BindingGraph graph;
+ private final String plugin;
+ private final TypeElement rootComponent;
+ private final ImmutableSet.Builder<Diagnostic.Kind> reportedDiagnosticKinds =
+ ImmutableSet.builder();
+
+ DiagnosticReporterImpl(BindingGraph graph, String plugin) {
+ this.graph = graph;
+ this.plugin = plugin;
+ this.rootComponent = graph.rootComponentNode().componentPath().currentComponent();
+ }
+
+ /** Returns which {@linkplain Diagnostic.Kind kinds} of diagnostics were reported. */
+ ImmutableSet<Diagnostic.Kind> reportedDiagnosticKinds() {
+ return reportedDiagnosticKinds.build();
+ }
+
+ @Override
+ public void reportComponent(
+ Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String messageFormat) {
+ StringBuilder message = new StringBuilder(messageFormat);
+ appendComponentPathUnlessAtRoot(message, componentNode);
+ // TODO(dpb): Report at the component node component.
+ printMessage(diagnosticKind, message, rootComponent);
+ }
+
+ @Override
+ @FormatMethod
+ public void reportComponent(
+ Diagnostic.Kind diagnosticKind,
+ ComponentNode componentNode,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportComponent(
+ diagnosticKind, componentNode, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ // TODO(ronshapiro): should this also include the binding element?
+ @Override
+ public void reportBinding(
+ Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message) {
+ printMessage(diagnosticKind, message + new DiagnosticInfo(binding), rootComponent);
+ }
+
+ @Override
+ public void reportBinding(
+ Diagnostic.Kind diagnosticKind,
+ MaybeBinding binding,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportBinding(diagnosticKind, binding, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ @Override
+ public void reportDependency(
+ Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) {
+ printMessage(diagnosticKind, message + new DiagnosticInfo(dependencyEdge), rootComponent);
+ }
+
+ @Override
+ public void reportDependency(
+ Diagnostic.Kind diagnosticKind,
+ DependencyEdge dependencyEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportDependency(
+ diagnosticKind, dependencyEdge, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ @Override
+ public void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String message) {
+ printMessage(diagnosticKind, message, childFactoryMethodEdge.factoryMethod());
+ }
+
+ @Override
+ public void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportSubcomponentFactoryMethod(
+ diagnosticKind, childFactoryMethodEdge, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ private String formatMessage(String messageFormat, Object firstArg, Object[] moreArgs) {
+ return String.format(messageFormat, asList(firstArg, moreArgs).toArray());
+ }
+
+ private Node source(Edge edge) {
+ return graph.network().incidentNodes(edge).source();
+ }
+
+ void printMessage(
+ Diagnostic.Kind diagnosticKind,
+ CharSequence message,
+ @NullableDecl Element elementToReport) {
+ if (graph.isFullBindingGraph()) {
+ ValidationType validationType =
+ compilerOptions.fullBindingGraphValidationType(rootComponent);
+ if (validationType.equals(NONE)) {
+ return;
+ }
+ if (diagnosticKind.equals(ERROR)) {
+ diagnosticKind = validationType.diagnosticKind().get();
+ }
+ }
+ reportedDiagnosticKinds.add(diagnosticKind);
+ StringBuilder fullMessage = new StringBuilder();
+ appendBracketPrefix(fullMessage, plugin);
+
+ // TODO(ronshapiro): should we create a HashSet out of elementEncloses() so we don't
+ // need to do an O(n) contains() each time?
+ if (elementToReport != null && !elementEncloses(rootComponent, elementToReport)) {
+ appendBracketPrefix(fullMessage, elementToString(elementToReport));
+ elementToReport = rootComponent;
+ }
+
+ messager.printMessage(diagnosticKind, fullMessage.append(message), elementToReport);
+ }
+
+ private void appendComponentPathUnlessAtRoot(StringBuilder message, Node node) {
+ if (!node.componentPath().equals(graph.rootComponentNode().componentPath())) {
+ message.append(String.format(" [%s]", node.componentPath()));
+ }
+ }
+
+ private void appendBracketPrefix(StringBuilder message, String prefix) {
+ message.append(String.format("[%s] ", prefix));
+ }
+
+ /** The diagnostic information associated with an error. */
+ private final class DiagnosticInfo {
+ final ImmutableList<DependencyEdge> dependencyTrace;
+ final ImmutableSet<DependencyEdge> requests;
+ final ImmutableSet<DependencyEdge> entryPoints;
+
+ DiagnosticInfo(MaybeBinding binding) {
+ entryPoints = graph.entryPointEdgesDependingOnBinding(binding);
+ requests = requests(binding);
+ dependencyTrace = dependencyTrace(binding, entryPoints);
+ }
+
+ DiagnosticInfo(DependencyEdge dependencyEdge) {
+ requests = ImmutableSet.of(dependencyEdge);
+ ImmutableList.Builder<DependencyEdge> dependencyTraceBuilder = ImmutableList.builder();
+ dependencyTraceBuilder.add(dependencyEdge);
+
+ if (dependencyEdge.isEntryPoint()) {
+ entryPoints = ImmutableSet.of(dependencyEdge);
+ } else {
+ // It's not an entry point, so it's part of a binding
+ dagger.model.Binding binding = (dagger.model.Binding) source(dependencyEdge);
+ entryPoints = graph.entryPointEdgesDependingOnBinding(binding);
+ dependencyTraceBuilder.addAll(dependencyTrace(binding, entryPoints));
+ }
+ dependencyTrace = dependencyTraceBuilder.build();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder message =
+ graph.isFullBindingGraph()
+ ? new StringBuilder()
+ : new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
+
+ // Print the dependency trace unless it's a full binding graph
+ if (!graph.isFullBindingGraph()) {
+ dependencyTrace.forEach(
+ edge ->
+ dependencyRequestFormatter.appendFormatLine(message, edge.dependencyRequest()));
+ if (!dependencyTrace.isEmpty()) {
+ appendComponentPathUnlessAtRoot(message, source(getLast(dependencyTrace)));
+ }
+ }
+
+ // Print any dependency requests that aren't shown as part of the dependency trace.
+ ImmutableSet<Element> requestsToPrint =
+ requests.stream()
+ // if printing entry points, skip entry points and the traced request
+ .filter(
+ request ->
+ graph.isFullBindingGraph()
+ || (!request.isEntryPoint() && !isTracedRequest(request)))
+ .map(request -> request.dependencyRequest().requestElement())
+ .flatMap(presentValues())
+ .collect(toImmutableSet());
+ if (!requestsToPrint.isEmpty()) {
+ message
+ .append("\nIt is")
+ .append(graph.isFullBindingGraph() ? " " : " also ")
+ .append("requested at:");
+ elementFormatter.formatIndentedList(message, requestsToPrint, 1);
+ }
+
+ // Print the remaining entry points, showing which component they're in, unless it's a full
+ // binding graph
+ if (!graph.isFullBindingGraph() && entryPoints.size() > 1) {
+ message.append("\nThe following other entry points also depend on it:");
+ entryPointFormatter.formatIndentedList(
+ message,
+ entryPoints.stream()
+ .filter(entryPoint -> !entryPoint.equals(getLast(dependencyTrace)))
+ .sorted(
+ // 1. List entry points in components closest to the root first.
+ // 2. List entry points declared in a component before those in a supertype.
+ // 3. List entry points in declaration order in their declaring type.
+ rootComponentFirst()
+ .thenComparing(nearestComponentSupertypeFirst())
+ .thenComparing(requestElementDeclarationOrder()))
+ .collect(toImmutableList()),
+ 1);
+ }
+ return message.toString();
+ }
+
+ private final Formatter<DependencyEdge> entryPointFormatter =
+ new Formatter<DependencyEdge>() {
+ @Override
+ public String format(DependencyEdge object) {
+ Element requestElement = object.dependencyRequest().requestElement().get();
+ StringBuilder element = new StringBuilder(elementToString(requestElement));
+
+ // For entry points declared in subcomponents or supertypes of the root component,
+ // append the component path to make clear to the user which component it's in.
+ ComponentPath componentPath = source(object).componentPath();
+ if (!componentPath.atRoot()
+ || !requestElement.getEnclosingElement().equals(rootComponent)) {
+ element.append(String.format(" [%s]", componentPath));
+ }
+ return element.toString();
+ }
+ };
+
+ private boolean isTracedRequest(DependencyEdge request) {
+ return !dependencyTrace.isEmpty() && request.equals(dependencyTrace.get(0));
+ }
+
+ /**
+ * Returns the dependency trace from one of the {@code entryPoints} to {@code binding} to
+ * {@code message} as a list <i>ending with</i> the entry point.
+ */
+ // TODO(ronshapiro): Adding a DependencyPath type to dagger.model could be useful, i.e.
+ // bindingGraph.shortestPathFromEntryPoint(DependencyEdge, MaybeBindingNode)
+ ImmutableList<DependencyEdge> dependencyTrace(
+ MaybeBinding binding, ImmutableSet<DependencyEdge> entryPoints) {
+ // Module binding graphs may have bindings unreachable from any entry points. If there are
+ // no entry points for this DiagnosticInfo, don't try to print a dependency trace.
+ if (entryPoints.isEmpty()) {
+ return ImmutableList.of();
+ }
+ // Show the full dependency trace for one entry point.
+ DependencyEdge entryPointForTrace =
+ min(
+ entryPoints,
+ // prefer entry points in components closest to the root
+ rootComponentFirst()
+ // then prefer entry points with a short dependency path to the error
+ .thenComparing(shortestDependencyPathFirst(binding))
+ // then prefer entry points declared in the component to those declared in a
+ // supertype
+ .thenComparing(nearestComponentSupertypeFirst())
+ // finally prefer entry points declared first in their enclosing type
+ .thenComparing(requestElementDeclarationOrder()));
+
+ ImmutableList<Node> shortestBindingPath =
+ shortestPathFromEntryPoint(entryPointForTrace, binding);
+ verify(
+ !shortestBindingPath.isEmpty(),
+ "no dependency path from %s to %s in %s",
+ entryPointForTrace,
+ binding,
+ graph);
+
+ ImmutableList.Builder<DependencyEdge> dependencyTrace = ImmutableList.builder();
+ dependencyTrace.add(entryPointForTrace);
+ for (int i = 0; i < shortestBindingPath.size() - 1; i++) {
+ Set<Edge> dependenciesBetween =
+ graph
+ .network()
+ .edgesConnecting(shortestBindingPath.get(i), shortestBindingPath.get(i + 1));
+ // If a binding requests a key more than once, any of them should be fine to get to the
+ // shortest path
+ dependencyTrace.add((DependencyEdge) Iterables.get(dependenciesBetween, 0));
+ }
+ return dependencyTrace.build().reverse();
+ }
+
+ /** Returns all the nonsynthetic dependency requests for a binding. */
+ ImmutableSet<DependencyEdge> requests(MaybeBinding binding) {
+ return graph.network().inEdges(binding).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .filter(edge -> edge.dependencyRequest().requestElement().isPresent())
+ .sorted(requestEnclosingTypeName().thenComparing(requestElementDeclarationOrder()))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns a comparator that sorts entry points in components whose paths from the root are
+ * shorter first.
+ */
+ Comparator<DependencyEdge> rootComponentFirst() {
+ return comparingInt(entryPoint -> source(entryPoint).componentPath().components().size());
+ }
+
+ /**
+ * Returns a comparator that puts entry points whose shortest dependency path to {@code
+ * binding} is shortest first.
+ */
+ Comparator<DependencyEdge> shortestDependencyPathFirst(MaybeBinding binding) {
+ return comparing(entryPoint -> shortestPathFromEntryPoint(entryPoint, binding).size());
+ }
+
+ ImmutableList<Node> shortestPathFromEntryPoint(
+ DependencyEdge entryPoint, MaybeBinding binding) {
+ return shortestPaths
+ .row(binding)
+ .computeIfAbsent(
+ entryPoint,
+ ep ->
+ shortestPath(
+ node ->
+ filter(
+ graph.network().successors(node), MaybeBinding.class::isInstance),
+ graph.network().incidentNodes(ep).target(),
+ binding));
+ }
+
+ /**
+ * Returns a comparator that sorts entry points in by the distance of the type that declares
+ * them from the type of the component that contains them.
+ *
+ * <p>For instance, an entry point declared directly in the component type would sort before
+ * one declared in a direct supertype, which would sort before one declared in a supertype of
+ * a supertype.
+ */
+ Comparator<DependencyEdge> nearestComponentSupertypeFirst() {
+ return comparingInt(
+ entryPoint ->
+ indexOf(
+ supertypes.apply(componentContainingEntryPoint(entryPoint)),
+ equalTo(typeDeclaringEntryPoint(entryPoint))));
+ }
+
+ TypeElement componentContainingEntryPoint(DependencyEdge entryPoint) {
+ return source(entryPoint).componentPath().currentComponent();
+ }
+
+ TypeElement typeDeclaringEntryPoint(DependencyEdge entryPoint) {
+ return MoreElements.asType(
+ entryPoint.dependencyRequest().requestElement().get().getEnclosingElement());
+ }
+
+ /**
+ * Returns a comparator that sorts dependency edges lexicographically by the qualified name of
+ * the type that contains them. Only appropriate for edges with request elements.
+ */
+ Comparator<DependencyEdge> requestEnclosingTypeName() {
+ return comparing(
+ edge ->
+ closestEnclosingTypeElement(edge.dependencyRequest().requestElement().get())
+ .getQualifiedName()
+ .toString());
+ }
+
+ /**
+ * Returns a comparator that sorts edges in the order in which their request elements were
+ * declared in their declaring type.
+ *
+ * <p>Only useful to compare edges whose request elements were declared in the same type.
+ */
+ Comparator<DependencyEdge> requestElementDeclarationOrder() {
+ return comparing(
+ edge -> edge.dependencyRequest().requestElement().get(), DECLARATION_ORDER);
+ }
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DuplicateBindingsValidator.java b/java/dagger/internal/codegen/DuplicateBindingsValidator.java
new file mode 100644
index 0000000..3eed45f
--- /dev/null
+++ b/java/dagger/internal/codegen/DuplicateBindingsValidator.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
+import static dagger.internal.codegen.Formatter.INDENT;
+import static dagger.internal.codegen.Optionals.emptiesLast;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.MEMBERS_INJECTION;
+import static java.util.Comparator.comparing;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import dagger.model.Binding;
+import dagger.model.BindingGraph;
+import dagger.model.BindingKind;
+import dagger.model.ComponentPath;
+import dagger.model.Key;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+
+/** Reports errors for conflicting bindings with the same key. */
+final class DuplicateBindingsValidator implements BindingGraphPlugin {
+
+ // 1. contributing module or enclosing type
+ // 2. binding element's simple name
+ // 3. binding element's type
+ private static final Comparator<BindingDeclaration> BINDING_DECLARATION_COMPARATOR =
+ comparing(
+ (BindingDeclaration declaration) ->
+ declaration.contributingModule().isPresent()
+ ? declaration.contributingModule()
+ : declaration.bindingTypeElement(),
+ emptiesLast(comparing((TypeElement type) -> type.getQualifiedName().toString())))
+ .thenComparing(
+ (BindingDeclaration declaration) -> declaration.bindingElement(),
+ emptiesLast(
+ comparing((Element element) -> element.getSimpleName().toString())
+ .thenComparing((Element element) -> element.asType().toString())));
+
+ private static final Comparator<Binding> BY_LENGTH_OF_COMPONENT_PATH =
+ comparing(binding -> binding.componentPath().components().size());
+
+ private final BindingDeclarationFormatter bindingDeclarationFormatter;
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ DuplicateBindingsValidator(
+ BindingDeclarationFormatter bindingDeclarationFormatter, CompilerOptions compilerOptions) {
+ this.bindingDeclarationFormatter = bindingDeclarationFormatter;
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/DuplicateBindings";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ // If two unrelated subcomponents have the same duplicate bindings only because they install the
+ // same two modules, then fixing the error in one subcomponent will uncover the second
+ // subcomponent to fix.
+ // TODO(ronshapiro): Explore ways to address such underreporting without overreporting.
+ Set<ImmutableSet<BindingElement>> reportedDuplicateBindingSets = new HashSet<>();
+ duplicateBindingSets(bindingGraph)
+ .forEach(
+ duplicateBindings -> {
+ // Only report each set of duplicate bindings once, ignoring the installed component.
+ if (reportedDuplicateBindingSets.add(duplicateBindings.keySet())) {
+ reportDuplicateBindings(duplicateBindings, bindingGraph, diagnosticReporter);
+ }
+ });
+ }
+
+ /**
+ * Returns sets of duplicate bindings. Bindings are duplicates if they bind the same key and are
+ * visible from the same component. Two bindings that differ only in the component that owns them
+ * are not considered to be duplicates, because that means the same binding was "copied" down to a
+ * descendant component because it depends on local multibindings or optional bindings. Hence each
+ * "set" is represented as a multimap from binding element (ignoring component path) to binding.
+ */
+ private ImmutableSet<ImmutableSetMultimap<BindingElement, Binding>> duplicateBindingSets(
+ BindingGraph bindingGraph) {
+ return groupBindingsByKey(bindingGraph).stream()
+ .flatMap(bindings -> mutuallyVisibleSubsets(bindings).stream())
+ .map(BindingElement::index)
+ .filter(duplicates -> duplicates.keySet().size() > 1)
+ .collect(toImmutableSet());
+ }
+
+ private static ImmutableSet<ImmutableSet<Binding>> groupBindingsByKey(BindingGraph bindingGraph) {
+ return valueSetsForEachKey(
+ bindingGraph.bindings().stream()
+ .filter(binding -> !binding.kind().equals(MEMBERS_INJECTION))
+ .collect(toImmutableSetMultimap(Binding::key, binding -> binding)));
+ }
+
+ /**
+ * Returns the subsets of the input set that contain bindings that are all visible from the same
+ * component. A binding is visible from its component and all its descendants.
+ */
+ private static ImmutableSet<ImmutableSet<Binding>> mutuallyVisibleSubsets(
+ Set<Binding> duplicateBindings) {
+ ImmutableListMultimap<ComponentPath, Binding> bindingsByComponentPath =
+ Multimaps.index(duplicateBindings, Binding::componentPath);
+ ImmutableSetMultimap.Builder<ComponentPath, Binding> mutuallyVisibleBindings =
+ ImmutableSetMultimap.builder();
+ bindingsByComponentPath
+ .asMap()
+ .forEach(
+ (componentPath, bindings) -> {
+ mutuallyVisibleBindings.putAll(componentPath, bindings);
+ for (ComponentPath ancestor = componentPath; !ancestor.atRoot(); ) {
+ ancestor = ancestor.parent();
+ ImmutableList<Binding> bindingsInAncestor = bindingsByComponentPath.get(ancestor);
+ mutuallyVisibleBindings.putAll(componentPath, bindingsInAncestor);
+ }
+ });
+ return valueSetsForEachKey(mutuallyVisibleBindings.build());
+ }
+
+ private void reportDuplicateBindings(
+ ImmutableSetMultimap<BindingElement, Binding> duplicateBindings,
+ BindingGraph bindingGraph,
+ DiagnosticReporter diagnosticReporter) {
+ if (explicitBindingConfictsWithInject(duplicateBindings.keySet())) {
+ compilerOptions
+ .explicitBindingConflictsWithInjectValidationType()
+ .diagnosticKind()
+ .ifPresent(
+ diagnosticKind ->
+ reportExplicitBindingConflictsWithInject(
+ duplicateBindings, diagnosticReporter, diagnosticKind));
+ return;
+ }
+ ImmutableSet<Binding> bindings = ImmutableSet.copyOf(duplicateBindings.values());
+ Binding oneBinding = bindings.asList().get(0);
+ diagnosticReporter.reportBinding(
+ ERROR,
+ oneBinding,
+ Iterables.any(bindings, binding -> binding.kind().isMultibinding())
+ ? incompatibleBindingsMessage(oneBinding.key(), bindings, bindingGraph)
+ : duplicateBindingMessage(oneBinding.key(), bindings, bindingGraph));
+ }
+
+ /**
+ * Returns {@code true} if the bindings contain one {@code @Inject} binding and one that isn't.
+ */
+ private static boolean explicitBindingConfictsWithInject(
+ ImmutableSet<BindingElement> duplicateBindings) {
+ ImmutableMultiset<BindingKind> bindingKinds =
+ Multimaps.index(duplicateBindings, BindingElement::bindingKind).keys();
+ return bindingKinds.count(INJECTION) == 1 && bindingKinds.size() == 2;
+ }
+
+ private void reportExplicitBindingConflictsWithInject(
+ ImmutableSetMultimap<BindingElement, Binding> duplicateBindings,
+ DiagnosticReporter diagnosticReporter,
+ Kind diagnosticKind) {
+ Binding injectBinding =
+ rootmostBindingWithKind(k -> k.equals(INJECTION), duplicateBindings.values());
+ Binding explicitBinding =
+ rootmostBindingWithKind(k -> !k.equals(INJECTION), duplicateBindings.values());
+ StringBuilder message =
+ new StringBuilder()
+ .append(explicitBinding.key())
+ .append(" is bound multiple times:")
+ .append(formatWithComponentPath(injectBinding))
+ .append(formatWithComponentPath(explicitBinding))
+ .append(
+ "\nThis condition was never validated before, and will soon be an error. "
+ + "See https://dagger.dev/conflicting-inject.");
+
+ diagnosticReporter.reportBinding(diagnosticKind, explicitBinding, message.toString());
+ }
+
+ private String formatWithComponentPath(Binding binding) {
+ return String.format(
+ "\n%s%s [%s]",
+ Formatter.INDENT,
+ bindingDeclarationFormatter.format(((BindingNode) binding).delegate()),
+ binding.componentPath());
+ }
+
+ private String duplicateBindingMessage(
+ Key key, ImmutableSet<Binding> duplicateBindings, BindingGraph graph) {
+ StringBuilder message = new StringBuilder().append(key).append(" is bound multiple times:");
+ formatDeclarations(message, 1, declarations(graph, duplicateBindings));
+ return message.toString();
+ }
+
+ private String incompatibleBindingsMessage(
+ Key key, ImmutableSet<Binding> duplicateBindings, BindingGraph graph) {
+ ImmutableSet<dagger.model.Binding> multibindings =
+ duplicateBindings.stream()
+ .filter(binding -> binding.kind().isMultibinding())
+ .collect(toImmutableSet());
+ verify(
+ multibindings.size() == 1, "expected only one multibinding for %s: %s", key, multibindings);
+ StringBuilder message = new StringBuilder();
+ java.util.Formatter messageFormatter = new java.util.Formatter(message);
+ messageFormatter.format("%s has incompatible bindings or declarations:\n", key);
+ message.append(INDENT);
+ dagger.model.Binding multibinding = getOnlyElement(multibindings);
+ messageFormatter.format("%s bindings and declarations:", multibindingTypeString(multibinding));
+ formatDeclarations(message, 2, declarations(graph, multibindings));
+
+ Set<dagger.model.Binding> uniqueBindings =
+ Sets.filter(duplicateBindings, binding -> !binding.equals(multibinding));
+ message.append('\n').append(INDENT).append("Unique bindings and declarations:");
+ formatDeclarations(
+ message,
+ 2,
+ Sets.filter(
+ declarations(graph, uniqueBindings),
+ declaration -> !(declaration instanceof MultibindingDeclaration)));
+ return message.toString();
+ }
+
+ private void formatDeclarations(
+ StringBuilder builder,
+ int indentLevel,
+ Iterable<? extends BindingDeclaration> bindingDeclarations) {
+ bindingDeclarationFormatter.formatIndentedList(
+ builder, ImmutableList.copyOf(bindingDeclarations), indentLevel);
+ }
+
+ private ImmutableSet<BindingDeclaration> declarations(
+ BindingGraph graph, Set<dagger.model.Binding> bindings) {
+ return bindings.stream()
+ .flatMap(binding -> declarations(graph, binding).stream())
+ .distinct()
+ .sorted(BINDING_DECLARATION_COMPARATOR)
+ .collect(toImmutableSet());
+ }
+
+ private ImmutableSet<BindingDeclaration> declarations(
+ BindingGraph graph, dagger.model.Binding binding) {
+ ImmutableSet.Builder<BindingDeclaration> declarations = ImmutableSet.builder();
+ BindingNode bindingNode = (BindingNode) binding;
+ bindingNode.associatedDeclarations().forEach(declarations::add);
+ if (bindingDeclarationFormatter.canFormat(bindingNode.delegate())) {
+ declarations.add(bindingNode.delegate());
+ } else {
+ graph.requestedBindings(binding).stream()
+ .flatMap(requestedBinding -> declarations(graph, requestedBinding).stream())
+ .forEach(declarations::add);
+ }
+ return declarations.build();
+ }
+
+ private String multibindingTypeString(dagger.model.Binding multibinding) {
+ switch (multibinding.kind()) {
+ case MULTIBOUND_MAP:
+ return "Map";
+ case MULTIBOUND_SET:
+ return "Set";
+ default:
+ throw new AssertionError(multibinding);
+ }
+ }
+
+ private static <E> ImmutableSet<ImmutableSet<E>> valueSetsForEachKey(Multimap<?, E> multimap) {
+ return multimap.asMap().values().stream().map(ImmutableSet::copyOf).collect(toImmutableSet());
+ }
+
+ /** Returns the binding of the given kind that is closest to the root component. */
+ private static Binding rootmostBindingWithKind(
+ Predicate<BindingKind> bindingKindPredicate, ImmutableCollection<Binding> bindings) {
+ return bindings.stream()
+ .filter(b -> bindingKindPredicate.test(b.kind()))
+ .min(BY_LENGTH_OF_COMPONENT_PATH)
+ .get();
+ }
+
+ /** The identifying information about a binding, excluding its {@link Binding#componentPath()}. */
+ @AutoValue
+ abstract static class BindingElement {
+
+ abstract BindingKind bindingKind();
+
+ abstract Optional<Element> bindingElement();
+
+ abstract Optional<TypeElement> contributingModule();
+
+ static ImmutableSetMultimap<BindingElement, Binding> index(Set<Binding> bindings) {
+ return bindings.stream().collect(toImmutableSetMultimap(BindingElement::forBinding, b -> b));
+ }
+
+ private static BindingElement forBinding(Binding binding) {
+ return new AutoValue_DuplicateBindingsValidator_BindingElement(
+ binding.kind(), binding.bindingElement(), binding.contributingModule());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ElementFormatter.java b/java/dagger/internal/codegen/ElementFormatter.java
new file mode 100644
index 0000000..40d7606
--- /dev/null
+++ b/java/dagger/internal/codegen/ElementFormatter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static java.util.stream.Collectors.joining;
+
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * Formats elements into a useful string representation.
+ *
+ * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
+ *
+ * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ */
+final class ElementFormatter extends Formatter<Element> {
+ @Inject
+ ElementFormatter() {}
+
+ @Override
+ public String format(Element element) {
+ return elementToString(element);
+ }
+
+ /**
+ * Returns a useful string form for an element.
+ *
+ * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
+ *
+ * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ */
+ static String elementToString(Element element) {
+ return element.accept(ELEMENT_TO_STRING, null);
+ }
+
+ private static final ElementVisitor<String, Void> ELEMENT_TO_STRING =
+ new ElementKindVisitor8<String, Void>() {
+ @Override
+ public String visitExecutable(ExecutableElement executableElement, Void aVoid) {
+ return enclosingTypeAndMemberName(executableElement)
+ .append(
+ executableElement.getParameters().stream()
+ .map(parameter -> parameter.asType().toString())
+ .collect(joining(", ", "(", ")")))
+ .toString();
+ }
+
+ @Override
+ public String visitVariableAsParameter(VariableElement parameter, Void aVoid) {
+ ExecutableElement methodOrConstructor = asExecutable(parameter.getEnclosingElement());
+ return enclosingTypeAndMemberName(methodOrConstructor)
+ .append('(')
+ .append(
+ formatArgumentInList(
+ methodOrConstructor.getParameters().indexOf(parameter),
+ methodOrConstructor.getParameters().size(),
+ parameter.getSimpleName()))
+ .append(')')
+ .toString();
+ }
+
+ @Override
+ public String visitVariableAsField(VariableElement field, Void aVoid) {
+ return enclosingTypeAndMemberName(field).toString();
+ }
+
+ @Override
+ public String visitType(TypeElement type, Void aVoid) {
+ return type.getQualifiedName().toString();
+ }
+
+ @Override
+ protected String defaultAction(Element element, Void aVoid) {
+ throw new UnsupportedOperationException(
+ "Can't determine string for " + element.getKind() + " element " + element);
+ }
+
+ private StringBuilder enclosingTypeAndMemberName(Element element) {
+ StringBuilder name = new StringBuilder(element.getEnclosingElement().accept(this, null));
+ if (!element.getSimpleName().contentEquals("<init>")) {
+ name.append('.').append(element.getSimpleName());
+ }
+ return name;
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/ErrorMessages.java b/java/dagger/internal/codegen/ErrorMessages.java
new file mode 100644
index 0000000..4195e1a
--- /dev/null
+++ b/java/dagger/internal/codegen/ErrorMessages.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** The collection of error messages to be reported back to users. */
+final class ErrorMessages {
+
+ private static final UnaryOperator<String> PRODUCTION =
+ s ->
+ s.replace("component", "production component")
+ .replace("Component", "ProductionComponent");
+
+ private static final UnaryOperator<String> SUBCOMPONENT =
+ s -> s.replace("component", "subcomponent").replace("Component", "Subcomponent");
+
+ private static final UnaryOperator<String> FACTORY = s -> s.replace("Builder", "Factory");
+
+ private static final ImmutableMap<ComponentKind, Function<String, String>>
+ COMPONENT_TRANSFORMATIONS =
+ ImmutableMap.of(
+ ComponentKind.COMPONENT, UnaryOperator.identity(),
+ ComponentKind.SUBCOMPONENT, SUBCOMPONENT,
+ ComponentKind.PRODUCTION_COMPONENT, PRODUCTION,
+ ComponentKind.PRODUCTION_SUBCOMPONENT, PRODUCTION.andThen(SUBCOMPONENT));
+
+ static ComponentMessages componentMessagesFor(ComponentKind componentKind) {
+ return new ComponentMessages(COMPONENT_TRANSFORMATIONS.get(componentKind));
+ }
+
+ static ComponentMessages componentMessagesFor(ComponentAnnotation componentAnnotation) {
+ return new ComponentMessages(
+ transformation(componentAnnotation.isProduction(), componentAnnotation.isSubcomponent()));
+ }
+
+ static ComponentCreatorMessages creatorMessagesFor(ComponentCreatorAnnotation creatorAnnotation) {
+ Function<String, String> transformation =
+ transformation(
+ creatorAnnotation.isProductionCreatorAnnotation(),
+ creatorAnnotation.isSubcomponentCreatorAnnotation());
+ switch (creatorAnnotation.creatorKind()) {
+ case BUILDER:
+ return new BuilderMessages(transformation);
+ case FACTORY:
+ return new FactoryMessages(transformation);
+ }
+ throw new AssertionError(creatorAnnotation);
+ }
+
+ private static Function<String, String> transformation(
+ boolean isProduction, boolean isSubcomponent) {
+ Function<String, String> transformation = isProduction ? PRODUCTION : UnaryOperator.identity();
+ return isSubcomponent ? transformation.andThen(SUBCOMPONENT) : transformation;
+ }
+
+ private abstract static class Messages {
+ private final Function<String, String> transformation;
+
+ Messages(Function<String, String> transformation) {
+ this.transformation = transformation;
+ }
+
+ protected final String process(String s) {
+ return transformation.apply(s);
+ }
+ }
+
+ /** Errors for components. */
+ static final class ComponentMessages extends Messages {
+ ComponentMessages(Function<String, String> transformation) {
+ super(transformation);
+ }
+
+ final String moreThanOne() {
+ return process("@Component has more than one @Component.Builder or @Component.Factory: %s");
+ }
+ }
+
+ /** Errors for component creators. */
+ abstract static class ComponentCreatorMessages extends Messages {
+ ComponentCreatorMessages(Function<String, String> transformation) {
+ super(transformation);
+ }
+
+ static String builderMethodRequiresNoArgs() {
+ return "Methods returning a @Component.Builder must have no arguments";
+ }
+
+ static String moreThanOneRefToSubcomponent() {
+ return "Only one method can create a given subcomponent. %s is created by: %s";
+ }
+
+ final String invalidConstructor() {
+ return process("@Component.Builder classes must have exactly one constructor,"
+ + " and it must not be private or have any parameters");
+ }
+
+ final String generics() {
+ return process("@Component.Builder types must not have any generic types");
+ }
+
+ final String mustBeInComponent() {
+ return process("@Component.Builder types must be nested within a @Component");
+ }
+
+ final String mustBeClassOrInterface() {
+ return process("@Component.Builder types must be abstract classes or interfaces");
+ }
+
+ final String isPrivate() {
+ return process("@Component.Builder types must not be private");
+ }
+
+ final String mustBeStatic() {
+ return process("@Component.Builder types must be static");
+ }
+
+ final String mustBeAbstract() {
+ return process("@Component.Builder types must be abstract");
+ }
+
+ abstract String missingFactoryMethod();
+
+ abstract String multipleSettersForModuleOrDependencyType();
+
+ abstract String extraSetters();
+
+ abstract String missingSetters();
+
+ abstract String twoFactoryMethods();
+
+ abstract String inheritedTwoFactoryMethods();
+
+ abstract String factoryMethodMustReturnComponentType();
+
+ final String inheritedFactoryMethodMustReturnComponentType() {
+ return factoryMethodMustReturnComponentType() + ". Inherited method: %s";
+ }
+
+ abstract String factoryMethodMayNotBeAnnotatedWithBindsInstance();
+
+ final String inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance() {
+ return factoryMethodMayNotBeAnnotatedWithBindsInstance() + ". Inherited method: %s";
+ }
+
+ final String setterMethodsMustTakeOneArg() {
+ return process("@Component.Builder methods must not have more than one argument");
+ }
+
+ final String inheritedSetterMethodsMustTakeOneArg() {
+ return setterMethodsMustTakeOneArg() + ". Inherited method: %s";
+ }
+
+ final String setterMethodsMustReturnVoidOrBuilder() {
+ return process("@Component.Builder setter methods must return void, the builder,"
+ + " or a supertype of the builder");
+ }
+
+ final String inheritedSetterMethodsMustReturnVoidOrBuilder() {
+ return setterMethodsMustReturnVoidOrBuilder() + ". Inherited method: %s";
+ }
+
+ final String methodsMayNotHaveTypeParameters() {
+ return process("@Component.Builder methods must not have type parameters");
+ }
+
+ final String inheritedMethodsMayNotHaveTypeParameters() {
+ return methodsMayNotHaveTypeParameters() + ". Inherited method: %s";
+ }
+
+ abstract String nonBindsInstanceParametersMayNotBePrimitives();
+
+ final String inheritedNonBindsInstanceParametersMayNotBePrimitives() {
+ return nonBindsInstanceParametersMayNotBePrimitives() + ". Inherited method: %s";
+ }
+
+ final String factoryMethodReturnsSupertypeWithMissingMethods(
+ TypeElement component,
+ TypeElement componentBuilder,
+ TypeMirror returnType,
+ ExecutableElement buildMethod,
+ Set<ExecutableElement> additionalMethods) {
+ return String.format(
+ "%1$s.%2$s() returns %3$s, but %4$s declares additional component method(s): %5$s. In "
+ + "order to provide type-safe access to these methods, override %2$s() to return "
+ + "%4$s",
+ componentBuilder.getQualifiedName(),
+ buildMethod.getSimpleName(),
+ returnType,
+ component.getQualifiedName(),
+ Joiner.on(", ").join(additionalMethods));
+ }
+
+ final String bindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
+ return process("@Component.Builder setter methods may not have @BindsInstance on both the "
+ + "method and its parameter; choose one or the other");
+ }
+
+ final String inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
+ return bindsInstanceNotAllowedOnBothSetterMethodAndParameter() + ". Inherited method: %s";
+ }
+ }
+
+ private static final class BuilderMessages extends ComponentCreatorMessages {
+ BuilderMessages(Function<String, String> transformation) {
+ super(transformation);
+ }
+
+ @Override
+ String missingFactoryMethod() {
+ return process(
+ "@Component.Builder types must have exactly one no-args method that "
+ + " returns the @Component type");
+ }
+
+ @Override
+ String multipleSettersForModuleOrDependencyType() {
+ return process(
+ "@Component.Builder types must not have more than one setter method per module or "
+ + "dependency, but %s is set by %s");
+ }
+
+ @Override
+ String extraSetters() {
+ return process(
+ "@Component.Builder has setters for modules or components that aren't required: %s");
+ }
+
+ @Override
+ String missingSetters() {
+ return process(
+ "@Component.Builder is missing setters for required modules or components: %s");
+ }
+
+ @Override
+ String twoFactoryMethods() {
+ return process(
+ "@Component.Builder types must have exactly one zero-arg method, and that"
+ + " method must return the @Component type. Already found: %s");
+ }
+
+ @Override
+ String inheritedTwoFactoryMethods() {
+ return process(
+ "@Component.Builder types must have exactly one zero-arg method, and that"
+ + " method must return the @Component type. Found %s and %s");
+ }
+
+ @Override
+ String factoryMethodMustReturnComponentType() {
+ return process(
+ "@Component.Builder methods that have no arguments must return the @Component type or a "
+ + "supertype of the @Component");
+ }
+
+ @Override
+ String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
+ return process(
+ "@Component.Builder no-arg build methods may not be annotated with @BindsInstance");
+ }
+
+ @Override
+ String nonBindsInstanceParametersMayNotBePrimitives() {
+ return process(
+ "@Component.Builder methods that are not annotated with @BindsInstance "
+ + "must take either a module or a component dependency, not a primitive");
+ }
+ }
+
+ private static final class FactoryMessages extends ComponentCreatorMessages {
+ FactoryMessages(Function<String, String> transformation) {
+ super(transformation.andThen(FACTORY));
+ }
+
+ @Override
+ String missingFactoryMethod() {
+ return process(
+ "@Component.Factory types must have exactly one method that "
+ + "returns the @Component type");
+ }
+
+ @Override
+ String multipleSettersForModuleOrDependencyType() {
+ return process(
+ "@Component.Factory methods must not have more than one parameter per module or "
+ + "dependency, but %s is set by %s");
+ }
+
+ @Override
+ String extraSetters() {
+ return process(
+ "@Component.Factory method has parameters for modules or components that aren't "
+ + "required: %s");
+ }
+
+ @Override
+ String missingSetters() {
+ return process(
+ "@Component.Factory method is missing parameters for required modules or components: %s");
+ }
+
+ @Override
+ String twoFactoryMethods() {
+ return process(
+ "@Component.Factory types must have exactly one abstract method. Already found: %s");
+ }
+
+ @Override
+ String inheritedTwoFactoryMethods() {
+ return twoFactoryMethods();
+ }
+
+ @Override
+ String factoryMethodMustReturnComponentType() {
+ return process(
+ "@Component.Factory abstract methods must return the @Component type or a "
+ + "supertype of the @Component");
+ }
+
+ @Override
+ String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
+ return process("@Component.Factory method may not be annotated with @BindsInstance");
+ }
+
+ @Override
+ String nonBindsInstanceParametersMayNotBePrimitives() {
+ return process(
+ "@Component.Factory method parameters that are not annotated with @BindsInstance "
+ + "must be either a module or a component dependency, not a primitive");
+ }
+ }
+
+ private ErrorMessages() {}
+}
diff --git a/java/dagger/internal/codegen/FactoryGenerator.java b/java/dagger/internal/codegen/FactoryGenerator.java
new file mode 100644
index 0000000..d367bc5
--- /dev/null
+++ b/java/dagger/internal/codegen/FactoryGenerator.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Maps.transformValues;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.DELEGATE;
+import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
+import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
+import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
+import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
+import static dagger.model.BindingKind.PROVISION;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
+import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for
+ * {@link Inject} constructors.
+ */
+final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ FactoryGenerator(
+ Filer filer,
+ SourceVersion sourceVersion,
+ DaggerTypes types,
+ DaggerElements elements,
+ CompilerOptions compilerOptions) {
+ super(filer, elements, sourceVersion);
+ this.types = types;
+ this.elements = elements;
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ ClassName nameGeneratedType(ProvisionBinding binding) {
+ return generatedClassNameForBinding(binding);
+ }
+
+ @Override
+ Element originatingElement(ProvisionBinding binding) {
+ // we only create factories for bindings that have a binding element
+ return binding.bindingElement().get();
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedTypeName, ProvisionBinding binding) {
+ // We don't want to write out resolved bindings -- we want to write out the generic version.
+ checkArgument(!binding.unresolved().isPresent());
+ checkArgument(binding.bindingElement().isPresent());
+
+ return binding.factoryCreationStrategy().equals(DELEGATE)
+ ? Optional.empty()
+ : Optional.of(factoryBuilder(binding));
+ }
+
+ private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
+ TypeSpec.Builder factoryBuilder =
+ classBuilder(nameGeneratedType(binding))
+ .addModifiers(PUBLIC, FINAL)
+ .addSuperinterface(factoryTypeName(binding))
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
+
+ addConstructorAndFields(binding, factoryBuilder);
+ factoryBuilder.addMethod(getMethod(binding));
+ addCreateMethod(binding, factoryBuilder);
+
+ factoryBuilder.addMethod(
+ ProvisionMethod.create(binding, compilerOptions, elements).toMethodSpec());
+ gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
+
+ return factoryBuilder;
+ }
+
+ private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
+ if (binding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)) {
+ return;
+ }
+ // TODO(user): Make the constructor private?
+ MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
+ constructorParams(binding).forEach(
+ param -> {
+ constructor.addParameter(param).addStatement("this.$1N = $1N", param);
+ factoryBuilder.addField(
+ FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build());
+ });
+ factoryBuilder.addMethod(constructor.build());
+ }
+
+ private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) {
+ ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder();
+ moduleParameter(binding).ifPresent(params::add);
+ frameworkFields(binding).values().forEach(field -> params.add(toParameter(field)));
+ return params.build();
+ }
+
+ private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
+ if (binding.requiresModuleInstance()) {
+ // TODO(user, dpb): Should this use contributingModule()?
+ TypeName type = TypeName.get(binding.bindingTypeElement().get().asType());
+ return Optional.of(ParameterSpec.builder(type, "module").build());
+ }
+ return Optional.empty();
+ }
+
+ private ImmutableMap<Key, FieldSpec> frameworkFields(ProvisionBinding binding) {
+ UniqueNameSet uniqueFieldNames = new UniqueNameSet();
+ // TODO(user, dpb): Add a test for the case when a Factory parameter is named "module".
+ if (binding.requiresModuleInstance()) {
+ uniqueFieldNames.claim("module");
+ }
+ return ImmutableMap.copyOf(
+ transformValues(
+ generateBindingFieldsForDependencies(binding),
+ field ->
+ FieldSpec.builder(
+ field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
+ .build()));
+ }
+
+ private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
+ // If constructing a factory for @Inject or @Provides bindings, we use a static create method
+ // so that generated components can avoid having to refer to the generic types
+ // of the factory. (Otherwise they may have visibility problems referring to the types.)
+ MethodSpec.Builder createMethodBuilder =
+ methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(parameterizedGeneratedTypeNameForBinding(binding))
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
+
+ switch (binding.factoryCreationStrategy()) {
+ case SINGLETON_INSTANCE:
+ FieldSpec.Builder instanceFieldBuilder =
+ FieldSpec.builder(nameGeneratedType(binding), "INSTANCE", PRIVATE, STATIC, FINAL)
+ .initializer("new $T()", nameGeneratedType(binding));
+
+ if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
+ // If the factory has type parameters, ignore them in the field declaration & initializer
+ instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
+
+ createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
+ }
+ createMethodBuilder.addStatement("return INSTANCE");
+ factoryBuilder.addField(instanceFieldBuilder.build());
+ break;
+ case CLASS_CONSTRUCTOR:
+ List<ParameterSpec> params = constructorParams(binding);
+ createMethodBuilder.addParameters(params);
+ createMethodBuilder.addStatement(
+ "return new $T($L)",
+ parameterizedGeneratedTypeNameForBinding(binding),
+ makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input))));
+ break;
+ default:
+ throw new AssertionError();
+ }
+ factoryBuilder.addMethod(createMethodBuilder.build());
+ }
+
+ private MethodSpec getMethod(ProvisionBinding binding) {
+ TypeName providedTypeName = providedTypeName(binding);
+ MethodSpec.Builder getMethod =
+ methodBuilder("get")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(providedTypeName);
+
+ ImmutableMap<Key, FieldSpec> frameworkFields = frameworkFields(binding);
+ CodeBlock parametersCodeBlock =
+ makeParametersCodeBlock(
+ frameworkFieldUsages(binding.provisionDependencies(), frameworkFields).values());
+
+ if (binding.kind().equals(PROVISION)) {
+ binding
+ .nullableType()
+ .ifPresent(nullableType -> CodeBlocks.addAnnotation(getMethod, nullableType));
+ getMethod.addStatement(
+ "return $L",
+ ProvisionMethod.invoke(
+ binding,
+ request ->
+ frameworkTypeUsageStatement(
+ CodeBlock.of("$N", frameworkFields.get(request.key())), request.kind()),
+ nameGeneratedType(binding),
+ binding.requiresModuleInstance()
+ ? Optional.of(CodeBlock.of("module"))
+ : Optional.empty(),
+ compilerOptions,
+ elements));
+ } else if (!binding.injectionSites().isEmpty()) {
+ CodeBlock instance = CodeBlock.of("instance");
+ getMethod
+ .addStatement("$1T $2L = new $1T($3L)", providedTypeName, instance, parametersCodeBlock)
+ .addCode(
+ InjectionSiteMethod.invokeAll(
+ binding.injectionSites(),
+ nameGeneratedType(binding),
+ instance,
+ binding.key().type(),
+ types,
+ frameworkFieldUsages(binding.dependencies(), frameworkFields)::get,
+ elements))
+ .addStatement("return $L", instance);
+ } else {
+ getMethod.addStatement(
+ "return new $T($L)", providedTypeName, parametersCodeBlock);
+ }
+ return getMethod.build();
+ }
+
+ private static TypeName providedTypeName(ProvisionBinding binding) {
+ return TypeName.get(binding.contributedType());
+ }
+
+ private static TypeName factoryTypeName(ProvisionBinding binding) {
+ return factoryOf(providedTypeName(binding));
+ }
+
+ private static ParameterSpec toParameter(FieldSpec field) {
+ return ParameterSpec.builder(field.type, field.name).build();
+ }
+
+ /**
+ * Returns {@code Preconditions.checkNotNull(providesMethodInvocation)} with a message suitable
+ * for {@code @Provides} methods.
+ */
+ static CodeBlock checkNotNullProvidesMethod(CodeBlock providesMethodInvocation) {
+ return CodeBlock.of(
+ "$T.checkNotNull($L, $S)",
+ Preconditions.class,
+ providesMethodInvocation,
+ "Cannot return null from a non-@Nullable @Provides method");
+ }
+}
diff --git a/java/dagger/internal/codegen/FeatureStatus.java b/java/dagger/internal/codegen/FeatureStatus.java
new file mode 100644
index 0000000..9ff254e
--- /dev/null
+++ b/java/dagger/internal/codegen/FeatureStatus.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+/** Allows options to control how features in component processing are enabled. */
+enum FeatureStatus {
+ ENABLED,
+ DISABLED;
+}
diff --git a/java/dagger/internal/codegen/Formatter.java b/java/dagger/internal/codegen/Formatter.java
new file mode 100644
index 0000000..53d4f9a
--- /dev/null
+++ b/java/dagger/internal/codegen/Formatter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkElementIndex;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+/**
+ * A formatter which transforms an instance of a particular type into a string
+ * representation.
+ *
+ * @param <T> the type of the object to be transformed.
+ */
+abstract class Formatter<T> implements Function<T, String> {
+
+ static final String INDENT = " ";
+ static final String DOUBLE_INDENT = INDENT + INDENT;
+ private static final int LIST_LIMIT = 10;
+
+ /**
+ * Performs the transformation of an object into a string representation.
+ */
+ public abstract String format(T object);
+
+ /**
+ * Performs the transformation of an object into a string representation in conformity with the
+ * {@link Function}{@code <T, String>} contract, delegating to {@link #format(Object)}.
+ *
+ * @deprecated Call {@link #format(Object)} instead. This method exists to make formatters easy to
+ * use when functions are required, but shouldn't be called directly.
+ */
+ @SuppressWarnings("javadoc")
+ @Deprecated
+ @Override
+ public final String apply(T object) {
+ return format(object);
+ }
+
+ /** Formats {@code items}, one per line. Stops after {@value #LIST_LIMIT} items. */
+ public void formatIndentedList(
+ StringBuilder builder, Iterable<? extends T> items, int indentLevel) {
+ for (T item : Iterables.limit(items, LIST_LIMIT)) {
+ String formatted = format(item);
+ if (formatted.isEmpty()) {
+ continue;
+ }
+ builder.append('\n');
+ appendIndent(builder, indentLevel);
+ builder.append(formatted);
+ }
+ int numberOfOtherItems = Iterables.size(items) - LIST_LIMIT;
+ if (numberOfOtherItems > 0) {
+ builder.append('\n');
+ appendIndent(builder, indentLevel);
+ builder.append("and ").append(numberOfOtherItems).append(" other");
+ }
+ if (numberOfOtherItems > 1) {
+ builder.append('s');
+ }
+ }
+
+ private void appendIndent(StringBuilder builder, int indentLevel) {
+ for (int i = 0; i < indentLevel; i++) {
+ builder.append(INDENT);
+ }
+ }
+
+ static String formatArgumentInList(int index, int size, CharSequence name) {
+ checkElementIndex(index, size);
+ StringBuilder builder = new StringBuilder();
+ if (index > 0) {
+ builder.append("…, ");
+ }
+ builder.append(name);
+ if (index < size - 1) {
+ builder.append(", …");
+ }
+ return builder.toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/ForwardingCompilerOptions.java b/java/dagger/internal/codegen/ForwardingCompilerOptions.java
new file mode 100644
index 0000000..4a1deda
--- /dev/null
+++ b/java/dagger/internal/codegen/ForwardingCompilerOptions.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+/** A {@link CompilerOptions} object that delegates to another one. */
+class ForwardingCompilerOptions extends CompilerOptions {
+
+ private final CompilerOptions delegate;
+
+ ForwardingCompilerOptions(CompilerOptions delegate) {
+ this.delegate = checkNotNull(delegate);
+ }
+
+ @Override
+ boolean usesProducers() {
+ return delegate.usesProducers();
+ }
+
+ @Override
+ boolean fastInit() {
+ return delegate.fastInit();
+ }
+
+ @Override
+ boolean formatGeneratedSource() {
+ return delegate.formatGeneratedSource();
+ }
+
+ @Override
+ boolean writeProducerNameInToken() {
+ return delegate.writeProducerNameInToken();
+ }
+
+ @Override
+ Diagnostic.Kind nullableValidationKind() {
+ return delegate.nullableValidationKind();
+ }
+
+ @Override
+ Diagnostic.Kind privateMemberValidationKind() {
+ return delegate.privateMemberValidationKind();
+ }
+
+ @Override
+ Diagnostic.Kind staticMemberValidationKind() {
+ return delegate.staticMemberValidationKind();
+ }
+
+ @Override
+ boolean ignorePrivateAndStaticInjectionForComponent() {
+ return delegate.ignorePrivateAndStaticInjectionForComponent();
+ }
+
+ @Override
+ ValidationType scopeCycleValidationType() {
+ return delegate.scopeCycleValidationType();
+ }
+
+ @Override
+ boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+ return delegate.warnIfInjectionFactoryNotGeneratedUpstream();
+ }
+
+ @Override
+ boolean headerCompilation() {
+ return delegate.headerCompilation();
+ }
+
+ @Override
+ boolean aheadOfTimeSubcomponents() {
+ return delegate.aheadOfTimeSubcomponents();
+ }
+
+ @Override
+ boolean forceUseSerializedComponentImplementations() {
+ return delegate.forceUseSerializedComponentImplementations();
+ }
+
+ @Override
+ boolean emitModifiableMetadataAnnotations() {
+ return delegate.emitModifiableMetadataAnnotations();
+ }
+
+ @Override
+ boolean useGradleIncrementalProcessing() {
+ return delegate.useGradleIncrementalProcessing();
+ }
+
+ @Override
+ ValidationType fullBindingGraphValidationType(TypeElement element) {
+ return delegate.fullBindingGraphValidationType(element);
+ }
+
+ @Override
+ Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+ return delegate.moduleHasDifferentScopesDiagnosticKind();
+ }
+
+ @Override
+ ValidationType explicitBindingConflictsWithInjectValidationType() {
+ return delegate.explicitBindingConflictsWithInjectValidationType();
+ }
+}
diff --git a/java/dagger/internal/codegen/FrameworkDependency.java b/java/dagger/internal/codegen/FrameworkDependency.java
new file mode 100644
index 0000000..feea7a0
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkDependency.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.auto.value.AutoValue;
+import dagger.model.Key;
+
+/**
+ * The framework class and binding key for a resolved dependency of a binding. If a binding has
+ * several dependencies for a key, then only one instance of this class will represent them all.
+ *
+ * <p>In the following example, the binding {@code provideFoo()} has two dependency requests:
+ *
+ * <ol>
+ * <li>{@code Bar bar}
+ * <li>{@code Provider<Bar> barProvider}
+ * </ol>
+ *
+ * But they both can be satisfied with the same instance of {@code Provider<Bar>}. So one instance
+ * of {@code FrameworkDependency} will be used for both. Its {@link #key()} will be for {@code Bar},
+ * and its {@link #frameworkType()} will be {@link FrameworkType#PROVIDER}.
+ *
+ * <pre><code>
+ * {@literal @Provides} static Foo provideFoo(Bar bar, {@literal Provider<Bar>} barProvider) {
+ * return new Foo(…);
+ * }
+ * </code></pre>
+ */
+@AutoValue
+abstract class FrameworkDependency {
+
+ /** The fully-resolved key shared by all the dependency requests. */
+ abstract Key key();
+
+ /** The type of the framework dependency. */
+ abstract FrameworkType frameworkType();
+
+ /** The framework class to use for this dependency. */
+ final Class<?> frameworkClass() {
+ return frameworkType().frameworkClass();
+ }
+
+ /** Returns a new instance with the given key and type. */
+ static FrameworkDependency create(Key key, FrameworkType frameworkType) {
+ return new AutoValue_FrameworkDependency(key, frameworkType);
+ }
+}
diff --git a/java/dagger/internal/codegen/FrameworkField.java b/java/dagger/internal/codegen/FrameworkField.java
new file mode 100644
index 0000000..de2ada0
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkField.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.model.BindingKind.MEMBERS_INJECTOR;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.CaseFormat;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * A value object that represents a field in the generated Component class.
+ *
+ * <p>Examples:
+ * <ul>
+ * <li>{@code Provider<String>}
+ * <li>{@code Producer<Widget>}
+ * <li>{@code Provider<Map<SomeMapKey, MapValue>>}.
+ * </ul>
+ */
+@AutoValue
+abstract class FrameworkField {
+
+ /**
+ * Creates a framework field.
+ *
+ * @param frameworkClassName the name of the framework class (e.g., {@link javax.inject.Provider})
+ * @param valueTypeName the name of the type parameter of the framework class (e.g., {@code Foo}
+ * for {@code Provider<Foo>}
+ * @param fieldName the name of the field
+ */
+ static FrameworkField create(
+ ClassName frameworkClassName, TypeName valueTypeName, String fieldName) {
+ String suffix = frameworkClassName.simpleName();
+ return new AutoValue_FrameworkField(
+ ParameterizedTypeName.get(frameworkClassName, valueTypeName),
+ fieldName.endsWith(suffix) ? fieldName : fieldName + suffix);
+ }
+
+ /**
+ * A framework field for a {@link ResolvedBindings}.
+ *
+ * @param frameworkClass if present, the field will use this framework class instead of the normal
+ * one for the bindings
+ */
+ static FrameworkField forResolvedBindings(
+ ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) {
+ return create(
+ frameworkClass.orElse(
+ ClassName.get(
+ FrameworkType.forBindingType(resolvedBindings.bindingType()).frameworkClass())),
+ TypeName.get(fieldValueType(resolvedBindings)),
+ frameworkFieldName(resolvedBindings));
+ }
+
+ private static TypeMirror fieldValueType(ResolvedBindings resolvedBindings) {
+ return resolvedBindings.isMultibindingContribution()
+ ? resolvedBindings.contributionBinding().contributedType()
+ : resolvedBindings.key().type();
+ }
+
+ private static String frameworkFieldName(ResolvedBindings resolvedBindings) {
+ if (!resolvedBindings.contributionBindings().isEmpty()) {
+ ContributionBinding binding = resolvedBindings.contributionBinding();
+ if (binding.bindingElement().isPresent()) {
+ String name = BINDING_ELEMENT_NAME.visit(binding.bindingElement().get(), binding);
+ return binding.kind().equals(MEMBERS_INJECTOR)
+ ? name + "MembersInjector"
+ : name;
+ }
+ }
+ return KeyVariableNamer.name(resolvedBindings.key());
+ }
+
+ private static final ElementVisitor<String, Binding> BINDING_ELEMENT_NAME =
+ new ElementKindVisitor8<String, Binding>() {
+
+ @Override
+ protected String defaultAction(Element e, Binding p) {
+ throw new IllegalArgumentException("Unexpected binding " + p);
+ }
+
+ @Override
+ public String visitExecutableAsConstructor(ExecutableElement e, Binding p) {
+ return visit(e.getEnclosingElement(), p);
+ }
+
+ @Override
+ public String visitExecutableAsMethod(ExecutableElement e, Binding p) {
+ return e.getSimpleName().toString();
+ }
+
+ @Override
+ public String visitType(TypeElement e, Binding p) {
+ return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, e.getSimpleName().toString());
+ }
+
+ @Override
+ public String visitVariableAsParameter(VariableElement e, Binding p) {
+ return e.getSimpleName().toString();
+ }
+ };
+
+ abstract ParameterizedTypeName type();
+ abstract String name();
+}
diff --git a/java/dagger/internal/codegen/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
new file mode 100644
index 0000000..a3de083
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.DelegateFactory;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.producers.internal.DelegateProducer;
+import java.util.Optional;
+
+/**
+ * An object that can initialize a framework-type component field for a binding. An instance should
+ * be created for each field.
+ */
+class FrameworkFieldInitializer implements FrameworkInstanceSupplier {
+
+ /**
+ * An object that can determine the expression to use to assign to the component field for a
+ * binding.
+ */
+ interface FrameworkInstanceCreationExpression {
+ /** Returns the expression to use to assign to the component field for the binding. */
+ CodeBlock creationExpression();
+
+ /**
+ * Returns the framework class to use for the field, if different from the one implied by the
+ * binding. This implementation returns {@link Optional#empty()}.
+ */
+ default Optional<ClassName> alternativeFrameworkClass() {
+ return Optional.empty();
+ }
+
+ /**
+ * Returns {@code true} if instead of using {@link #creationExpression()} to create a framework
+ * instance, a case in {@link InnerSwitchingProviders} should be created for this binding.
+ */
+ // TODO(ronshapiro): perhaps this isn't the right approach. Instead of saying "Use
+ // SetFactory.EMPTY because you will only get 1 class for all types of bindings that use
+ // SetFactory", maybe we should still use an inner switching provider but the same switching
+ // provider index for all cases.
+ default boolean useInnerSwitchingProvider() {
+ return true;
+ }
+ }
+
+ private final ComponentImplementation componentImplementation;
+ private final ResolvedBindings resolvedBindings;
+ private final FrameworkInstanceCreationExpression frameworkInstanceCreationExpression;
+ private FieldSpec fieldSpec;
+ private InitializationState fieldInitializationState = InitializationState.UNINITIALIZED;
+
+ FrameworkFieldInitializer(
+ ComponentImplementation componentImplementation,
+ ResolvedBindings resolvedBindings,
+ FrameworkInstanceCreationExpression frameworkInstanceCreationExpression) {
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.resolvedBindings = checkNotNull(resolvedBindings);
+ this.frameworkInstanceCreationExpression = checkNotNull(frameworkInstanceCreationExpression);
+ }
+
+ /**
+ * Returns the {@link MemberSelect} for the framework field, and adds the field and its
+ * initialization code to the component if it's needed and not already added.
+ */
+ @Override
+ public final MemberSelect memberSelect() {
+ initializeField();
+ return MemberSelect.localField(componentImplementation.name(), checkNotNull(fieldSpec).name);
+ }
+
+ /** Adds the field and its initialization code to the component. */
+ private void initializeField() {
+ switch (fieldInitializationState) {
+ case UNINITIALIZED:
+ // Change our state in case we are recursively invoked via initializeBindingExpression
+ fieldInitializationState = InitializationState.INITIALIZING;
+ CodeBlock.Builder codeBuilder = CodeBlock.builder();
+ CodeBlock fieldInitialization = frameworkInstanceCreationExpression.creationExpression();
+ CodeBlock initCode = CodeBlock.of("this.$N = $L;", getOrCreateField(), fieldInitialization);
+
+ if (isReplacingSuperclassFrameworkInstance()
+ || fieldInitializationState == InitializationState.DELEGATED) {
+ codeBuilder.add(
+ "$T.setDelegate($N, $L);", delegateType(), fieldSpec, fieldInitialization);
+ } else {
+ codeBuilder.add(initCode);
+ }
+ componentImplementation.addInitialization(codeBuilder.build());
+
+ fieldInitializationState = InitializationState.INITIALIZED;
+ break;
+
+ case INITIALIZING:
+ // We were recursively invoked, so create a delegate factory instead
+ fieldInitializationState = InitializationState.DELEGATED;
+ componentImplementation.addInitialization(
+ CodeBlock.of("this.$N = new $T<>();", getOrCreateField(), delegateType()));
+ break;
+
+ case DELEGATED:
+ case INITIALIZED:
+ break;
+ }
+ }
+
+ /**
+ * Adds a field representing the resolved bindings, optionally forcing it to use a particular
+ * binding type (instead of the type the resolved bindings would typically use).
+ */
+ private FieldSpec getOrCreateField() {
+ if (fieldSpec != null) {
+ return fieldSpec;
+ }
+ boolean useRawType = !componentImplementation.isTypeAccessible(resolvedBindings.key().type());
+ FrameworkField contributionBindingField =
+ FrameworkField.forResolvedBindings(
+ resolvedBindings, frameworkInstanceCreationExpression.alternativeFrameworkClass());
+
+ TypeName fieldType =
+ useRawType ? contributionBindingField.type().rawType : contributionBindingField.type();
+
+ FieldSpec.Builder contributionField =
+ FieldSpec.builder(
+ fieldType, componentImplementation.getUniqueFieldName(contributionBindingField.name()));
+ contributionField.addModifiers(PRIVATE);
+ if (useRawType) {
+ contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES));
+ }
+
+ if (isReplacingSuperclassFrameworkInstance()) {
+ // If a binding is modified in a subclass, the framework instance will be replaced in the
+ // subclass implementation. The superclass framework instance initialization will run first,
+ // however, and may refer to the modifiable binding method returning this type's modified
+ // framework instance before it is initialized, so we use a delegate factory as a placeholder
+ // until it has properly been initialized.
+ contributionField.initializer("new $T<>()", delegateType());
+ }
+
+ fieldSpec = contributionField.build();
+ componentImplementation.addField(FRAMEWORK_FIELD, fieldSpec);
+
+ return fieldSpec;
+ }
+
+ /**
+ * Returns true if this framework field is replacing a superclass's implementation of the
+ * framework field.
+ */
+ private boolean isReplacingSuperclassFrameworkInstance() {
+ return componentImplementation
+ .superclassImplementation()
+ .flatMap(
+ superclassImplementation ->
+ // TODO(b/117833324): can we constrain this further?
+ superclassImplementation.getModifiableBindingMethod(
+ BindingRequest.bindingRequest(
+ resolvedBindings.key(),
+ isProvider() ? FrameworkType.PROVIDER : FrameworkType.PRODUCER_NODE)))
+ .isPresent();
+ }
+
+ private Class<?> delegateType() {
+ return isProvider() ? DelegateFactory.class : DelegateProducer.class;
+ }
+
+ private boolean isProvider() {
+ return resolvedBindings.bindingType().equals(BindingType.PROVISION)
+ && frameworkInstanceCreationExpression
+ .alternativeFrameworkClass()
+ .map(TypeNames.PROVIDER::equals)
+ .orElse(true);
+ }
+
+ /** Initialization state for a factory field. */
+ private enum InitializationState {
+ /** The field is {@code null}. */
+ UNINITIALIZED,
+
+ /**
+ * The field's dependencies are being set up. If the field is needed in this state, use a {@link
+ * DelegateFactory}.
+ */
+ INITIALIZING,
+
+ /**
+ * The field's dependencies are being set up, but the field can be used because it has already
+ * been set to a {@link DelegateFactory}.
+ */
+ DELEGATED,
+
+ /** The field is set to an undelegated factory. */
+ INITIALIZED;
+ }
+}
diff --git a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
new file mode 100644
index 0000000..6f62d66
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression that uses a {@link FrameworkType} field. */
+abstract class FrameworkInstanceBindingExpression extends BindingExpression {
+ private final ResolvedBindings resolvedBindings;
+ private final FrameworkInstanceSupplier frameworkInstanceSupplier;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ FrameworkInstanceBindingExpression(
+ ResolvedBindings resolvedBindings,
+ FrameworkInstanceSupplier frameworkInstanceSupplier,
+ DaggerTypes types,
+ DaggerElements elements) {
+ this.resolvedBindings = checkNotNull(resolvedBindings);
+ this.frameworkInstanceSupplier = checkNotNull(frameworkInstanceSupplier);
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ }
+
+ /**
+ * The expression for the framework instance for this binding. The field will be {@link
+ * ComponentImplementation#addInitialization(CodeBlock) initialized} and {@link
+ * ComponentImplementation#addField(ComponentImplementation.FieldSpecKind, FieldSpec) added} to
+ * the component the first time this method is invoked.
+ */
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ MemberSelect memberSelect = frameworkInstanceSupplier.memberSelect();
+ TypeMirror contributedType = resolvedBindings.contributionBinding().contributedType();
+ TypeMirror expressionType =
+ isTypeAccessibleFrom(contributedType, requestingClass.packageName())
+ || isInlinedFactoryCreation(memberSelect)
+ ? types.wrapType(contributedType, frameworkType().frameworkClass())
+ : rawFrameworkType();
+ return Expression.create(expressionType, memberSelect.getExpressionFor(requestingClass));
+ }
+
+ /** Returns the framework type for the binding. */
+ protected abstract FrameworkType frameworkType();
+
+ /**
+ * Returns {@code true} if a factory is created inline each time it is requested. For example, in
+ * the initialization {@code this.fooProvider = Foo_Factory.create(Bar_Factory.create());}, {@code
+ * Bar_Factory} is considered to be inline.
+ *
+ * <p>This is used in {@link #getDependencyExpression(ClassName)} when determining the type of a
+ * factory. Normally if the {@link ContributionBinding#contributedType()} is not accessible from
+ * the component, the type of the expression will be a raw {@link javax.inject.Provider}. However,
+ * if the factory is created inline, even if contributed type is not accessible, javac will still
+ * be able to determine the type that is returned from the {@code Foo_Factory.create()} method.
+ */
+ private static boolean isInlinedFactoryCreation(MemberSelect memberSelect) {
+ return memberSelect.staticMember();
+ }
+
+ private DeclaredType rawFrameworkType() {
+ return types.getDeclaredType(elements.getTypeElement(frameworkType().frameworkClass()));
+ }
+}
diff --git a/java/dagger/internal/codegen/FrameworkInstanceSupplier.java b/java/dagger/internal/codegen/FrameworkInstanceSupplier.java
new file mode 100644
index 0000000..4c45630
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkInstanceSupplier.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+/** An object that supplies a {@link MemberSelect} for a framework instance. */
+interface FrameworkInstanceSupplier {
+ /** Returns a {@link MemberSelect}, with possible side effects on the first call. */
+ MemberSelect memberSelect();
+}
diff --git a/java/dagger/internal/codegen/FrameworkType.java b/java/dagger/internal/codegen/FrameworkType.java
new file mode 100644
index 0000000..f4e3779
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkType.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static dagger.model.RequestKind.INSTANCE;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import dagger.Lazy;
+import dagger.internal.DoubleCheck;
+import dagger.internal.ProviderOfLazy;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.internal.Producers;
+import java.util.Optional;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** One of the core types initialized as fields in a generated component. */
+enum FrameworkType {
+ /** A {@link Provider}. */
+ PROVIDER {
+ @Override
+ Class<?> frameworkClass() {
+ return Provider.class;
+ }
+
+ @Override
+ Optional<RequestKind> requestKind() {
+ return Optional.of(RequestKind.PROVIDER);
+ }
+
+ @Override
+ CodeBlock to(RequestKind requestKind, CodeBlock from) {
+ switch (requestKind) {
+ case INSTANCE:
+ return CodeBlock.of("$L.get()", from);
+
+ case LAZY:
+ return CodeBlock.of("$T.lazy($L)", DoubleCheck.class, from);
+
+ case PROVIDER:
+ return from;
+
+ case PROVIDER_OF_LAZY:
+ return CodeBlock.of("$T.create($L)", ProviderOfLazy.class, from);
+
+ case PRODUCER:
+ return CodeBlock.of("$T.producerFromProvider($L)", Producers.class, from);
+
+ case FUTURE:
+ return CodeBlock.of("$T.immediateFuture($L)", Futures.class, to(INSTANCE, from));
+
+ case PRODUCED:
+ return CodeBlock.of("$T.successful($L)", Produced.class, to(INSTANCE, from));
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Cannot request a %s from a %s", requestKind, this));
+ }
+ }
+
+ @Override
+ Expression to(RequestKind requestKind, Expression from, DaggerTypes types) {
+ CodeBlock codeBlock = to(requestKind, from.codeBlock());
+ switch (requestKind) {
+ case INSTANCE:
+ return Expression.create(types.unwrapTypeOrObject(from.type()), codeBlock);
+
+ case PROVIDER:
+ return from;
+
+ case PROVIDER_OF_LAZY:
+ TypeMirror lazyType = types.rewrapType(from.type(), Lazy.class);
+ return Expression.create(types.wrapType(lazyType, Provider.class), codeBlock);
+
+ case FUTURE:
+ return Expression.create(
+ types.rewrapType(from.type(), ListenableFuture.class), codeBlock);
+
+ default:
+ return Expression.create(
+ types.rewrapType(from.type(), RequestKinds.frameworkClass(requestKind)), codeBlock);
+ }
+ }
+ },
+
+ /** A {@link Producer}. */
+ PRODUCER_NODE {
+ @Override
+ Class<?> frameworkClass() {
+ // TODO(cgdecker): Replace this with new class for representing internal producer nodes.
+ // Currently the new class is CancellableProducer, but it may be changed to ProducerNode and
+ // made to not implement Producer.
+ return Producer.class;
+ }
+
+ @Override
+ Optional<RequestKind> requestKind() {
+ return Optional.empty();
+ }
+
+ @Override
+ CodeBlock to(RequestKind requestKind, CodeBlock from) {
+ switch (requestKind) {
+ case FUTURE:
+ return CodeBlock.of("$L.get()", from);
+
+ case PRODUCER:
+ return from;
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Cannot request a %s from a %s", requestKind, this));
+ }
+ }
+
+ @Override
+ Expression to(RequestKind requestKind, Expression from, DaggerTypes types) {
+ switch (requestKind) {
+ case FUTURE:
+ return Expression.create(
+ types.rewrapType(from.type(), ListenableFuture.class),
+ to(requestKind, from.codeBlock()));
+
+ case PRODUCER:
+ return Expression.create(from.type(), to(requestKind, from.codeBlock()));
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Cannot request a %s from a %s", requestKind, this));
+ }
+ }
+ },
+ ;
+
+ /** Returns the framework type appropriate for fields for a given binding type. */
+ static FrameworkType forBindingType(BindingType bindingType) {
+ switch (bindingType) {
+ case PROVISION:
+ return PROVIDER;
+ case PRODUCTION:
+ return PRODUCER_NODE;
+ case MEMBERS_INJECTION:
+ }
+ throw new AssertionError(bindingType);
+ }
+
+ /** Returns the framework type that exactly matches the given request kind, if one exists. */
+ static Optional<FrameworkType> forRequestKind(RequestKind requestKind) {
+ switch (requestKind) {
+ case PROVIDER:
+ return Optional.of(FrameworkType.PROVIDER);
+ default:
+ return Optional.empty();
+ }
+ }
+
+ /** The class of fields of this type. */
+ abstract Class<?> frameworkClass();
+
+ /** Returns the {@link #frameworkClass()} parameterized with a type. */
+ ParameterizedTypeName frameworkClassOf(TypeName valueType) {
+ return ParameterizedTypeName.get(ClassName.get(frameworkClass()), valueType);
+ }
+
+ /** The request kind that an instance of this framework type can satisfy directly, if any. */
+ abstract Optional<RequestKind> requestKind();
+
+ /**
+ * Returns a {@link CodeBlock} that evaluates to a requested object given an expression that
+ * evaluates to an instance of this framework type.
+ *
+ * @param requestKind the kind of {@link DependencyRequest} that the returned expression can
+ * satisfy
+ * @param from a {@link CodeBlock} that evaluates to an instance of this framework type
+ * @throws IllegalArgumentException if a valid expression cannot be generated for {@code
+ * requestKind}
+ */
+ abstract CodeBlock to(RequestKind requestKind, CodeBlock from);
+
+ /**
+ * Returns an {@link Expression} that evaluates to a requested object given an expression that
+ * evaluates to an instance of this framework type.
+ *
+ * @param requestKind the kind of {@link DependencyRequest} that the returned expression can
+ * satisfy
+ * @param from an expression that evaluates to an instance of this framework type
+ * @throws IllegalArgumentException if a valid expression cannot be generated for {@code
+ * requestKind}
+ */
+ abstract Expression to(RequestKind requestKind, Expression from, DaggerTypes types);
+
+ @Override
+ public String toString() {
+ return UPPER_UNDERSCORE.to(UPPER_CAMEL, super.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/FrameworkTypeMapper.java b/java/dagger/internal/codegen/FrameworkTypeMapper.java
new file mode 100644
index 0000000..7746692
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkTypeMapper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingType.PRODUCTION;
+import static java.util.stream.Collectors.toSet;
+
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import java.util.Set;
+import javax.inject.Provider;
+
+/**
+ * A mapper for associating a {@link RequestKind} to a {@link FrameworkType}, dependent on the type
+ * of code to be generated (e.g., for {@link Provider} or {@link Producer}).
+ */
+enum FrameworkTypeMapper {
+ FOR_PROVIDER() {
+ @Override
+ public FrameworkType getFrameworkType(RequestKind requestKind) {
+ switch (requestKind) {
+ case INSTANCE:
+ case PROVIDER:
+ case PROVIDER_OF_LAZY:
+ case LAZY:
+ return FrameworkType.PROVIDER;
+ case PRODUCED:
+ case PRODUCER:
+ throw new IllegalArgumentException(requestKind.toString());
+ default:
+ throw new AssertionError(requestKind);
+ }
+ }
+ },
+ FOR_PRODUCER() {
+ @Override
+ public FrameworkType getFrameworkType(RequestKind requestKind) {
+ switch (requestKind) {
+ case INSTANCE:
+ case PRODUCED:
+ case PRODUCER:
+ return FrameworkType.PRODUCER_NODE;
+ case PROVIDER:
+ case PROVIDER_OF_LAZY:
+ case LAZY:
+ return FrameworkType.PROVIDER;
+ default:
+ throw new AssertionError(requestKind);
+ }
+ }
+ };
+
+ static FrameworkTypeMapper forBindingType(BindingType bindingType) {
+ return bindingType.equals(PRODUCTION) ? FOR_PRODUCER : FOR_PROVIDER;
+ }
+
+ abstract FrameworkType getFrameworkType(RequestKind requestKind);
+
+ /**
+ * Returns the {@link FrameworkType} to use for a collection of requests of the same {@link Key}.
+ * This allows factories to only take a single argument for multiple requests of the same key.
+ */
+ FrameworkType getFrameworkType(Set<DependencyRequest> requests) {
+ Set<FrameworkType> frameworkTypes =
+ requests.stream().map(request -> getFrameworkType(request.kind())).collect(toSet());
+ return frameworkTypes.size() == 1 ? getOnlyElement(frameworkTypes) : FrameworkType.PROVIDER;
+ }
+}
diff --git a/java/dagger/internal/codegen/FrameworkTypes.java b/java/dagger/internal/codegen/FrameworkTypes.java
new file mode 100644
index 0000000..19d2eda
--- /dev/null
+++ b/java/dagger/internal/codegen/FrameworkTypes.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.isType;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Lazy;
+import dagger.MembersInjector;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.Set;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A collection of utility methods for dealing with Dagger framework types. A framework type is any
+ * type that the framework itself defines.
+ */
+final class FrameworkTypes {
+ private static final ImmutableSet<Class<?>> PROVISION_TYPES =
+ ImmutableSet.of(Provider.class, Lazy.class, MembersInjector.class);
+
+ // NOTE(beder): ListenableFuture is not considered a producer framework type because it is not
+ // defined by the framework, so we can't treat it specially in ordinary Dagger.
+ private static final ImmutableSet<Class<?>> PRODUCTION_TYPES =
+ ImmutableSet.of(Produced.class, Producer.class);
+
+ /** Returns true if the type represents a producer-related framework type. */
+ static boolean isProducerType(TypeMirror type) {
+ return isType(type) && typeIsOneOf(PRODUCTION_TYPES, type);
+ }
+
+ /** Returns true if the type represents a framework type. */
+ static boolean isFrameworkType(TypeMirror type) {
+ return isType(type)
+ && (typeIsOneOf(PROVISION_TYPES, type)
+ || typeIsOneOf(PRODUCTION_TYPES, type));
+ }
+
+ private static boolean typeIsOneOf(Set<Class<?>> classes, TypeMirror type) {
+ for (Class<?> clazz : classes) {
+ if (MoreTypes.isTypeOf(clazz, type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private FrameworkTypes() {}
+}
diff --git a/java/dagger/internal/codegen/GenerationCompilerOptions.java b/java/dagger/internal/codegen/GenerationCompilerOptions.java
new file mode 100644
index 0000000..1b14a7e
--- /dev/null
+++ b/java/dagger/internal/codegen/GenerationCompilerOptions.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A {@link Qualifier} for bindings associated with the serialization/deserialization of {@link
+ * dagger.internal.GenerationOptions}.
+ */
+@Retention(RUNTIME)
+@Qualifier
+@interface GenerationCompilerOptions {}
diff --git a/java/dagger/internal/codegen/GenerationOptionsModule.java b/java/dagger/internal/codegen/GenerationOptionsModule.java
new file mode 100644
index 0000000..aa3c461
--- /dev/null
+++ b/java/dagger/internal/codegen/GenerationOptionsModule.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.GenerationOptions;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+
+/** Adds bindings for serializing and rereading {@link GenerationOptions}. */
+@Module
+interface GenerationOptionsModule {
+ @Provides
+ @PerComponentImplementation
+ @GenerationCompilerOptions
+ static CompilerOptions generationOptions(
+ CompilerOptions defaultOptions,
+ ComponentImplementation componentImplementation,
+ DaggerElements elements) {
+ // Avoid looking up types that don't exist. Performance improves for large components.
+ if (!defaultOptions.aheadOfTimeSubcomponents()) {
+ return defaultOptions;
+ }
+ // Inspect the base implementation for the @GenerationOptions annotation. Even if
+ // componentImplementation is the base implementation, inspect it for the case where we are
+ // recomputing the ComponentImplementation from a previous compilation.
+ // TODO(b/117833324): consider adding a method that returns baseImplementation.orElse(this).
+ // The current state of the world is a little confusing and maybe not intuitive: the base
+ // implementation has no base implementation, but it _is_ a base implementation.
+ return Optional.of(componentImplementation.baseImplementation().orElse(componentImplementation))
+ .map(baseImplementation -> elements.getTypeElement(baseImplementation.name()))
+ // If this returns null, the type has not been generated yet and Optional will switch to an
+ // empty state. This means that we're currently generating componentImplementation, or that
+ // the base implementation is being generated in this round, and thus the options passed to
+ // this compilation are applicable
+ .map(typeElement -> typeElement.getAnnotation(GenerationOptions.class))
+ .map(defaultOptions::withGenerationOptions)
+ .orElse(defaultOptions);
+ }
+}
diff --git a/java/dagger/internal/codegen/GwtCompatibility.java b/java/dagger/internal/codegen/GwtCompatibility.java
new file mode 100644
index 0000000..34d8f6d
--- /dev/null
+++ b/java/dagger/internal/codegen/GwtCompatibility.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.squareup.javapoet.AnnotationSpec;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Name;
+
+final class GwtCompatibility {
+
+ /**
+ * Returns a {@code @GwtIncompatible} annotation that is applied to {@code binding}'s {@link
+ * Binding#bindingElement()} or any enclosing type.
+ */
+ static Optional<AnnotationSpec> gwtIncompatibleAnnotation(Binding binding) {
+ checkArgument(binding.bindingElement().isPresent());
+ Element element = binding.bindingElement().get();
+ while (element != null) {
+ Optional<AnnotationSpec> gwtIncompatible =
+ element
+ .getAnnotationMirrors()
+ .stream()
+ .filter(annotation -> isGwtIncompatible(annotation))
+ .map(AnnotationSpec::get)
+ .findFirst();
+ if (gwtIncompatible.isPresent()) {
+ return gwtIncompatible;
+ }
+ element = element.getEnclosingElement();
+ }
+ return Optional.empty();
+ }
+
+ private static boolean isGwtIncompatible(AnnotationMirror annotation) {
+ Name simpleName = annotation.getAnnotationType().asElement().getSimpleName();
+ return simpleName.contentEquals("GwtIncompatible");
+ }
+}
diff --git a/java/dagger/internal/codegen/HjarSourceFileGenerator.java b/java/dagger/internal/codegen/HjarSourceFileGenerator.java
new file mode 100644
index 0000000..5c36322
--- /dev/null
+++ b/java/dagger/internal/codegen/HjarSourceFileGenerator.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+
+/**
+ * A source file generator that only writes the relevant code necessary for Bazel to create a
+ * correct header (ABI) jar.
+ */
+final class HjarSourceFileGenerator<T> extends SourceFileGenerator<T> {
+ private final SourceFileGenerator<T> delegate;
+
+ private HjarSourceFileGenerator(SourceFileGenerator<T> delegate) {
+ super(delegate);
+ this.delegate = delegate;
+ }
+
+ static <T> SourceFileGenerator<T> wrap(SourceFileGenerator<T> delegate) {
+ return new HjarSourceFileGenerator<>(delegate);
+ }
+
+ @Override
+ ClassName nameGeneratedType(T input) {
+ return delegate.nameGeneratedType(input);
+ }
+
+ @Override
+ Element originatingElement(T input) {
+ return delegate.originatingElement(input);
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedTypeName, T input) {
+ return delegate
+ .write(generatedTypeName, input)
+ .map(completeType -> skeletonType(completeType.build()));
+ }
+
+ private TypeSpec.Builder skeletonType(TypeSpec completeType) {
+ TypeSpec.Builder skeleton =
+ classBuilder(completeType.name)
+ .addSuperinterfaces(completeType.superinterfaces)
+ .addTypeVariables(completeType.typeVariables)
+ .addModifiers(completeType.modifiers.toArray(new Modifier[0]))
+ .addAnnotations(completeType.annotations);
+
+ if (!completeType.superclass.equals(ClassName.OBJECT)) {
+ skeleton.superclass(completeType.superclass);
+ }
+
+ completeType.methodSpecs.stream()
+ .filter(method -> !method.modifiers.contains(PRIVATE) || method.isConstructor())
+ .map(this::skeletonMethod)
+ .forEach(skeleton::addMethod);
+
+ completeType.fieldSpecs.stream()
+ .filter(field -> !field.modifiers.contains(PRIVATE))
+ .map(this::skeletonField)
+ .forEach(skeleton::addField);
+
+ completeType.typeSpecs.stream()
+ .map(type -> skeletonType(type).build())
+ .forEach(skeleton::addType);
+
+ return skeleton;
+ }
+
+ private MethodSpec skeletonMethod(MethodSpec completeMethod) {
+ MethodSpec.Builder skeleton =
+ completeMethod.isConstructor()
+ ? constructorBuilder()
+ : methodBuilder(completeMethod.name).returns(completeMethod.returnType);
+
+ if (completeMethod.isConstructor()) {
+ // Code in Turbine must (for technical reasons in javac) have a valid super() call for
+ // constructors, otherwise javac will bark, and Turbine has no way to avoid this. So we retain
+ // constructor method bodies if they do exist
+ skeleton.addCode(completeMethod.code);
+ }
+
+ return skeleton
+ .addModifiers(completeMethod.modifiers)
+ .addTypeVariables(completeMethod.typeVariables)
+ .addParameters(completeMethod.parameters)
+ .addExceptions(completeMethod.exceptions)
+ .varargs(completeMethod.varargs)
+ .addAnnotations(completeMethod.annotations)
+ .build();
+ }
+
+ private FieldSpec skeletonField(FieldSpec completeField) {
+ return FieldSpec.builder(
+ completeField.type,
+ completeField.name,
+ completeField.modifiers.toArray(new Modifier[0]))
+ .addAnnotations(completeField.annotations)
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java b/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java
new file mode 100644
index 0000000..69b107c
--- /dev/null
+++ b/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import javax.lang.model.SourceVersion;
+
+final class ImmediateFutureBindingExpression extends BindingExpression {
+
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final SourceVersion sourceVersion;
+ private final Key key;
+
+ ImmediateFutureBindingExpression(
+ ResolvedBindings resolvedBindings,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ SourceVersion sourceVersion) {
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.types = checkNotNull(types);
+ this.sourceVersion = checkNotNull(sourceVersion);
+ this.key = resolvedBindings.key();
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(
+ types.wrapType(key.type(), ListenableFuture.class),
+ CodeBlock.of("$T.immediateFuture($L)", Futures.class, instanceExpression(requestingClass)));
+ }
+
+ private CodeBlock instanceExpression(ClassName requestingClass) {
+ Expression expression =
+ componentBindingExpressions.getDependencyExpression(
+ bindingRequest(key, RequestKind.INSTANCE), requestingClass);
+ if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+ // Java 7 type inference is not as strong as in Java 8, and therefore some generated code must
+ // cast.
+ //
+ // For example, javac7 cannot detect that Futures.immediateFuture(ImmutableSet.of("T"))
+ // can safely be assigned to ListenableFuture<Set<T>>.
+ if (!types.isSameType(expression.type(), key.type())) {
+ return CodeBlock.of(
+ "($T) $L", types.accessibleType(key.type(), requestingClass), expression.codeBlock());
+ }
+ }
+ return expression.codeBlock();
+ }
+}
diff --git a/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java b/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java
new file mode 100644
index 0000000..19c4d19
--- /dev/null
+++ b/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * Generates a class that exposes a non-{@code public} {@link
+ * ContributionBinding#mapKeyAnnotation()} @MapKey} annotation.
+ */
+final class InaccessibleMapKeyProxyGenerator extends SourceFileGenerator<ContributionBinding> {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ @Inject
+ InaccessibleMapKeyProxyGenerator(
+ Filer filer, DaggerTypes types, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ this.types = types;
+ this.elements = elements;
+ }
+
+ @Override
+ ClassName nameGeneratedType(ContributionBinding binding) {
+ return MapKeys.mapKeyProxyClassName(binding);
+ }
+
+ @Override
+ Element originatingElement(ContributionBinding binding) {
+ // a map key is only ever present on bindings that have a binding element
+ return binding.bindingElement().get();
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedName, ContributionBinding binding) {
+ return MapKeys.mapKeyFactoryMethod(binding, types, elements)
+ .map(
+ method ->
+ classBuilder(generatedName)
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
+ .addMethod(method));
+ }
+}
diff --git a/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java b/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java
new file mode 100644
index 0000000..d649e46
--- /dev/null
+++ b/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.Formatter.INDENT;
+import static dagger.internal.codegen.Scopes.getReadableSource;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static dagger.model.BindingKind.INJECTION;
+import static java.util.stream.Collectors.joining;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimaps;
+import dagger.model.Binding;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.tools.Diagnostic;
+
+/**
+ * Reports an error for any component that uses bindings with scopes that are not assigned to the
+ * component.
+ */
+final class IncompatiblyScopedBindingsValidator implements BindingGraphPlugin {
+
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ IncompatiblyScopedBindingsValidator(
+ MethodSignatureFormatter methodSignatureFormatter, CompilerOptions compilerOptions) {
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/IncompatiblyScopedBindings";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ ImmutableSetMultimap.Builder<ComponentNode, dagger.model.Binding> incompatibleBindings =
+ ImmutableSetMultimap.builder();
+ for (dagger.model.Binding binding : bindingGraph.bindings()) {
+ binding
+ .scope()
+ .filter(scope -> !scope.isReusable())
+ .ifPresent(
+ scope -> {
+ ComponentNode componentNode =
+ bindingGraph.componentNode(binding.componentPath()).get();
+ if (!componentNode.scopes().contains(scope)) {
+ // @Inject bindings in module or subcomponent binding graphs will appear at the
+ // properly scoped ancestor component, so ignore them here.
+ if (binding.kind().equals(INJECTION)
+ && (bindingGraph.rootComponentNode().isSubcomponent()
+ || !bindingGraph.rootComponentNode().isRealComponent())) {
+ return;
+ }
+ incompatibleBindings.put(componentNode, binding);
+ }
+ });
+ }
+ Multimaps.asMap(incompatibleBindings.build())
+ .forEach((componentNode, bindings) -> report(componentNode, bindings, diagnosticReporter));
+ }
+
+ private void report(
+ ComponentNode componentNode,
+ Set<Binding> bindings,
+ DiagnosticReporter diagnosticReporter) {
+ Diagnostic.Kind diagnosticKind = ERROR;
+ StringBuilder message =
+ new StringBuilder(componentNode.componentPath().currentComponent().getQualifiedName());
+
+ if (!componentNode.isRealComponent()) {
+ // If the "component" is really a module, it will have no scopes attached. We want to report
+ // if there is more than one scope in that component.
+ if (bindings.stream().map(Binding::scope).map(Optional::get).distinct().count() <= 1) {
+ return;
+ }
+ message.append(" contains bindings with different scopes:");
+ diagnosticKind = compilerOptions.moduleHasDifferentScopesDiagnosticKind();
+ } else if (componentNode.scopes().isEmpty()) {
+ message.append(" (unscoped) may not reference scoped bindings:");
+ } else {
+ message
+ .append(" scoped with ")
+ .append(
+ componentNode.scopes().stream().map(Scopes::getReadableSource).collect(joining(" ")))
+ .append(" may not reference bindings with different scopes:");
+ }
+
+ // TODO(ronshapiro): Should we group by scope?
+ for (Binding binding : bindings) {
+ message.append('\n').append(INDENT);
+
+ // TODO(dpb): Use BindingDeclarationFormatter.
+ // But that doesn't print scopes for @Inject-constructed types.
+ switch (binding.kind()) {
+ case DELEGATE:
+ case PROVISION:
+ message.append(
+ methodSignatureFormatter.format(
+ MoreElements.asExecutable(binding.bindingElement().get())));
+ break;
+
+ case INJECTION:
+ message
+ .append(getReadableSource(binding.scope().get()))
+ .append(" class ")
+ .append(
+ closestEnclosingTypeElement(binding.bindingElement().get()).getQualifiedName());
+ break;
+
+ default:
+ throw new AssertionError(binding);
+ }
+ }
+ diagnosticReporter.reportComponent(diagnosticKind, componentNode, message.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectBindingRegistry.java b/java/dagger/internal/codegen/InjectBindingRegistry.java
new file mode 100644
index 0000000..2840d75
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectBindingRegistry.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.Component;
+import dagger.Provides;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Maintains the collection of provision bindings from {@link Inject} constructors and members
+ * injection bindings from {@link Inject} fields and methods known to the annotation processor. Note
+ * that this registry <b>does not</b> handle any explicit bindings (those from {@link Provides}
+ * methods, {@link Component} dependencies, etc.).
+ */
+interface InjectBindingRegistry {
+ /**
+ * Returns a {@link ProvisionBinding} for {@code key}. If none has been registered yet, registers
+ * one.
+ */
+ Optional<ProvisionBinding> getOrFindProvisionBinding(Key key);
+
+ /**
+ * Returns a {@link MembersInjectionBinding} for {@code key}. If none has been registered yet,
+ * registers one, along with all necessary members injection bindings for superclasses.
+ */
+ Optional<MembersInjectionBinding> getOrFindMembersInjectionBinding(Key key);
+
+ /**
+ * Returns a {@link ProvisionBinding} for a {@link dagger.MembersInjector} of {@code key}. If none
+ * has been registered yet, registers one.
+ */
+ Optional<ProvisionBinding> getOrFindMembersInjectorProvisionBinding(Key key);
+
+ @CanIgnoreReturnValue
+ Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement);
+
+ @CanIgnoreReturnValue
+ Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement);
+
+ /**
+ * This method ensures that sources for all registered {@link Binding bindings} (either explicitly
+ * or implicitly via {@link #getOrFindMembersInjectionBinding} or {@link
+ * #getOrFindProvisionBinding}) are generated.
+ */
+ void generateSourcesForRequiredBindings(
+ SourceFileGenerator<ProvisionBinding> factoryGenerator,
+ SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator)
+ throws SourceFileGenerationException;
+}
diff --git a/java/dagger/internal/codegen/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/InjectBindingRegistryImpl.java
new file mode 100644
index 0000000..45dc391
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectBindingRegistryImpl.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.InjectionAnnotations.injectedConstructors;
+import static dagger.internal.codegen.Keys.isValidImplicitProvisionKey;
+import static dagger.internal.codegen.Keys.isValidMembersInjectionKey;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
+import dagger.Component;
+import dagger.MembersInjector;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * Maintains the collection of provision bindings from {@link Inject} constructors and members
+ * injection bindings from {@link Inject} fields and methods known to the annotation processor.
+ * Note that this registry <b>does not</b> handle any explicit bindings (those from {@link Provides}
+ * methods, {@link Component} dependencies, etc.).
+ */
+@Singleton
+final class InjectBindingRegistryImpl implements InjectBindingRegistry {
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final Messager messager;
+ private final InjectValidator injectValidator;
+ private final InjectValidator injectValidatorWhenGeneratingCode;
+ private final KeyFactory keyFactory;
+ private final BindingFactory bindingFactory;
+ private final CompilerOptions compilerOptions;
+
+ final class BindingsCollection<B extends Binding> {
+ private final Class<?> factoryClass;
+ private final Map<Key, B> bindingsByKey = Maps.newLinkedHashMap();
+ private final Deque<B> bindingsRequiringGeneration = new ArrayDeque<>();
+ private final Set<Key> materializedBindingKeys = Sets.newLinkedHashSet();
+
+ BindingsCollection(Class<?> factoryClass) {
+ this.factoryClass = factoryClass;
+ }
+
+ void generateBindings(SourceFileGenerator<B> generator) throws SourceFileGenerationException {
+ for (B binding = bindingsRequiringGeneration.poll();
+ binding != null;
+ binding = bindingsRequiringGeneration.poll()) {
+ checkState(!binding.unresolved().isPresent());
+ if (injectValidatorWhenGeneratingCode.isValidType(binding.key().type())) {
+ generator.generate(binding);
+ }
+ materializedBindingKeys.add(binding.key());
+ }
+ // Because Elements instantiated across processing rounds are not guaranteed to be equals() to
+ // the logically same element, clear the cache after generating
+ bindingsByKey.clear();
+ }
+
+ /** Returns a previously cached binding. */
+ B getBinding(Key key) {
+ return bindingsByKey.get(key);
+ }
+
+ /** Caches the binding and generates it if it needs generation. */
+ void tryRegisterBinding(B binding, boolean warnIfNotAlreadyGenerated) {
+ tryToCacheBinding(binding);
+ tryToGenerateBinding(binding, warnIfNotAlreadyGenerated);
+ }
+
+ /**
+ * Tries to generate a binding, not generating if it already is generated. For resolved
+ * bindings, this will try to generate the unresolved version of the binding.
+ */
+ void tryToGenerateBinding(B binding, boolean warnIfNotAlreadyGenerated) {
+ if (shouldGenerateBinding(binding, generatedClassNameForBinding(binding))) {
+ bindingsRequiringGeneration.offer(binding);
+ if (compilerOptions.warnIfInjectionFactoryNotGeneratedUpstream()
+ && warnIfNotAlreadyGenerated) {
+ messager.printMessage(
+ Kind.NOTE,
+ String.format(
+ "Generating a %s for %s. "
+ + "Prefer to run the dagger processor over that class instead.",
+ factoryClass.getSimpleName(),
+ types.erasure(binding.key().type()))); // erasure to strip <T> from msgs.
+ }
+ }
+ }
+
+ /** Returns true if the binding needs to be generated. */
+ private boolean shouldGenerateBinding(B binding, ClassName factoryName) {
+ return !binding.unresolved().isPresent()
+ && !materializedBindingKeys.contains(binding.key())
+ && !bindingsRequiringGeneration.contains(binding)
+ && elements.getTypeElement(factoryName) == null;
+ }
+
+ /** Caches the binding for future lookups by key. */
+ private void tryToCacheBinding(B binding) {
+ // We only cache resolved bindings or unresolved bindings w/o type arguments.
+ // Unresolved bindings w/ type arguments aren't valid for the object graph.
+ if (binding.unresolved().isPresent()
+ || binding.bindingTypeElement().get().getTypeParameters().isEmpty()) {
+ Key key = binding.key();
+ Binding previousValue = bindingsByKey.put(key, binding);
+ checkState(previousValue == null || binding.equals(previousValue),
+ "couldn't register %s. %s was already registered for %s",
+ binding, previousValue, key);
+ }
+ }
+ }
+
+ private final BindingsCollection<ProvisionBinding> provisionBindings =
+ new BindingsCollection<>(Provider.class);
+ private final BindingsCollection<MembersInjectionBinding> membersInjectionBindings =
+ new BindingsCollection<>(MembersInjector.class);
+
+ @Inject
+ InjectBindingRegistryImpl(
+ DaggerElements elements,
+ DaggerTypes types,
+ Messager messager,
+ InjectValidator injectValidator,
+ KeyFactory keyFactory,
+ BindingFactory bindingFactory,
+ CompilerOptions compilerOptions) {
+ this.elements = elements;
+ this.types = types;
+ this.messager = messager;
+ this.injectValidator = injectValidator;
+ this.injectValidatorWhenGeneratingCode = injectValidator.whenGeneratingCode();
+ this.keyFactory = keyFactory;
+ this.bindingFactory = bindingFactory;
+ this.compilerOptions = compilerOptions;
+ }
+
+ // TODO(dpb): make the SourceFileGenerators fields so they don't have to be passed in
+ @Override
+ public void generateSourcesForRequiredBindings(
+ SourceFileGenerator<ProvisionBinding> factoryGenerator,
+ SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator)
+ throws SourceFileGenerationException {
+ provisionBindings.generateBindings(factoryGenerator);
+ membersInjectionBindings.generateBindings(membersInjectorGenerator);
+ }
+
+ /**
+ * Registers the binding for generation and later lookup. If the binding is resolved, we also
+ * attempt to register an unresolved version of it.
+ */
+ private void registerBinding(ProvisionBinding binding, boolean warnIfNotAlreadyGenerated) {
+ provisionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
+ if (binding.unresolved().isPresent()) {
+ provisionBindings.tryToGenerateBinding(binding.unresolved().get(), warnIfNotAlreadyGenerated);
+ }
+ }
+
+ /**
+ * Registers the binding for generation and later lookup. If the binding is resolved, we also
+ * attempt to register an unresolved version of it.
+ */
+ private void registerBinding(MembersInjectionBinding binding, boolean warnIfNotAlreadyGenerated) {
+ /*
+ * We generate MembersInjector classes for types with @Inject constructors only if they have any
+ * injection sites.
+ *
+ * We generate MembersInjector classes for types without @Inject constructors only if they have
+ * local (non-inherited) injection sites.
+ *
+ * Warn only when registering bindings post-hoc for those types.
+ */
+ warnIfNotAlreadyGenerated =
+ warnIfNotAlreadyGenerated
+ && (!injectedConstructors(binding.membersInjectedType()).isEmpty()
+ ? !binding.injectionSites().isEmpty()
+ : binding.hasLocalInjectionSites());
+ membersInjectionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
+ if (binding.unresolved().isPresent()) {
+ membersInjectionBindings.tryToGenerateBinding(
+ binding.unresolved().get(), warnIfNotAlreadyGenerated);
+ }
+ }
+
+ @Override
+ public Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement) {
+ return tryRegisterConstructor(constructorElement, Optional.empty(), false);
+ }
+
+ @CanIgnoreReturnValue
+ private Optional<ProvisionBinding> tryRegisterConstructor(
+ ExecutableElement constructorElement,
+ Optional<TypeMirror> resolvedType,
+ boolean warnIfNotAlreadyGenerated) {
+ TypeElement typeElement = MoreElements.asType(constructorElement.getEnclosingElement());
+ DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
+ Key key = keyFactory.forInjectConstructorWithResolvedType(type);
+ ProvisionBinding cachedBinding = provisionBindings.getBinding(key);
+ if (cachedBinding != null) {
+ return Optional.of(cachedBinding);
+ }
+
+ ValidationReport<TypeElement> report = injectValidator.validateConstructor(constructorElement);
+ report.printMessagesTo(messager);
+ if (report.isClean()) {
+ ProvisionBinding binding = bindingFactory.injectionBinding(constructorElement, resolvedType);
+ registerBinding(binding, warnIfNotAlreadyGenerated);
+ if (!binding.injectionSites().isEmpty()) {
+ tryRegisterMembersInjectedType(typeElement, resolvedType, warnIfNotAlreadyGenerated);
+ }
+ return Optional.of(binding);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement) {
+ return tryRegisterMembersInjectedType(typeElement, Optional.empty(), false);
+ }
+
+ @CanIgnoreReturnValue
+ private Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(
+ TypeElement typeElement,
+ Optional<TypeMirror> resolvedType,
+ boolean warnIfNotAlreadyGenerated) {
+ DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
+ Key key = keyFactory.forInjectConstructorWithResolvedType(type);
+ MembersInjectionBinding cachedBinding = membersInjectionBindings.getBinding(key);
+ if (cachedBinding != null) {
+ return Optional.of(cachedBinding);
+ }
+
+ ValidationReport<TypeElement> report =
+ injectValidator.validateMembersInjectionType(typeElement);
+ report.printMessagesTo(messager);
+ if (report.isClean()) {
+ MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType);
+ registerBinding(binding, warnIfNotAlreadyGenerated);
+ for (Optional<DeclaredType> supertype = types.nonObjectSuperclass(type);
+ supertype.isPresent();
+ supertype = types.nonObjectSuperclass(supertype.get())) {
+ getOrFindMembersInjectionBinding(keyFactory.forMembersInjectedType(supertype.get()));
+ }
+ return Optional.of(binding);
+ }
+ return Optional.empty();
+ }
+
+ @CanIgnoreReturnValue
+ @Override
+ public Optional<ProvisionBinding> getOrFindProvisionBinding(Key key) {
+ checkNotNull(key);
+ if (!isValidImplicitProvisionKey(key, types)) {
+ return Optional.empty();
+ }
+ ProvisionBinding binding = provisionBindings.getBinding(key);
+ if (binding != null) {
+ return Optional.of(binding);
+ }
+
+ // ok, let's see if we can find an @Inject constructor
+ TypeElement element = MoreElements.asType(types.asElement(key.type()));
+ ImmutableSet<ExecutableElement> injectConstructors = injectedConstructors(element);
+ switch (injectConstructors.size()) {
+ case 0:
+ // No constructor found.
+ return Optional.empty();
+ case 1:
+ return tryRegisterConstructor(
+ Iterables.getOnlyElement(injectConstructors), Optional.of(key.type()), true);
+ default:
+ throw new IllegalStateException("Found multiple @Inject constructors: "
+ + injectConstructors);
+ }
+ }
+
+ @CanIgnoreReturnValue
+ @Override
+ public Optional<MembersInjectionBinding> getOrFindMembersInjectionBinding(Key key) {
+ checkNotNull(key);
+ // TODO(gak): is checking the kind enough?
+ checkArgument(isValidMembersInjectionKey(key));
+ MembersInjectionBinding binding = membersInjectionBindings.getBinding(key);
+ if (binding != null) {
+ return Optional.of(binding);
+ }
+ Optional<MembersInjectionBinding> newBinding =
+ tryRegisterMembersInjectedType(
+ MoreTypes.asTypeElement(key.type()), Optional.of(key.type()), true);
+ return newBinding;
+ }
+
+ @Override
+ public Optional<ProvisionBinding> getOrFindMembersInjectorProvisionBinding(Key key) {
+ if (!isValidMembersInjectionKey(key)) {
+ return Optional.empty();
+ }
+ Key membersInjectionKey = keyFactory.forMembersInjectedType(types.unwrapType(key.type()));
+ return getOrFindMembersInjectionBinding(membersInjectionKey)
+ .map(binding -> bindingFactory.membersInjectorBinding(key, binding));
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectBindingRegistryModule.java b/java/dagger/internal/codegen/InjectBindingRegistryModule.java
new file mode 100644
index 0000000..4563362
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectBindingRegistryModule.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+interface InjectBindingRegistryModule {
+ @Binds
+ InjectBindingRegistry injectBindingRegistry(InjectBindingRegistryImpl impl);
+}
diff --git a/java/dagger/internal/codegen/InjectBindingValidator.java b/java/dagger/internal/codegen/InjectBindingValidator.java
new file mode 100644
index 0000000..183d162
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectBindingValidator.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.model.BindingKind.INJECTION;
+
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.ValidationReport.Item;
+import dagger.model.BindingGraph;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+
+/** Validates bindings from {@code @Inject}-annotated constructors. */
+final class InjectBindingValidator implements BindingGraphPlugin {
+
+ private final InjectValidator injectValidator;
+
+ @Inject
+ InjectBindingValidator(InjectValidator injectValidator) {
+ this.injectValidator = injectValidator.whenGeneratingCode();
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/InjectBinding";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ bindingGraph.bindings().stream()
+ .filter(binding -> binding.kind().equals(INJECTION)) // TODO(dpb): Move to BindingGraph
+ .forEach(binding -> validateInjectionBinding(binding, diagnosticReporter));
+ }
+
+ private void validateInjectionBinding(
+ dagger.model.Binding node, DiagnosticReporter diagnosticReporter) {
+ ValidationReport<TypeElement> typeReport =
+ injectValidator.validateType(MoreTypes.asTypeElement(node.key().type()));
+ for (Item item : typeReport.allItems()) {
+ diagnosticReporter.reportBinding(item.kind(), node, item.message());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectProcessingStep.java b/java/dagger/internal/codegen/InjectProcessingStep.java
new file mode 100644
index 0000000..be8c975
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectProcessingStep.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * An annotation processor for generating Dagger implementation code based on the {@link Inject}
+ * annotation.
+ */
+// TODO(gak): add some error handling for bad source files
+final class InjectProcessingStep extends TypeCheckingProcessingStep<Element> {
+ private final ElementVisitor<Void, Void> visitor;
+
+ @Inject
+ InjectProcessingStep(InjectBindingRegistry injectBindingRegistry) {
+ super(e -> e);
+ this.visitor =
+ new ElementKindVisitor8<Void, Void>() {
+ @Override
+ public Void visitExecutableAsConstructor(
+ ExecutableElement constructorElement, Void aVoid) {
+ injectBindingRegistry.tryRegisterConstructor(constructorElement);
+ return null;
+ }
+
+ @Override
+ public Void visitVariableAsField(VariableElement fieldElement, Void aVoid) {
+ injectBindingRegistry.tryRegisterMembersInjectedType(
+ MoreElements.asType(fieldElement.getEnclosingElement()));
+ return null;
+ }
+
+ @Override
+ public Void visitExecutableAsMethod(ExecutableElement methodElement, Void aVoid) {
+ injectBindingRegistry.tryRegisterMembersInjectedType(
+ MoreElements.asType(methodElement.getEnclosingElement()));
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public Set<Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(Inject.class);
+ }
+
+ @Override
+ protected void process(
+ Element injectElement, ImmutableSet<Class<? extends Annotation>> annotations) {
+ injectElement.accept(visitor, null);
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectValidator.java b/java/dagger/internal/codegen/InjectValidator.java
new file mode 100644
index 0000000..d3c4ce8
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectValidator.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
+import static dagger.internal.codegen.InjectionAnnotations.injectedConstructors;
+import static dagger.internal.codegen.Scopes.scopesOf;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.type.TypeKind.DECLARED;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Scope;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.tools.Diagnostic;
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * A {@linkplain ValidationReport validator} for {@link Inject}-annotated elements and the types
+ * that contain them.
+ */
+final class InjectValidator {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final CompilerOptions compilerOptions;
+ private final DependencyRequestValidator dependencyRequestValidator;
+ private final Optional<Diagnostic.Kind> privateAndStaticInjectionDiagnosticKind;
+
+ @Inject
+ InjectValidator(
+ DaggerTypes types,
+ DaggerElements elements,
+ DependencyRequestValidator dependencyRequestValidator,
+ CompilerOptions compilerOptions) {
+ this(types, elements, compilerOptions, dependencyRequestValidator, Optional.empty());
+ }
+
+ private InjectValidator(
+ DaggerTypes types,
+ DaggerElements elements,
+ CompilerOptions compilerOptions,
+ DependencyRequestValidator dependencyRequestValidator,
+ Optional<Kind> privateAndStaticInjectionDiagnosticKind) {
+ this.types = types;
+ this.elements = elements;
+ this.compilerOptions = compilerOptions;
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ this.privateAndStaticInjectionDiagnosticKind = privateAndStaticInjectionDiagnosticKind;
+ }
+
+ /**
+ * Returns a new validator that performs the same validation as this one, but is strict about
+ * rejecting optionally-specified JSR 330 behavior that Dagger doesn't support (unless {@code
+ * -Adagger.ignorePrivateAndStaticInjectionForComponent=enabled} was set in the javac options).
+ */
+ InjectValidator whenGeneratingCode() {
+ return compilerOptions.ignorePrivateAndStaticInjectionForComponent()
+ ? this
+ : new InjectValidator(
+ types,
+ elements,
+ compilerOptions,
+ dependencyRequestValidator,
+ Optional.of(Diagnostic.Kind.ERROR));
+ }
+
+ ValidationReport<TypeElement> validateConstructor(ExecutableElement constructorElement) {
+ ValidationReport.Builder<TypeElement> builder =
+ ValidationReport.about(MoreElements.asType(constructorElement.getEnclosingElement()));
+ if (constructorElement.getModifiers().contains(PRIVATE)) {
+ builder.addError(
+ "Dagger does not support injection into private constructors", constructorElement);
+ }
+
+ for (AnnotationMirror qualifier : getQualifiers(constructorElement)) {
+ builder.addError(
+ "@Qualifier annotations are not allowed on @Inject constructors",
+ constructorElement,
+ qualifier);
+ }
+
+ for (Scope scope : scopesOf(constructorElement)) {
+ builder.addError(
+ "@Scope annotations are not allowed on @Inject constructors; annotate the class instead",
+ constructorElement,
+ scope.scopeAnnotation());
+ }
+
+ for (VariableElement parameter : constructorElement.getParameters()) {
+ validateDependencyRequest(builder, parameter);
+ }
+
+ if (throwsCheckedExceptions(constructorElement)) {
+ builder.addItem(
+ "Dagger does not support checked exceptions on @Inject constructors",
+ privateMemberDiagnosticKind(),
+ constructorElement);
+ }
+
+ checkInjectIntoPrivateClass(constructorElement, builder);
+
+ TypeElement enclosingElement =
+ MoreElements.asType(constructorElement.getEnclosingElement());
+
+ Set<Modifier> typeModifiers = enclosingElement.getModifiers();
+ if (typeModifiers.contains(ABSTRACT)) {
+ builder.addError(
+ "@Inject is nonsense on the constructor of an abstract class", constructorElement);
+ }
+
+ if (enclosingElement.getNestingKind().isNested()
+ && !typeModifiers.contains(STATIC)) {
+ builder.addError(
+ "@Inject constructors are invalid on inner classes. "
+ + "Did you mean to make the class static?",
+ constructorElement);
+ }
+
+ // This is computationally expensive, but probably preferable to a giant index
+ ImmutableSet<ExecutableElement> injectConstructors = injectedConstructors(enclosingElement);
+
+ if (injectConstructors.size() > 1) {
+ builder.addError("Types may only contain one @Inject constructor", constructorElement);
+ }
+
+ ImmutableSet<Scope> scopes = scopesOf(enclosingElement);
+ if (scopes.size() > 1) {
+ for (Scope scope : scopes) {
+ builder.addError(
+ "A single binding may not declare more than one @Scope",
+ enclosingElement,
+ scope.scopeAnnotation());
+ }
+ }
+
+ return builder.build();
+ }
+
+ private ValidationReport<VariableElement> validateField(VariableElement fieldElement) {
+ ValidationReport.Builder<VariableElement> builder = ValidationReport.about(fieldElement);
+ Set<Modifier> modifiers = fieldElement.getModifiers();
+ if (modifiers.contains(FINAL)) {
+ builder.addError("@Inject fields may not be final", fieldElement);
+ }
+
+ if (modifiers.contains(PRIVATE)) {
+ builder.addItem(
+ "Dagger does not support injection into private fields",
+ privateMemberDiagnosticKind(),
+ fieldElement);
+ }
+
+ if (modifiers.contains(STATIC)) {
+ builder.addItem(
+ "Dagger does not support injection into static fields",
+ staticMemberDiagnosticKind(),
+ fieldElement);
+ }
+
+ validateDependencyRequest(builder, fieldElement);
+
+ return builder.build();
+ }
+
+ private ValidationReport<ExecutableElement> validateMethod(ExecutableElement methodElement) {
+ ValidationReport.Builder<ExecutableElement> builder = ValidationReport.about(methodElement);
+ Set<Modifier> modifiers = methodElement.getModifiers();
+ if (modifiers.contains(ABSTRACT)) {
+ builder.addError("Methods with @Inject may not be abstract", methodElement);
+ }
+
+ if (modifiers.contains(PRIVATE)) {
+ builder.addItem(
+ "Dagger does not support injection into private methods",
+ privateMemberDiagnosticKind(),
+ methodElement);
+ }
+
+ if (modifiers.contains(STATIC)) {
+ builder.addItem(
+ "Dagger does not support injection into static methods",
+ staticMemberDiagnosticKind(),
+ methodElement);
+ }
+
+ if (!methodElement.getTypeParameters().isEmpty()) {
+ builder.addError("Methods with @Inject may not declare type parameters", methodElement);
+ }
+
+ for (VariableElement parameter : methodElement.getParameters()) {
+ validateDependencyRequest(builder, parameter);
+ }
+
+ return builder.build();
+ }
+
+ private void validateDependencyRequest(
+ ValidationReport.Builder<?> builder, VariableElement parameter) {
+ dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.asType());
+ dependencyRequestValidator.checkNotProducer(builder, parameter);
+ }
+
+ ValidationReport<TypeElement> validateMembersInjectionType(TypeElement typeElement) {
+ // TODO(beder): This element might not be currently compiled, so this error message could be
+ // left in limbo. Find an appropriate way to display the error message in that case.
+ ValidationReport.Builder<TypeElement> builder = ValidationReport.about(typeElement);
+ boolean hasInjectedMembers = false;
+ for (VariableElement element : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
+ if (MoreElements.isAnnotationPresent(element, Inject.class)) {
+ hasInjectedMembers = true;
+ ValidationReport<VariableElement> report = validateField(element);
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ }
+ for (ExecutableElement element : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
+ if (MoreElements.isAnnotationPresent(element, Inject.class)) {
+ hasInjectedMembers = true;
+ ValidationReport<ExecutableElement> report = validateMethod(element);
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ }
+
+ if (hasInjectedMembers) {
+ checkInjectIntoPrivateClass(typeElement, builder);
+ }
+ TypeMirror superclass = typeElement.getSuperclass();
+ if (!superclass.getKind().equals(TypeKind.NONE)) {
+ ValidationReport<TypeElement> report = validateType(MoreTypes.asTypeElement(superclass));
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ return builder.build();
+ }
+
+ ValidationReport<TypeElement> validateType(TypeElement typeElement) {
+ ValidationReport.Builder<TypeElement> builder = ValidationReport.about(typeElement);
+ ValidationReport<TypeElement> membersInjectionReport =
+ validateMembersInjectionType(typeElement);
+ if (!membersInjectionReport.isClean()) {
+ builder.addSubreport(membersInjectionReport);
+ }
+ for (ExecutableElement element :
+ ElementFilter.constructorsIn(typeElement.getEnclosedElements())) {
+ if (isAnnotationPresent(element, Inject.class)) {
+ ValidationReport<TypeElement> report = validateConstructor(element);
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ boolean isValidType(TypeMirror type) {
+ if (!type.getKind().equals(DECLARED)) {
+ return true;
+ }
+ return validateType(MoreTypes.asTypeElement(type)).isClean();
+ }
+
+ /** Returns true if the given method element declares a checked exception. */
+ private boolean throwsCheckedExceptions(ExecutableElement methodElement) {
+ TypeMirror runtimeExceptionType = elements.getTypeElement(RuntimeException.class).asType();
+ TypeMirror errorType = elements.getTypeElement(Error.class).asType();
+ for (TypeMirror thrownType : methodElement.getThrownTypes()) {
+ if (!types.isSubtype(thrownType, runtimeExceptionType)
+ && !types.isSubtype(thrownType, errorType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void checkInjectIntoPrivateClass(
+ Element element, ValidationReport.Builder<TypeElement> builder) {
+ if (!Accessibility.isElementAccessibleFromOwnPackage(
+ DaggerElements.closestEnclosingTypeElement(element))) {
+ builder.addItem(
+ "Dagger does not support injection into private classes",
+ privateMemberDiagnosticKind(),
+ element);
+ }
+ }
+
+ private Diagnostic.Kind privateMemberDiagnosticKind() {
+ return privateAndStaticInjectionDiagnosticKind.orElse(
+ compilerOptions.privateMemberValidationKind());
+ }
+
+ private Diagnostic.Kind staticMemberDiagnosticKind() {
+ return privateAndStaticInjectionDiagnosticKind.orElse(
+ compilerOptions.staticMemberValidationKind());
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectionAnnotations.java b/java/dagger/internal/codegen/InjectionAnnotations.java
new file mode 100644
index 0000000..521ad43
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionAnnotations.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.SuperficialValidation;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Utilities relating to annotations defined in the {@code javax.inject} package.
+ */
+final class InjectionAnnotations {
+ static Optional<AnnotationMirror> getQualifier(Element e) {
+ if (!SuperficialValidation.validateElement(e)) {
+ throw new TypeNotPresentException(e.toString(), null);
+ }
+ checkNotNull(e);
+ ImmutableSet<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
+ switch (qualifierAnnotations.size()) {
+ case 0:
+ return Optional.empty();
+ case 1:
+ return Optional.<AnnotationMirror>of(qualifierAnnotations.iterator().next());
+ default:
+ throw new IllegalArgumentException(
+ e + " was annotated with more than one @Qualifier annotation");
+ }
+ }
+
+ static ImmutableSet<? extends AnnotationMirror> getQualifiers(Element element) {
+ return AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
+ }
+
+ /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
+ static ImmutableSet<ExecutableElement> injectedConstructors(TypeElement type) {
+ return FluentIterable.from(constructorsIn(type.getEnclosedElements()))
+ .filter(constructor -> isAnnotationPresent(constructor, Inject.class))
+ .toSet();
+ }
+
+ private InjectionAnnotations() {}
+}
diff --git a/java/dagger/internal/codegen/InjectionMethod.java b/java/dagger/internal/codegen/InjectionMethod.java
new file mode 100644
index 0000000..2785b18
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionMethod.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Parameterizable;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A static method that implements provision and/or injection in one step:
+ *
+ * <ul>
+ * <li>methods that invoke {@code @Inject} constructors and do members injection if necessary
+ * <li>methods that call {@code @Provides} module methods
+ * <li>methods that perform members injection
+ * </ul>
+ *
+ * <p>Note that although this type uses {@code @AutoValue}, it uses instance equality. It uses
+ * {@code @AutoValue} to avoid the boilerplate of writing a correct builder, but is not intended to
+ * actually be a value type.
+ */
+@AutoValue
+abstract class InjectionMethod {
+ abstract String name();
+
+ abstract boolean varargs();
+
+ abstract ImmutableList<TypeVariableName> typeVariables();
+
+ abstract ImmutableMap<ParameterSpec, TypeMirror> parameters();
+
+ abstract Optional<TypeMirror> returnType();
+
+ abstract Optional<DeclaredType> nullableAnnotation();
+
+ abstract ImmutableList<TypeMirror> exceptions();
+
+ abstract CodeBlock methodBody();
+
+ abstract ClassName enclosingClass();
+
+ MethodSpec toMethodSpec() {
+ MethodSpec.Builder builder =
+ methodBuilder(name())
+ .addModifiers(PUBLIC, STATIC)
+ .varargs(varargs())
+ .addTypeVariables(typeVariables())
+ .addParameters(parameters().keySet())
+ .addCode(methodBody());
+ returnType().map(TypeName::get).ifPresent(builder::returns);
+ nullableAnnotation()
+ .ifPresent(nullableType -> CodeBlocks.addAnnotation(builder, nullableType));
+ exceptions().stream().map(TypeName::get).forEach(builder::addException);
+ return builder.build();
+ }
+
+ CodeBlock invoke(List<CodeBlock> arguments, ClassName requestingClass) {
+ checkArgument(arguments.size() == parameters().size());
+ CodeBlock.Builder invocation = CodeBlock.builder();
+ if (!enclosingClass().equals(requestingClass)) {
+ invocation.add("$T.", enclosingClass());
+ }
+ return invocation.add("$L($L)", name(), makeParametersCodeBlock(arguments)).build();
+ }
+
+ @Override
+ public final int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ static Builder builder(DaggerElements elements) {
+ Builder builder = new AutoValue_InjectionMethod.Builder();
+ builder.elements = elements;
+ builder.varargs(false).exceptions(ImmutableList.of()).nullableAnnotation(Optional.empty());
+ return builder;
+ }
+
+ @CanIgnoreReturnValue
+ @AutoValue.Builder
+ abstract static class Builder {
+ private final UniqueNameSet parameterNames = new UniqueNameSet();
+ private final CodeBlock.Builder methodBody = CodeBlock.builder();
+ private DaggerElements elements;
+
+ abstract ImmutableMap.Builder<ParameterSpec, TypeMirror> parametersBuilder();
+ abstract ImmutableList.Builder<TypeVariableName> typeVariablesBuilder();
+ abstract Builder name(String name);
+ abstract Builder varargs(boolean varargs);
+ abstract Builder returnType(TypeMirror returnType);
+ abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions);
+ abstract Builder nullableAnnotation(Optional<DeclaredType> nullableAnnotation);
+ abstract Builder methodBody(CodeBlock methodBody);
+
+ final CodeBlock.Builder methodBodyBuilder() {
+ return methodBody;
+ }
+
+ abstract Builder enclosingClass(ClassName enclosingClass);
+
+ /**
+ * Adds a parameter for the given name and type. If another parameter has already been added
+ * with the same name, the name is disambiguated.
+ */
+ ParameterSpec addParameter(String name, TypeMirror type) {
+ ParameterSpec parameter =
+ ParameterSpec.builder(TypeName.get(type), parameterNames.getUniqueName(name)).build();
+ parametersBuilder().put(parameter, type);
+ return parameter;
+ }
+
+ /**
+ * Calls {@link #copyParameter(VariableElement)} for each parameter of of {@code method}, and
+ * concatenates the results of each call, {@link CodeBlocks#makeParametersCodeBlock(Iterable)
+ * separated with commas}.
+ */
+ CodeBlock copyParameters(ExecutableElement method) {
+ ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder();
+ for (VariableElement parameter : method.getParameters()) {
+ argumentsBuilder.add(copyParameter(parameter));
+ }
+ varargs(method.isVarArgs());
+ return makeParametersCodeBlock(argumentsBuilder.build());
+ }
+
+ /**
+ * Adds {@code parameter} as a parameter of this method, using a publicly accessible version of
+ * the parameter's type. Returns a {@link CodeBlock} of the usage of this parameter within the
+ * injection method's {@link #methodBody()}.
+ */
+ CodeBlock copyParameter(VariableElement parameter) {
+ TypeMirror elementType = parameter.asType();
+ boolean useObject = !isRawTypePubliclyAccessible(elementType);
+ TypeMirror publicType = useObject ? objectType() : elementType;
+ ParameterSpec parameterSpec = addParameter(parameter.getSimpleName().toString(), publicType);
+ return useObject
+ ? CodeBlock.of("($T) $N", elementType, parameterSpec)
+ : CodeBlock.of("$N", parameterSpec);
+ }
+
+ private TypeMirror objectType() {
+ return elements.getTypeElement(Object.class).asType();
+ }
+
+ /**
+ * Adds each type parameter of {@code parameterizable} as a type parameter of this injection
+ * method.
+ */
+ Builder copyTypeParameters(Parameterizable parameterizable) {
+ parameterizable.getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEach(typeVariablesBuilder()::add);
+ return this;
+ }
+
+ Builder copyThrows(ExecutableElement element) {
+ exceptions(element.getThrownTypes());
+ return this;
+ }
+
+ @CheckReturnValue
+ final InjectionMethod build() {
+ return methodBody(methodBody.build()).buildInternal();
+ }
+
+ abstract InjectionMethod buildInternal();
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectionMethods.java b/java/dagger/internal/codegen/InjectionMethods.java
new file mode 100644
index 0000000..d61aaa5
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionMethods.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
+import static dagger.internal.codegen.RequestKinds.requestTypeName;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toConcatenatedCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static java.util.stream.Collectors.toList;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** Convenience methods for creating and invoking {@link InjectionMethod}s. */
+final class InjectionMethods {
+
+ /**
+ * A method that returns an object from a {@code @Provides} method or an {@code @Inject}ed
+ * constructor. Its parameters match the dependency requests for constructor and members
+ * injection.
+ *
+ * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". If the
+ * {@code @Provides} method and its raw parameter types are publicly accessible, no method is
+ * necessary and this method returns {@link Optional#empty()}.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * abstract class FooModule {
+ * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
+ * }
+ *
+ * public static proxyProvideFoo(Bar bar, Baz baz) { … }
+ * </code></pre>
+ *
+ * <p>For {@code @Inject}ed constructors, the method name is "newFoo". If the constructor and its
+ * raw parameter types are publicly accessible, no method is necessary and this method returns
+ * {@code Optional#empty()}.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * class Foo {
+ * {@literal @Inject} Foo(Bar bar) {}
+ * }
+ *
+ * public static Foo newFoo(Bar bar) { … }
+ * </code></pre>
+ */
+ static final class ProvisionMethod {
+ /**
+ * Names of methods that are already defined in factories and shouldn't be used for the proxy
+ * method name.
+ */
+ private static final ImmutableSet<String> BANNED_PROXY_NAMES = ImmutableSet.of("get", "create");
+
+ /**
+ * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement()
+ * constructor} and injects the instance's members.
+ */
+ static InjectionMethod create(
+ ProvisionBinding binding, CompilerOptions compilerOptions, DaggerElements elements) {
+ ClassName proxyEnclosingClass = generatedClassNameForBinding(binding);
+ ExecutableElement element = MoreElements.asExecutable(binding.bindingElement().get());
+ switch (element.getKind()) {
+ case CONSTRUCTOR:
+ return constructorProxy(proxyEnclosingClass, element, elements);
+ case METHOD:
+ return methodProxy(
+ proxyEnclosingClass,
+ element,
+ methodName(element),
+ ReceiverAccessibility.IGNORE,
+ CheckNotNullPolicy.get(binding, compilerOptions),
+ elements);
+ default:
+ throw new AssertionError(element);
+ }
+ }
+
+ /**
+ * Invokes the injection method for {@code binding}, with the dependencies transformed with the
+ * {@code dependencyUsage} function.
+ */
+ // TODO(ronshapiro): Further extract a ProvisionMethod type that composes an InjectionMethod, so
+ // users can write ProvisionMethod.create().invoke()
+ static CodeBlock invoke(
+ ProvisionBinding binding,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ ClassName requestingClass,
+ Optional<CodeBlock> moduleReference,
+ CompilerOptions compilerOptions,
+ DaggerElements elements) {
+ ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+ moduleReference.ifPresent(arguments::add);
+ arguments.addAll(
+ injectionMethodArguments(
+ binding.provisionDependencies(), dependencyUsage, requestingClass));
+ // TODO(ronshapiro): make InjectionMethods @Injectable
+ return create(binding, compilerOptions, elements).invoke(arguments.build(), requestingClass);
+ }
+
+ private static InjectionMethod constructorProxy(
+ ClassName proxyEnclosingClass, ExecutableElement constructor, DaggerElements elements) {
+ TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
+ InjectionMethod.Builder injectionMethod =
+ InjectionMethod.builder(elements)
+ .name(methodName(constructor))
+ .returnType(enclosingType.asType())
+ .enclosingClass(proxyEnclosingClass);
+
+ injectionMethod
+ .copyTypeParameters(enclosingType)
+ .copyThrows(constructor);
+
+ CodeBlock arguments = injectionMethod.copyParameters(constructor);
+ injectionMethod
+ .methodBodyBuilder()
+ .addStatement("return new $T($L)", enclosingType, arguments);
+ return injectionMethod.build();
+ }
+
+ /**
+ * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage}
+ * requires the use of an injection method.
+ */
+ static boolean requiresInjectionMethod(
+ ProvisionBinding binding,
+ ImmutableList<Expression> arguments,
+ CompilerOptions compilerOptions,
+ String callingPackage,
+ DaggerTypes types) {
+ ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
+ return !binding.injectionSites().isEmpty()
+ || binding.shouldCheckForNull(compilerOptions)
+ || !isElementAccessibleFrom(method, callingPackage)
+ || !areParametersAssignable(method, arguments, types)
+ // This check should be removable once we drop support for -source 7
+ || method.getParameters().stream()
+ .map(VariableElement::asType)
+ .anyMatch(type -> !isRawTypeAccessible(type, callingPackage));
+ }
+
+ private static boolean areParametersAssignable(
+ ExecutableElement element, ImmutableList<Expression> arguments, DaggerTypes types) {
+ List<? extends VariableElement> parameters = element.getParameters();
+ checkArgument(parameters.size() == arguments.size());
+ for (int i = 0; i < parameters.size(); i++) {
+ if (!types.isAssignable(arguments.get(i).type(), parameters.get(i).asType())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the name of the {@code static} method that wraps {@code method}. For methods that are
+ * associated with {@code @Inject} constructors, the method will also inject all {@link
+ * InjectionSite}s.
+ */
+ private static String methodName(ExecutableElement method) {
+ switch (method.getKind()) {
+ case CONSTRUCTOR:
+ return "newInstance";
+ case METHOD:
+ String methodName = method.getSimpleName().toString();
+ return BANNED_PROXY_NAMES.contains(methodName)
+ ? "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, methodName)
+ : methodName;
+ default:
+ throw new AssertionError(method);
+ }
+ }
+ }
+
+ /**
+ * A static method that injects one member of an instance of a type. Its first parameter is an
+ * instance of the type to be injected. The remaining parameters match the dependency requests for
+ * the injection site.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * class Foo {
+ * {@literal @Inject} Bar bar;
+ * {@literal @Inject} void setThings(Baz baz, Qux qux) {}
+ * }
+ *
+ * public static injectBar(Foo instance, Bar bar) { … }
+ * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … }
+ * </code></pre>
+ */
+ static final class InjectionSiteMethod {
+ /**
+ * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent
+ * that's in a different package), a method in the supertype's package must be generated to give
+ * the subclass's members injector a way to inject it. Each potentially inaccessible member
+ * receives its own method, as the subclass may need to inject them in a different order from
+ * the parent class.
+ */
+ static InjectionMethod create(InjectionSite injectionSite, DaggerElements elements) {
+ String methodName = methodName(injectionSite);
+ ClassName proxyEnclosingClass = membersInjectorNameForType(
+ MoreElements.asType(injectionSite.element().getEnclosingElement()));
+ switch (injectionSite.kind()) {
+ case METHOD:
+ return methodProxy(
+ proxyEnclosingClass,
+ MoreElements.asExecutable(injectionSite.element()),
+ methodName,
+ ReceiverAccessibility.CAST_IF_NOT_PUBLIC,
+ CheckNotNullPolicy.IGNORE,
+ elements);
+ case FIELD:
+ return fieldProxy(
+ proxyEnclosingClass,
+ MoreElements.asVariable(injectionSite.element()),
+ methodName,
+ elements);
+ }
+ throw new AssertionError(injectionSite);
+ }
+
+ /**
+ * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
+ * transformed using the {@code dependencyUsage} function.
+ *
+ * @param instanceType the type of the {@code instance} parameter
+ */
+ static CodeBlock invokeAll(
+ ImmutableSet<InjectionSite> injectionSites,
+ ClassName generatedTypeName,
+ CodeBlock instanceCodeBlock,
+ TypeMirror instanceType,
+ DaggerTypes types,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ DaggerElements elements) {
+ return injectionSites
+ .stream()
+ .map(
+ injectionSite -> {
+ TypeMirror injectSiteType =
+ types.erasure(injectionSite.element().getEnclosingElement().asType());
+
+ // If instance has been declared as Object because it is not accessible from the
+ // component, but the injectionSite is in a supertype of instanceType that is
+ // publicly accessible, the InjectionSiteMethod will request the actual type and not
+ // Object as the first parameter. If so, cast to the supertype which is accessible
+ // from within generatedTypeName
+ CodeBlock maybeCastedInstance =
+ !types.isSubtype(instanceType, injectSiteType)
+ && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName())
+ ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock)
+ : instanceCodeBlock;
+ return CodeBlock.of(
+ "$L;",
+ invoke(
+ injectionSite,
+ generatedTypeName,
+ maybeCastedInstance,
+ dependencyUsage,
+ elements));
+ })
+ .collect(toConcatenatedCodeBlock());
+ }
+
+ /**
+ * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
+ * using the {@code dependencyUsage} function.
+ */
+ private static CodeBlock invoke(
+ InjectionSite injectionSite,
+ ClassName generatedTypeName,
+ CodeBlock instanceCodeBlock,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ DaggerElements elements) {
+ List<CodeBlock> arguments = new ArrayList<>();
+ arguments.add(instanceCodeBlock);
+ if (!injectionSite.dependencies().isEmpty()) {
+ arguments.addAll(
+ injectionSite
+ .dependencies()
+ .stream()
+ .map(dependencyUsage)
+ .collect(toList()));
+ }
+ return create(injectionSite, elements).invoke(arguments, generatedTypeName);
+ }
+
+ /*
+ * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
+ *
+ * - @Inject void members() {} will generate a method that conflicts with the instance
+ * method `injectMembers(T)`
+ * - Adding the index could conflict with another member:
+ * @Inject void a(Object o) {}
+ * @Inject void a(String s) {}
+ * @Inject void a1(String s) {}
+ *
+ * Here, Method a(String) will add the suffix "1", which will conflict with the method
+ * generated for a1(String)
+ * - Members named "members" or "methods" could also conflict with the {@code static} injection
+ * method.
+ */
+ private static String methodName(InjectionSite injectionSite) {
+ int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
+ String indexString = index == 0 ? "" : String.valueOf(index + 1);
+ return "inject"
+ + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
+ + indexString;
+ }
+ }
+
+ /**
+ * Returns an argument list suitable for calling an injection method. Down-casts any arguments
+ * that are {@code Object} (or {@code Provider<Object>}) at the caller but not the method.
+ *
+ * @param dependencies the dependencies used by the method
+ * @param dependencyUsage function to apply on each of {@code dependencies} before casting
+ * @param requestingClass the class calling the injection method
+ */
+ private static ImmutableList<CodeBlock> injectionMethodArguments(
+ ImmutableSet<DependencyRequest> dependencies,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ ClassName requestingClass) {
+ return dependencies.stream()
+ .map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass))
+ .collect(toImmutableList());
+ }
+
+ private static CodeBlock injectionMethodArgument(
+ DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) {
+ TypeMirror keyType = dependency.key().type();
+ CodeBlock.Builder codeBlock = CodeBlock.builder();
+ if (!isRawTypeAccessible(keyType, generatedTypeName.packageName())
+ && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) {
+ if (!dependency.kind().equals(RequestKind.INSTANCE)) {
+ TypeName usageTypeName = accessibleType(dependency);
+ codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName));
+ } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) {
+ codeBlock.add("($T)", keyType);
+ }
+ }
+ return codeBlock.add(argument).build();
+ }
+
+ /**
+ * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns
+ * {@link Object}.
+ */
+ private static TypeName accessibleType(DependencyRequest dependency) {
+ TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type()));
+ return dependency
+ .requestElement()
+ .map(element -> element.asType().getKind().isPrimitive())
+ .orElse(false)
+ ? typeName.unbox()
+ : typeName;
+ }
+
+ /**
+ * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
+ * Object}.
+ */
+ private static TypeName accessibleType(TypeMirror type) {
+ return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT;
+ }
+
+ /**
+ * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
+ * Object}.
+ */
+ // TODO(ronshapiro): Can we use DaggerTypes.publiclyAccessibleType in place of this method?
+ private static TypeMirror accessibleType(TypeMirror type, DaggerElements elements) {
+ return isRawTypePubliclyAccessible(type)
+ ? type
+ : elements.getTypeElement(Object.class).asType();
+ }
+
+ private enum ReceiverAccessibility {
+ CAST_IF_NOT_PUBLIC {
+ @Override
+ TypeMirror parameterType(TypeMirror type, DaggerElements elements) {
+ return accessibleType(type, elements);
+ }
+
+ @Override
+ CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
+ return instanceWithPotentialCast(instance, instanceType);
+ }
+ },
+ IGNORE {
+ @Override
+ TypeMirror parameterType(TypeMirror type, DaggerElements elements) {
+ return type;
+ }
+
+ @Override
+ CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
+ return instance;
+ }
+ },
+ ;
+
+ abstract TypeMirror parameterType(TypeMirror type, DaggerElements elements);
+ abstract CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType);
+ }
+
+ private static CodeBlock instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType) {
+ return isRawTypePubliclyAccessible(instanceType)
+ ? instance
+ : CodeBlock.of("(($T) $L)", instanceType, instance);
+ }
+
+ private enum CheckNotNullPolicy {
+ IGNORE, CHECK_FOR_NULL;
+ CodeBlock checkForNull(CodeBlock maybeNull) {
+ if (this.equals(IGNORE)) {
+ return maybeNull;
+ }
+ return checkNotNullProvidesMethod(maybeNull);
+ }
+
+ static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) {
+ return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE;
+ }
+ }
+
+ private static InjectionMethod methodProxy(
+ ClassName proxyEnclosingClass,
+ ExecutableElement method,
+ String methodName,
+ ReceiverAccessibility receiverAccessibility,
+ CheckNotNullPolicy checkNotNullPolicy,
+ DaggerElements elements) {
+ TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
+ InjectionMethod.Builder injectionMethod =
+ InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass);
+ ParameterSpec instance = null;
+ if (!method.getModifiers().contains(STATIC)) {
+ instance =
+ injectionMethod.addParameter(
+ "instance", receiverAccessibility.parameterType(enclosingType.asType(), elements));
+ }
+
+ CodeBlock arguments = injectionMethod.copyParameters(method);
+ if (!method.getReturnType().getKind().equals(VOID)) {
+ injectionMethod
+ .returnType(method.getReturnType())
+ .nullableAnnotation(getNullableType(method));
+ injectionMethod.methodBodyBuilder().add("return ");
+ }
+ CodeBlock.Builder proxyInvocation = CodeBlock.builder();
+ if (method.getModifiers().contains(STATIC)) {
+ proxyInvocation.add("$T", rawTypeName(TypeName.get(enclosingType.asType())));
+ } else {
+ injectionMethod.copyTypeParameters(enclosingType);
+ proxyInvocation.add(
+ receiverAccessibility.potentiallyCast(
+ CodeBlock.of("$N", instance), enclosingType.asType()));
+ }
+
+ injectionMethod
+ .copyTypeParameters(method)
+ .copyThrows(method);
+
+ proxyInvocation.add(".$N($L)", method.getSimpleName(), arguments);
+ injectionMethod
+ .methodBodyBuilder()
+ .add(checkNotNullPolicy.checkForNull(proxyInvocation.build()))
+ .add(";\n");
+ return injectionMethod.build();
+ }
+
+ private static InjectionMethod fieldProxy(
+ ClassName proxyEnclosingClass,
+ VariableElement field,
+ String methodName,
+ DaggerElements elements) {
+ TypeElement enclosingType = MoreElements.asType(field.getEnclosingElement());
+ InjectionMethod.Builder injectionMethod =
+ InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass);
+ injectionMethod.copyTypeParameters(enclosingType);
+
+ ParameterSpec instance =
+ injectionMethod.addParameter("instance", accessibleType(enclosingType.asType(), elements));
+ CodeBlock parameter = injectionMethod.copyParameter(field);
+ injectionMethod
+ .methodBodyBuilder()
+ .addStatement(
+ "$L.$L = $L",
+ instanceWithPotentialCast(CodeBlock.of("$N", instance), enclosingType.asType()),
+ field.getSimpleName(),
+ parameter);
+ return injectionMethod.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java b/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java
new file mode 100644
index 0000000..2d2e8cb
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.model.BindingKind.INJECTION;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import javax.inject.Provider;
+
+/**
+ * A {@link Provider} creation expression for an {@link javax.inject.Inject @Inject}-constructed
+ * class or a {@link dagger.Provides @Provides}-annotated module method.
+ */
+// TODO(dpb): Resolve with ProducerCreationExpression.
+final class InjectionOrProvisionProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ContributionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ InjectionOrProvisionProviderCreationExpression(
+ ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ CodeBlock createFactory =
+ CodeBlock.of(
+ "$T.create($L)",
+ generatedClassNameForBinding(binding),
+ componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+
+ // When scoping a parameterized factory for an @Inject class, Java 7 cannot always infer the
+ // type properly, so cast to a raw framework type before scoping.
+ if (binding.kind().equals(INJECTION)
+ && binding.unresolved().isPresent()
+ && binding.scope().isPresent()) {
+ return CodeBlocks.cast(createFactory, Provider.class);
+ } else {
+ return createFactory;
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/InjectionSiteFactory.java b/java/dagger/internal/codegen/InjectionSiteFactory.java
new file mode 100644
index 0000000..d09f91d
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionSiteFactory.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.MembersInjectionBinding.InjectionSite.Kind.METHOD;
+import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/** A factory for {@link Binding} objects. */
+final class InjectionSiteFactory {
+ private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor =
+ new ElementKindVisitor8<Optional<InjectionSite>, DeclaredType>(Optional.empty()) {
+ @Override
+ public Optional<InjectionSite> visitExecutableAsMethod(
+ ExecutableElement method, DeclaredType type) {
+ ExecutableType resolved = MoreTypes.asExecutable(types.asMemberOf(type, method));
+ return Optional.of(
+ InjectionSite.method(
+ method,
+ dependencyRequestFactory.forRequiredResolvedVariables(
+ method.getParameters(), resolved.getParameterTypes())));
+ }
+
+ @Override
+ public Optional<InjectionSite> visitVariableAsField(
+ VariableElement field, DeclaredType type) {
+ if (!isAnnotationPresent(field, Inject.class)
+ || field.getModifiers().contains(PRIVATE)
+ || field.getModifiers().contains(STATIC)) {
+ return Optional.empty();
+ }
+ TypeMirror resolved = types.asMemberOf(type, field);
+ return Optional.of(
+ InjectionSite.field(
+ field, dependencyRequestFactory.forRequiredResolvedVariable(field, resolved)));
+ }
+ };
+
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final DependencyRequestFactory dependencyRequestFactory;
+
+ @Inject
+ InjectionSiteFactory(
+ DaggerTypes types,
+ DaggerElements elements,
+ DependencyRequestFactory dependencyRequestFactory) {
+ this.types = types;
+ this.elements = elements;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ }
+
+ /** Returns the injection sites for a type. */
+ ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
+ Set<InjectionSite> injectionSites = new HashSet<>();
+ List<TypeElement> ancestors = new ArrayList<>();
+ SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create();
+ for (Optional<DeclaredType> currentType = Optional.of(declaredType);
+ currentType.isPresent();
+ currentType = types.nonObjectSuperclass(currentType.get())) {
+ DeclaredType type = currentType.get();
+ ancestors.add(MoreElements.asType(type.asElement()));
+ for (Element enclosedElement : type.asElement().getEnclosedElements()) {
+ Optional<InjectionSite> maybeInjectionSite =
+ injectionSiteVisitor.visit(enclosedElement, type);
+ if (maybeInjectionSite.isPresent()) {
+ InjectionSite injectionSite = maybeInjectionSite.get();
+ if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) {
+ injectionSites.add(injectionSite);
+ }
+ if (injectionSite.kind().equals(METHOD)) {
+ ExecutableElement injectionSiteMethod =
+ MoreElements.asExecutable(injectionSite.element());
+ overriddenMethodMap.put(
+ injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod);
+ }
+ }
+ }
+ }
+ return ImmutableSortedSet.copyOf(
+ // supertypes before subtypes
+ Comparator.comparing(
+ (InjectionSite injectionSite) ->
+ ancestors.indexOf(injectionSite.element().getEnclosingElement()))
+ .reversed()
+ // fields before methods
+ .thenComparing(injectionSite -> injectionSite.element().getKind())
+ // then sort by whichever element comes first in the parent
+ // this isn't necessary, but makes the processor nice and predictable
+ .thenComparing(InjectionSite::element, DECLARATION_ORDER),
+ injectionSites);
+ }
+
+ private boolean shouldBeInjected(
+ Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) {
+ if (!isAnnotationPresent(injectionSite, Inject.class)
+ || injectionSite.getModifiers().contains(PRIVATE)
+ || injectionSite.getModifiers().contains(STATIC)) {
+ return false;
+ }
+
+ if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors)
+ return true;
+ }
+
+ // For each method with the same name belonging to any descendant class, return false if any
+ // method has already overridden the injectionSite method. To decrease the number of methods
+ // that are checked, we store the already injected methods in a SetMultimap and only
+ // check the methods with the same name.
+ ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite);
+ TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement());
+ for (ExecutableElement method :
+ overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) {
+ if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/dagger/internal/codegen/InnerSwitchingProviders.java b/java/dagger/internal/codegen/InnerSwitchingProviders.java
new file mode 100644
index 0000000..74c4366
--- /dev/null
+++ b/java/dagger/internal/codegen/InnerSwitchingProviders.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.model.RequestKind.INSTANCE;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Generates {@linkplain BindingExpression binding expressions} for a binding that is represented by
+ * an inner {@code SwitchingProvider} class.
+ */
+final class InnerSwitchingProviders extends SwitchingProviders {
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+
+ InnerSwitchingProviders(
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types) {
+ super(componentImplementation, types);
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ }
+
+ /**
+ * Returns the binding expression for a binding that satisfies a {@link Provider} requests with a
+ * inner {@code SwitchingProvider} class.
+ */
+ BindingExpression newBindingExpression(ContributionBinding binding) {
+ return new BindingExpression() {
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return getProviderExpression(new SwitchCase(binding, requestingClass));
+ }
+ };
+ }
+
+ @Override
+ protected TypeSpec createSwitchingProviderType(TypeSpec.Builder builder) {
+ return builder
+ .addModifiers(PRIVATE, FINAL)
+ .addField(TypeName.INT, "id", PRIVATE, FINAL)
+ .addMethod(
+ constructorBuilder()
+ .addParameter(TypeName.INT, "id")
+ .addStatement("this.id = id")
+ .build())
+ .build();
+ }
+
+ private final class SwitchCase implements SwitchingProviders.SwitchCase {
+ private final ContributionBinding binding;
+ private final ClassName requestingClass;
+
+ SwitchCase(ContributionBinding binding, ClassName requestingClass) {
+ this.binding = binding;
+ this.requestingClass = requestingClass;
+ }
+
+ @Override
+ public Key key() {
+ return binding.key();
+ }
+
+ @Override
+ public Expression getProviderExpression(ClassName switchingProviderClass, int switchId) {
+ TypeMirror instanceType = types.accessibleType(binding.contributedType(), requestingClass);
+ return Expression.create(
+ types.wrapType(instanceType, Provider.class),
+ CodeBlock.of("new $T<>($L)", switchingProviderClass, switchId));
+ }
+
+ @Override
+ public Expression getReturnExpression(ClassName switchingProviderClass) {
+ return componentBindingExpressions.getDependencyExpression(
+ bindingRequest(binding.key(), INSTANCE), switchingProviderClass);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/InstanceFactoryCreationExpression.java b/java/dagger/internal/codegen/InstanceFactoryCreationExpression.java
new file mode 100644
index 0000000..c9b26d7
--- /dev/null
+++ b/java/dagger/internal/codegen/InstanceFactoryCreationExpression.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.InstanceFactory;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import java.util.function.Supplier;
+
+/**
+ * A {@link FrameworkInstanceCreationExpression} that creates an {@link InstanceFactory} for an
+ * instance.
+ */
+final class InstanceFactoryCreationExpression implements FrameworkInstanceCreationExpression {
+
+ private final boolean nullable;
+ private final Supplier<CodeBlock> instanceExpression;
+
+ InstanceFactoryCreationExpression(Supplier<CodeBlock> instanceExpression) {
+ this(false, instanceExpression);
+ }
+
+ InstanceFactoryCreationExpression(boolean nullable, Supplier<CodeBlock> instanceExpression) {
+ this.nullable = nullable;
+ this.instanceExpression = checkNotNull(instanceExpression);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return CodeBlock.of(
+ "$T.$L($L)",
+ InstanceFactory.class,
+ nullable ? "createNullable" : "create",
+ instanceExpression.get());
+ }
+
+ @Override
+ public boolean useInnerSwitchingProvider() {
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/JavacPluginModule.java b/java/dagger/internal/codegen/JavacPluginModule.java
new file mode 100644
index 0000000..bc5b66e
--- /dev/null
+++ b/java/dagger/internal/codegen/JavacPluginModule.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.ValidationType.NONE;
+import static javax.tools.Diagnostic.Kind.NOTE;
+
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.model.JavacTypes;
+import com.sun.tools.javac.util.Context;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+
+/**
+ * A module that provides a {@link BindingGraphFactory} and {@link ComponentDescriptorFactory} for
+ * use in {@code javac} plugins. Requires a binding for the {@code javac} {@link Context}.
+ */
+@Module(includes = InjectBindingRegistryModule.class)
+abstract class JavacPluginModule {
+ @Provides
+ static CompilerOptions compilerOptions() {
+ return new CompilerOptions() {
+ @Override
+ boolean usesProducers() {
+ return true;
+ }
+
+ @Override
+ boolean fastInit() {
+ return false;
+ }
+
+ @Override
+ boolean formatGeneratedSource() {
+ return false;
+ }
+
+ @Override
+ boolean writeProducerNameInToken() {
+ return true;
+ }
+
+ @Override
+ Diagnostic.Kind nullableValidationKind() {
+ return NOTE;
+ }
+
+ @Override
+ Diagnostic.Kind privateMemberValidationKind() {
+ return NOTE;
+ }
+
+ @Override
+ Diagnostic.Kind staticMemberValidationKind() {
+ return NOTE;
+ }
+
+ @Override
+ boolean ignorePrivateAndStaticInjectionForComponent() {
+ return false;
+ }
+
+ @Override
+ ValidationType scopeCycleValidationType() {
+ return NONE;
+ }
+
+ @Override
+ boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+ return false;
+ }
+
+ @Override
+ boolean headerCompilation() {
+ return false;
+ }
+
+ @Override
+ boolean aheadOfTimeSubcomponents() {
+ return false;
+ }
+
+ @Override
+ boolean forceUseSerializedComponentImplementations() {
+ return false;
+ }
+
+ @Override
+ boolean emitModifiableMetadataAnnotations() {
+ return false;
+ }
+
+ @Override
+ boolean useGradleIncrementalProcessing() {
+ return false;
+ }
+
+ @Override
+ ValidationType fullBindingGraphValidationType(TypeElement element) {
+ return NONE;
+ }
+
+ @Override
+ Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+ return NOTE;
+ }
+
+ @Override
+ ValidationType explicitBindingConflictsWithInjectValidationType() {
+ return NONE;
+ }
+ };
+ }
+
+ @Binds
+ abstract Messager messager(NullMessager nullMessager);
+
+ static final class NullMessager implements Messager {
+
+ @Inject
+ NullMessager() {}
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence charSequence) {}
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence charSequence, Element element) {}
+
+ @Override
+ public void printMessage(
+ Diagnostic.Kind kind,
+ CharSequence charSequence,
+ Element element,
+ AnnotationMirror annotationMirror) {}
+
+ @Override
+ public void printMessage(
+ Diagnostic.Kind kind,
+ CharSequence charSequence,
+ Element element,
+ AnnotationMirror annotationMirror,
+ AnnotationValue annotationValue) {}
+ }
+
+ @Provides
+ static DaggerElements daggerElements(Context javaContext) {
+ return new DaggerElements(
+ JavacElements.instance(javaContext), JavacTypes.instance(javaContext));
+ }
+
+ @Provides
+ static DaggerTypes daggerTypes(Context javaContext, DaggerElements elements) {
+ return new DaggerTypes(JavacTypes.instance(javaContext), elements);
+ }
+
+ @Binds abstract Types types(DaggerTypes daggerTypes);
+
+ private JavacPluginModule() {}
+}
diff --git a/java/dagger/internal/codegen/KeyFactory.java b/java/dagger/internal/codegen/KeyFactory.java
new file mode 100644
index 0000000..b6ac27f
--- /dev/null
+++ b/java/dagger/internal/codegen/KeyFactory.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.auto.common.MoreTypes.isType;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
+import static dagger.internal.codegen.MapKeys.getMapKey;
+import static dagger.internal.codegen.MapKeys.mapKeyType;
+import static dagger.internal.codegen.Optionals.firstPresent;
+import static dagger.internal.codegen.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.RequestKinds.getRequestKind;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
+import static java.util.Arrays.asList;
+import static javax.lang.model.element.ElementKind.METHOD;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.KeyProto;
+import dagger.model.Key;
+import dagger.model.Key.MultibindingContributionIdentifier;
+import dagger.model.RequestKind;
+import dagger.multibindings.Multibinds;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.Production;
+import dagger.producers.internal.ProductionImplementation;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory for {@link Key}s. */
+final class KeyFactory {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final TypeProtoConverter typeProtoConverter;
+ private final AnnotationProtoConverter annotationProtoConverter;
+
+ @Inject
+ KeyFactory(
+ DaggerTypes types,
+ DaggerElements elements,
+ TypeProtoConverter typeProtoConverter,
+ AnnotationProtoConverter annotationProtoConverter) {
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ this.typeProtoConverter = typeProtoConverter;
+ this.annotationProtoConverter = annotationProtoConverter;
+ }
+
+ private TypeMirror boxPrimitives(TypeMirror type) {
+ return type.getKind().isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type;
+ }
+
+ private DeclaredType setOf(TypeMirror elementType) {
+ return types.getDeclaredType(elements.getTypeElement(Set.class), boxPrimitives(elementType));
+ }
+
+ private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) {
+ return types.getDeclaredType(
+ elements.getTypeElement(Map.class), boxPrimitives(keyType), boxPrimitives(valueType));
+ }
+
+ /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
+ private TypeMirror mapOfFrameworkType(
+ TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) {
+ return mapOf(keyType, types.getDeclaredType(frameworkType, boxPrimitives(valueType)));
+ }
+
+ Key forComponentMethod(ExecutableElement componentMethod) {
+ checkArgument(componentMethod.getKind().equals(METHOD));
+ return forMethod(componentMethod, componentMethod.getReturnType());
+ }
+
+ Key forProductionComponentMethod(ExecutableElement componentMethod) {
+ checkArgument(componentMethod.getKind().equals(METHOD));
+ TypeMirror returnType = componentMethod.getReturnType();
+ TypeMirror keyType =
+ isFutureType(returnType)
+ ? getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments())
+ : returnType;
+ return forMethod(componentMethod, keyType);
+ }
+
+ Key forSubcomponentCreatorMethod(
+ ExecutableElement subcomponentCreatorMethod, DeclaredType declaredContainer) {
+ checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD));
+ ExecutableType resolvedMethod =
+ asExecutable(types.asMemberOf(declaredContainer, subcomponentCreatorMethod));
+ return Key.builder(resolvedMethod.getReturnType()).build();
+ }
+
+ Key forSubcomponentCreator(TypeMirror creatorType) {
+ return Key.builder(creatorType).build();
+ }
+
+ Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) {
+ return forBindingMethod(
+ method, contributingModule, Optional.of(elements.getTypeElement(Provider.class)));
+ }
+
+ Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) {
+ return forBindingMethod(
+ method, contributingModule, Optional.of(elements.getTypeElement(Producer.class)));
+ }
+
+ /** Returns the key bound by a {@link Binds} method. */
+ Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) {
+ checkArgument(isAnnotationPresent(method, Binds.class));
+ return forBindingMethod(method, contributingModule, Optional.empty());
+ }
+
+ /** Returns the base key bound by a {@link BindsOptionalOf} method. */
+ Key forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule) {
+ checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
+ return forBindingMethod(method, contributingModule, Optional.empty());
+ }
+
+ private Key forBindingMethod(
+ ExecutableElement method,
+ TypeElement contributingModule,
+ Optional<TypeElement> frameworkType) {
+ checkArgument(method.getKind().equals(METHOD));
+ ExecutableType methodType =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method));
+ ContributionType contributionType = ContributionType.fromBindingElement(method);
+ TypeMirror returnType = methodType.getReturnType();
+ if (frameworkType.isPresent()
+ && frameworkType.get().equals(elements.getTypeElement(Producer.class))
+ && isType(returnType)) {
+ if (isFutureType(methodType.getReturnType())) {
+ returnType = getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
+ } else if (contributionType.equals(ContributionType.SET_VALUES)
+ && SetType.isSet(returnType)) {
+ SetType setType = SetType.from(returnType);
+ if (isFutureType(setType.elementType())) {
+ returnType =
+ types.getDeclaredType(
+ elements.getTypeElement(Set.class), types.unwrapType(setType.elementType()));
+ }
+ }
+ }
+ TypeMirror keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkType);
+ Key key = forMethod(method, keyType);
+ return contributionType.equals(ContributionType.UNIQUE)
+ ? key
+ : key.toBuilder()
+ .multibindingContributionIdentifier(
+ new MultibindingContributionIdentifier(method, contributingModule))
+ .build();
+ }
+
+ /**
+ * Returns the key for a {@link Multibinds @Multibinds} method.
+ *
+ * <p>The key's type is either {@code Set<T>} or {@code Map<K, Provider<V>>}. The latter works
+ * even for maps used by {@code Producer}s.
+ */
+ Key forMultibindsMethod(ExecutableType executableType, ExecutableElement method) {
+ checkArgument(method.getKind().equals(METHOD), "%s must be a method", method);
+ TypeMirror returnType = executableType.getReturnType();
+ TypeMirror keyType =
+ MapType.isMap(returnType)
+ ? mapOfFrameworkType(
+ MapType.from(returnType).keyType(),
+ elements.getTypeElement(Provider.class),
+ MapType.from(returnType).valueType())
+ : returnType;
+ return forMethod(method, keyType);
+ }
+
+ private TypeMirror bindingMethodKeyType(
+ TypeMirror returnType,
+ ExecutableElement method,
+ ContributionType contributionType,
+ Optional<TypeElement> frameworkType) {
+ switch (contributionType) {
+ case UNIQUE:
+ return returnType;
+ case SET:
+ return setOf(returnType);
+ case MAP:
+ TypeMirror mapKeyType = mapKeyType(getMapKey(method).get(), types);
+ return frameworkType.isPresent()
+ ? mapOfFrameworkType(mapKeyType, frameworkType.get(), returnType)
+ : mapOf(mapKeyType, returnType);
+ case SET_VALUES:
+ // TODO(gak): do we want to allow people to use "covariant return" here?
+ checkArgument(SetType.isSet(returnType));
+ return returnType;
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns the key for a binding associated with a {@link DelegateDeclaration}.
+ *
+ * <p>If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key
+ * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code
+ * delegateDeclaration} is not a map contribution, its key is returned.
+ */
+ Key forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> frameworkType) {
+ return delegateDeclaration.contributionType().equals(ContributionType.MAP)
+ ? wrapMapValue(delegateDeclaration.key(), frameworkType)
+ : delegateDeclaration.key();
+ }
+
+ private Key forMethod(ExecutableElement method, TypeMirror keyType) {
+ return forQualifiedType(getQualifier(method), keyType);
+ }
+
+ Key forInjectConstructorWithResolvedType(TypeMirror type) {
+ return Key.builder(type).build();
+ }
+
+ // TODO(ronshapiro): Remove these conveniences which are simple wrappers around Key.Builder
+ Key forType(TypeMirror type) {
+ return Key.builder(type).build();
+ }
+
+ Key forMembersInjectedType(TypeMirror type) {
+ return Key.builder(type).build();
+ }
+
+ Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) {
+ return Key.builder(boxPrimitives(type)).qualifier(qualifier).build();
+ }
+
+ Key forProductionExecutor() {
+ return Key.builder(elements.getTypeElement(Executor.class).asType())
+ .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(Production.class)))
+ .build();
+ }
+
+ Key forProductionImplementationExecutor() {
+ return Key.builder(elements.getTypeElement(Executor.class).asType())
+ .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(ProductionImplementation.class)))
+ .build();
+ }
+
+ Key forProductionComponentMonitor() {
+ return Key.builder(elements.getTypeElement(ProductionComponentMonitor.class).asType()).build();
+ }
+
+ /**
+ * If {@code requestKey} is for a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns keys
+ * for {@code Map<K, Provider<V>>} and {@code Map<K, Producer<V>>} (if Dagger-Producers is on
+ * the classpath).
+ */
+ ImmutableSet<Key> implicitFrameworkMapKeys(Key requestKey) {
+ return Stream.of(implicitMapProviderKeyFrom(requestKey), implicitMapProducerKeyFrom(requestKey))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Optionally extract a {@link Key} for the underlying provision binding(s) if such a valid key
+ * can be inferred from the given key. Specifically, if the key represents a {@link Map}{@code
+ * <K, V>} or {@code Map<K, Producer<V>>}, a key of {@code Map<K, Provider<V>>} will be
+ * returned.
+ */
+ Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
+ return firstPresent(
+ rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
+ wrapMapKey(possibleMapKey, Provider.class));
+ }
+
+ /**
+ * Optionally extract a {@link Key} for the underlying production binding(s) if such a
+ * valid key can be inferred from the given key. Specifically, if the key represents a
+ * {@link Map}{@code <K, V>} or {@code Map<K, Produced<V>>}, a key of
+ * {@code Map<K, Producer<V>>} will be returned.
+ */
+ Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) {
+ return firstPresent(
+ rewrapMapKey(possibleMapKey, Produced.class, Producer.class),
+ wrapMapKey(possibleMapKey, Producer.class));
+ }
+
+ /**
+ * If {@code key}'s type is {@code Map<K, Provider<V>>}, {@code Map<K, Producer<V>>}, or {@code
+ * Map<K, Produced<V>>}, returns a key with the same qualifier and {@link
+ * Key#multibindingContributionIdentifier()} whose type is simply {@code Map<K, V>}.
+ *
+ * <p>Otherwise, returns {@code key}.
+ */
+ Key unwrapMapValueType(Key key) {
+ if (MapType.isMap(key)) {
+ MapType mapType = MapType.from(key);
+ if (!mapType.isRawType()) {
+ for (Class<?> frameworkClass : asList(Provider.class, Producer.class, Produced.class)) {
+ if (mapType.valuesAreTypeOf(frameworkClass)) {
+ return key.toBuilder()
+ .type(mapOf(mapType.keyType(), mapType.unwrappedValueType(frameworkClass)))
+ .build();
+ }
+ }
+ }
+ }
+ return key;
+ }
+
+ /**
+ * Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}.
+ */
+ private Key wrapMapValue(Key key, Class<?> newWrappingClass) {
+ checkArgument(
+ FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClass).asType()));
+ return wrapMapKey(key, newWrappingClass).get();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Map<K, CurrentWrappingClass<Bar>>}, returns a key with type
+ * {@code Map<K, NewWrappingClass<Bar>>} with the same qualifier. Otherwise returns {@link
+ * Optional#empty()}.
+ *
+ * <p>Returns {@link Optional#empty()} if {@code newWrappingClass} is not in the classpath.
+ *
+ * @throws IllegalArgumentException if {@code newWrappingClass} is the same as {@code
+ * currentWrappingClass}
+ */
+ Optional<Key> rewrapMapKey(
+ Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) {
+ checkArgument(!currentWrappingClass.equals(newWrappingClass));
+ if (MapType.isMap(possibleMapKey)) {
+ MapType mapType = MapType.from(possibleMapKey);
+ if (!mapType.isRawType() && mapType.valuesAreTypeOf(currentWrappingClass)) {
+ TypeElement wrappingElement = elements.getTypeElement(newWrappingClass);
+ if (wrappingElement == null) {
+ // This target might not be compiled with Producers, so wrappingClass might not have an
+ // associated element.
+ return Optional.empty();
+ }
+ DeclaredType wrappedValueType =
+ types.getDeclaredType(
+ wrappingElement, mapType.unwrappedValueType(currentWrappingClass));
+ return Optional.of(
+ possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Map<K, Foo>} and {@code Foo} is not {@code WrappingClass
+ * <Bar>}, returns a key with type {@code Map<K, WrappingClass<Foo>>} with the same qualifier.
+ * Otherwise returns {@link Optional#empty()}.
+ *
+ * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath.
+ */
+ private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) {
+ if (MapType.isMap(possibleMapKey)) {
+ MapType mapType = MapType.from(possibleMapKey);
+ if (!mapType.isRawType() && !mapType.valuesAreTypeOf(wrappingClass)) {
+ TypeElement wrappingElement = elements.getTypeElement(wrappingClass);
+ if (wrappingElement == null) {
+ // This target might not be compiled with Producers, so wrappingClass might not have an
+ // associated element.
+ return Optional.empty();
+ }
+ DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement, mapType.valueType());
+ return Optional.of(
+ possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set
+ * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}.
+ */
+ Optional<Key> unwrapSetKey(Key key, Class<?> wrappingClass) {
+ if (SetType.isSet(key)) {
+ SetType setType = SetType.from(key);
+ if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClass)) {
+ return Optional.of(
+ key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same
+ * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, TypeMirror)}
+ * extracted} from {@code T}.
+ */
+ Optional<Key> unwrapOptional(Key key) {
+ if (!OptionalType.isOptional(key)) {
+ return Optional.empty();
+ }
+
+ TypeMirror optionalValueType = OptionalType.from(key).valueType();
+ return Optional.of(
+ key.toBuilder()
+ .type(extractKeyType(getRequestKind(optionalValueType), optionalValueType))
+ .build());
+ }
+
+ /** Translates a {@link Key} to a proto representation. */
+ static KeyProto toProto(Key key) {
+ KeyProto.Builder builder =
+ KeyProto.newBuilder().setType(TypeProtoConverter.toProto(key.type()));
+ key.qualifier().map(AnnotationProtoConverter::toProto).ifPresent(builder::setQualifier);
+ key.multibindingContributionIdentifier()
+ .ifPresent(
+ mci ->
+ builder
+ .getMultibindingContributionIdentifierBuilder()
+ .setModule(mci.module())
+ .setBindingElement(mci.bindingElement()));
+ return builder.build();
+ }
+
+ /** Creates a {@link Key} from its proto representation. */
+ Key fromProto(KeyProto key) {
+ Key.Builder builder = Key.builder(typeProtoConverter.fromProto(key.getType()));
+ if (key.hasQualifier()) {
+ builder.qualifier(annotationProtoConverter.fromProto(key.getQualifier()));
+ }
+ if (key.hasMultibindingContributionIdentifier()) {
+ KeyProto.MultibindingContributionIdentifier multibindingContributionIdentifier =
+ key.getMultibindingContributionIdentifier();
+ builder.multibindingContributionIdentifier(
+ new MultibindingContributionIdentifier(
+ multibindingContributionIdentifier.getBindingElement(),
+ multibindingContributionIdentifier.getModule()));
+ }
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/KeyVariableNamer.java b/java/dagger/internal/codegen/KeyVariableNamer.java
new file mode 100644
index 0000000..407f208
--- /dev/null
+++ b/java/dagger/internal/codegen/KeyVariableNamer.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static dagger.internal.codegen.SourceFiles.protectAgainstKeywords;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.Iterator;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Suggests a variable name for a type based on a {@link Key}. Prefer {@link
+ * DependencyVariableNamer} for cases where a specific {@link DependencyRequest} is present.
+ */
+final class KeyVariableNamer {
+ /** Simple names that are very common. Inspired by https://errorprone.info/bugpattern/BadImport */
+ private static final ImmutableSet<String> VERY_SIMPLE_NAMES =
+ ImmutableSet.of(
+ "Builder",
+ "Factory",
+ "Component",
+ "Subcomponent",
+ "Injector");
+
+ private static final TypeVisitor<Void, StringBuilder> TYPE_NAMER =
+ new SimpleTypeVisitor8<Void, StringBuilder>() {
+ @Override
+ public Void visitDeclared(DeclaredType declaredType, StringBuilder builder) {
+ TypeElement element = MoreTypes.asTypeElement(declaredType);
+ if (element.getNestingKind().isNested()
+ && VERY_SIMPLE_NAMES.contains(element.getSimpleName().toString())) {
+ builder.append(element.getEnclosingElement().getSimpleName());
+ }
+
+ builder.append(element.getSimpleName());
+ Iterator<? extends TypeMirror> argumentIterator =
+ declaredType.getTypeArguments().iterator();
+ if (argumentIterator.hasNext()) {
+ builder.append("Of");
+ TypeMirror first = argumentIterator.next();
+ first.accept(this, builder);
+ while (argumentIterator.hasNext()) {
+ builder.append("And");
+ argumentIterator.next().accept(this, builder);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitPrimitive(PrimitiveType type, StringBuilder builder) {
+ builder.append(LOWER_CAMEL.to(UPPER_CAMEL, type.toString()));
+ return null;
+ }
+
+ @Override
+ public Void visitArray(ArrayType type, StringBuilder builder) {
+ type.getComponentType().accept(this, builder);
+ builder.append("Array");
+ return null;
+ }
+ };
+
+ private KeyVariableNamer() {}
+
+ static String name(Key key) {
+ if (key.multibindingContributionIdentifier().isPresent()) {
+ return key.multibindingContributionIdentifier().get().bindingElement();
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ if (key.qualifier().isPresent()) {
+ // TODO(gak): Use a better name for fields with qualifiers with members.
+ builder.append(key.qualifier().get().getAnnotationType().asElement().getSimpleName());
+ }
+
+ key.type().accept(TYPE_NAMER, builder);
+
+ return protectAgainstKeywords(UPPER_CAMEL.to(LOWER_CAMEL, builder.toString()));
+ }
+}
diff --git a/java/dagger/internal/codegen/Keys.java b/java/dagger/internal/codegen/Keys.java
new file mode 100644
index 0000000..670fda7
--- /dev/null
+++ b/java/dagger/internal/codegen/Keys.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/** Utility methods related to {@link Key}s. */
+final class Keys {
+ static boolean isValidMembersInjectionKey(Key key) {
+ return !key.qualifier().isPresent()
+ && !key.multibindingContributionIdentifier().isPresent()
+ && key.type().getKind().equals(TypeKind.DECLARED);
+ }
+
+ /**
+ * Returns {@code true} if this is valid as an implicit key (that is, if it's valid for a
+ * just-in-time binding by discovering an {@code @Inject} constructor).
+ */
+ static boolean isValidImplicitProvisionKey(Key key, DaggerTypes types) {
+ return isValidImplicitProvisionKey(key.qualifier(), key.type(), types);
+ }
+
+ /**
+ * Returns {@code true} if a key with {@code qualifier} and {@code type} is valid as an implicit
+ * key (that is, if it's valid for a just-in-time binding by discovering an {@code @Inject}
+ * constructor).
+ */
+ static boolean isValidImplicitProvisionKey(
+ Optional<? extends AnnotationMirror> qualifier, TypeMirror type, final DaggerTypes types) {
+ // Qualifiers disqualify implicit provisioning.
+ if (qualifier.isPresent()) {
+ return false;
+ }
+
+ return type.accept(
+ new SimpleTypeVisitor6<Boolean, Void>(false) {
+ @Override
+ public Boolean visitDeclared(DeclaredType type, Void ignored) {
+ // Non-classes or abstract classes aren't allowed.
+ TypeElement element = MoreElements.asType(type.asElement());
+ if (!element.getKind().equals(ElementKind.CLASS)
+ || element.getModifiers().contains(Modifier.ABSTRACT)) {
+ return false;
+ }
+
+ // If the key has type arguments, validate that each type argument is declared.
+ // Otherwise the type argument may be a wildcard (or other type), and we can't
+ // resolve that to actual types.
+ for (TypeMirror arg : type.getTypeArguments()) {
+ if (arg.getKind() != TypeKind.DECLARED) {
+ return false;
+ }
+ }
+
+ // Also validate that the key is not the erasure of a generic type.
+ // If it is, that means the user referred to Foo<T> as just 'Foo',
+ // which we don't allow. (This is a judgement call -- we *could*
+ // allow it and instantiate the type bounds... but we don't.)
+ return MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty()
+ || !types.isSameType(types.erasure(element.asType()), type);
+ }
+ },
+ null);
+ }
+}
diff --git a/java/dagger/internal/codegen/MapBindingExpression.java b/java/dagger/internal/codegen/MapBindingExpression.java
new file mode 100644
index 0000000..526c493
--- /dev/null
+++ b/java/dagger/internal/codegen/MapBindingExpression.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.MapBuilder;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import java.util.Collections;
+import java.util.Optional;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A {@link BindingExpression} for multibound maps. */
+final class MapBindingExpression extends MultibindingExpression {
+ /** Maximum number of key-value pairs that can be passed to ImmutableMap.of(K, V, K, V, ...). */
+ private static final int MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS = 5;
+
+ private final ProvisionBinding binding;
+ private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ MapBindingExpression(
+ ResolvedBindings resolvedBindings,
+ ComponentImplementation componentImplementation,
+ BindingGraph graph,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ DaggerElements elements) {
+ super(resolvedBindings, componentImplementation);
+ this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
+ BindingKind bindingKind = this.binding.kind();
+ checkArgument(bindingKind.equals(MULTIBOUND_MAP), bindingKind);
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ this.elements = elements;
+ this.dependencies =
+ Maps.toMap(
+ binding.dependencies(),
+ dep -> graph.contributionBindings().get(dep.key()).contributionBinding());
+ }
+
+ @Override
+ protected Expression buildDependencyExpression(ClassName requestingClass) {
+ Optional<CodeBlock> superMethodCall = superMethodCall();
+ // TODO(ronshapiro): We should also make an ImmutableMap version of MapFactory
+ boolean isImmutableMapAvailable = isImmutableMapAvailable();
+ // TODO(ronshapiro, gak): Use Maps.immutableEnumMap() if it's available?
+ if (isImmutableMapAvailable
+ && dependencies.size() <= MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS
+ && !superMethodCall.isPresent()) {
+ return Expression.create(
+ immutableMapType(),
+ CodeBlock.builder()
+ .add("$T.", ImmutableMap.class)
+ .add(maybeTypeParameters(requestingClass))
+ .add(
+ "of($L)",
+ dependencies
+ .keySet()
+ .stream()
+ .map(dependency -> keyAndValueExpression(dependency, requestingClass))
+ .collect(toParametersCodeBlock()))
+ .build());
+ }
+ switch (dependencies.size()) {
+ case 0:
+ return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptyMap()"));
+ case 1:
+ return collectionsStaticFactoryInvocation(
+ requestingClass,
+ CodeBlock.of(
+ "singletonMap($L)",
+ keyAndValueExpression(getOnlyElement(dependencies.keySet()), requestingClass)));
+ default:
+ CodeBlock.Builder instantiation = CodeBlock.builder();
+ instantiation
+ .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
+ .add(maybeTypeParameters(requestingClass));
+ if (isImmutableMapBuilderWithExpectedSizeAvailable()) {
+ instantiation.add("builderWithExpectedSize($L)", dependencies.size());
+ } else if (isImmutableMapAvailable) {
+ instantiation.add("builder()");
+ } else {
+ instantiation.add("newMapBuilder($L)", dependencies.size());
+ }
+ for (DependencyRequest dependency : getNewContributions(dependencies.keySet())) {
+ instantiation.add(".put($L)", keyAndValueExpression(dependency, requestingClass));
+ }
+ if (superMethodCall.isPresent()) {
+ instantiation.add(CodeBlock.of(".putAll($L)", superMethodCall.get()));
+ }
+ return Expression.create(
+ isImmutableMapAvailable ? immutableMapType() : binding.key().type(),
+ instantiation.add(".build()").build());
+ }
+ }
+
+ private DeclaredType immutableMapType() {
+ MapType mapType = MapType.from(binding.key());
+ return types.getDeclaredType(
+ elements.getTypeElement(ImmutableMap.class), mapType.keyType(), mapType.valueType());
+ }
+
+ private CodeBlock keyAndValueExpression(DependencyRequest dependency, ClassName requestingClass) {
+ return CodeBlock.of(
+ "$L, $L",
+ getMapKeyExpression(dependencies.get(dependency), requestingClass, elements),
+ componentBindingExpressions
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock());
+ }
+
+ private Expression collectionsStaticFactoryInvocation(
+ ClassName requestingClass, CodeBlock methodInvocation) {
+ return Expression.create(
+ binding.key().type(),
+ CodeBlock.builder()
+ .add("$T.", Collections.class)
+ .add(maybeTypeParameters(requestingClass))
+ .add(methodInvocation)
+ .build());
+ }
+
+ private CodeBlock maybeTypeParameters(ClassName requestingClass) {
+ TypeMirror bindingKeyType = binding.key().type();
+ MapType mapType = MapType.from(binding.key());
+ return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
+ ? CodeBlock.of("<$T, $T>", mapType.keyType(), mapType.valueType())
+ : CodeBlock.of("");
+ }
+
+ private boolean isImmutableMapBuilderWithExpectedSizeAvailable() {
+ if (isImmutableMapAvailable()) {
+ return methodsIn(elements.getTypeElement(ImmutableMap.class).getEnclosedElements())
+ .stream()
+ .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
+ }
+ return false;
+ }
+
+ private boolean isImmutableMapAvailable() {
+ return elements.getTypeElement(ImmutableMap.class) != null;
+ }
+}
diff --git a/java/dagger/internal/codegen/MapFactoryCreationExpression.java b/java/dagger/internal/codegen/MapFactoryCreationExpression.java
new file mode 100644
index 0000000..187b792
--- /dev/null
+++ b/java/dagger/internal/codegen/MapFactoryCreationExpression.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
+import static dagger.internal.codegen.SourceFiles.mapFactoryClassName;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.DependencyRequest;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory creation expression for a multibound map. */
+final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpression {
+
+ private final ComponentImplementation componentImplementation;
+ private final BindingGraph graph;
+ private final ContributionBinding binding;
+ private final DaggerElements elements;
+
+ MapFactoryCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions,
+ BindingGraph graph,
+ DaggerElements elements) {
+ super(binding, componentImplementation, componentBindingExpressions);
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.graph = checkNotNull(graph);
+ this.elements = checkNotNull(elements);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ CodeBlock.Builder builder = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
+ if (!useRawType()) {
+ MapType mapType = MapType.from(binding.key().type());
+ // TODO(ronshapiro): either inline this into mapFactoryClassName, or add a
+ // mapType.unwrappedValueType() method that doesn't require a framework type
+ TypeMirror valueType = mapType.valueType();
+ for (Class<?> frameworkClass :
+ ImmutableSet.of(Provider.class, Producer.class, Produced.class)) {
+ if (mapType.valuesAreTypeOf(frameworkClass)) {
+ valueType = mapType.unwrappedValueType(frameworkClass);
+ break;
+ }
+ }
+ builder.add("<$T, $T>", mapType.keyType(), valueType);
+ }
+
+ builder.add("builder($L)", binding.dependencies().size());
+
+ superContributions()
+ .ifPresent(superContributions -> builder.add(".putAll($L)", superContributions));
+
+ for (DependencyRequest dependency : dependenciesToImplement()) {
+ ContributionBinding contributionBinding =
+ graph.contributionBindings().get(dependency.key()).contributionBinding();
+ builder.add(
+ ".put($L, $L)",
+ getMapKeyExpression(contributionBinding, componentImplementation.name(), elements),
+ multibindingDependencyExpression(dependency));
+ }
+ builder.add(".build()");
+
+ componentImplementation.registerImplementedMultibinding(binding, bindingRequest());
+
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/MapKeyAccessibility.java b/java/dagger/internal/codegen/MapKeyAccessibility.java
new file mode 100644
index 0000000..ce27877
--- /dev/null
+++ b/java/dagger/internal/codegen/MapKeyAccessibility.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import dagger.internal.codegen.langmodel.Accessibility;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+final class MapKeyAccessibility extends SimpleAnnotationValueVisitor8<Boolean, Void> {
+ private final Predicate<TypeMirror> accessibilityChecker;
+
+ private MapKeyAccessibility(Predicate<TypeMirror> accessibilityChecker) {
+ this.accessibilityChecker = accessibilityChecker;
+ }
+
+ @Override
+ public Boolean visitAnnotation(AnnotationMirror annotation, Void aVoid) {
+ // The annotation type is not checked, as the generated code will refer to the @AutoAnnotation
+ // generated type which is always public
+ return visitValues(annotation.getElementValues().values());
+ }
+
+ @Override
+ public Boolean visitArray(List<? extends AnnotationValue> values, Void aVoid) {
+ return visitValues(values);
+ }
+
+ private boolean visitValues(Collection<? extends AnnotationValue> values) {
+ return values.stream().allMatch(value -> value.accept(this, null));
+ }
+
+ @Override
+ public Boolean visitEnumConstant(VariableElement enumConstant, Void aVoid) {
+ return accessibilityChecker.test(enumConstant.getEnclosingElement().asType());
+ }
+
+ @Override
+ public Boolean visitType(TypeMirror type, Void aVoid) {
+ return accessibilityChecker.test(type);
+ }
+
+ @Override
+ protected Boolean defaultAction(Object o, Void aVoid) {
+ return true;
+ }
+
+ static boolean isMapKeyAccessibleFrom(AnnotationMirror annotation, String accessingPackage) {
+ return new MapKeyAccessibility(type -> isTypeAccessibleFrom(type, accessingPackage))
+ .visitAnnotation(annotation, null);
+ }
+
+ static boolean isMapKeyPubliclyAccessible(AnnotationMirror annotation) {
+ return new MapKeyAccessibility(Accessibility::isTypePubliclyAccessible)
+ .visitAnnotation(annotation, null);
+ }
+}
diff --git a/java/dagger/internal/codegen/MapKeyProcessingStep.java b/java/dagger/internal/codegen/MapKeyProcessingStep.java
new file mode 100644
index 0000000..19975a7
--- /dev/null
+++ b/java/dagger/internal/codegen/MapKeyProcessingStep.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType;
+import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+
+/**
+ * The annotation processor responsible for validating the mapKey annotation and auto-generate
+ * implementation of annotations marked with {@link MapKey @MapKey} where necessary.
+ */
+public class MapKeyProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+ private final Messager messager;
+ private final DaggerTypes types;
+ private final MapKeyValidator mapKeyValidator;
+ private final AnnotationCreatorGenerator annotationCreatorGenerator;
+ private final UnwrappedMapKeyGenerator unwrappedMapKeyGenerator;
+
+ @Inject
+ MapKeyProcessingStep(
+ Messager messager,
+ DaggerTypes types,
+ MapKeyValidator mapKeyValidator,
+ AnnotationCreatorGenerator annotationCreatorGenerator,
+ UnwrappedMapKeyGenerator unwrappedMapKeyGenerator) {
+ super(MoreElements::asType);
+ this.messager = messager;
+ this.types = types;
+ this.mapKeyValidator = mapKeyValidator;
+ this.annotationCreatorGenerator = annotationCreatorGenerator;
+ this.unwrappedMapKeyGenerator = unwrappedMapKeyGenerator;
+ }
+
+ @Override
+ public Set<Class<? extends Annotation>> annotations() {
+ return ImmutableSet.<Class<? extends Annotation>>of(MapKey.class);
+ }
+
+ @Override
+ protected void process(
+ TypeElement mapKeyAnnotationType, ImmutableSet<Class<? extends Annotation>> annotations) {
+ ValidationReport<Element> mapKeyReport = mapKeyValidator.validate(mapKeyAnnotationType);
+ mapKeyReport.printMessagesTo(messager);
+
+ if (mapKeyReport.isClean()) {
+ MapKey mapkey = mapKeyAnnotationType.getAnnotation(MapKey.class);
+ if (!mapkey.unwrapValue()) {
+ annotationCreatorGenerator.generate(mapKeyAnnotationType, messager);
+ } else if (unwrappedValueKind(mapKeyAnnotationType).equals(ANNOTATION_TYPE)) {
+ unwrappedMapKeyGenerator.generate(mapKeyAnnotationType, messager);
+ }
+ }
+ }
+
+ private ElementKind unwrappedValueKind(TypeElement mapKeyAnnotationType) {
+ DeclaredType unwrappedMapKeyType =
+ getUnwrappedMapKeyType(MoreTypes.asDeclared(mapKeyAnnotationType.asType()), types);
+ return unwrappedMapKeyType.asElement().getKind();
+ }
+}
diff --git a/java/dagger/internal/codegen/MapKeyValidator.java b/java/dagger/internal/codegen/MapKeyValidator.java
new file mode 100644
index 0000000..f2568ef
--- /dev/null
+++ b/java/dagger/internal/codegen/MapKeyValidator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.List;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+
+/**
+ * A validator for {@link MapKey} annotations.
+ */
+// TODO(dpb,gak): Should unwrapped MapKeys be required to have their single member be named "value"?
+final class MapKeyValidator {
+ private final DaggerElements elements;
+
+ @Inject
+ MapKeyValidator(DaggerElements elements) {
+ this.elements = elements;
+ }
+
+ ValidationReport<Element> validate(Element element) {
+ ValidationReport.Builder<Element> builder = ValidationReport.about(element);
+ List<ExecutableElement> members = methodsIn(((TypeElement) element).getEnclosedElements());
+ if (members.isEmpty()) {
+ builder.addError("Map key annotations must have members", element);
+ } else if (element.getAnnotation(MapKey.class).unwrapValue()) {
+ if (members.size() > 1) {
+ builder.addError(
+ "Map key annotations with unwrapped values must have exactly one member", element);
+ } else if (members.get(0).getReturnType().getKind() == TypeKind.ARRAY) {
+ builder.addError("Map key annotations with unwrapped values cannot use arrays", element);
+ }
+ } else if (autoAnnotationIsMissing()) {
+ builder.addError(
+ "@AutoAnnotation is a necessary dependency if @MapKey(unwrapValue = false). Add a "
+ + "dependency on com.google.auto.value:auto-value:<current version>");
+ }
+ return builder.build();
+ }
+
+ private boolean autoAnnotationIsMissing() {
+ return elements.getTypeElement("com.google.auto.value.AutoAnnotation") == null;
+ }
+}
diff --git a/java/dagger/internal/codegen/MapKeys.java b/java/dagger/internal/codegen/MapKeys.java
new file mode 100644
index 0000000..d8da6af
--- /dev/null
+++ b/java/dagger/internal/codegen/MapKeys.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.MapKeyAccessibility.isMapKeyPubliclyAccessible;
+import static dagger.internal.codegen.SourceFiles.elementBasedClassName;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/**
+ * Methods for extracting {@link MapKey} annotations and key code blocks from binding elements.
+ */
+final class MapKeys {
+
+ /**
+ * If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
+ * annotation
+ */
+ static Optional<AnnotationMirror> getMapKey(Element bindingElement) {
+ ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(bindingElement);
+ return mapKeys.isEmpty()
+ ? Optional.empty()
+ : Optional.<AnnotationMirror>of(getOnlyElement(mapKeys));
+ }
+
+ /**
+ * Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}.
+ */
+ static ImmutableSet<? extends AnnotationMirror> getMapKeys(Element bindingElement) {
+ return getAnnotatedAnnotations(bindingElement, MapKey.class);
+ }
+
+ /**
+ * Returns the annotation value if {@code mapKey}'s type is annotated with
+ * {@link MapKey @MapKey(unwrapValue = true)}.
+ *
+ * @throws IllegalArgumentException if {@code mapKey}'s type is not annotated with
+ * {@link MapKey @MapKey} at all.
+ */
+ static Optional<? extends AnnotationValue> unwrapValue(AnnotationMirror mapKey) {
+ MapKey mapKeyAnnotation = mapKey.getAnnotationType().asElement().getAnnotation(MapKey.class);
+ checkArgument(
+ mapKeyAnnotation != null, "%s is not annotated with @MapKey", mapKey.getAnnotationType());
+ return mapKeyAnnotation.unwrapValue()
+ ? Optional.of(getOnlyElement(getAnnotationValuesWithDefaults(mapKey).values()))
+ : Optional.empty();
+ }
+
+ static TypeMirror mapKeyType(AnnotationMirror mapKeyAnnotation, DaggerTypes types) {
+ return unwrapValue(mapKeyAnnotation).isPresent()
+ ? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types)
+ : mapKeyAnnotation.getAnnotationType();
+ }
+
+ /**
+ * Returns the map key type for an unwrapped {@link MapKey} annotation type. If the single member
+ * type is primitive, returns the boxed type.
+ *
+ * @throws IllegalArgumentException if {@code mapKeyAnnotationType} is not an annotation type or
+ * has more than one member, or if its single member is an array
+ * @throws NoSuchElementException if the annotation has no members
+ */
+ static DeclaredType getUnwrappedMapKeyType(
+ final DeclaredType mapKeyAnnotationType, final DaggerTypes types) {
+ checkArgument(
+ MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE,
+ "%s is not an annotation type",
+ mapKeyAnnotationType);
+
+ final ExecutableElement onlyElement =
+ getOnlyElement(methodsIn(mapKeyAnnotationType.asElement().getEnclosedElements()));
+
+ SimpleTypeVisitor6<DeclaredType, Void> keyTypeElementVisitor =
+ new SimpleTypeVisitor6<DeclaredType, Void>() {
+
+ @Override
+ public DeclaredType visitArray(ArrayType t, Void p) {
+ throw new IllegalArgumentException(
+ mapKeyAnnotationType + "." + onlyElement.getSimpleName() + " cannot be an array");
+ }
+
+ @Override
+ public DeclaredType visitPrimitive(PrimitiveType t, Void p) {
+ return MoreTypes.asDeclared(types.boxedClass(t).asType());
+ }
+
+ @Override
+ public DeclaredType visitDeclared(DeclaredType t, Void p) {
+ return t;
+ }
+ };
+ return keyTypeElementVisitor.visit(onlyElement.getReturnType());
+ }
+
+ /**
+ * Returns a code block for {@code binding}'s {@link ContributionBinding#mapKeyAnnotation() map
+ * key}. If for whatever reason the map key is not accessible from within {@code requestingClass}
+ * (i.e. it has a package-private {@code enum} from a different package), this will return an
+ * invocation of a proxy-method giving it access.
+ *
+ * @throws IllegalStateException if {@code binding} is not a {@link dagger.multibindings.IntoMap
+ * map} contribution.
+ */
+ static CodeBlock getMapKeyExpression(
+ ContributionBinding binding, ClassName requestingClass, DaggerElements elements) {
+ AnnotationMirror mapKeyAnnotation = binding.mapKeyAnnotation().get();
+ return MapKeyAccessibility.isMapKeyAccessibleFrom(
+ mapKeyAnnotation, requestingClass.packageName())
+ ? directMapKeyExpression(mapKeyAnnotation, elements)
+ : CodeBlock.of("$T.create()", mapKeyProxyClassName(binding));
+ }
+
+ /**
+ * Returns a code block for the map key annotation {@code mapKey}.
+ *
+ * <p>This method assumes the map key will be accessible in the context that the returned {@link
+ * CodeBlock} is used. Use {@link #getMapKeyExpression(ContributionBinding, ClassName,
+ * DaggerElements)} when that assumption is not guaranteed.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
+ * annotation
+ * @throws IllegalStateException if {@code bindingElement} is not annotated with a {@code MapKey}
+ * annotation
+ */
+ private static CodeBlock directMapKeyExpression(
+ AnnotationMirror mapKey, DaggerElements elements) {
+ Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey);
+ AnnotationExpression annotationExpression = new AnnotationExpression(mapKey);
+
+ if (MoreTypes.asTypeElement(mapKey.getAnnotationType())
+ .getQualifiedName()
+ .contentEquals("dagger.android.AndroidInjectionKey")) {
+ TypeElement unwrappedType =
+ elements.checkTypePresent((String) unwrappedValue.get().getValue());
+ return CodeBlock.of(
+ "$T.of($S)",
+ ClassName.get("dagger.android.internal", "AndroidInjectionKeys"),
+ ClassName.get(unwrappedType).reflectionName());
+ }
+
+ if (unwrappedValue.isPresent()) {
+ TypeMirror unwrappedValueType =
+ getOnlyElement(getAnnotationValuesWithDefaults(mapKey).keySet()).getReturnType();
+ return annotationExpression.getValueExpression(unwrappedValueType, unwrappedValue.get());
+ } else {
+ return annotationExpression.getAnnotationInstanceExpression();
+ }
+ }
+
+ /**
+ * Returns the {@link ClassName} in which {@link #mapKeyFactoryMethod(ContributionBinding,
+ * DaggerTypes, DaggerElements)} is generated.
+ */
+ static ClassName mapKeyProxyClassName(ContributionBinding binding) {
+ return elementBasedClassName(
+ MoreElements.asExecutable(binding.bindingElement().get()), "MapKey");
+ }
+
+ /**
+ * A {@code static create()} method to be added to {@link
+ * #mapKeyProxyClassName(ContributionBinding)} when the {@code @MapKey} annotation is not publicly
+ * accessible.
+ */
+ static Optional<MethodSpec> mapKeyFactoryMethod(
+ ContributionBinding binding, DaggerTypes types, DaggerElements elements) {
+ return binding
+ .mapKeyAnnotation()
+ .filter(mapKey -> !isMapKeyPubliclyAccessible(mapKey))
+ .map(
+ mapKey ->
+ methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(TypeName.get(mapKeyType(mapKey, types)))
+ .addStatement("return $L", directMapKeyExpression(mapKey, elements))
+ .build());
+ }
+
+ private MapKeys() {}
+}
diff --git a/java/dagger/internal/codegen/MapMultibindingValidator.java b/java/dagger/internal/codegen/MapMultibindingValidator.java
new file mode 100644
index 0000000..346d271
--- /dev/null
+++ b/java/dagger/internal/codegen/MapMultibindingValidator.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Multimaps.filterKeys;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
+import static dagger.internal.codegen.Formatter.INDENT;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import dagger.model.BindingGraph;
+import dagger.model.Key;
+import dagger.producers.Producer;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.type.DeclaredType;
+
+/**
+ * Reports an error for any map binding with either more than one contribution with the same map key
+ * or contributions with inconsistent map key annotation types.
+ */
+final class MapMultibindingValidator implements BindingGraphPlugin {
+
+ private final BindingDeclarationFormatter bindingDeclarationFormatter;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ MapMultibindingValidator(
+ BindingDeclarationFormatter bindingDeclarationFormatter, KeyFactory keyFactory) {
+ this.bindingDeclarationFormatter = bindingDeclarationFormatter;
+ this.keyFactory = keyFactory;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/MapKeys";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ mapMultibindings(bindingGraph)
+ .forEach(
+ binding -> {
+ ImmutableSet<ContributionBinding> contributions =
+ mapBindingContributions(binding, bindingGraph);
+ checkForDuplicateMapKeys(binding, contributions, diagnosticReporter);
+ checkForInconsistentMapKeyAnnotationTypes(binding, contributions, diagnosticReporter);
+ });
+ }
+
+ /**
+ * Returns the map multibindings in the binding graph. If a graph contains bindings for more than
+ * one of the following for the same {@code K} and {@code V}, then only the first one found will
+ * be returned so we don't report the same map contribution problem more than once.
+ *
+ * <ol>
+ * <li>{@code Map<K, V>}
+ * <li>{@code Map<K, Provider<V>>}
+ * <li>{@code Map<K, Producer<V>>}
+ * </ol>
+ */
+ private ImmutableSet<dagger.model.Binding> mapMultibindings(BindingGraph bindingGraph) {
+ ImmutableSetMultimap<Key, dagger.model.Binding> mapMultibindings =
+ bindingGraph.bindings().stream()
+ .filter(node -> node.kind().equals(MULTIBOUND_MAP))
+ .collect(toImmutableSetMultimap(dagger.model.Binding::key, node -> node));
+
+ // Mutlbindings for Map<K, V>
+ SetMultimap<Key, dagger.model.Binding> plainValueMapMultibindings =
+ filterKeys(mapMultibindings, key -> !MapType.from(key).valuesAreFrameworkType());
+
+ // Multibindings for Map<K, Provider<V>> where Map<K, V> isn't in plainValueMapMultibindings
+ SetMultimap<Key, dagger.model.Binding> providerValueMapMultibindings =
+ filterKeys(
+ mapMultibindings,
+ key ->
+ MapType.from(key).valuesAreTypeOf(Provider.class)
+ && !plainValueMapMultibindings.containsKey(keyFactory.unwrapMapValueType(key)));
+
+ // Multibindings for Map<K, Producer<V>> where Map<K, V> isn't in plainValueMapMultibindings and
+ // Map<K, Provider<V>> isn't in providerValueMapMultibindings
+ SetMultimap<Key, dagger.model.Binding> producerValueMapMultibindings =
+ filterKeys(
+ mapMultibindings,
+ key ->
+ MapType.from(key).valuesAreTypeOf(Producer.class)
+ && !plainValueMapMultibindings.containsKey(keyFactory.unwrapMapValueType(key))
+ && !providerValueMapMultibindings.containsKey(
+ keyFactory.rewrapMapKey(key, Producer.class, Provider.class).get()));
+
+ return new ImmutableSet.Builder<dagger.model.Binding>()
+ .addAll(plainValueMapMultibindings.values())
+ .addAll(providerValueMapMultibindings.values())
+ .addAll(producerValueMapMultibindings.values())
+ .build();
+ }
+
+ private ImmutableSet<ContributionBinding> mapBindingContributions(
+ dagger.model.Binding binding, BindingGraph bindingGraph) {
+ checkArgument(binding.kind().equals(MULTIBOUND_MAP));
+ return bindingGraph.requestedBindings(binding).stream()
+ .map(b -> (BindingNode) b)
+ .map(b -> (ContributionBinding) b.delegate())
+ .collect(toImmutableSet());
+ }
+
+ private void checkForDuplicateMapKeys(
+ dagger.model.Binding multiboundMapBinding,
+ ImmutableSet<ContributionBinding> contributions,
+ DiagnosticReporter diagnosticReporter) {
+ ImmutableSetMultimap<Object, ContributionBinding> contributionsByMapKey =
+ ImmutableSetMultimap.copyOf(Multimaps.index(contributions, ContributionBinding::mapKey));
+
+ for (Set<ContributionBinding> contributionsForOneMapKey :
+ Multimaps.asMap(contributionsByMapKey).values()) {
+ if (contributionsForOneMapKey.size() > 1) {
+ diagnosticReporter.reportBinding(
+ ERROR,
+ multiboundMapBinding,
+ duplicateMapKeyErrorMessage(contributionsForOneMapKey, multiboundMapBinding.key()));
+ }
+ }
+ }
+
+ private void checkForInconsistentMapKeyAnnotationTypes(
+ dagger.model.Binding multiboundMapBinding,
+ ImmutableSet<ContributionBinding> contributions,
+ DiagnosticReporter diagnosticReporter) {
+ ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
+ contributionsByMapKeyAnnotationType = indexByMapKeyAnnotationType(contributions);
+
+ if (contributionsByMapKeyAnnotationType.keySet().size() > 1) {
+ diagnosticReporter.reportBinding(
+ ERROR,
+ multiboundMapBinding,
+ inconsistentMapKeyAnnotationTypesErrorMessage(
+ contributionsByMapKeyAnnotationType, multiboundMapBinding.key()));
+ }
+ }
+
+ private static ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
+ indexByMapKeyAnnotationType(ImmutableSet<ContributionBinding> contributions) {
+ return ImmutableSetMultimap.copyOf(
+ Multimaps.index(
+ contributions,
+ mapBinding ->
+ MoreTypes.equivalence()
+ .wrap(mapBinding.mapKeyAnnotation().get().getAnnotationType())));
+ }
+
+ private String inconsistentMapKeyAnnotationTypesErrorMessage(
+ ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
+ contributionsByMapKeyAnnotationType,
+ Key mapBindingKey) {
+ StringBuilder message =
+ new StringBuilder(mapBindingKey.toString())
+ .append(" uses more than one @MapKey annotation type");
+ Multimaps.asMap(contributionsByMapKeyAnnotationType)
+ .forEach(
+ (annotationType, contributions) -> {
+ message.append('\n').append(INDENT).append(annotationType.get()).append(':');
+ bindingDeclarationFormatter.formatIndentedList(message, contributions, 2);
+ });
+ return message.toString();
+ }
+
+ private String duplicateMapKeyErrorMessage(
+ Set<ContributionBinding> contributionsForOneMapKey, Key mapBindingKey) {
+ StringBuilder message =
+ new StringBuilder("The same map key is bound more than once for ").append(mapBindingKey);
+ bindingDeclarationFormatter.formatIndentedList(message, contributionsForOneMapKey, 1);
+ return message.toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/MapType.java b/java/dagger/internal/codegen/MapType.java
new file mode 100644
index 0000000..73ecdbf
--- /dev/null
+++ b/java/dagger/internal/codegen/MapType.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import dagger.model.Key;
+import java.util.Map;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Information about a {@link Map} {@link TypeMirror}.
+ */
+@AutoValue
+abstract class MapType {
+ /**
+ * The map type itself, wrapped using {@link MoreTypes#equivalence()}. Use
+ * {@link #declaredMapType()} instead.
+ */
+ protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredMapType();
+
+ /**
+ * The map type itself.
+ */
+ DeclaredType declaredMapType() {
+ return wrappedDeclaredMapType().get();
+ }
+
+ /**
+ * {@code true} if the map type is the raw {@link Map} type.
+ */
+ boolean isRawType() {
+ return declaredMapType().getTypeArguments().isEmpty();
+ }
+
+ /**
+ * The map key type.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true.
+ */
+ TypeMirror keyType() {
+ checkState(!isRawType());
+ return declaredMapType().getTypeArguments().get(0);
+ }
+
+ /**
+ * The map value type.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true.
+ */
+ TypeMirror valueType() {
+ checkState(!isRawType());
+ return declaredMapType().getTypeArguments().get(1);
+ }
+
+ /**
+ * {@code true} if {@link #valueType()} is a {@code clazz}.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true.
+ */
+ boolean valuesAreTypeOf(Class<?> clazz) {
+ return MoreTypes.isType(valueType()) && MoreTypes.isTypeOf(clazz, valueType());
+ }
+
+ /**
+ * Returns {@code true} if the {@linkplain #valueType() value type} of the {@link Map} is a
+ * {@linkplain FrameworkTypes#isFrameworkType(TypeMirror) framework type}.
+ */
+ boolean valuesAreFrameworkType() {
+ return FrameworkTypes.isFrameworkType(valueType());
+ }
+
+ /**
+ * {@code V} if {@link #valueType()} is a framework type like {@code Provider<V>} or {@code
+ * Producer<V>}.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true or {@link #valueType()} is not a
+ * framework type
+ */
+ TypeMirror unwrappedFrameworkValueType() {
+ checkState(
+ valuesAreFrameworkType(), "called unwrappedFrameworkValueType() on %s", declaredMapType());
+ return uncheckedUnwrappedValueType();
+ }
+
+ /**
+ * {@code V} if {@link #valueType()} is a {@code WrappingClass<V>}.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true or {@link #valueType()} is not a
+ * {@code WrappingClass<V>}
+ * @throws IllegalArgumentException if {@code wrappingClass} does not have exactly one type
+ * parameter
+ */
+ TypeMirror unwrappedValueType(Class<?> wrappingClass) {
+ checkArgument(
+ wrappingClass.getTypeParameters().length == 1,
+ "%s must have exactly one type parameter",
+ wrappingClass);
+ checkState(valuesAreTypeOf(wrappingClass), "expected values to be %s: %s", wrappingClass, this);
+ return uncheckedUnwrappedValueType();
+ }
+
+ private TypeMirror uncheckedUnwrappedValueType() {
+ return MoreTypes.asDeclared(valueType()).getTypeArguments().get(0);
+ }
+
+ /**
+ * {@code true} if {@code type} is a {@link Map} type.
+ */
+ static boolean isMap(TypeMirror type) {
+ return MoreTypes.isType(type) && MoreTypes.isTypeOf(Map.class, type);
+ }
+
+ /**
+ * {@code true} if {@code key.type()} is a {@link Map} type.
+ */
+ static boolean isMap(Key key) {
+ return isMap(key.type());
+ }
+
+ /**
+ * Returns a {@link MapType} for {@code type}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not a {@link Map} type
+ */
+ static MapType from(TypeMirror type) {
+ checkArgument(isMap(type), "%s is not a Map", type);
+ return new AutoValue_MapType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+ }
+
+ /**
+ * Returns a {@link MapType} for {@code key}'s {@link Key#type() type}.
+ *
+ * @throws IllegalArgumentException if {@code key.type()} is not a {@link Map} type
+ */
+ static MapType from(Key key) {
+ return from(key.type());
+ }
+}
diff --git a/java/dagger/internal/codegen/MemberSelect.java b/java/dagger/internal/codegen/MemberSelect.java
new file mode 100644
index 0000000..c42c7c9
--- /dev/null
+++ b/java/dagger/internal/codegen/MemberSelect.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static javax.lang.model.type.TypeKind.DECLARED;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Represents a {@link com.sun.source.tree.MemberSelectTree} as a {@link CodeBlock}.
+ */
+abstract class MemberSelect {
+
+ /**
+ * Returns a {@link MemberSelect} that accesses the field given by {@code fieldName} owned by
+ * {@code owningClass}. In this context "local" refers to the fact that the field is owned by the
+ * type (or an enclosing type) from which the code block will be used. The returned
+ * {@link MemberSelect} will not be valid for accessing the field from a different class
+ * (regardless of accessibility).
+ */
+ static MemberSelect localField(ClassName owningClass, String fieldName) {
+ return new LocalField(owningClass, fieldName);
+ }
+
+ private static final class LocalField extends MemberSelect {
+ final String fieldName;
+
+ LocalField(ClassName owningClass, String fieldName) {
+ super(owningClass, false);
+ this.fieldName = checkNotNull(fieldName);
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ return owningClass().equals(usingClass)
+ ? CodeBlock.of("$N", fieldName)
+ : CodeBlock.of("$T.this.$N", owningClass(), fieldName);
+ }
+ }
+
+ /**
+ * Returns a {@link MemberSelect} that accesses the method given by {@code methodName} owned by
+ * {@code owningClass}. In this context "local" refers to the fact that the method is owned by the
+ * type (or an enclosing type) from which the code block will be used. The returned {@link
+ * MemberSelect} will not be valid for accessing the method from a different class (regardless of
+ * accessibility).
+ */
+ static MemberSelect localMethod(ClassName owningClass, String methodName) {
+ return new LocalMethod(owningClass, methodName);
+ }
+
+ private static final class LocalMethod extends MemberSelect {
+ final String methodName;
+
+ LocalMethod(ClassName owningClass, String methodName) {
+ super(owningClass, false);
+ this.methodName = checkNotNull(methodName);
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ return owningClass().equals(usingClass)
+ ? CodeBlock.of("$N()", methodName)
+ : CodeBlock.of("$T.this.$N()", owningClass(), methodName);
+ }
+ }
+
+ /**
+ * If {@code resolvedBindings} is an unscoped provision binding with no factory arguments or a
+ * no-op members injection binding, then we don't need a field to hold its factory. In that case,
+ * this method returns the static member select that returns the factory or no-op members
+ * injector.
+ */
+ static Optional<MemberSelect> staticFactoryCreation(ResolvedBindings resolvedBindings) {
+ if (resolvedBindings.contributionBindings().isEmpty()) {
+ throw new AssertionError(
+ "Expected a contribution binding, but none found. *THIS IS A DAGGER BUG* - please "
+ + "report it on Github with as much context as you can provide. Thanks!"
+ + "\n\nKey: "
+ + resolvedBindings.key()
+ + "\nMultibinding declarations: "
+ + resolvedBindings.multibindingDeclarations()
+ + "\nSubcomponent declarations: "
+ + resolvedBindings.subcomponentDeclarations()
+ + "\nOptional binding declarations: "
+ + resolvedBindings.optionalBindingDeclarations());
+ }
+ ContributionBinding contributionBinding = resolvedBindings.contributionBinding();
+ if (contributionBinding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)
+ && !contributionBinding.scope().isPresent()) {
+ switch (contributionBinding.kind()) {
+ case MULTIBOUND_MAP:
+ return Optional.of(emptyMapFactory(contributionBinding));
+
+ case MULTIBOUND_SET:
+ return Optional.of(emptySetFactory(contributionBinding));
+
+ case INJECTION:
+ case PROVISION:
+ TypeMirror keyType = resolvedBindings.key().type();
+ if (keyType.getKind().equals(DECLARED)) {
+ ImmutableList<TypeVariableName> typeVariables =
+ bindingTypeElementTypeVariableNames(contributionBinding);
+ if (!typeVariables.isEmpty()) {
+ List<? extends TypeMirror> typeArguments =
+ ((DeclaredType) keyType).getTypeArguments();
+ return Optional.of(
+ MemberSelect.parameterizedFactoryCreateMethod(
+ generatedClassNameForBinding(contributionBinding), typeArguments));
+ }
+ }
+ // fall through
+
+ default:
+ return Optional.of(
+ new StaticMethod(
+ generatedClassNameForBinding(contributionBinding), CodeBlock.of("create()")));
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ /**
+ * Returns a {@link MemberSelect} for the instance of a {@code create()} method on a factory. This
+ * only applies for factories that do not have any dependencies.
+ */
+ private static MemberSelect parameterizedFactoryCreateMethod(
+ ClassName owningClass, List<? extends TypeMirror> parameters) {
+ return new ParameterizedStaticMethod(
+ owningClass, ImmutableList.copyOf(parameters), CodeBlock.of("create()"), FACTORY);
+ }
+
+ private static final class StaticMethod extends MemberSelect {
+ final CodeBlock methodCodeBlock;
+
+ StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock) {
+ super(owningClass, true);
+ this.methodCodeBlock = checkNotNull(methodCodeBlock);
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ return owningClass().equals(usingClass)
+ ? methodCodeBlock
+ : CodeBlock.of("$T.$L", owningClass(), methodCodeBlock);
+ }
+ }
+
+ /** A {@link MemberSelect} for a factory of an empty map. */
+ private static MemberSelect emptyMapFactory(ContributionBinding contributionBinding) {
+ BindingType bindingType = contributionBinding.bindingType();
+ ImmutableList<TypeMirror> typeParameters =
+ ImmutableList.copyOf(
+ MoreTypes.asDeclared(contributionBinding.key().type()).getTypeArguments());
+ if (bindingType.equals(BindingType.PRODUCTION)) {
+ return new ParameterizedStaticMethod(
+ PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER);
+ } else {
+ return new ParameterizedStaticMethod(
+ MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), PROVIDER);
+ }
+ }
+
+ /**
+ * A static member select for an empty set factory. Calls {@link
+ * dagger.internal.SetFactory#empty()}, {@link dagger.producers.internal.SetProducer#empty()}, or
+ * {@link dagger.producers.internal.SetOfProducedProducer#empty()}, depending on the set bindings.
+ */
+ private static MemberSelect emptySetFactory(ContributionBinding binding) {
+ return new ParameterizedStaticMethod(
+ setFactoryClassName(binding),
+ ImmutableList.of(SetType.from(binding.key()).elementType()),
+ CodeBlock.of("empty()"),
+ FACTORY);
+ }
+
+ private static final class ParameterizedStaticMethod extends MemberSelect {
+ final ImmutableList<TypeMirror> typeParameters;
+ final CodeBlock methodCodeBlock;
+ final ClassName rawReturnType;
+
+ ParameterizedStaticMethod(
+ ClassName owningClass,
+ ImmutableList<TypeMirror> typeParameters,
+ CodeBlock methodCodeBlock,
+ ClassName rawReturnType) {
+ super(owningClass, true);
+ this.typeParameters = typeParameters;
+ this.methodCodeBlock = methodCodeBlock;
+ this.rawReturnType = rawReturnType;
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ boolean accessible = true;
+ for (TypeMirror typeParameter : typeParameters) {
+ accessible &= isTypeAccessibleFrom(typeParameter, usingClass.packageName());
+ }
+
+ if (accessible) {
+ return CodeBlock.of(
+ "$T.<$L>$L",
+ owningClass(),
+ typeParameters.stream().map(CodeBlocks::type).collect(toParametersCodeBlock()),
+ methodCodeBlock);
+ } else {
+ return CodeBlock.of("(($T) $T.$L)", rawReturnType, owningClass(), methodCodeBlock);
+ }
+ }
+ }
+
+ private final ClassName owningClass;
+ private final boolean staticMember;
+
+ MemberSelect(ClassName owningClass, boolean staticMemeber) {
+ this.owningClass = owningClass;
+ this.staticMember = staticMemeber;
+ }
+
+ /** Returns the class that owns the member being selected. */
+ ClassName owningClass() {
+ return owningClass;
+ }
+
+ /**
+ * Returns true if the member being selected is static and does not require an instance of
+ * {@link #owningClass()}.
+ */
+ boolean staticMember() {
+ return staticMember;
+ }
+
+ /**
+ * Returns a {@link CodeBlock} suitable for accessing the member from the given {@code
+ * usingClass}.
+ */
+ abstract CodeBlock getExpressionFor(ClassName usingClass);
+}
diff --git a/java/dagger/internal/codegen/MembersInjectionBinding.java b/java/dagger/internal/codegen/MembersInjectionBinding.java
new file mode 100644
index 0000000..4918fa1
--- /dev/null
+++ b/java/dagger/internal/codegen/MembersInjectionBinding.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static java.util.stream.Collectors.toList;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/**
+ * Represents the full members injection of a particular type.
+ */
+@AutoValue
+abstract class MembersInjectionBinding extends Binding {
+ @Override
+ public final Optional<Element> bindingElement() {
+ return Optional.of(membersInjectedType());
+ }
+
+ abstract TypeElement membersInjectedType();
+
+ @Override
+ abstract Optional<MembersInjectionBinding> unresolved();
+
+ @Override
+ public Optional<TypeElement> contributingModule() {
+ return Optional.empty();
+ }
+
+ /** The set of individual sites where {@link Inject} is applied. */
+ abstract ImmutableSortedSet<InjectionSite> injectionSites();
+
+ @Override
+ BindingType bindingType() {
+ return BindingType.MEMBERS_INJECTION;
+ }
+
+ @Override
+ public BindingKind kind() {
+ return BindingKind.MEMBERS_INJECTION;
+ }
+
+ @Override
+ public boolean isNullable() {
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if any of this binding's injection sites are directly on the bound type.
+ */
+ boolean hasLocalInjectionSites() {
+ return injectionSites()
+ .stream()
+ .anyMatch(
+ injectionSite ->
+ injectionSite.element().getEnclosingElement().equals(membersInjectedType()));
+ }
+
+ @Override
+ boolean requiresModuleInstance() {
+ return false;
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // TODO(ronshapiro,dpb): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @AutoValue
+ abstract static class InjectionSite {
+ enum Kind {
+ FIELD,
+ METHOD,
+ }
+
+ abstract Kind kind();
+
+ abstract Element element();
+
+ abstract ImmutableSet<DependencyRequest> dependencies();
+
+ /**
+ * Returns the index of {@link #element()} in its parents {@code @Inject} members that have the
+ * same simple name. This method filters out private elements so that the results will be
+ * consistent independent of whether the build system uses header jars or not.
+ */
+ @Memoized
+ int indexAmongAtInjectMembersWithSameSimpleName() {
+ return element()
+ .getEnclosingElement()
+ .getEnclosedElements()
+ .stream()
+ .filter(element -> isAnnotationPresent(element, Inject.class))
+ .filter(element -> !element.getModifiers().contains(Modifier.PRIVATE))
+ .filter(element -> element.getSimpleName().equals(this.element().getSimpleName()))
+ .collect(toList())
+ .indexOf(element());
+ }
+
+ static InjectionSite field(VariableElement element, DependencyRequest dependency) {
+ return new AutoValue_MembersInjectionBinding_InjectionSite(
+ Kind.FIELD, element, ImmutableSet.of(dependency));
+ }
+
+ static InjectionSite method(
+ ExecutableElement element, Iterable<DependencyRequest> dependencies) {
+ return new AutoValue_MembersInjectionBinding_InjectionSite(
+ Kind.METHOD, element, ImmutableSet.copyOf(dependencies));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/MembersInjectionBindingExpression.java b/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
new file mode 100644
index 0000000..e9a8ffc
--- /dev/null
+++ b/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterSpec;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import javax.lang.model.element.ExecutableElement;
+
+/**
+ * A binding expression for members injection component methods. See {@link
+ * MembersInjectionMethods}.
+ */
+final class MembersInjectionBindingExpression extends BindingExpression {
+ private final MembersInjectionBinding binding;
+ private final MembersInjectionMethods membersInjectionMethods;
+
+ MembersInjectionBindingExpression(
+ ResolvedBindings resolvedBindings, MembersInjectionMethods membersInjectionMethods) {
+ this.binding = resolvedBindings.membersInjectionBinding().get();
+ this.membersInjectionMethods = membersInjectionMethods;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ throw new UnsupportedOperationException(binding.toString());
+ }
+
+ // TODO(ronshapiro): This class doesn't need to be a BindingExpression, as
+ // getDependencyExpression() should never be called for members injection methods. It's probably
+ // better suited as a method on MembersInjectionMethods
+ @Override
+ protected CodeBlock getComponentMethodImplementation(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ ExecutableElement methodElement = componentMethod.methodElement();
+ ParameterSpec parameter = ParameterSpec.get(getOnlyElement(methodElement.getParameters()));
+
+ if (binding.injectionSites().isEmpty()) {
+ return methodElement.getReturnType().getKind().equals(VOID)
+ ? CodeBlock.of("")
+ : CodeBlock.of("return $N;", parameter);
+ } else {
+ return methodElement.getReturnType().getKind().equals(VOID)
+ ? CodeBlock.of("$L;", membersInjectionInvocation(parameter))
+ : CodeBlock.of("return $L;", membersInjectionInvocation(parameter));
+ }
+ }
+
+ CodeBlock membersInjectionInvocation(ParameterSpec target) {
+ return CodeBlock.of("$N($N)", membersInjectionMethods.getOrCreate(binding.key()), target);
+ }
+}
diff --git a/java/dagger/internal/codegen/MembersInjectionMethods.java b/java/dagger/internal/codegen/MembersInjectionMethods.java
new file mode 100644
index 0000000..1e04669
--- /dev/null
+++ b/java/dagger/internal/codegen/MembersInjectionMethods.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** Manages the member injection methods for a component. */
+final class MembersInjectionMethods {
+ private final Map<Key, MethodSpec> membersInjectionMethods = new LinkedHashMap<>();
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions bindingExpressions;
+ private final BindingGraph graph;
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+
+ MembersInjectionMethods(
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions bindingExpressions,
+ BindingGraph graph,
+ DaggerElements elements,
+ DaggerTypes types) {
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.bindingExpressions = checkNotNull(bindingExpressions);
+ this.graph = checkNotNull(graph);
+ this.elements = checkNotNull(elements);
+ this.types = checkNotNull(types);
+ }
+
+ /**
+ * Returns the members injection {@link MethodSpec} for the given {@link Key}, creating it if
+ * necessary.
+ */
+ MethodSpec getOrCreate(Key key) {
+ return reentrantComputeIfAbsent(membersInjectionMethods, key, this::membersInjectionMethod);
+ }
+
+ private MethodSpec membersInjectionMethod(Key key) {
+ ResolvedBindings resolvedBindings =
+ graph.membersInjectionBindings().getOrDefault(key, graph.contributionBindings().get(key));
+ Binding binding = resolvedBindings.binding();
+ TypeMirror keyType = binding.key().type();
+ TypeMirror membersInjectedType =
+ isTypeAccessibleFrom(keyType, componentImplementation.name().packageName())
+ ? keyType
+ : elements.getTypeElement(Object.class).asType();
+ TypeName membersInjectedTypeName = TypeName.get(membersInjectedType);
+ Name bindingTypeName = binding.bindingTypeElement().get().getSimpleName();
+ // TODO(ronshapiro): include type parameters in this name e.g. injectFooOfT, and outer class
+ // simple names Foo.Builder -> injectFooBuilder
+ String methodName = componentImplementation.getUniqueMethodName("inject" + bindingTypeName);
+ ParameterSpec parameter = ParameterSpec.builder(membersInjectedTypeName, "instance").build();
+ MethodSpec.Builder methodBuilder =
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ .returns(membersInjectedTypeName)
+ .addParameter(parameter);
+ TypeElement canIgnoreReturnValue =
+ elements.getTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue");
+ if (canIgnoreReturnValue != null) {
+ methodBuilder.addAnnotation(ClassName.get(canIgnoreReturnValue));
+ }
+ CodeBlock instance = CodeBlock.of("$N", parameter);
+ methodBuilder.addCode(
+ InjectionSiteMethod.invokeAll(
+ injectionSites(binding),
+ componentImplementation.name(),
+ instance,
+ membersInjectedType,
+ types,
+ request ->
+ bindingExpressions
+ .getDependencyArgumentExpression(request, componentImplementation.name())
+ .codeBlock(),
+ elements));
+ methodBuilder.addStatement("return $L", instance);
+
+ MethodSpec method = methodBuilder.build();
+ componentImplementation.addMethod(MEMBERS_INJECTION_METHOD, method);
+ return method;
+ }
+
+ private static ImmutableSet<InjectionSite> injectionSites(Binding binding) {
+ if (binding instanceof ProvisionBinding) {
+ return ((ProvisionBinding) binding).injectionSites();
+ } else if (binding instanceof MembersInjectionBinding) {
+ return ((MembersInjectionBinding) binding).injectionSites();
+ }
+ throw new IllegalArgumentException(binding.key().toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/MembersInjectionValidator.java b/java/dagger/internal/codegen/MembersInjectionValidator.java
new file mode 100644
index 0000000..036315e
--- /dev/null
+++ b/java/dagger/internal/codegen/MembersInjectionValidator.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
+
+import com.google.auto.common.MoreElements;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Validates members injection requests (members injection methods on components and requests for
+ * {@code MembersInjector<Foo>}).
+ */
+final class MembersInjectionValidator {
+
+ @Inject
+ MembersInjectionValidator() {}
+
+ /** Reports errors if a request for a {@code MembersInjector<Foo>}) is invalid. */
+ ValidationReport<Element> validateMembersInjectionRequest(
+ Element requestElement, TypeMirror membersInjectedType) {
+ ValidationReport.Builder<Element> report = ValidationReport.about(requestElement);
+ checkQualifiers(report, requestElement);
+ membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
+ return report.build();
+ }
+
+ /**
+ * Reports errors if a members injection method on a component is invalid.
+ *
+ * @throws IllegalArgumentException if the method doesn't have exactly one parameter
+ */
+ ValidationReport<ExecutableElement> validateMembersInjectionMethod(
+ ExecutableElement method, TypeMirror membersInjectedType) {
+ checkArgument(
+ method.getParameters().size() == 1, "expected a method with one parameter: %s", method);
+
+ ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
+ checkQualifiers(report, method);
+ checkQualifiers(report, method.getParameters().get(0));
+ membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
+ return report.build();
+ }
+
+ private void checkQualifiers(ValidationReport.Builder<?> report, Element element) {
+ for (AnnotationMirror qualifier : getQualifiers(element)) {
+ report.addError("Cannot inject members into qualified types", element, qualifier);
+ break; // just report on the first qualifier, in case there is more than one
+ }
+ }
+
+ private static final TypeVisitor<Void, ValidationReport.Builder<?>>
+ VALIDATE_MEMBERS_INJECTED_TYPE =
+ new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() {
+ // Only declared types can be members-injected.
+ @Override
+ protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) {
+ report.addError("Cannot inject members into " + type);
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) {
+ if (type.getTypeArguments().isEmpty()) {
+ // If the type is the erasure of a generic type, that means the user referred to
+ // Foo<T> as just 'Foo', which we don't allow. (This is a judgement call; we
+ // *could* allow it and instantiate the type bounds, but we don't.)
+ if (!MoreElements.asType(type.asElement()).getTypeParameters().isEmpty()) {
+ report.addError("Cannot inject members into raw type " + type);
+ }
+ } else {
+ // If the type has arguments, validate that each type argument is declared.
+ // Otherwise the type argument may be a wildcard (or other type), and we can't
+ // resolve that to actual types. For array type arguments, validate the type of the
+ // array.
+ for (TypeMirror arg : type.getTypeArguments()) {
+ if (!arg.accept(DECLARED_OR_ARRAY, null)) {
+ report.addError(
+ "Cannot inject members into types with unbounded type arguments: " + type);
+ }
+ }
+ }
+ return null;
+ }
+ };
+
+ // TODO(dpb): Can this be inverted so it explicitly rejects wildcards or type variables?
+ // This logic is hard to describe.
+ private static final TypeVisitor<Boolean, Void> DECLARED_OR_ARRAY =
+ new SimpleTypeVisitor8<Boolean, Void>(false) {
+ @Override
+ public Boolean visitArray(ArrayType arrayType, Void p) {
+ return arrayType
+ .getComponentType()
+ .accept(
+ new SimpleTypeVisitor8<Boolean, Void>(false) {
+ @Override
+ public Boolean visitDeclared(DeclaredType declaredType, Void p) {
+ for (TypeMirror arg : declaredType.getTypeArguments()) {
+ if (!arg.accept(this, null)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Boolean visitArray(ArrayType arrayType, Void p) {
+ return arrayType.getComponentType().accept(this, null);
+ }
+
+ @Override
+ public Boolean visitPrimitive(PrimitiveType primitiveType, Void p) {
+ return true;
+ }
+ },
+ null);
+ }
+
+ @Override
+ public Boolean visitDeclared(DeclaredType t, Void p) {
+ return true;
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/MembersInjectorGenerator.java b/java/dagger/internal/codegen/MembersInjectorGenerator.java
new file mode 100644
index 0000000..4360af7
--- /dev/null
+++ b/java/dagger/internal/codegen/MembersInjectorGenerator.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
+import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
+import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
+import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.membersInjectorOf;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.MembersInjector;
+import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * Generates {@link MembersInjector} implementations from {@link MembersInjectionBinding} instances.
+ */
+final class MembersInjectorGenerator extends SourceFileGenerator<MembersInjectionBinding> {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ @Inject
+ MembersInjectorGenerator(
+ Filer filer, DaggerElements elements, DaggerTypes types, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ this.types = types;
+ this.elements = elements;
+ }
+
+ @Override
+ ClassName nameGeneratedType(MembersInjectionBinding binding) {
+ return membersInjectorNameForType(binding.membersInjectedType());
+ }
+
+ @Override
+ Element originatingElement(MembersInjectionBinding binding) {
+ return binding.membersInjectedType();
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedTypeName, MembersInjectionBinding binding) {
+ // Empty members injection bindings are special and don't need source files.
+ if (binding.injectionSites().isEmpty()) {
+ return Optional.empty();
+ }
+ // We don't want to write out resolved bindings -- we want to write out the generic version.
+ checkState(
+ !binding.unresolved().isPresent(),
+ "tried to generate a MembersInjector for a binding of a resolved generic type: %s",
+ binding);
+
+ ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
+ TypeSpec.Builder injectorTypeBuilder =
+ classBuilder(generatedTypeName)
+ .addModifiers(PUBLIC, FINAL)
+ .addTypeVariables(typeParameters);
+
+ TypeName injectedTypeName = TypeName.get(binding.key().type());
+ TypeName implementedType = membersInjectorOf(injectedTypeName);
+ injectorTypeBuilder.addSuperinterface(implementedType);
+
+ MethodSpec.Builder injectMembersBuilder =
+ methodBuilder("injectMembers")
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(injectedTypeName, "instance");
+
+ ImmutableMap<Key, FrameworkField> fields = generateBindingFieldsForDependencies(binding);
+
+ ImmutableMap.Builder<Key, FieldSpec> dependencyFieldsBuilder = ImmutableMap.builder();
+
+ MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PUBLIC);
+
+ // We use a static create method so that generated components can avoid having
+ // to refer to the generic types of the factory.
+ // (Otherwise they may have visibility problems referring to the types.)
+ MethodSpec.Builder createMethodBuilder =
+ methodBuilder("create")
+ .returns(implementedType)
+ .addModifiers(PUBLIC, STATIC)
+ .addTypeVariables(typeParameters);
+
+ createMethodBuilder.addCode(
+ "return new $T(", parameterizedGeneratedTypeNameForBinding(binding));
+ ImmutableList.Builder<CodeBlock> constructorInvocationParameters = ImmutableList.builder();
+
+ boolean usesRawFrameworkTypes = false;
+ UniqueNameSet fieldNames = new UniqueNameSet();
+ for (Entry<Key, FrameworkField> fieldEntry : fields.entrySet()) {
+ Key dependencyKey = fieldEntry.getKey();
+ FrameworkField bindingField = fieldEntry.getValue();
+
+ // If the dependency type is not visible to this members injector, then use the raw framework
+ // type for the field.
+ boolean useRawFrameworkType =
+ !isTypeAccessibleFrom(dependencyKey.type(), generatedTypeName.packageName());
+
+ String fieldName = fieldNames.getUniqueName(bindingField.name());
+ TypeName fieldType = useRawFrameworkType ? bindingField.type().rawType : bindingField.type();
+ FieldSpec.Builder fieldBuilder = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL);
+ ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(fieldType, fieldName);
+
+ // If we're using the raw type for the field, then suppress the injectMembers method's
+ // unchecked-type warning and the field's and the constructor and create-method's
+ // parameters' raw-type warnings.
+ if (useRawFrameworkType) {
+ usesRawFrameworkTypes = true;
+ fieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
+ parameterBuilder.addAnnotation(suppressWarnings(RAWTYPES));
+ }
+ constructorBuilder.addParameter(parameterBuilder.build());
+ createMethodBuilder.addParameter(parameterBuilder.build());
+
+ FieldSpec field = fieldBuilder.build();
+ injectorTypeBuilder.addField(field);
+ constructorBuilder.addStatement("this.$1N = $1N", field);
+ dependencyFieldsBuilder.put(dependencyKey, field);
+ constructorInvocationParameters.add(CodeBlock.of("$N", field));
+ }
+
+ createMethodBuilder.addCode(
+ constructorInvocationParameters.build().stream().collect(toParametersCodeBlock()));
+ createMethodBuilder.addCode(");");
+
+ injectorTypeBuilder.addMethod(constructorBuilder.build());
+ injectorTypeBuilder.addMethod(createMethodBuilder.build());
+
+ ImmutableMap<Key, FieldSpec> dependencyFields = dependencyFieldsBuilder.build();
+
+ injectMembersBuilder.addCode(
+ InjectionSiteMethod.invokeAll(
+ binding.injectionSites(),
+ generatedTypeName,
+ CodeBlock.of("instance"),
+ binding.key().type(),
+ types,
+ frameworkFieldUsages(binding.dependencies(), dependencyFields)::get,
+ elements));
+
+ if (usesRawFrameworkTypes) {
+ injectMembersBuilder.addAnnotation(suppressWarnings(UNCHECKED));
+ }
+ injectorTypeBuilder.addMethod(injectMembersBuilder.build());
+
+ for (InjectionSite injectionSite : binding.injectionSites()) {
+ if (injectionSite.element().getEnclosingElement().equals(binding.membersInjectedType())) {
+ injectorTypeBuilder.addMethod(
+ InjectionSiteMethod.create(injectionSite, elements).toMethodSpec());
+ }
+ }
+
+ gwtIncompatibleAnnotation(binding).ifPresent(injectorTypeBuilder::addAnnotation);
+
+ return Optional.of(injectorTypeBuilder);
+ }
+}
diff --git a/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java b/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java
new file mode 100644
index 0000000..8e863e5
--- /dev/null
+++ b/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MEMBERS_INJECTORS;
+
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import javax.lang.model.type.TypeMirror;
+
+/** A {@code Provider<MembersInjector<Foo>>} creation expression. */
+final class MembersInjectorProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ProvisionBinding binding;
+
+ MembersInjectorProviderCreationExpression(
+ ProvisionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ TypeMirror membersInjectedType =
+ getOnlyElement(MoreTypes.asDeclared(binding.key().type()).getTypeArguments());
+
+ CodeBlock membersInjector =
+ binding.injectionSites().isEmpty()
+ ? CodeBlock.of("$T.<$T>noOp()", MEMBERS_INJECTORS, membersInjectedType)
+ : CodeBlock.of(
+ "$T.create($L)",
+ membersInjectorNameForType(MoreTypes.asTypeElement(membersInjectedType)),
+ componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+
+ // TODO(ronshapiro): consider adding a MembersInjectorBindingExpression to return this directly
+ // (as it's rarely requested as a Provider).
+ return CodeBlock.of("$T.create($L)", INSTANCE_FACTORY, membersInjector);
+ }
+
+ @Override
+ public boolean useInnerSwitchingProvider() {
+ return !binding.injectionSites().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/MethodBindingExpression.java b/java/dagger/internal/codegen/MethodBindingExpression.java
new file mode 100644
index 0000000..2120e80
--- /dev/null
+++ b/java/dagger/internal/codegen/MethodBindingExpression.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.PRIVATE_METHOD_SCOPED_FIELD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.VOLATILE;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.DoubleCheck;
+import dagger.internal.MemoizedSentinel;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression that wraps another in a nullary method on the component. */
+abstract class MethodBindingExpression extends BindingExpression {
+ private final BindingRequest request;
+ private final ResolvedBindings resolvedBindings;
+ private final ContributionBinding binding;
+ private final BindingMethodImplementation bindingMethodImplementation;
+ private final ComponentImplementation componentImplementation;
+ private final ProducerEntryPointView producerEntryPointView;
+ private final BindingExpression wrappedBindingExpression;
+ private final DaggerTypes types;
+
+ protected MethodBindingExpression(
+ BindingRequest request,
+ ResolvedBindings resolvedBindings,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression,
+ ComponentImplementation componentImplementation,
+ DaggerTypes types) {
+ this.request = checkNotNull(request);
+ this.resolvedBindings = resolvedBindings;
+ this.binding = resolvedBindings.contributionBinding();
+ this.bindingMethodImplementation = bindingMethodImplementation(methodImplementationStrategy);
+ this.wrappedBindingExpression = checkNotNull(wrappedBindingExpression);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.producerEntryPointView = new ProducerEntryPointView(types);
+ this.types = checkNotNull(types);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ if (request.frameworkType().isPresent()) {
+ // Initializing a framework instance that participates in a cycle requires that the underlying
+ // FrameworkInstanceBindingExpression is invoked in order for a cycle to be detected properly.
+ // When a MethodBindingExpression wraps a FrameworkInstanceBindingExpression, the wrapped
+ // expression will only be invoked once to implement the method body. This is a hack to work
+ // around that weirdness - methodImplementation.body() will invoke the framework instance
+ // initialization again in case the field is not fully initialized.
+ // TODO(b/121196706): use a less hacky approach to fix this bug
+ Object unused = methodBody();
+ }
+
+ addMethod();
+ return Expression.create(
+ returnType(),
+ requestingClass.equals(componentImplementation.name())
+ ? CodeBlock.of("$N()", methodName())
+ : CodeBlock.of("$T.this.$N()", componentImplementation.name(), methodName()));
+ }
+
+ @Override
+ final CodeBlock getModifiableBindingMethodImplementation(
+ ModifiableBindingMethod modifiableBindingMethod,
+ ComponentImplementation component,
+ DaggerTypes types) {
+ // A matching modifiable binding method means that we have previously created the binding method
+ // and we are now implementing it. If there is no matching method we need to first create the
+ // method. We create the method by deferring to getDependencyExpression (defined above) via a
+ // call to super.getModifiableBindingMethodImplementation().
+ if (supertypeModifiableBindingMethod().isPresent()) {
+ checkState(
+ supertypeModifiableBindingMethod().get().fulfillsSameRequestAs(modifiableBindingMethod));
+ return methodBody();
+ }
+ return super.getModifiableBindingMethodImplementation(
+ modifiableBindingMethod, component, types);
+ }
+
+ protected final Optional<ModifiableBindingMethod> supertypeModifiableBindingMethod() {
+ return componentImplementation.supertypeModifiableBindingMethod(request);
+ }
+
+ @Override
+ Expression getDependencyExpressionForComponentMethod(ComponentMethodDescriptor componentMethod,
+ ComponentImplementation component) {
+ return producerEntryPointView
+ .getProducerEntryPointField(this, componentMethod, component)
+ .orElseGet(
+ () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
+ }
+
+ /** Adds the method to the component (if necessary) the first time it's called. */
+ protected abstract void addMethod();
+
+ /** Returns the name of the method to call. */
+ protected abstract String methodName();
+
+ /**
+ * Returns {@code true} if the method of this binding expression is modifiable and is not a
+ * component method.
+ */
+ protected boolean isModifiableImplementationMethod() {
+ return false;
+ }
+
+ /** The method's body. */
+ protected final CodeBlock methodBody() {
+ return implementation(
+ wrappedBindingExpression.getDependencyExpression(componentImplementation.name())
+ ::codeBlock);
+ }
+
+ /** The method's body if this method is a component method. */
+ protected final CodeBlock methodBodyForComponentMethod(
+ ComponentMethodDescriptor componentMethod) {
+ return implementation(
+ wrappedBindingExpression.getDependencyExpressionForComponentMethod(
+ componentMethod, componentImplementation)
+ ::codeBlock);
+ }
+
+ private CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ return bindingMethodImplementation.implementation(simpleBindingExpression);
+ }
+
+ private BindingMethodImplementation bindingMethodImplementation(
+ MethodImplementationStrategy methodImplementationStrategy) {
+ switch (methodImplementationStrategy) {
+ case SIMPLE:
+ return new SimpleMethodImplementation();
+ case SINGLE_CHECK:
+ return new SingleCheckedMethodImplementation();
+ case DOUBLE_CHECK:
+ return new DoubleCheckedMethodImplementation();
+ }
+ throw new AssertionError(methodImplementationStrategy);
+ }
+
+ /** Returns the return type for the dependency request. */
+ protected TypeMirror returnType() {
+ if (request.isRequestKind(RequestKind.INSTANCE)
+ && binding.contributedPrimitiveType().isPresent()) {
+ return binding.contributedPrimitiveType().get();
+ }
+
+ if (matchingComponentMethod().isPresent()) {
+ // Component methods are part of the user-defined API, and thus we must use the user-defined
+ // type.
+ return matchingComponentMethod().get().resolvedReturnType(types);
+ }
+
+ // If the component is abstract, this method may be overridden by another implementation in a
+ // different package for which requestedType is inaccessible. In order to make that method
+ // overridable, we use the publicly accessible type. If the method is private, we don't need to
+ // worry about this, and instead just need to check accessibility of the file we're about to
+ // write
+ TypeMirror requestedType = request.requestedType(binding.contributedType(), types);
+ return isModifiableImplementationMethod()
+ ? types.publiclyAccessibleType(requestedType)
+ : types.accessibleType(requestedType, componentImplementation.name());
+ }
+
+ private Optional<ComponentMethodDescriptor> matchingComponentMethod() {
+ return componentImplementation.componentDescriptor().firstMatchingComponentMethod(request);
+ }
+
+ /** Strateg for implementing the body of this method. */
+ enum MethodImplementationStrategy {
+ SIMPLE,
+ SINGLE_CHECK,
+ DOUBLE_CHECK,
+ ;
+ }
+
+ private abstract static class BindingMethodImplementation {
+ /**
+ * Returns the method body, which contains zero or more statements (including semicolons).
+ *
+ * <p>If the implementation has a non-void return type, the body will also include the {@code
+ * return} statement.
+ *
+ * @param simpleBindingExpression the expression to retrieve an instance of this binding without
+ * the wrapping method.
+ */
+ abstract CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression);
+ }
+
+ /** Returns the {@code wrappedBindingExpression} directly. */
+ private static final class SimpleMethodImplementation extends BindingMethodImplementation {
+ @Override
+ CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ return CodeBlock.of("return $L;", simpleBindingExpression.get());
+ }
+ }
+
+ /**
+ * Defines a method body for single checked caching of the given {@code wrappedBindingExpression}.
+ */
+ private final class SingleCheckedMethodImplementation extends BindingMethodImplementation {
+ private final Supplier<FieldSpec> field = Suppliers.memoize(this::createField);
+
+ @Override
+ CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ String fieldExpression = field.get().name.equals("local") ? "this.local" : field.get().name;
+
+ CodeBlock.Builder builder = CodeBlock.builder()
+ .addStatement("Object local = $N", fieldExpression);
+
+ if (isNullable()) {
+ builder.beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class);
+ } else {
+ builder.beginControlFlow("if (local == null)");
+ }
+
+ return builder
+ .addStatement("local = $L", simpleBindingExpression.get())
+ .addStatement("$N = ($T) local", fieldExpression, returnType())
+ .endControlFlow()
+ .addStatement("return ($T) local", returnType())
+ .build();
+ }
+
+ FieldSpec createField() {
+ String name =
+ componentImplementation.getUniqueFieldName(
+ request.isRequestKind(RequestKind.INSTANCE)
+ ? KeyVariableNamer.name(binding.key())
+ // TODO(ronshapiro): Use KeyVariableNamer directly so we don't need to use a
+ // ResolvedBindings instance and construct a whole framework field just to get the
+ // name
+ : FrameworkField.forResolvedBindings(resolvedBindings, Optional.empty()).name());
+
+ FieldSpec.Builder builder = FieldSpec.builder(fieldType(), name, PRIVATE, VOLATILE);
+ if (isNullable()) {
+ builder.initializer("new $T()", MemoizedSentinel.class);
+ }
+
+ FieldSpec field = builder.build();
+ componentImplementation.addField(PRIVATE_METHOD_SCOPED_FIELD, field);
+ return field;
+ }
+
+ TypeName fieldType() {
+ if (isNullable()) {
+ // Nullable instances use `MemoizedSentinel` instead of `null` as the initialization value,
+ // so the field type must accept that and the return type
+ return TypeName.OBJECT;
+ }
+ TypeName returnType = TypeName.get(returnType());
+ return returnType.isPrimitive() ? returnType.box() : returnType;
+ }
+
+ private boolean isNullable() {
+ return request.isRequestKind(RequestKind.INSTANCE) && binding.isNullable();
+ }
+ }
+
+ /**
+ * Defines a method body for double checked caching of the given {@code wrappedBindingExpression}.
+ */
+ private final class DoubleCheckedMethodImplementation extends BindingMethodImplementation {
+ private final Supplier<String> fieldName = Suppliers.memoize(this::createField);
+
+ @Override
+ CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ String fieldExpression = fieldName.get().equals("local") ? "this.local" : fieldName.get();
+ return CodeBlock.builder()
+ .addStatement("$T local = $L", TypeName.OBJECT, fieldExpression)
+ .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
+ .beginControlFlow("synchronized (local)")
+ .addStatement("local = $L", fieldExpression)
+ .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
+ .addStatement("local = $L", simpleBindingExpression.get())
+ .addStatement("$1L = $2T.reentrantCheck($1L, local)", fieldExpression, DoubleCheck.class)
+ .endControlFlow()
+ .endControlFlow()
+ .endControlFlow()
+ .addStatement("return ($T) local", returnType())
+ .build();
+ }
+
+ private String createField() {
+ String name =
+ componentImplementation.getUniqueFieldName(KeyVariableNamer.name(binding.key()));
+ componentImplementation.addField(
+ PRIVATE_METHOD_SCOPED_FIELD,
+ FieldSpec.builder(TypeName.OBJECT, name, PRIVATE, VOLATILE)
+ .initializer("new $T()", MemoizedSentinel.class)
+ .build());
+ return name;
+ }
+ }
+
+}
diff --git a/java/dagger/internal/codegen/MethodSignature.java b/java/dagger/internal/codegen/MethodSignature.java
new file mode 100644
index 0000000..ef5c9c5
--- /dev/null
+++ b/java/dagger/internal/codegen/MethodSignature.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.List;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+@AutoValue
+abstract class MethodSignature {
+
+ abstract String name();
+
+ abstract ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>> parameterTypes();
+
+ abstract ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>> thrownTypes();
+
+ static MethodSignature forComponentMethod(
+ ComponentMethodDescriptor componentMethod, DeclaredType componentType, DaggerTypes types) {
+ ExecutableType methodType =
+ MoreTypes.asExecutable(types.asMemberOf(componentType, componentMethod.methodElement()));
+ return new AutoValue_MethodSignature(
+ componentMethod.methodElement().getSimpleName().toString(),
+ wrapInEquivalence(methodType.getParameterTypes()),
+ wrapInEquivalence(methodType.getThrownTypes()));
+ }
+
+ private static ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>>
+ wrapInEquivalence(List<? extends TypeMirror> types) {
+ return types.stream().map(MoreTypes.equivalence()::wrap).collect(toImmutableList());
+ }
+}
diff --git a/java/dagger/internal/codegen/MethodSignatureFormatter.java b/java/dagger/internal/codegen/MethodSignatureFormatter.java
new file mode 100644
index 0000000..012d5b0
--- /dev/null
+++ b/java/dagger/internal/codegen/MethodSignatureFormatter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Formats the signature of an {@link ExecutableElement} suitable for use in error messages.
+ */
+final class MethodSignatureFormatter extends Formatter<ExecutableElement> {
+ private final DaggerTypes types;
+
+ @Inject
+ MethodSignatureFormatter(DaggerTypes types) {
+ this.types = types;
+ }
+
+ /**
+ * A formatter that uses the type where the method is declared for the annotations and name of the
+ * method, but the method's resolved type as a member of {@code declaredType} for the key.
+ */
+ Formatter<ExecutableElement> typedFormatter(DeclaredType declaredType) {
+ return new Formatter<ExecutableElement>() {
+ @Override
+ public String format(ExecutableElement method) {
+ return MethodSignatureFormatter.this.format(
+ method,
+ MoreTypes.asExecutable(types.asMemberOf(declaredType, method)),
+ MoreElements.asType(method.getEnclosingElement()));
+ }
+ };
+ }
+
+ @Override public String format(ExecutableElement method) {
+ return format(method, Optional.empty());
+ }
+
+ /**
+ * Formats an ExecutableElement as if it were contained within the container, if the container is
+ * present.
+ */
+ public String format(ExecutableElement method, Optional<DeclaredType> container) {
+ TypeElement type = MoreElements.asType(method.getEnclosingElement());
+ ExecutableType executableType = MoreTypes.asExecutable(method.asType());
+ if (container.isPresent()) {
+ executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method));
+ type = MoreElements.asType(container.get().asElement());
+ }
+ return format(method, executableType, type);
+ }
+
+ private String format(
+ ExecutableElement method, ExecutableType methodType, TypeElement declaringType) {
+ StringBuilder builder = new StringBuilder();
+ // TODO(cgruber): AnnotationMirror formatter.
+ List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors();
+ if (!annotations.isEmpty()) {
+ Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator();
+ for (int i = 0; annotationIterator.hasNext(); i++) {
+ if (i > 0) {
+ builder.append(' ');
+ }
+ builder.append(formatAnnotation(annotationIterator.next()));
+ }
+ builder.append(' ');
+ }
+ if (method.getSimpleName().contentEquals("<init>")) {
+ builder.append(declaringType.getQualifiedName());
+ } else {
+ builder
+ .append(nameOfType(methodType.getReturnType()))
+ .append(' ')
+ .append(declaringType.getQualifiedName())
+ .append('.')
+ .append(method.getSimpleName());
+ }
+ builder.append('(');
+ checkState(method.getParameters().size() == methodType.getParameterTypes().size());
+ Iterator<? extends VariableElement> parameters = method.getParameters().iterator();
+ Iterator<? extends TypeMirror> parameterTypes = methodType.getParameterTypes().iterator();
+ for (int i = 0; parameters.hasNext(); i++) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ appendParameter(builder, parameters.next(), parameterTypes.next());
+ }
+ builder.append(')');
+ return builder.toString();
+ }
+
+ private static void appendParameter(StringBuilder builder, VariableElement parameter,
+ TypeMirror type) {
+ getQualifier(parameter)
+ .ifPresent(
+ qualifier -> {
+ builder.append(formatAnnotation(qualifier)).append(' ');
+ });
+ builder.append(nameOfType(type));
+ }
+
+ private static String nameOfType(TypeMirror type) {
+ return stripCommonTypePrefixes(type.toString());
+ }
+
+ private static String formatAnnotation(AnnotationMirror annotation) {
+ return stripCommonTypePrefixes(annotation.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/MissingBindingExpression.java b/java/dagger/internal/codegen/MissingBindingExpression.java
new file mode 100644
index 0000000..610d052
--- /dev/null
+++ b/java/dagger/internal/codegen/MissingBindingExpression.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A {@link ModifiableAbstractMethodBindingExpression} for a binding that is missing when generating
+ * the abstract base class implementation of a subcomponent. The (unimplemented) method is added to
+ * the {@link ComponentImplementation} when the dependency expression is requested. The method is
+ * overridden when generating the implementation of an ancestor component.
+ */
+final class MissingBindingExpression extends ModifiableAbstractMethodBindingExpression {
+ private final ComponentImplementation componentImplementation;
+ private final BindingRequest request;
+
+ MissingBindingExpression(
+ ComponentImplementation componentImplementation,
+ BindingRequest request,
+ Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
+ Optional<ComponentMethodDescriptor> matchingComponentMethod,
+ DaggerTypes types) {
+ super(
+ componentImplementation,
+ ModifiableBindingType.MISSING,
+ request,
+ matchingModifiableBindingMethod,
+ matchingComponentMethod,
+ types);
+ this.componentImplementation = componentImplementation;
+ this.request = request;
+ }
+
+ @Override
+ String chooseMethodName() {
+ return componentImplementation.getUniqueMethodName(request);
+ }
+
+ @Override
+ protected TypeMirror contributedType() {
+ return request.key().type();
+ }
+}
diff --git a/java/dagger/internal/codegen/MissingBindingValidator.java b/java/dagger/internal/codegen/MissingBindingValidator.java
new file mode 100644
index 0000000..f39361d
--- /dev/null
+++ b/java/dagger/internal/codegen/MissingBindingValidator.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.Keys.isValidImplicitProvisionKey;
+import static dagger.internal.codegen.Keys.isValidMembersInjectionKey;
+import static dagger.internal.codegen.RequestKinds.canBeSatisfiedByProductionBinding;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.MissingBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.Key;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+import javax.lang.model.type.TypeKind;
+
+/** Reports errors for missing bindings. */
+final class MissingBindingValidator implements BindingGraphPlugin {
+
+ private final DaggerTypes types;
+ private final InjectBindingRegistry injectBindingRegistry;
+
+ @Inject
+ MissingBindingValidator(
+ DaggerTypes types, InjectBindingRegistry injectBindingRegistry) {
+ this.types = types;
+ this.injectBindingRegistry = injectBindingRegistry;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/MissingBinding";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) {
+ // Don't report missing bindings when validating a full binding graph or a graph built from a
+ // subcomponent.
+ if (graph.isFullBindingGraph() || graph.rootComponentNode().isSubcomponent()) {
+ return;
+ }
+ graph
+ .missingBindings()
+ .forEach(missingBinding -> reportMissingBinding(missingBinding, graph, diagnosticReporter));
+ }
+
+ private void reportMissingBinding(
+ MissingBinding missingBinding, BindingGraph graph, DiagnosticReporter diagnosticReporter) {
+ diagnosticReporter.reportBinding(
+ ERROR, missingBinding, missingBindingErrorMessage(missingBinding, graph));
+ }
+
+ private String missingBindingErrorMessage(MissingBinding missingBinding, BindingGraph graph) {
+ Key key = missingBinding.key();
+ StringBuilder errorMessage = new StringBuilder();
+ // Wildcards should have already been checked by DependencyRequestValidator.
+ verify(!key.type().getKind().equals(TypeKind.WILDCARD), "unexpected wildcard request: %s", key);
+ // TODO(ronshapiro): replace "provided" with "satisfied"?
+ errorMessage.append(key).append(" cannot be provided without ");
+ if (isValidImplicitProvisionKey(key, types)) {
+ errorMessage.append("an @Inject constructor or ");
+ }
+ errorMessage.append("an @Provides-"); // TODO(dpb): s/an/a
+ if (allIncomingDependenciesCanUseProduction(missingBinding, graph)) {
+ errorMessage.append(" or @Produces-");
+ }
+ errorMessage.append("annotated method.");
+ if (isValidMembersInjectionKey(key) && typeHasInjectionSites(key)) {
+ errorMessage.append(
+ " This type supports members injection but cannot be implicitly provided.");
+ }
+ graph.bindings(key).stream()
+ .map(binding -> binding.componentPath().currentComponent())
+ .distinct()
+ .forEach(
+ component ->
+ errorMessage
+ .append("\nA binding with matching key exists in component: ")
+ .append(component.getQualifiedName()));
+ return errorMessage.toString();
+ }
+
+ private boolean allIncomingDependenciesCanUseProduction(
+ MissingBinding missingBinding, BindingGraph graph) {
+ return graph.network().inEdges(missingBinding).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .allMatch(edge -> dependencyCanBeProduction(edge, graph));
+ }
+
+ // TODO(ronshapiro): merge with
+ // ProvisionDependencyOnProduerBindingValidator.dependencyCanUseProduction
+ private boolean dependencyCanBeProduction(DependencyEdge edge, BindingGraph graph) {
+ Node source = graph.network().incidentNodes(edge).source();
+ if (source instanceof ComponentNode) {
+ return canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind());
+ }
+ if (source instanceof dagger.model.Binding) {
+ return ((dagger.model.Binding) source).isProduction();
+ }
+ throw new IllegalArgumentException(
+ "expected a dagger.model.Binding or ComponentNode: " + source);
+ }
+
+ private boolean typeHasInjectionSites(Key key) {
+ return injectBindingRegistry
+ .getOrFindMembersInjectionBinding(key)
+ .map(binding -> !binding.injectionSites().isEmpty())
+ .orElse(false);
+ }
+}
diff --git a/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java b/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
new file mode 100644
index 0000000..412acae
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PROTECTED;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A {@link BindingExpression} that invokes a method that encapsulates a binding that cannot be
+ * satisfied when generating the abstract base class implementation of a subcomponent. The
+ * (unimplemented) method is added to the {@link ComponentImplementation} when the dependency
+ * expression is requested. The method is overridden when generating the implementation of an
+ * ancestor component.
+ */
+abstract class ModifiableAbstractMethodBindingExpression extends BindingExpression {
+ private final ComponentImplementation componentImplementation;
+ private final ModifiableBindingType modifiableBindingType;
+ private final BindingRequest request;
+ private final Optional<ComponentMethodDescriptor> matchingComponentMethod;
+ private final DaggerTypes types;
+ private Optional<String> methodName;
+
+ ModifiableAbstractMethodBindingExpression(
+ ComponentImplementation componentImplementation,
+ ModifiableBindingType modifiableBindingType,
+ BindingRequest request,
+ Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
+ Optional<ComponentMethodDescriptor> matchingComponentMethod,
+ DaggerTypes types) {
+ this.componentImplementation = componentImplementation;
+ this.modifiableBindingType = modifiableBindingType;
+ this.request = request;
+ this.matchingComponentMethod = matchingComponentMethod;
+ this.types = types;
+ this.methodName =
+ initializeMethodName(matchingComponentMethod, matchingModifiableBindingMethod);
+ }
+
+ /**
+ * If this binding corresponds to an existing component method, or a known modifiable binding
+ * method, use them to initialize the method name, which is a signal to call the existing method
+ * rather than emit an abstract method.
+ */
+ private static Optional<String> initializeMethodName(
+ Optional<ComponentMethodDescriptor> matchingComponentMethod,
+ Optional<ModifiableBindingMethod> matchingModifiableBindingMethod) {
+ if (matchingComponentMethod.isPresent()) {
+ return Optional.of(matchingComponentMethod.get().methodElement().getSimpleName().toString());
+ }
+ if (matchingModifiableBindingMethod.isPresent()) {
+ return Optional.of(matchingModifiableBindingMethod.get().methodSpec().name);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ final Expression getDependencyExpression(ClassName requestingClass) {
+ addUnimplementedMethod();
+ return Expression.create(
+ returnType(),
+ componentImplementation.name().equals(requestingClass)
+ ? CodeBlock.of("$N()", methodName.get())
+ : CodeBlock.of("$T.this.$N()", componentImplementation.name(), methodName.get()));
+ }
+
+ private void addUnimplementedMethod() {
+ if (!methodName.isPresent()) {
+ // Only add the method once in case of repeated references to the missing binding.
+ methodName = Optional.of(chooseMethodName());
+ TypeMirror returnType = returnType();
+ componentImplementation.addModifiableBindingMethod(
+ modifiableBindingType,
+ request,
+ returnType,
+ MethodSpec.methodBuilder(methodName.get())
+ .addModifiers(PROTECTED, ABSTRACT)
+ .returns(TypeName.get(returnType))
+ .build(),
+ false /* finalized */);
+ }
+ }
+
+ /**
+ * The return type of this abstract method expression:
+ *
+ * <ul>
+ * <li>If there's a {@code matchingComponentMethod}, use its return type.
+ * <li>Otherwise, use the {@linkplain DaggerTypes#publiclyAccessibleType(TypeMirror) publicly
+ * accessible type} of the request. We can't use the {@linkplain
+ * Accessibility#isTypeAccessibleFrom(TypeMirror, String) type accessible from the current
+ * implementation's package} because a subclass implementation may be in a different package
+ * from which the request type is not accessible.
+ * </ul>
+ */
+ private TypeMirror returnType() {
+ if (matchingComponentMethod.isPresent()) {
+ return matchingComponentMethod.get().resolvedReturnType(types);
+ }
+
+ TypeMirror requestedType = request.requestedType(contributedType(), types);
+ return types.publiclyAccessibleType(requestedType);
+ }
+
+ /**
+ * The {@link ContributionBinding#contributedType() type contributed} by the binding of this
+ * expression. For missing bindings, this will be the key type.
+ */
+ protected abstract TypeMirror contributedType();
+
+ /** Returns a unique 'getter' method name for the current component. */
+ abstract String chooseMethodName();
+}
diff --git a/java/dagger/internal/codegen/ModifiableBindingExpressions.java b/java/dagger/internal/codegen/ModifiableBindingExpressions.java
new file mode 100644
index 0000000..76bcb8b
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableBindingExpressions.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static java.util.stream.Collectors.toList;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PROTECTED;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.MethodSpec;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ComponentImplementation.MethodSpecKind;
+import dagger.internal.codegen.MethodBindingExpression.MethodImplementationStrategy;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import java.util.Optional;
+
+/**
+ * A central repository of code expressions used to access modifiable bindings available to a
+ * component. A binding is modifiable if it can be modified across implementations of a
+ * subcomponent. This is only relevant for ahead-of-time subcomponents.
+ */
+final class ModifiableBindingExpressions {
+ private final Optional<ModifiableBindingExpressions> parent;
+ private final ComponentBindingExpressions bindingExpressions;
+ private final BindingGraph graph;
+ private final ComponentImplementation componentImplementation;
+ private final CompilerOptions compilerOptions;
+ private final DaggerTypes types;
+
+ ModifiableBindingExpressions(
+ Optional<ModifiableBindingExpressions> parent,
+ ComponentBindingExpressions bindingExpressions,
+ BindingGraph graph,
+ ComponentImplementation componentImplementation,
+ CompilerOptions compilerOptions,
+ DaggerTypes types) {
+ this.parent = parent;
+ this.bindingExpressions = bindingExpressions;
+ this.graph = graph;
+ this.componentImplementation = componentImplementation;
+ this.compilerOptions = compilerOptions;
+ this.types = types;
+ }
+
+ /**
+ * Adds {@code method} to the component implementation. If the binding for the method is
+ * modifiable, also registers the relevant modifiable binding information.
+ */
+ void addPossiblyModifiableComponentMethod(
+ ComponentMethodDescriptor componentMethod, MethodSpec method) {
+ BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get());
+ ModifiableBindingType modifiableBindingType = getModifiableBindingType(request);
+ if (modifiableBindingType.isModifiable()) {
+ componentImplementation.addModifiableComponentMethod(
+ modifiableBindingType,
+ request,
+ componentMethod.resolvedReturnType(types),
+ method,
+ newModifiableBindingWillBeFinalized(modifiableBindingType, request));
+ } else {
+ componentImplementation.addMethod(MethodSpecKind.COMPONENT_METHOD, method);
+ }
+ }
+
+ /**
+ * Returns the implementation of a modifiable binding method originally defined in a supertype
+ * implementation of this subcomponent. Returns {@link Optional#empty()} when the binding cannot
+ * or should not be modified by the current binding graph.
+ */
+ Optional<ModifiableBindingMethod> possiblyReimplementedMethod(
+ ModifiableBindingMethod modifiableBindingMethod) {
+ checkState(componentImplementation.superclassImplementation().isPresent());
+ BindingRequest request = modifiableBindingMethod.request();
+ ModifiableBindingType newModifiableBindingType = getModifiableBindingType(request);
+ ModifiableBindingType oldModifiableBindingType = modifiableBindingMethod.type();
+ boolean modifiableBindingTypeChanged =
+ !newModifiableBindingType.equals(oldModifiableBindingType);
+
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
+ // Don't reimplement modifiable bindings that were perceived to be provision bindings in a
+ // superclass implementation but are now production bindings.
+ if ((modifiableBindingTypeChanged
+ // Optional bindings don't need the same treatment since the only transition they can
+ // make is empty -> present. In that case, the Producer<Optional<T>> will be overridden
+ // and the absentOptionalProvider() will be a dangling reference that is never attempted
+ // to be overridden.
+ || newModifiableBindingType.equals(ModifiableBindingType.MULTIBINDING))
+ && resolvedBindings != null
+ && resolvedBindings.bindingType().equals(BindingType.PRODUCTION)
+ && !request.canBeSatisfiedByProductionBinding()) {
+ return oldModifiableBindingType.hasBaseClassImplementation()
+ ? Optional.empty()
+ : Optional.of(
+ reimplementedMethod(
+ modifiableBindingMethod,
+ newModifiableBindingType,
+ new PrunedConcreteMethodBindingExpression(),
+ componentImplementation.isAbstract()));
+ }
+
+ if (modifiableBindingTypeChanged
+ && !newModifiableBindingType.hasBaseClassImplementation()
+ && (oldModifiableBindingType.hasBaseClassImplementation()
+ || componentImplementation.isAbstract())) {
+ // We don't want to override one abstract method with another one. However, If the component
+ // is not abstract (such as a transition from GENERATED_INSTANCE -> MISSING), we must provide
+ // an implementation like normal.
+ return Optional.empty();
+ }
+
+ if (modifiableBindingTypeChanged
+ || shouldModifyImplementation(newModifiableBindingType, request)) {
+ boolean markMethodFinal =
+ knownModifiableBindingWillBeFinalized(modifiableBindingMethod)
+ // no need to mark the method final if the component implementation will be final
+ && componentImplementation.isAbstract();
+ return Optional.of(
+ reimplementedMethod(
+ modifiableBindingMethod,
+ newModifiableBindingType,
+ bindingExpressions.getBindingExpression(request),
+ markMethodFinal));
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns a new {@link ModifiableBindingMethod} that overrides {@code supertypeMethod} and is
+ * implemented with {@code bindingExpression}.
+ */
+ private ModifiableBindingMethod reimplementedMethod(
+ ModifiableBindingMethod supertypeMethod,
+ ModifiableBindingType newModifiableBindingType,
+ BindingExpression bindingExpression,
+ boolean markMethodFinal) {
+ MethodSpec baseMethod = supertypeMethod.methodSpec();
+ return supertypeMethod.reimplement(
+ newModifiableBindingType,
+ MethodSpec.methodBuilder(baseMethod.name)
+ .addModifiers(baseMethod.modifiers.contains(PUBLIC) ? PUBLIC : PROTECTED)
+ .addModifiers(markMethodFinal ? ImmutableSet.of(FINAL) : ImmutableSet.of())
+ .returns(baseMethod.returnType)
+ .addAnnotation(Override.class)
+ .addCode(
+ bindingExpression.getModifiableBindingMethodImplementation(
+ supertypeMethod, componentImplementation, types))
+ .build(),
+ markMethodFinal);
+ }
+
+ /**
+ * Returns true if a modifiable binding method that was registered in a superclass implementation
+ * of this subcomponent should be marked as "finalized" if it is being overridden by this
+ * subcomponent implementation. "Finalized" means we should not attempt to modify the binding in
+ * any subcomponent subclass.
+ */
+ private boolean knownModifiableBindingWillBeFinalized(
+ ModifiableBindingMethod modifiableBindingMethod) {
+ ModifiableBindingType newModifiableBindingType =
+ getModifiableBindingType(modifiableBindingMethod.request());
+ if (!newModifiableBindingType.isModifiable()) {
+ // If a modifiable binding has become non-modifiable it is final by definition.
+ return true;
+ }
+ return modifiableBindingWillBeFinalized(
+ newModifiableBindingType,
+ shouldModifyImplementation(newModifiableBindingType, modifiableBindingMethod.request()));
+ }
+
+ /**
+ * Returns true if a newly discovered modifiable binding method, once it is defined in this
+ * subcomponent implementation, should be marked as "finalized", meaning we should not attempt to
+ * modify the binding in any subcomponent subclass.
+ */
+ private boolean newModifiableBindingWillBeFinalized(
+ ModifiableBindingType modifiableBindingType, BindingRequest request) {
+ return modifiableBindingWillBeFinalized(
+ modifiableBindingType, shouldModifyImplementation(modifiableBindingType, request));
+ }
+
+ /**
+ * Returns true if we shouldn't attempt to further modify a modifiable binding once we complete
+ * the implementation for the current subcomponent.
+ */
+ private boolean modifiableBindingWillBeFinalized(
+ ModifiableBindingType modifiableBindingType, boolean modifyingBinding) {
+ switch (modifiableBindingType) {
+ case MISSING:
+ case BINDS_METHOD_WITH_MISSING_DEPENDENCY:
+ case GENERATED_INSTANCE:
+ case OPTIONAL:
+ case INJECTION:
+ // Once we modify any of the above a single time, then they are finalized.
+ return modifyingBinding;
+ case MULTIBINDING:
+ return false;
+ default:
+ throw new IllegalStateException(
+ String.format(
+ "Building binding expression for unsupported ModifiableBindingType [%s].",
+ modifiableBindingType));
+ }
+ }
+
+ /**
+ * Creates a binding expression for a binding if it may be modified across implementations of a
+ * subcomponent.
+ */
+ Optional<BindingExpression> maybeCreateModifiableBindingExpression(BindingRequest request) {
+ ModifiableBindingType type = getModifiableBindingType(request);
+ if (!type.isModifiable()) {
+ return Optional.empty();
+ }
+ return Optional.of(createModifiableBindingExpression(type, request));
+ }
+
+ /** Creates a binding expression for a modifiable binding. */
+ private BindingExpression createModifiableBindingExpression(
+ ModifiableBindingType type, BindingRequest request) {
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
+ Optional<ModifiableBindingMethod> matchingModifiableBindingMethod =
+ componentImplementation.getModifiableBindingMethod(request);
+ Optional<ComponentMethodDescriptor> matchingComponentMethod =
+ graph.componentDescriptor().firstMatchingComponentMethod(request);
+ switch (type) {
+ case GENERATED_INSTANCE:
+ // If the subcomponent is abstract then we need to define an (un-implemented)
+ // DeferredModifiableBindingExpression.
+ if (componentImplementation.isAbstract()) {
+ return new DeferredModifiableBindingExpression(
+ componentImplementation,
+ type,
+ resolvedBindings.contributionBinding(),
+ request,
+ matchingModifiableBindingMethod,
+ matchingComponentMethod,
+ types);
+ }
+ // Otherwise return a concrete implementation.
+ return bindingExpressions.createBindingExpression(resolvedBindings, request);
+
+ case MISSING:
+ // If we need an expression for a missing binding and the current implementation is
+ // abstract, then we need an (un-implemented) MissingBindingExpression.
+ if (componentImplementation.isAbstract()) {
+ return new MissingBindingExpression(
+ componentImplementation,
+ request,
+ matchingModifiableBindingMethod,
+ matchingComponentMethod,
+ types);
+ }
+ // Otherwise we assume that it is valid to have a missing binding as it is part of a
+ // dependency chain that has been passively pruned.
+ // TODO(b/117833324): Identify pruned bindings when generating the subcomponent
+ // implementation in which the bindings are pruned. If we hold a reference to the binding
+ // graph used to generate a given implementation then we can compare a implementation's
+ // graph with its superclass implementation's graph to detect pruned dependency branches.
+ return new PrunedConcreteMethodBindingExpression();
+
+ case BINDS_METHOD_WITH_MISSING_DEPENDENCY:
+ checkState(componentImplementation.isAbstract());
+ return new DeferredModifiableBindingExpression(
+ componentImplementation,
+ type,
+ resolvedBindings.contributionBinding(),
+ request,
+ matchingModifiableBindingMethod,
+ matchingComponentMethod,
+ types);
+
+ case OPTIONAL:
+ case MULTIBINDING:
+ case INJECTION:
+ return bindingExpressions.wrapInMethod(
+ resolvedBindings,
+ request,
+ bindingExpressions.createBindingExpression(resolvedBindings, request));
+ default:
+ throw new IllegalStateException(
+ String.format(
+ "Building binding expression for unsupported ModifiableBindingType [%s].", type));
+ }
+ }
+
+ /**
+ * The reason why a binding may need to be modified across implementations of a subcomponent, if
+ * at all.
+ */
+ ModifiableBindingType getModifiableBindingType(BindingRequest request) {
+ if (!compilerOptions.aheadOfTimeSubcomponents()) {
+ return ModifiableBindingType.NONE;
+ }
+
+ // When generating a component the binding is not considered modifiable. Bindings are modifiable
+ // only across subcomponent implementations.
+ if (!componentImplementation.componentDescriptor().isSubcomponent()) {
+ return ModifiableBindingType.NONE;
+ }
+
+ if (request.requestKind().filter(RequestKinds::isDerivedFromProvider).isPresent()) {
+ return ModifiableBindingType.NONE;
+ }
+
+ if (resolvedInThisComponent(request)) {
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
+ if (resolvedBindings.contributionBindings().isEmpty()) {
+ // TODO(ronshapiro): Confirm whether a resolved binding must have a single contribution
+ // binding.
+ return ModifiableBindingType.NONE;
+ }
+
+ ContributionBinding binding = resolvedBindings.contributionBinding();
+ if (binding.requiresGeneratedInstance()) {
+ return ModifiableBindingType.GENERATED_INSTANCE;
+ }
+
+ if (binding.kind().equals(BindingKind.DELEGATE)
+ && graph
+ .contributionBindings()
+ .get(getOnlyElement(binding.dependencies()).key())
+ .isEmpty()) {
+ return ModifiableBindingType.BINDS_METHOD_WITH_MISSING_DEPENDENCY;
+ }
+
+ if (binding.kind().equals(BindingKind.OPTIONAL) && binding.dependencies().isEmpty()) {
+ // only empty optional bindings can be modified
+ return ModifiableBindingType.OPTIONAL;
+ }
+
+ if (binding.isSyntheticMultibinding()) {
+ return ModifiableBindingType.MULTIBINDING;
+ }
+
+ if (binding.kind().equals(BindingKind.INJECTION)) {
+ return ModifiableBindingType.INJECTION;
+ }
+ } else if (!resolvableBinding(request)) {
+ return ModifiableBindingType.MISSING;
+ }
+
+ return ModifiableBindingType.NONE;
+ }
+
+ /**
+ * Returns true if the current binding graph can, and should, modify a binding by overriding a
+ * modifiable binding method.
+ */
+ private boolean shouldModifyImplementation(
+ ModifiableBindingType modifiableBindingType, BindingRequest request) {
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
+ if (request.requestKind().isPresent()) {
+ switch (request.requestKind().get()) {
+ case FUTURE:
+ // Futures backed by production bindings are always requested by a Producer.get() call, so
+ // if the binding is modifiable, the producer will be wrapped in a modifiable method and
+ // the future can refer to that method; even if the producer binding is modified,
+ // getModifiableProducer().get() will never need to be modified. Furthermore, because
+ // cancellation is treated by wrapped producers, and those producers point to the
+ // modifiable producer wrapper methods, we never need or want to change the access of
+ // these wrapped producers for entry methods
+ //
+ // Futures backed by provision bindings are inlined and contain no wrapping producer, so
+ // if the binding is modifiable and is resolved as a provision binding in a superclass
+ // but later resolved as a production binding, we can't take the same shortcut as before.
+ Optional<ComponentImplementation> superclassImplementation =
+ componentImplementation.superclassImplementation();
+ if (superclassImplementation.isPresent()) {
+ if (superclassImplementation.get().isDeserializedImplementation()) {
+ // TODO(b/117833324): consider serializing the binding type so that we don't need to
+ // branch here. Or, instead, consider removing this optimization entirely if there
+ // aren't that many FUTURE entry point methods to justify the extra code.
+ break;
+ } else {
+ return bindingTypeChanged(request, resolvedBindings);
+ }
+ }
+ return false;
+
+ case LAZY:
+ case PROVIDER_OF_LAZY:
+ // Lazy and ProviderOfLazy are always created from a Provider, and therefore this request
+ // never needs to be modifiable. It will refer (via DoubleCheck.lazy() or
+ // ProviderOfLazy.create()) to the modifiable method and not the framework instance.
+ return false;
+
+ case MEMBERS_INJECTION:
+ case PRODUCED:
+ // MEMBERS_INJECTION has a completely different code path for binding expressions, and
+ // PRODUCED requests are only requestable in @Produces methods, which are hidden from
+ // generated components inside Producer factories
+ throw new AssertionError(request);
+
+ case INSTANCE:
+ case PROVIDER:
+ case PRODUCER:
+ // These may be modifiable, so run through the regular logic. They're spelled out
+ // explicitly so that ErrorProne will detect if a new enum value is created and missing
+ // from this list.
+ break;
+ }
+ }
+
+ switch (modifiableBindingType) {
+ case GENERATED_INSTANCE:
+ return !componentImplementation.isAbstract();
+
+ case MISSING:
+ // TODO(b/117833324): investigate beder@'s comment about having intermediate component
+ // ancestors satisfy missing bindings of their children with their own missing binding
+ // methods so that we can minimize the cases where we need to reach into doubly-nested
+ // descendant component implementations.
+
+ // Implement a missing binding if it is resolvable, or if we're generating a concrete
+ // subcomponent implementation. If a binding is still missing when the subcomponent
+ // implementation is concrete then it is assumed to be part of a dependency that would have
+ // been passively pruned when implementing the full component hierarchy.
+ return resolvableBinding(request) || !componentImplementation.isAbstract();
+
+ case BINDS_METHOD_WITH_MISSING_DEPENDENCY:
+ DependencyRequest dependency =
+ getOnlyElement(resolvedBindings.contributionBinding().dependencies());
+ return !graph.contributionBindings().get(dependency.key()).isEmpty();
+
+ case OPTIONAL:
+ // Only override optional binding methods if we have a non-empty binding.
+ return !resolvedBindings.contributionBinding().dependencies().isEmpty();
+
+ case MULTIBINDING:
+ // Only modify a multibinding if there are new contributions.
+ return !componentImplementation
+ .superclassContributionsMade(request)
+ .containsAll(
+ resolvedBindings.contributionBinding().dependencies().stream()
+ .map(DependencyRequest::key)
+ .collect(toList()));
+
+ case INJECTION:
+ return !resolvedBindings.contributionBinding().kind().equals(BindingKind.INJECTION);
+
+ default:
+ throw new IllegalStateException(
+ String.format(
+ "Overriding modifiable binding method with unsupported ModifiableBindingType [%s].",
+ modifiableBindingType));
+ }
+ }
+
+ /**
+ * Returns {@code true} if the {@link BindingType} for {@code request} is not the same in this
+ * implementation and it's superclass implementation.
+ */
+ private boolean bindingTypeChanged(BindingRequest request, ResolvedBindings resolvedBindings) {
+ BindingGraph superclassGraph =
+ componentImplementation.superclassImplementation().get().graph();
+ ResolvedBindings superclassBindings = superclassGraph.resolvedBindings(request);
+ return superclassBindings != null
+ && resolvedBindings != null
+ && !superclassBindings.bindingType().equals(resolvedBindings.bindingType());
+ }
+
+ /**
+ * Returns true if the binding can be resolved by the graph for this component or any parent
+ * component.
+ */
+ private boolean resolvableBinding(BindingRequest request) {
+ for (ModifiableBindingExpressions expressions = this;
+ expressions != null;
+ expressions = expressions.parent.orElse(null)) {
+ if (expressions.resolvedInThisComponent(request)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Returns true if the binding can be resolved by the graph for this component. */
+ private boolean resolvedInThisComponent(BindingRequest request) {
+ ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
+ return resolvedBindings != null
+ && !resolvedBindings.bindingsOwnedBy(graph.componentDescriptor()).isEmpty();
+ }
+
+ /**
+ * Wraps a modifiable binding expression in a method that can be overridden in a subclass
+ * implementation.
+ */
+ BindingExpression wrapInModifiableMethodBindingExpression(
+ BindingRequest request,
+ ResolvedBindings resolvedBindings,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression) {
+ ModifiableBindingType modifiableBindingType = getModifiableBindingType(request);
+ checkState(modifiableBindingType.isModifiable());
+ return new ModifiableConcreteMethodBindingExpression(
+ request,
+ resolvedBindings,
+ methodImplementationStrategy,
+ wrappedBindingExpression,
+ modifiableBindingType,
+ componentImplementation,
+ newModifiableBindingWillBeFinalized(modifiableBindingType, request),
+ types);
+ }
+}
diff --git a/java/dagger/internal/codegen/ModifiableBindingMethods.java b/java/dagger/internal/codegen/ModifiableBindingMethods.java
new file mode 100644
index 0000000..ead708d
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableBindingMethods.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Verify.verify;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.MethodSpec;
+import java.util.Map;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A registry for those methods which each wrap a binding whose definition may be modified across
+ * each class in the class hierarchy implementing a subcomponent. Subcomponent implementations are
+ * spread across a class hierarchy when generating ahead-of-time subcomponents. There is one
+ * subcomponent implementation class for each of the subcomponent's ancestor components. An instance
+ * of {@link ModifiableBindingMethod} is associated with a single class in this hierarchy. For a
+ * given subcomponent implementation class we can use the {@link ModifiableBindingMethod}s of its
+ * superclasses to know what binding methods to attempt to modify.
+ */
+final class ModifiableBindingMethods {
+ private final Map<BindingRequest, ModifiableBindingMethod> methods = Maps.newLinkedHashMap();
+
+ /** Registers a new method encapsulating a modifiable binding. */
+ void addModifiableMethod(
+ ModifiableBindingType type,
+ BindingRequest request,
+ TypeMirror returnType,
+ MethodSpec method,
+ boolean finalized) {
+ // It's ok for the type to not be modifiable, since it could be overriding a previously
+ // modifiable method (such as with addReimplementedMethod).
+ addMethod(ModifiableBindingMethod.create(type, request, returnType, method, finalized));
+ }
+
+ /** Registers a reimplemented modifiable method. */
+ void addReimplementedMethod(ModifiableBindingMethod method) {
+ addMethod(method);
+ }
+
+ private void addMethod(ModifiableBindingMethod method) {
+ ModifiableBindingMethod previousMethod = methods.put(method.request(), method);
+ verify(
+ previousMethod == null,
+ "registering %s but %s is already registered for the same binding request",
+ method,
+ previousMethod);
+ }
+
+ /** Returns all {@link ModifiableBindingMethod}s that have not been marked as finalized. */
+ ImmutableMap<BindingRequest, ModifiableBindingMethod> getNonFinalizedMethods() {
+ return ImmutableMap.copyOf(Maps.filterValues(methods, m -> !m.finalized()));
+ }
+
+ /** Returns the {@link ModifiableBindingMethod} for the given binding if present. */
+ Optional<ModifiableBindingMethod> getMethod(BindingRequest request) {
+ return Optional.ofNullable(methods.get(request));
+ }
+
+ /** Returns all of the {@link ModifiableBindingMethod}s. */
+ ImmutableList<ModifiableBindingMethod> allMethods() {
+ return ImmutableList.copyOf(methods.values());
+ }
+
+ /** Whether a given binding has been marked as finalized. */
+ // TODO(ronshapiro): possibly rename this to something that indicates that the BindingRequest for
+ // `method` has been finalized in *this* component implementation?
+ boolean finalized(ModifiableBindingMethod method) {
+ ModifiableBindingMethod storedMethod = methods.get(method.request());
+ return storedMethod != null && storedMethod.finalized();
+ }
+
+ @AutoValue
+ abstract static class ModifiableBindingMethod {
+ private static ModifiableBindingMethod create(
+ ModifiableBindingType type,
+ BindingRequest request,
+ TypeMirror returnType,
+ MethodSpec methodSpec,
+ boolean finalized) {
+ return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
+ type, request, MoreTypes.equivalence().wrap(returnType), methodSpec, finalized);
+ }
+
+ /** Creates a {@ModifiableBindingMethod} that reimplements the current method. */
+ ModifiableBindingMethod reimplement(
+ ModifiableBindingType newModifiableBindingType,
+ MethodSpec newImplementation,
+ boolean finalized) {
+ return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
+ newModifiableBindingType, request(), returnTypeWrapper(), newImplementation, finalized);
+ }
+
+ abstract ModifiableBindingType type();
+
+ abstract BindingRequest request();
+
+ final TypeMirror returnType() {
+ return returnTypeWrapper().get();
+ }
+
+ abstract Equivalence.Wrapper<TypeMirror> returnTypeWrapper();
+
+ abstract MethodSpec methodSpec();
+
+ abstract boolean finalized();
+
+ /** Whether a {@link ModifiableBindingMethod} is for the same binding request. */
+ boolean fulfillsSameRequestAs(ModifiableBindingMethod other) {
+ return request().equals(other.request());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ModifiableBindingType.java b/java/dagger/internal/codegen/ModifiableBindingType.java
new file mode 100644
index 0000000..7e43ec1
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableBindingType.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * A label for a binding indicating whether, and how, it may be redefined across implementations of
+ * a subcomponent.
+ *
+ * <p>A subcomponent has multiple implementations only when generating ahead-of-time subcomponents.
+ * Specifically, each subcomponent type in a component hierarchy is implemented as an abstract
+ * class, and descendent components are implemented as abstract inner classes. A consequence of this
+ * is that a given subcomponent has an implementation for each ancestor component. Each
+ * implementation represents a different sub-binding-graph of the full subcomponent. A binding is
+ * modifiable if it's definition may change depending on the characteristics of its ancestor
+ * components.
+ */
+enum ModifiableBindingType {
+ /** A binding that is not modifiable */
+ NONE,
+
+ /**
+ * A binding that is missing when generating the abstract base class implementation of a
+ * subcomponent.
+ */
+ MISSING,
+
+ /**
+ * A binding that requires an instance of a generated type. These binding are modifiable in the
+ * sense that they are encapsulated in a method when they are first required, possibly in an
+ * abstract implementation of a subcomponent, where, in general, no concrete instances of
+ * generated types are available, and the method is satisfied in a final concrete implementation.
+ */
+ GENERATED_INSTANCE,
+
+ /**
+ * Multibindings may have contributions come from any ancestor component. Therefore, each
+ * implementation of a subcomponent may have newly available contributions, and so the binding
+ * method is reimplemented with each subcomponent implementation.
+ */
+ MULTIBINDING,
+
+ /**
+ * A Optional binding that may be empty when looking at a partial binding graph, but bound to a
+ * value when considering the complete binding graph, thus modifiable across subcomponent
+ * implementations.
+ */
+ OPTIONAL,
+
+ /**
+ * If a binding is defined according to an {@code @Inject} annotated constructor on the object it
+ * is valid for that binding to be redefined a single time by an {@code @Provides} annotated
+ * module method. It is possible that the {@code @Provides} binding isn't available in a partial
+ * binding graph, but becomes available when considering a more complete binding graph, therefore
+ * such bindings are modifiable across subcomponent implementations.
+ */
+ INJECTION,
+
+ /**
+ * A {@link dagger.Binds} method whose dependency is {@link #MISSING}.
+ *
+ * <p>There's not much to do for @Binds bindings if the dependency is missing - at best, if the
+ * dependency is a weaker scope/unscoped, we save only a few lines that implement the scoping. But
+ * it's also possible, if the dependency is the same or stronger scope, that no extra code is
+ * necessary, in which case we'd be overriding a method that just returns another.
+ */
+ BINDS_METHOD_WITH_MISSING_DEPENDENCY,
+ ;
+
+ private static final ImmutableSet<ModifiableBindingType> TYPES_WITH_BASE_CLASS_IMPLEMENTATIONS =
+ ImmutableSet.of(NONE, INJECTION, MULTIBINDING, OPTIONAL);
+
+ boolean isModifiable() {
+ return !equals(NONE);
+ }
+
+ /**
+ * Returns true if the method encapsulating the modifiable binding should have a concrete
+ * implementation in the abstract base class for a subcomponent.
+ */
+ boolean hasBaseClassImplementation() {
+ return TYPES_WITH_BASE_CLASS_IMPLEMENTATIONS.contains(this);
+ }
+}
diff --git a/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java b/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
new file mode 100644
index 0000000..907466b
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PROTECTED;
+
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Optional;
+
+/**
+ * A binding expression that wraps a modifiable binding expression in a public, no-arg method.
+ *
+ * <p>Dependents of this binding expression will just call the modifiable binding method.
+ */
+final class ModifiableConcreteMethodBindingExpression extends MethodBindingExpression {
+
+ private final BindingRequest request;
+ private final ModifiableBindingType modifiableBindingType;
+ private final ComponentImplementation componentImplementation;
+ private final boolean bindingCannotBeModified;
+ private Optional<String> methodName = Optional.empty();
+
+ ModifiableConcreteMethodBindingExpression(
+ BindingRequest request,
+ ResolvedBindings resolvedBindings,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression,
+ ModifiableBindingType modifiableBindingType,
+ ComponentImplementation componentImplementation,
+ boolean bindingCannotBeModified,
+ DaggerTypes types) {
+ super(
+ request,
+ resolvedBindings,
+ methodImplementationStrategy,
+ wrappedBindingExpression,
+ componentImplementation,
+ types);
+ this.request = checkNotNull(request);
+ this.modifiableBindingType = checkNotNull(modifiableBindingType);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.bindingCannotBeModified = bindingCannotBeModified;
+ }
+
+ @Override
+ protected void addMethod() {
+ if (methodName.isPresent()) {
+ return;
+ }
+
+ if (supertypeModifiableBindingMethod().isPresent()) {
+ methodName = supertypeModifiableBindingMethod().map(method -> method.methodSpec().name);
+ return;
+ }
+
+ // Add the modifiable binding method to the component if we haven't already.
+ methodName = Optional.of(componentImplementation.getUniqueMethodName(request));
+ componentImplementation.addModifiableBindingMethod(
+ modifiableBindingType,
+ request,
+ returnType(),
+ methodBuilder(methodName.get())
+ .addModifiers(bindingCannotBeModified ? PRIVATE : PROTECTED)
+ .returns(TypeName.get(returnType()))
+ .addCode(methodBody())
+ .build(),
+ bindingCannotBeModified);
+ }
+
+ @Override
+ protected String methodName() {
+ checkState(methodName.isPresent(), "addMethod() must be called before methodName().");
+ return methodName.get();
+ }
+
+ @Override
+ protected boolean isModifiableImplementationMethod() {
+ return true;
+ }
+}
diff --git a/java/dagger/internal/codegen/ModuleAnnotation.java b/java/dagger/internal/codegen/ModuleAnnotation.java
new file mode 100644
index 0000000..27dd071
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleAnnotation.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.producers.ProducerModule;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.TypeElement;
+
+/** A {@code @Module} or {@code @ProducerModule} annotation. */
+@AutoValue
+abstract class ModuleAnnotation {
+ private static final ImmutableSet<Class<? extends Annotation>> MODULE_ANNOTATIONS =
+ ImmutableSet.of(Module.class, ProducerModule.class);
+
+ /** The annotation itself. */
+ // This does not use AnnotationMirrors.equivalence() because we want the actual annotation
+ // instance.
+ abstract AnnotationMirror annotation();
+
+ /** The type of the annotation. */
+ @Memoized
+ Class<?> annotationClass() {
+ try {
+ return Class.forName(
+ asTypeElement(annotation().getAnnotationType()).getQualifiedName().toString());
+ } catch (ClassNotFoundException e) {
+ AssertionError assertionError = new AssertionError();
+ assertionError.initCause(e);
+ throw assertionError;
+ }
+ }
+
+ /**
+ * The types specified in the {@code includes} attribute.
+ *
+ * @throws IllegalArgumentException if any of the values are error types
+ */
+ @Memoized
+ ImmutableList<TypeElement> includes() {
+ return includesAsAnnotationValues().stream()
+ .map(MoreAnnotationValues::asType)
+ .map(MoreTypes::asTypeElement)
+ .collect(toImmutableList());
+ }
+
+ /** The values specified in the {@code includes} attribute. */
+ @Memoized
+ ImmutableList<AnnotationValue> includesAsAnnotationValues() {
+ return asAnnotationValues(getAnnotationValue(annotation(), "includes"));
+ }
+
+ /**
+ * The types specified in the {@code subcomponents} attribute.
+ *
+ * @throws IllegalArgumentException if any of the values are error types
+ */
+ @Memoized
+ ImmutableList<TypeElement> subcomponents() {
+ return subcomponentsAsAnnotationValues().stream()
+ .map(MoreAnnotationValues::asType)
+ .map(MoreTypes::asTypeElement)
+ .collect(toImmutableList());
+ }
+
+ /** The values specified in the {@code subcomponents} attribute. */
+ @Memoized
+ ImmutableList<AnnotationValue> subcomponentsAsAnnotationValues() {
+ return asAnnotationValues(getAnnotationValue(annotation(), "subcomponents"));
+ }
+
+ /** Returns {@code true} if the argument is a {@code @Module} or {@code @ProducerModule}. */
+ static boolean isModuleAnnotation(AnnotationMirror annotation) {
+ return MODULE_ANNOTATIONS.stream()
+ .map(Class::getCanonicalName)
+ .anyMatch(asTypeElement(annotation.getAnnotationType()).getQualifiedName()::contentEquals);
+ }
+
+ /** The module annotation types. */
+ static ImmutableSet<Class<? extends Annotation>> moduleAnnotations() {
+ return MODULE_ANNOTATIONS;
+ }
+
+ /**
+ * Creates an object that represents a {@code @Module} or {@code @ProducerModule}.
+ *
+ * @throws IllegalArgumentException if {@link #isModuleAnnotation(AnnotationMirror)} returns
+ * {@code false}
+ */
+ static ModuleAnnotation moduleAnnotation(AnnotationMirror annotation) {
+ checkArgument(
+ isModuleAnnotation(annotation),
+ "%s is not a Module or ProducerModule annotation",
+ annotation);
+ return new AutoValue_ModuleAnnotation(annotation);
+ }
+
+ /**
+ * Returns an object representing the {@code @Module} or {@code @ProducerModule} annotation if one
+ * annotates {@code typeElement}.
+ */
+ static Optional<ModuleAnnotation> moduleAnnotation(TypeElement typeElement) {
+ return getAnyAnnotation(typeElement, Module.class, ProducerModule.class)
+ .map(ModuleAnnotation::moduleAnnotation);
+ }
+}
diff --git a/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java b/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java
new file mode 100644
index 0000000..5f0d687
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.ModuleKind.checkIsModule;
+import static dagger.internal.codegen.ModuleProxies.nonPublicNullaryConstructor;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Generates a {@code public static} method that calls {@code new SomeModule()} for modules that
+ * don't have {@linkplain ModuleProxies#nonPublicNullaryConstructor(TypeElement,
+ * DaggerElements) publicly accessible constructors}.
+ */
+// TODO(dpb): See if this can become a SourceFileGenerator<ModuleDescriptor> instead. Doing so may
+// cause ModuleProcessingStep to defer elements multiple times.
+final class ModuleConstructorProxyGenerator extends SourceFileGenerator<TypeElement> {
+ private final DaggerElements elements;
+
+ @Inject
+ ModuleConstructorProxyGenerator(
+ Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ this.elements = elements;
+ }
+
+ @Override
+ ClassName nameGeneratedType(TypeElement moduleElement) {
+ return ModuleProxies.constructorProxyTypeName(moduleElement);
+ }
+
+ @Override
+ Element originatingElement(TypeElement moduleElement) {
+ return moduleElement;
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedTypeName, TypeElement moduleElement) {
+ checkIsModule(moduleElement);
+ return nonPublicNullaryConstructor(moduleElement, elements).isPresent()
+ ? Optional.of(buildProxy(generatedTypeName, moduleElement))
+ : Optional.empty();
+ }
+
+ private TypeSpec.Builder buildProxy(ClassName generatedTypeName, TypeElement moduleElement) {
+ return classBuilder(generatedTypeName)
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
+ .addMethod(
+ methodBuilder("newInstance")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(ClassName.get(moduleElement))
+ .addStatement("return new $T()", moduleElement)
+ .build());
+ }
+}
diff --git a/java/dagger/internal/codegen/ModuleDescriptor.java b/java/dagger/internal/codegen/ModuleDescriptor.java
new file mode 100644
index 0000000..ac7a4b7
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleDescriptor.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.getPackage;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.transform;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.SourceFiles.classFileName;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.NONE;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.Traverser;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.Key;
+import dagger.multibindings.Multibinds;
+import dagger.producers.Produces;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+@AutoValue
+abstract class ModuleDescriptor {
+
+ abstract TypeElement moduleElement();
+
+ abstract ImmutableSet<TypeElement> includedModules();
+
+ abstract ImmutableSet<ContributionBinding> bindings();
+
+ /** The multibinding declarations contained in this module. */
+ abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
+
+ /** The {@link Module#subcomponents() subcomponent declarations} contained in this module. */
+ abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
+
+ /** The {@link Binds} method declarations that define delegate bindings. */
+ abstract ImmutableSet<DelegateDeclaration> delegateDeclarations();
+
+ /** The {@link BindsOptionalOf} method declarations that define optional bindings. */
+ abstract ImmutableSet<OptionalBindingDeclaration> optionalDeclarations();
+
+ /** The kind of the module. */
+ abstract ModuleKind kind();
+
+ /** Returns all of the bindings declared in this module. */
+ @Memoized
+ ImmutableSet<BindingDeclaration> allBindingDeclarations() {
+ return ImmutableSet.<BindingDeclaration>builder()
+ .addAll(bindings())
+ .addAll(delegateDeclarations())
+ .addAll(multibindingDeclarations())
+ .addAll(optionalDeclarations())
+ .addAll(subcomponentDeclarations())
+ .build();
+ }
+
+ /** Returns the keys of all bindings declared by this module. */
+ ImmutableSet<Key> allBindingKeys() {
+ return allBindingDeclarations().stream().map(BindingDeclaration::key).collect(toImmutableSet());
+ }
+
+ @Singleton
+ static final class Factory implements ClearableCache {
+ private final DaggerElements elements;
+ private final BindingFactory bindingFactory;
+ private final MultibindingDeclaration.Factory multibindingDeclarationFactory;
+ private final DelegateDeclaration.Factory bindingDelegateDeclarationFactory;
+ private final SubcomponentDeclaration.Factory subcomponentDeclarationFactory;
+ private final OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory;
+ private final Map<TypeElement, ModuleDescriptor> cache = new HashMap<>();
+
+ @Inject
+ Factory(
+ DaggerElements elements,
+ BindingFactory bindingFactory,
+ MultibindingDeclaration.Factory multibindingDeclarationFactory,
+ DelegateDeclaration.Factory bindingDelegateDeclarationFactory,
+ SubcomponentDeclaration.Factory subcomponentDeclarationFactory,
+ OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory) {
+ this.elements = elements;
+ this.bindingFactory = bindingFactory;
+ this.multibindingDeclarationFactory = multibindingDeclarationFactory;
+ this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory;
+ this.subcomponentDeclarationFactory = subcomponentDeclarationFactory;
+ this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory;
+ }
+
+ ModuleDescriptor create(TypeElement moduleElement) {
+ return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached);
+ }
+
+ ModuleDescriptor createUncached(TypeElement moduleElement) {
+ ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
+ ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder();
+ ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
+ ImmutableSet.builder();
+ ImmutableSet.Builder<OptionalBindingDeclaration> optionalDeclarations =
+ ImmutableSet.builder();
+
+ for (ExecutableElement moduleMethod : methodsIn(elements.getAllMembers(moduleElement))) {
+ if (isAnnotationPresent(moduleMethod, Provides.class)) {
+ bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, Produces.class)) {
+ bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, Binds.class)) {
+ delegates.add(bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, Multibinds.class)) {
+ multibindingDeclarations.add(
+ multibindingDeclarationFactory.forMultibindsMethod(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, BindsOptionalOf.class)) {
+ optionalDeclarations.add(
+ optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement));
+ }
+ }
+
+ return new AutoValue_ModuleDescriptor(
+ moduleElement,
+ ImmutableSet.copyOf(collectIncludedModules(new LinkedHashSet<>(), moduleElement)),
+ bindings.build(),
+ multibindingDeclarations.build(),
+ subcomponentDeclarationFactory.forModule(moduleElement),
+ delegates.build(),
+ optionalDeclarations.build(),
+ ModuleKind.forAnnotatedElement(moduleElement).get());
+ }
+
+ /** Returns all the modules transitively included by given modules, including the arguments. */
+ ImmutableSet<ModuleDescriptor> transitiveModules(Iterable<TypeElement> modules) {
+ return ImmutableSet.copyOf(
+ Traverser.forGraph(
+ (ModuleDescriptor module) -> transform(module.includedModules(), this::create))
+ .depthFirstPreOrder(transform(modules, this::create)));
+ }
+
+ @CanIgnoreReturnValue
+ private Set<TypeElement> collectIncludedModules(
+ Set<TypeElement> includedModules, TypeElement moduleElement) {
+ TypeMirror superclass = moduleElement.getSuperclass();
+ if (!superclass.getKind().equals(NONE)) {
+ verify(superclass.getKind().equals(DECLARED));
+ TypeElement superclassElement = MoreTypes.asTypeElement(superclass);
+ if (!superclassElement.getQualifiedName().contentEquals(Object.class.getCanonicalName())) {
+ collectIncludedModules(includedModules, superclassElement);
+ }
+ }
+ moduleAnnotation(moduleElement)
+ .ifPresent(
+ moduleAnnotation -> {
+ includedModules.addAll(moduleAnnotation.includes());
+ includedModules.addAll(implicitlyIncludedModules(moduleElement));
+ });
+ return includedModules;
+ }
+
+ // @ContributesAndroidInjector generates a module that is implicitly included in the enclosing
+ // module
+ private ImmutableSet<TypeElement> implicitlyIncludedModules(TypeElement moduleElement) {
+ TypeElement contributesAndroidInjector =
+ elements.getTypeElement("dagger.android.ContributesAndroidInjector");
+ if (contributesAndroidInjector == null) {
+ return ImmutableSet.of();
+ }
+ return methodsIn(moduleElement.getEnclosedElements()).stream()
+ .filter(method -> isAnnotationPresent(method, contributesAndroidInjector.asType()))
+ .map(method -> elements.checkTypePresent(implicitlyIncludedModuleName(method)))
+ .collect(toImmutableSet());
+ }
+
+ private String implicitlyIncludedModuleName(ExecutableElement method) {
+ return getPackage(method).getQualifiedName()
+ + "."
+ + classFileName(ClassName.get(MoreElements.asType(method.getEnclosingElement())))
+ + "_"
+ + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
+ }
+
+ @Override
+ public void clearCache() {
+ cache.clear();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ModuleGenerator.java b/java/dagger/internal/codegen/ModuleGenerator.java
new file mode 100644
index 0000000..161b47b
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleGenerator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifier for a {@link SourceFileGenerator} for modules. */
+@Qualifier
+@Retention(RUNTIME)
+@interface ModuleGenerator {}
diff --git a/java/dagger/internal/codegen/ModuleKind.java b/java/dagger/internal/codegen/ModuleKind.java
new file mode 100644
index 0000000..c93d8ce
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleKind.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import dagger.Module;
+import dagger.producers.ProducerModule;
+import java.lang.annotation.Annotation;
+import java.util.EnumSet;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.TypeElement;
+
+/** Enumeration of the kinds of modules. */
+enum ModuleKind {
+ /** {@code @Module} */
+ MODULE(Module.class),
+
+ /** {@code @ProducerModule} */
+ PRODUCER_MODULE(ProducerModule.class);
+
+ /** Returns the annotations for modules of the given kinds. */
+ static ImmutableSet<Class<? extends Annotation>> annotationsFor(Set<ModuleKind> kinds) {
+ return kinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the kind of an annotated element if it is annotated with one of the module {@linkplain
+ * #annotation() annotations}.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one of the module
+ * annotations
+ */
+ static Optional<ModuleKind> forAnnotatedElement(TypeElement element) {
+ Set<ModuleKind> kinds = EnumSet.noneOf(ModuleKind.class);
+ for (ModuleKind kind : values()) {
+ if (MoreElements.isAnnotationPresent(element, kind.annotation())) {
+ kinds.add(kind);
+ }
+ }
+
+ if (kinds.size() > 1) {
+ throw new IllegalArgumentException(
+ element + " cannot be annotated with more than one of " + annotationsFor(kinds));
+ }
+ return kinds.stream().findAny();
+ }
+
+ static void checkIsModule(TypeElement moduleElement) {
+ checkArgument(forAnnotatedElement(moduleElement).isPresent());
+ }
+
+ private final Class<? extends Annotation> moduleAnnotation;
+
+ ModuleKind(Class<? extends Annotation> moduleAnnotation) {
+ this.moduleAnnotation = moduleAnnotation;
+ }
+
+ /**
+ * Returns the annotation mirror for this module kind on the given type.
+ *
+ * @throws IllegalArgumentException if the annotation is not present on the type
+ */
+ AnnotationMirror getModuleAnnotation(TypeElement element) {
+ Optional<AnnotationMirror> result = getAnnotationMirror(element, moduleAnnotation);
+ checkArgument(
+ result.isPresent(), "annotation %s is not present on type %s", moduleAnnotation, element);
+ return result.get();
+ }
+
+ /** Returns the annotation that marks a module of this kind. */
+ Class<? extends Annotation> annotation() {
+ return moduleAnnotation;
+ }
+
+ /** Returns the kinds of modules that a module of this kind is allowed to include. */
+ ImmutableSet<ModuleKind> legalIncludedModuleKinds() {
+ switch (this) {
+ case MODULE:
+ return Sets.immutableEnumSet(MODULE);
+ case PRODUCER_MODULE:
+ return Sets.immutableEnumSet(MODULE, PRODUCER_MODULE);
+ }
+ throw new AssertionError(this);
+ }
+}
diff --git a/java/dagger/internal/codegen/ModuleProcessingStep.java b/java/dagger/internal/codegen/ModuleProcessingStep.java
new file mode 100644
index 0000000..27cd531
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleProcessingStep.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.DelegateDeclaration.Factory;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A {@link ProcessingStep} that validates module classes and generates factories for binding
+ * methods.
+ */
+final class ModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+ private final Messager messager;
+ private final ModuleValidator moduleValidator;
+ private final BindingFactory bindingFactory;
+ private final SourceFileGenerator<ProvisionBinding> factoryGenerator;
+ private final SourceFileGenerator<ProductionBinding> producerFactoryGenerator;
+ private final SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator;
+ private final InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator;
+ private final DelegateDeclaration.Factory delegateDeclarationFactory;
+ private final Set<TypeElement> processedModuleElements = Sets.newLinkedHashSet();
+
+ @Inject
+ ModuleProcessingStep(
+ Messager messager,
+ ModuleValidator moduleValidator,
+ BindingFactory bindingFactory,
+ SourceFileGenerator<ProvisionBinding> factoryGenerator,
+ SourceFileGenerator<ProductionBinding> producerFactoryGenerator,
+ @ModuleGenerator SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator,
+ InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator,
+ Factory delegateDeclarationFactory) {
+ super(MoreElements::asType);
+ this.messager = messager;
+ this.moduleValidator = moduleValidator;
+ this.bindingFactory = bindingFactory;
+ this.factoryGenerator = factoryGenerator;
+ this.producerFactoryGenerator = producerFactoryGenerator;
+ this.moduleConstructorProxyGenerator = moduleConstructorProxyGenerator;
+ this.inaccessibleMapKeyProxyGenerator = inaccessibleMapKeyProxyGenerator;
+ this.delegateDeclarationFactory = delegateDeclarationFactory;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(Module.class, ProducerModule.class);
+ }
+
+ @Override
+ public ImmutableSet<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ List<TypeElement> modules = typesIn(elementsByAnnotation.values());
+ moduleValidator.addKnownModules(modules);
+ return super.process(elementsByAnnotation);
+ }
+
+ @Override
+ protected void process(
+ TypeElement module, ImmutableSet<Class<? extends Annotation>> annotations) {
+ if (processedModuleElements.contains(module)) {
+ return;
+ }
+ ValidationReport<TypeElement> report = moduleValidator.validate(module);
+ report.printMessagesTo(messager);
+ if (report.isClean()) {
+ for (ExecutableElement method : methodsIn(module.getEnclosedElements())) {
+ if (isAnnotationPresent(method, Provides.class)) {
+ generate(factoryGenerator, bindingFactory.providesMethodBinding(method, module));
+ } else if (isAnnotationPresent(method, Produces.class)) {
+ generate(producerFactoryGenerator, bindingFactory.producesMethodBinding(method, module));
+ } else if (isAnnotationPresent(method, Binds.class)) {
+ inaccessibleMapKeyProxyGenerator.generate(bindsMethodBinding(module, method), messager);
+ }
+ }
+ moduleConstructorProxyGenerator.generate(module, messager);
+ }
+ processedModuleElements.add(module);
+ }
+
+ private <B extends ContributionBinding> void generate(
+ SourceFileGenerator<B> generator, B binding) {
+ generator.generate(binding, messager);
+ inaccessibleMapKeyProxyGenerator.generate(binding, messager);
+ }
+
+ private ContributionBinding bindsMethodBinding(TypeElement module, ExecutableElement method) {
+ return bindingFactory.unresolvedDelegateBinding(
+ delegateDeclarationFactory.create(method, module));
+ }
+}
diff --git a/java/dagger/internal/codegen/ModuleProxies.java b/java/dagger/internal/codegen/ModuleProxies.java
new file mode 100644
index 0000000..6426e69
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleProxies.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/** Convenience methods for generating and using module constructor proxy methods. */
+final class ModuleProxies {
+ /** The name of the class that hosts the module constructor proxy method. */
+ static ClassName constructorProxyTypeName(TypeElement moduleElement) {
+ ModuleKind.checkIsModule(moduleElement);
+ ClassName moduleClassName = ClassName.get(moduleElement);
+ return moduleClassName
+ .topLevelClassName()
+ .peerClass(SourceFiles.classFileName(moduleClassName) + "_Proxy");
+ }
+
+ /**
+ * The module constructor being proxied. A proxy is generated if it is not publicly accessible and
+ * has no arguments. If an implicit reference to the enclosing class exists, or the module is
+ * abstract, no proxy method can be generated.
+ */
+ // TODO(ronshapiro): make this an @Injectable class that injects DaggerElements
+ static Optional<ExecutableElement> nonPublicNullaryConstructor(
+ TypeElement moduleElement, DaggerElements elements) {
+ ModuleKind.checkIsModule(moduleElement);
+ if (moduleElement.getModifiers().contains(ABSTRACT)
+ || (moduleElement.getNestingKind().isNested()
+ && !moduleElement.getModifiers().contains(STATIC))) {
+ return Optional.empty();
+ }
+ return constructorsIn(elements.getAllMembers(moduleElement)).stream()
+ .filter(constructor -> !Accessibility.isElementPubliclyAccessible(constructor))
+ .filter(constructor -> !constructor.getModifiers().contains(PRIVATE))
+ .filter(constructor -> constructor.getParameters().isEmpty())
+ .findAny();
+ }
+
+ /**
+ * Returns a code block that creates a new module instance, either by invoking the nullary
+ * constructor if it's accessible from {@code requestingClass} or else by invoking the
+ * constructor's generated proxy method.
+ */
+ static CodeBlock newModuleInstance(
+ TypeElement moduleElement, ClassName requestingClass, DaggerElements elements) {
+ ModuleKind.checkIsModule(moduleElement);
+ String packageName = requestingClass.packageName();
+ return nonPublicNullaryConstructor(moduleElement, elements)
+ .filter(constructor -> !isElementAccessibleFrom(constructor, packageName))
+ .map(
+ constructor ->
+ CodeBlock.of("$T.newInstance()", constructorProxyTypeName(moduleElement)))
+ .orElse(CodeBlock.of("new $T()", moduleElement));
+ }
+}
diff --git a/java/dagger/internal/codegen/ModuleValidator.java b/java/dagger/internal/codegen/ModuleValidator.java
new file mode 100644
index 0000000..094bf34
--- /dev/null
+++ b/java/dagger/internal/codegen/ModuleValidator.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.auto.common.Visibility.PRIVATE;
+import static com.google.auto.common.Visibility.PUBLIC;
+import static com.google.auto.common.Visibility.effectiveVisibilityOfElement;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ComponentAnnotation.componentAnnotation;
+import static dagger.internal.codegen.ComponentAnnotation.isComponentAnnotation;
+import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations;
+import static dagger.internal.codegen.ConfigurationAnnotations.getSubcomponentCreator;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ModuleAnnotation.isModuleAnnotation;
+import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.MoreAnnotationMirrors.simpleName;
+import static dagger.internal.codegen.MoreAnnotationValues.asType;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.ValidationType.NONE;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static java.util.EnumSet.noneOf;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.common.Visibility;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Sets;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Scope;
+import javax.inject.Singleton;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * A {@linkplain ValidationReport validator} for {@link Module}s or {@link ProducerModule}s.
+ */
+@Singleton
+final class ModuleValidator {
+ private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_TYPES =
+ ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
+ private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_CREATOR_TYPES =
+ ImmutableSet.of(
+ Subcomponent.Builder.class,
+ Subcomponent.Factory.class,
+ ProductionSubcomponent.Builder.class,
+ ProductionSubcomponent.Factory.class);
+ private static final Optional<Class<?>> ANDROID_PROCESSOR;
+ private static final String CONTRIBUTES_ANDROID_INJECTOR_NAME =
+ "dagger.android.ContributesAndroidInjector";
+ private static final String ANDROID_PROCESSOR_NAME = "dagger.android.processor.AndroidProcessor";
+
+ static {
+ Class<?> clazz;
+ try {
+ clazz = Class.forName(ANDROID_PROCESSOR_NAME, false, ModuleValidator.class.getClassLoader());
+ } catch (ClassNotFoundException ignored) {
+ clazz = null;
+ }
+ ANDROID_PROCESSOR = Optional.ofNullable(clazz);
+ }
+
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final AnyBindingMethodValidator anyBindingMethodValidator;
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final ComponentDescriptorFactory componentDescriptorFactory;
+ private final BindingGraphFactory bindingGraphFactory;
+ private final BindingGraphConverter bindingGraphConverter;
+ private final BindingGraphValidator bindingGraphValidator;
+ private final CompilerOptions compilerOptions;
+ private final Map<TypeElement, ValidationReport<TypeElement>> cache = new HashMap<>();
+ private final Set<TypeElement> knownModules = new HashSet<>();
+
+ @Inject
+ ModuleValidator(
+ DaggerTypes types,
+ DaggerElements elements,
+ AnyBindingMethodValidator anyBindingMethodValidator,
+ MethodSignatureFormatter methodSignatureFormatter,
+ ComponentDescriptorFactory componentDescriptorFactory,
+ BindingGraphFactory bindingGraphFactory,
+ BindingGraphConverter bindingGraphConverter,
+ BindingGraphValidator bindingGraphValidator,
+ CompilerOptions compilerOptions) {
+ this.types = types;
+ this.elements = elements;
+ this.anyBindingMethodValidator = anyBindingMethodValidator;
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.componentDescriptorFactory = componentDescriptorFactory;
+ this.bindingGraphFactory = bindingGraphFactory;
+ this.bindingGraphConverter = bindingGraphConverter;
+ this.bindingGraphValidator = bindingGraphValidator;
+ this.compilerOptions = compilerOptions;
+ }
+
+ /**
+ * Adds {@code modules} to the set of module types that will be validated during this compilation
+ * step. If a component or module includes a module that is not in this set, that included module
+ * is assumed to be valid because it was processed in a previous compilation step. If it were
+ * invalid, that previous compilation step would have failed and blocked this one.
+ *
+ * <p>This logic depends on this method being called before {@linkplain #validate(TypeElement)
+ * validating} any module or {@linkplain #validateReferencedModules(TypeElement, AnnotationMirror,
+ * ImmutableSet, Set) component}.
+ */
+ void addKnownModules(Collection<TypeElement> modules) {
+ knownModules.addAll(modules);
+ }
+
+ /** Returns a validation report for a module type. */
+ ValidationReport<TypeElement> validate(TypeElement module) {
+ return validate(module, new HashSet<>());
+ }
+
+ private ValidationReport<TypeElement> validate(
+ TypeElement module, Set<TypeElement> visitedModules) {
+ if (visitedModules.add(module)) {
+ return reentrantComputeIfAbsent(cache, module, m -> validateUncached(module, visitedModules));
+ }
+ return ValidationReport.about(module).build();
+ }
+
+ private ValidationReport<TypeElement> validateUncached(
+ TypeElement module, Set<TypeElement> visitedModules) {
+ ValidationReport.Builder<TypeElement> builder = ValidationReport.about(module);
+ ModuleKind moduleKind = ModuleKind.forAnnotatedElement(module).get();
+
+ ListMultimap<String, ExecutableElement> allMethodsByName = ArrayListMultimap.create();
+ ListMultimap<String, ExecutableElement> bindingMethodsByName = ArrayListMultimap.create();
+
+ Set<ModuleMethodKind> methodKinds = noneOf(ModuleMethodKind.class);
+ TypeElement contributesAndroidInjectorElement =
+ elements.getTypeElement(CONTRIBUTES_ANDROID_INJECTOR_NAME);
+ TypeMirror contributesAndroidInjector =
+ contributesAndroidInjectorElement != null
+ ? contributesAndroidInjectorElement.asType()
+ : null;
+ for (ExecutableElement moduleMethod : methodsIn(module.getEnclosedElements())) {
+ if (anyBindingMethodValidator.isBindingMethod(moduleMethod)) {
+ builder.addSubreport(anyBindingMethodValidator.validate(moduleMethod));
+ bindingMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod);
+ methodKinds.add(ModuleMethodKind.ofMethod(moduleMethod));
+ }
+ allMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod);
+
+ for (AnnotationMirror annotation : moduleMethod.getAnnotationMirrors()) {
+ if (!ANDROID_PROCESSOR.isPresent()
+ && MoreTypes.equivalence()
+ .equivalent(contributesAndroidInjector, annotation.getAnnotationType())) {
+ builder.addSubreport(
+ ValidationReport.about(moduleMethod)
+ .addError(
+ String.format(
+ "@%s was used, but %s was not found on the processor path",
+ CONTRIBUTES_ANDROID_INJECTOR_NAME, ANDROID_PROCESSOR_NAME))
+ .build());
+ break;
+ }
+ }
+ }
+
+ if (methodKinds.containsAll(
+ EnumSet.of(ModuleMethodKind.ABSTRACT_DECLARATION, ModuleMethodKind.INSTANCE_BINDING))) {
+ builder.addError(
+ String.format(
+ "A @%s may not contain both non-static and abstract binding methods",
+ moduleKind.annotation().getSimpleName()));
+ }
+
+ validateModuleVisibility(module, moduleKind, builder);
+ validateMethodsWithSameName(builder, bindingMethodsByName);
+ if (module.getKind() != ElementKind.INTERFACE) {
+ validateBindingMethodOverrides(module, builder, allMethodsByName, bindingMethodsByName);
+ }
+ validateModifiers(module, builder);
+ validateReferencedModules(module, moduleKind, visitedModules, builder);
+ validateReferencedSubcomponents(module, moduleKind, builder);
+ validateNoScopeAnnotationsOnModuleElement(module, moduleKind, builder);
+ validateSelfCycles(module, builder);
+
+ if (builder.build().isClean()
+ && !compilerOptions.fullBindingGraphValidationType(module).equals(NONE)) {
+ validateModuleBindings(module, builder);
+ }
+
+ return builder.build();
+ }
+
+ private void validateReferencedSubcomponents(
+ final TypeElement subject,
+ ModuleKind moduleKind,
+ final ValidationReport.Builder<TypeElement> builder) {
+ // TODO(ronshapiro): use validateTypesAreDeclared when it is checked in
+ ModuleAnnotation moduleAnnotation = moduleAnnotation(moduleKind.getModuleAnnotation(subject));
+ for (AnnotationValue subcomponentAttribute :
+ moduleAnnotation.subcomponentsAsAnnotationValues()) {
+ asType(subcomponentAttribute)
+ .accept(
+ new SimpleTypeVisitor8<Void, Void>() {
+ @Override
+ protected Void defaultAction(TypeMirror e, Void aVoid) {
+ builder.addError(
+ e + " is not a valid subcomponent type",
+ subject,
+ moduleAnnotation.annotation(),
+ subcomponentAttribute);
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType declaredType, Void aVoid) {
+ TypeElement attributeType = MoreTypes.asTypeElement(declaredType);
+ if (isAnyAnnotationPresent(attributeType, SUBCOMPONENT_TYPES)) {
+ validateSubcomponentHasBuilder(
+ attributeType, moduleAnnotation.annotation(), builder);
+ } else {
+ builder.addError(
+ isAnyAnnotationPresent(attributeType, SUBCOMPONENT_CREATOR_TYPES)
+ ? moduleSubcomponentsIncludesCreator(attributeType)
+ : moduleSubcomponentsIncludesNonSubcomponent(attributeType),
+ subject,
+ moduleAnnotation.annotation(),
+ subcomponentAttribute);
+ }
+
+ return null;
+ }
+ },
+ null);
+ }
+ }
+
+ private static String moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent) {
+ return notSubcomponent.getQualifiedName()
+ + " is not a @Subcomponent or @ProductionSubcomponent";
+ }
+
+ private static String moduleSubcomponentsIncludesCreator(
+ TypeElement moduleSubcomponentsAttribute) {
+ TypeElement subcomponentType =
+ MoreElements.asType(moduleSubcomponentsAttribute.getEnclosingElement());
+ ComponentCreatorAnnotation creatorAnnotation =
+ getOnlyElement(getCreatorAnnotations(moduleSubcomponentsAttribute));
+ return String.format(
+ "%s is a @%s.%s. Did you mean to use %s?",
+ moduleSubcomponentsAttribute.getQualifiedName(),
+ subcomponentAnnotation(subcomponentType).get().simpleName(),
+ creatorAnnotation.creatorKind().typeName(),
+ subcomponentType.getQualifiedName());
+ }
+
+ private static void validateSubcomponentHasBuilder(
+ TypeElement subcomponentAttribute,
+ AnnotationMirror moduleAnnotation,
+ ValidationReport.Builder<TypeElement> builder) {
+ if (getSubcomponentCreator(subcomponentAttribute).isPresent()) {
+ return;
+ }
+ builder.addError(
+ moduleSubcomponentsDoesntHaveCreator(subcomponentAttribute, moduleAnnotation),
+ builder.getSubject(),
+ moduleAnnotation);
+ }
+
+ private static String moduleSubcomponentsDoesntHaveCreator(
+ TypeElement subcomponent, AnnotationMirror moduleAnnotation) {
+ return String.format(
+ "%1$s doesn't have a @%2$s.Builder or @%2$s.Factory, which is required when used with "
+ + "@%3$s.subcomponents",
+ subcomponent.getQualifiedName(),
+ subcomponentAnnotation(subcomponent).get().simpleName(),
+ simpleName(moduleAnnotation));
+ }
+
+ enum ModuleMethodKind {
+ ABSTRACT_DECLARATION,
+ INSTANCE_BINDING,
+ STATIC_BINDING,
+ ;
+
+ static ModuleMethodKind ofMethod(ExecutableElement moduleMethod) {
+ if (moduleMethod.getModifiers().contains(STATIC)) {
+ return STATIC_BINDING;
+ } else if (moduleMethod.getModifiers().contains(ABSTRACT)) {
+ return ABSTRACT_DECLARATION;
+ } else {
+ return INSTANCE_BINDING;
+ }
+ }
+ }
+
+ private void validateModifiers(
+ TypeElement subject, ValidationReport.Builder<TypeElement> builder) {
+ // This coupled with the check for abstract modules in ComponentValidator guarantees that
+ // only modules without type parameters are referenced from @Component(modules={...}).
+ if (!subject.getTypeParameters().isEmpty() && !subject.getModifiers().contains(ABSTRACT)) {
+ builder.addError("Modules with type parameters must be abstract", subject);
+ }
+ }
+
+ private void validateMethodsWithSameName(
+ ValidationReport.Builder<TypeElement> builder,
+ ListMultimap<String, ExecutableElement> bindingMethodsByName) {
+ for (Entry<String, Collection<ExecutableElement>> entry :
+ bindingMethodsByName.asMap().entrySet()) {
+ if (entry.getValue().size() > 1) {
+ for (ExecutableElement offendingMethod : entry.getValue()) {
+ builder.addError(
+ String.format(
+ "Cannot have more than one binding method with the same name in a single module"),
+ offendingMethod);
+ }
+ }
+ }
+ }
+
+ private void validateReferencedModules(
+ TypeElement subject,
+ ModuleKind moduleKind,
+ Set<TypeElement> visitedModules,
+ ValidationReport.Builder<TypeElement> builder) {
+ // Validate that all the modules we include are valid for inclusion.
+ AnnotationMirror mirror = moduleKind.getModuleAnnotation(subject);
+ builder.addSubreport(
+ validateReferencedModules(
+ subject, mirror, moduleKind.legalIncludedModuleKinds(), visitedModules));
+ }
+
+ /**
+ * Validates modules included in a given module or installed in a given component.
+ *
+ * <p>Checks that the referenced modules are non-generic types annotated with {@code @Module} or
+ * {@code @ProducerModule}.
+ *
+ * <p>If the referenced module is in the {@linkplain #addKnownModules(Collection) known modules
+ * set} and has errors, reports an error at that module's inclusion.
+ *
+ * @param annotatedType the annotated module or component
+ * @param annotation the annotation specifying the referenced modules ({@code @Component},
+ * {@code @ProductionComponent}, {@code @Subcomponent}, {@code @ProductionSubcomponent},
+ * {@code @Module}, or {@code @ProducerModule})
+ * @param validModuleKinds the module kinds that the annotated type is permitted to include
+ */
+ ValidationReport<TypeElement> validateReferencedModules(
+ TypeElement annotatedType,
+ AnnotationMirror annotation,
+ ImmutableSet<ModuleKind> validModuleKinds,
+ Set<TypeElement> visitedModules) {
+ ValidationReport.Builder<TypeElement> subreport = ValidationReport.about(annotatedType);
+ ImmutableSet<? extends Class<? extends Annotation>> validModuleAnnotations =
+ validModuleKinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
+
+ for (AnnotationValue includedModule : getModules(annotation)) {
+ asType(includedModule)
+ .accept(
+ new SimpleTypeVisitor8<Void, Void>() {
+ @Override
+ protected Void defaultAction(TypeMirror mirror, Void p) {
+ reportError("%s is not a valid module type.", mirror);
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType t, Void p) {
+ TypeElement module = MoreElements.asType(t.asElement());
+ if (!t.getTypeArguments().isEmpty()) {
+ reportError(
+ "%s is listed as a module, but has type parameters",
+ module.getQualifiedName());
+ }
+ if (!isAnyAnnotationPresent(module, validModuleAnnotations)) {
+ reportError(
+ "%s is listed as a module, but is not annotated with %s",
+ module.getQualifiedName(),
+ (validModuleAnnotations.size() > 1 ? "one of " : "")
+ + validModuleAnnotations
+ .stream()
+ .map(otherClass -> "@" + otherClass.getSimpleName())
+ .collect(joining(", ")));
+ } else if (knownModules.contains(module)
+ && !validate(module, visitedModules).isClean()) {
+ reportError("%s has errors", module.getQualifiedName());
+ }
+ return null;
+ }
+
+ @FormatMethod
+ private void reportError(String format, Object... args) {
+ subreport.addError(
+ String.format(format, args), annotatedType, annotation, includedModule);
+ }
+ },
+ null);
+ }
+ return subreport.build();
+ }
+
+ private static ImmutableList<AnnotationValue> getModules(AnnotationMirror annotation) {
+ if (isModuleAnnotation(annotation)) {
+ return moduleAnnotation(annotation).includesAsAnnotationValues();
+ }
+ if (isComponentAnnotation(annotation)) {
+ return componentAnnotation(annotation).moduleValues();
+ }
+ throw new IllegalArgumentException(String.format("unsupported annotation: %s", annotation));
+ }
+
+ private void validateBindingMethodOverrides(
+ TypeElement subject,
+ ValidationReport.Builder<TypeElement> builder,
+ ListMultimap<String, ExecutableElement> allMethodsByName,
+ ListMultimap<String, ExecutableElement> bindingMethodsByName) {
+ // For every binding method, confirm it overrides nothing *and* nothing overrides it.
+ // Consider the following hierarchy:
+ // class Parent {
+ // @Provides Foo a() {}
+ // @Provides Foo b() {}
+ // Foo c() {}
+ // }
+ // class Child extends Parent {
+ // @Provides Foo a() {}
+ // Foo b() {}
+ // @Provides Foo c() {}
+ // }
+ // In each of those cases, we want to fail. "a" is clear, "b" because Child is overriding
+ // a binding method in Parent, and "c" because Child is defining a binding method that overrides
+ // Parent.
+ TypeElement currentClass = subject;
+ TypeMirror objectType = elements.getTypeElement(Object.class).asType();
+ // We keep track of methods that failed so we don't spam with multiple failures.
+ Set<ExecutableElement> failedMethods = Sets.newHashSet();
+ while (!types.isSameType(currentClass.getSuperclass(), objectType)) {
+ currentClass = MoreElements.asType(types.asElement(currentClass.getSuperclass()));
+ List<ExecutableElement> superclassMethods = methodsIn(currentClass.getEnclosedElements());
+ for (ExecutableElement superclassMethod : superclassMethods) {
+ String name = superclassMethod.getSimpleName().toString();
+ // For each method in the superclass, confirm our binding methods don't override it
+ for (ExecutableElement bindingMethod : bindingMethodsByName.get(name)) {
+ if (failedMethods.add(bindingMethod)
+ && elements.overrides(bindingMethod, superclassMethod, subject)) {
+ builder.addError(
+ String.format(
+ "Binding methods may not override another method. Overrides: %s",
+ methodSignatureFormatter.format(superclassMethod)),
+ bindingMethod);
+ }
+ }
+ // For each binding method in superclass, confirm our methods don't override it.
+ if (anyBindingMethodValidator.isBindingMethod(superclassMethod)) {
+ for (ExecutableElement method : allMethodsByName.get(name)) {
+ if (failedMethods.add(method)
+ && elements.overrides(method, superclassMethod, subject)) {
+ builder.addError(
+ String.format(
+ "Binding methods may not be overridden in modules. Overrides: %s",
+ methodSignatureFormatter.format(superclassMethod)),
+ method);
+ }
+ }
+ }
+ allMethodsByName.put(superclassMethod.getSimpleName().toString(), superclassMethod);
+ }
+ }
+ }
+
+ private void validateModuleVisibility(
+ final TypeElement moduleElement,
+ ModuleKind moduleKind,
+ final ValidationReport.Builder<?> reportBuilder) {
+ ModuleAnnotation moduleAnnotation =
+ moduleAnnotation(getAnnotationMirror(moduleElement, moduleKind.annotation()).get());
+ Visibility moduleVisibility = Visibility.ofElement(moduleElement);
+ Visibility moduleEffectiveVisibility = effectiveVisibilityOfElement(moduleElement);
+ if (moduleVisibility.equals(PRIVATE)) {
+ reportBuilder.addError("Modules cannot be private.", moduleElement);
+ } else if (moduleEffectiveVisibility.equals(PRIVATE)) {
+ reportBuilder.addError("Modules cannot be enclosed in private types.", moduleElement);
+ }
+
+ switch (moduleElement.getNestingKind()) {
+ case ANONYMOUS:
+ throw new IllegalStateException("Can't apply @Module to an anonymous class");
+ case LOCAL:
+ throw new IllegalStateException("Local classes shouldn't show up in the processor");
+ case MEMBER:
+ case TOP_LEVEL:
+ if (moduleEffectiveVisibility.equals(PUBLIC)) {
+ ImmutableSet<TypeElement> invalidVisibilityIncludes =
+ getModuleIncludesWithInvalidVisibility(moduleAnnotation);
+ if (!invalidVisibilityIncludes.isEmpty()) {
+ reportBuilder.addError(
+ String.format(
+ "This module is public, but it includes non-public (or effectively non-public) "
+ + "modules (%s) that have non-static, non-abstract binding methods. Either "
+ + "reduce the visibility of this module, make the included modules "
+ + "public, or make all of the binding methods on the included modules "
+ + "abstract or static.",
+ formatListForErrorMessage(invalidVisibilityIncludes.asList())),
+ moduleElement);
+ }
+ }
+ }
+ }
+
+ private ImmutableSet<TypeElement> getModuleIncludesWithInvalidVisibility(
+ ModuleAnnotation moduleAnnotation) {
+ return moduleAnnotation.includes().stream()
+ .filter(include -> !effectiveVisibilityOfElement(include).equals(PUBLIC))
+ .filter(this::requiresModuleInstance)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns {@code true} if a module instance is needed for any of the binding methods on the
+ * given {@code module}. This is the case when the module has any binding methods that are neither
+ * {@code abstract} nor {@code static}.
+ */
+ private boolean requiresModuleInstance(TypeElement module) {
+ // Note elements.getAllMembers(module) rather than module.getEnclosedElements() here: we need to
+ // include binding methods declared in supertypes because unlike most other validations being
+ // done in this class, which assume that supertype binding methods will be validated in a
+ // separate call to the validator since the supertype itself must be a @Module, we need to look
+ // at all the binding methods in the module's type hierarchy here.
+ return methodsIn(elements.getAllMembers(module)).stream()
+ .filter(method -> anyBindingMethodValidator.isBindingMethod(method))
+ .map(ExecutableElement::getModifiers)
+ .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
+ }
+
+ private void validateNoScopeAnnotationsOnModuleElement(
+ TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report) {
+ for (AnnotationMirror scope : getAnnotatedAnnotations(module, Scope.class)) {
+ report.addError(
+ String.format(
+ "@%ss cannot be scoped. Did you mean to scope a method instead?",
+ moduleKind.annotation().getSimpleName()),
+ module,
+ scope);
+ }
+ }
+
+ private void validateSelfCycles(
+ TypeElement module, ValidationReport.Builder<TypeElement> builder) {
+ ModuleAnnotation moduleAnnotation = moduleAnnotation(module).get();
+ moduleAnnotation
+ .includesAsAnnotationValues()
+ .forEach(
+ value ->
+ value.accept(
+ new SimpleAnnotationValueVisitor8<Void, Void>() {
+ @Override
+ public Void visitType(TypeMirror includedModule, Void aVoid) {
+ if (MoreTypes.equivalence().equivalent(module.asType(), includedModule)) {
+ String moduleKind = moduleAnnotation.annotationClass().getSimpleName();
+ builder.addError(
+ String.format("@%s cannot include themselves.", moduleKind),
+ module,
+ moduleAnnotation.annotation(),
+ value);
+ }
+ return null;
+ }
+ },
+ null));
+ }
+
+ private void validateModuleBindings(
+ TypeElement module, ValidationReport.Builder<TypeElement> report) {
+ BindingGraph bindingGraph =
+ bindingGraphConverter.convert(
+ bindingGraphFactory.create(
+ componentDescriptorFactory.moduleComponentDescriptor(module), true));
+ if (!bindingGraphValidator.isValid(bindingGraph)) {
+ // Since the validator uses a DiagnosticReporter to report errors, the ValdiationReport won't
+ // have any Items for them. We have to tell the ValidationReport that some errors were
+ // reported for the subject.
+ report.markDirty();
+ }
+ }
+
+ private static String formatListForErrorMessage(List<?> things) {
+ switch (things.size()) {
+ case 0:
+ return "";
+ case 1:
+ return things.get(0).toString();
+ default:
+ StringBuilder output = new StringBuilder();
+ Joiner.on(", ").appendTo(output, things.subList(0, things.size() - 1));
+ output.append(" and ").append(things.get(things.size() - 1));
+ return output.toString();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/MonitoringModuleGenerator.java b/java/dagger/internal/codegen/MonitoringModuleGenerator.java
new file mode 100644
index 0000000..1738fe9
--- /dev/null
+++ b/java/dagger/internal/codegen/MonitoringModuleGenerator.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCTION_COMPONENT_MONITOR_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.setOf;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.multibindings.Multibinds;
+import dagger.producers.ProductionScope;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import dagger.producers.monitoring.internal.Monitors;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Generates a monitoring module for use with production components. */
+final class MonitoringModuleGenerator extends SourceFileGenerator<TypeElement> {
+
+ @Inject
+ MonitoringModuleGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ ClassName nameGeneratedType(TypeElement componentElement) {
+ return SourceFiles.generatedMonitoringModuleName(componentElement);
+ }
+
+ @Override
+ Element originatingElement(TypeElement componentElement) {
+ return componentElement;
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedTypeName, TypeElement componentElement) {
+ return Optional.of(
+ classBuilder(generatedTypeName)
+ .addAnnotation(Module.class)
+ .addModifiers(ABSTRACT)
+ .addMethod(privateConstructor())
+ .addMethod(setOfFactories())
+ .addMethod(monitor(componentElement)));
+ }
+
+ private MethodSpec privateConstructor() {
+ return constructorBuilder().addModifiers(PRIVATE).build();
+ }
+
+ private MethodSpec setOfFactories() {
+ return methodBuilder("setOfFactories")
+ .addAnnotation(Multibinds.class)
+ .addModifiers(ABSTRACT)
+ .returns(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY))
+ .build();
+ }
+
+ private MethodSpec monitor(TypeElement componentElement) {
+ return methodBuilder("monitor")
+ .returns(ProductionComponentMonitor.class)
+ .addModifiers(STATIC)
+ .addAnnotation(Provides.class)
+ .addAnnotation(ProductionScope.class)
+ .addParameter(providerOf(ClassName.get(componentElement.asType())), "component")
+ .addParameter(
+ providerOf(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY)), "factories")
+ .addStatement(
+ "return $T.createMonitorForComponent(component, factories)", Monitors.class)
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java b/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
new file mode 100644
index 0000000..55ae579
--- /dev/null
+++ b/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A processing step that is responsible for generating a special module for a {@link
+ * ProductionComponent} or {@link ProductionSubcomponent}.
+ */
+final class MonitoringModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+ private final Messager messager;
+ private final MonitoringModuleGenerator monitoringModuleGenerator;
+
+ @Inject
+ MonitoringModuleProcessingStep(
+ Messager messager, MonitoringModuleGenerator monitoringModuleGenerator) {
+ super(MoreElements::asType);
+ this.messager = messager;
+ this.monitoringModuleGenerator = monitoringModuleGenerator;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(ProductionComponent.class, ProductionSubcomponent.class);
+ }
+
+ @Override
+ protected void process(
+ TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
+ monitoringModuleGenerator.generate(MoreElements.asType(element), messager);
+ }
+}
diff --git a/java/dagger/internal/codegen/MoreAnnotationMirrors.java b/java/dagger/internal/codegen/MoreAnnotationMirrors.java
new file mode 100644
index 0000000..92825a0
--- /dev/null
+++ b/java/dagger/internal/codegen/MoreAnnotationMirrors.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Name;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A utility class for working with {@link AnnotationMirror} instances, similar to {@link
+ * AnnotationMirrors}.
+ */
+final class MoreAnnotationMirrors {
+
+ private MoreAnnotationMirrors() {}
+
+ /**
+ * Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Equivalence.Wrapper} for
+ * that type.
+ */
+ static Optional<Equivalence.Wrapper<AnnotationMirror>> wrapOptionalInEquivalence(
+ Optional<AnnotationMirror> optional) {
+ return optional.map(AnnotationMirrors.equivalence()::wrap);
+ }
+
+ /**
+ * Unwraps an {@link Optional} of a {@link Equivalence.Wrapper} into an {@code Optional} of the
+ * underlying type.
+ */
+ static Optional<AnnotationMirror> unwrapOptionalEquivalence(
+ Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedOptional) {
+ return wrappedOptional.map(Equivalence.Wrapper::get);
+ }
+
+ static Name simpleName(AnnotationMirror annotationMirror) {
+ return annotationMirror.getAnnotationType().asElement().getSimpleName();
+ }
+
+ /**
+ * Returns the list of types that is the value named {@code name} from {@code annotationMirror}.
+ *
+ * @throws IllegalArgumentException unless that member represents an array of types
+ */
+ static ImmutableList<TypeMirror> getTypeListValue(
+ AnnotationMirror annotationMirror, String name) {
+ return asAnnotationValues(getAnnotationValue(annotationMirror, name))
+ .stream()
+ .map(MoreAnnotationValues::asType)
+ .collect(toImmutableList());
+ }
+}
diff --git a/java/dagger/internal/codegen/MoreAnnotationValues.java b/java/dagger/internal/codegen/MoreAnnotationValues.java
new file mode 100644
index 0000000..84a4d94
--- /dev/null
+++ b/java/dagger/internal/codegen/MoreAnnotationValues.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/** Utility methods for working with {@link AnnotationValue} instances. */
+final class MoreAnnotationValues {
+ /**
+ * Returns the list of values represented by an array annotation value.
+ *
+ * @throws IllegalArgumentException unless {@code annotationValue} represents an array
+ */
+ static ImmutableList<AnnotationValue> asAnnotationValues(AnnotationValue annotationValue) {
+ return annotationValue.accept(AS_ANNOTATION_VALUES, null);
+ }
+
+ private static final AnnotationValueVisitor<ImmutableList<AnnotationValue>, String>
+ AS_ANNOTATION_VALUES =
+ new SimpleAnnotationValueVisitor8<ImmutableList<AnnotationValue>, String>() {
+ @Override
+ public ImmutableList<AnnotationValue> visitArray(
+ List<? extends AnnotationValue> vals, String elementName) {
+ return ImmutableList.copyOf(vals);
+ }
+
+ @Override
+ protected ImmutableList<AnnotationValue> defaultAction(Object o, String elementName) {
+ throw new IllegalArgumentException(elementName + " is not an array: " + o);
+ }
+ };
+
+ /**
+ * Returns the type represented by an annotation value.
+ *
+ * @throws IllegalArgumentException unless {@code annotationValue} represents a single type
+ */
+ static TypeMirror asType(AnnotationValue annotationValue) {
+ return AS_TYPE.visit(annotationValue);
+ }
+
+ private static final AnnotationValueVisitor<TypeMirror, Void> AS_TYPE =
+ new SimpleAnnotationValueVisitor8<TypeMirror, Void>() {
+ @Override
+ public TypeMirror visitType(TypeMirror t, Void p) {
+ return t;
+ }
+
+ @Override
+ protected TypeMirror defaultAction(Object o, Void p) {
+ throw new TypeNotPresentException(o.toString(), null);
+ }
+ };
+
+ private MoreAnnotationValues() {}
+}
diff --git a/java/dagger/internal/codegen/MultibindingAnnotations.java b/java/dagger/internal/codegen/MultibindingAnnotations.java
new file mode 100644
index 0000000..b478704
--- /dev/null
+++ b/java/dagger/internal/codegen/MultibindingAnnotations.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.langmodel.DaggerElements.getAllAnnotations;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+/**
+ * Utility methods related to processing {@link IntoSet}, {@link ElementsIntoSet}, and {@link
+ * IntoMap}.
+ */
+final class MultibindingAnnotations {
+ static ImmutableSet<AnnotationMirror> forElement(Element method) {
+ return getAllAnnotations(method, IntoSet.class, ElementsIntoSet.class, IntoMap.class);
+ }
+}
diff --git a/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java b/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java
new file mode 100644
index 0000000..2bb0a7e
--- /dev/null
+++ b/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+
+/**
+ * Processing step that verifies that {@link IntoSet}, {@link ElementsIntoSet} and {@link IntoMap}
+ * are not present on non-binding methods.
+ */
+final class MultibindingAnnotationsProcessingStep
+ extends TypeCheckingProcessingStep<ExecutableElement> {
+ private final AnyBindingMethodValidator anyBindingMethodValidator;
+ private final Messager messager;
+
+ @Inject
+ MultibindingAnnotationsProcessingStep(
+ AnyBindingMethodValidator anyBindingMethodValidator, Messager messager) {
+ super(MoreElements::asExecutable);
+ this.anyBindingMethodValidator = anyBindingMethodValidator;
+ this.messager = messager;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(IntoSet.class, ElementsIntoSet.class, IntoMap.class);
+ }
+
+ @Override
+ protected void process(
+ ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
+ if (!anyBindingMethodValidator.isBindingMethod(method)) {
+ annotations.forEach(
+ annotation ->
+ messager.printMessage(
+ ERROR,
+ "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods",
+ method,
+ getAnnotationMirror(method, annotation).get()));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/MultibindingDeclaration.java b/java/dagger/internal/codegen/MultibindingDeclaration.java
new file mode 100644
index 0000000..c3724dc
--- /dev/null
+++ b/java/dagger/internal/codegen/MultibindingDeclaration.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import dagger.internal.codegen.ContributionType.HasContributionType;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.multibindings.Multibinds;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A declaration that a multibinding with a certain key is available to be injected in a component
+ * even if the component has no multibindings for that key. Identified by a map- or set-returning
+ * method annotated with {@link Multibinds @Multibinds}.
+ */
+@AutoValue
+abstract class MultibindingDeclaration extends BindingDeclaration implements HasContributionType {
+
+ /**
+ * The map or set key whose availability is declared. For maps, this will be {@code Map<K,
+ * Provider<V>>}. For sets, this will be {@code Set<T>}.
+ */
+ @Override
+ public abstract Key key();
+
+ /**
+ * {@link ContributionType#SET} if the declared type is a {@link Set}, or
+ * {@link ContributionType#MAP} if it is a {@link Map}.
+ */
+ @Override
+ public abstract ContributionType contributionType();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /**
+ * A factory for {@link MultibindingDeclaration}s.
+ */
+ static final class Factory {
+ private final DaggerTypes types;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ Factory(DaggerTypes types, KeyFactory keyFactory) {
+ this.types = types;
+ this.keyFactory = keyFactory;
+ }
+
+ /** A multibinding declaration for a {@link Multibinds @Multibinds} method. */
+ MultibindingDeclaration forMultibindsMethod(
+ ExecutableElement moduleMethod, TypeElement moduleElement) {
+ checkArgument(isAnnotationPresent(moduleMethod, Multibinds.class));
+ return forDeclaredMethod(
+ moduleMethod,
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(moduleElement.asType()), moduleMethod)),
+ moduleElement);
+ }
+
+ private MultibindingDeclaration forDeclaredMethod(
+ ExecutableElement method,
+ ExecutableType methodType,
+ TypeElement contributingType) {
+ TypeMirror returnType = methodType.getReturnType();
+ checkArgument(
+ SetType.isSet(returnType) || MapType.isMap(returnType),
+ "%s must return a set or map",
+ method);
+ return new AutoValue_MultibindingDeclaration(
+ Optional.<Element>of(method),
+ Optional.of(contributingType),
+ keyFactory.forMultibindsMethod(methodType, method),
+ contributionType(returnType));
+ }
+
+ private ContributionType contributionType(TypeMirror returnType) {
+ if (MapType.isMap(returnType)) {
+ return ContributionType.MAP;
+ } else if (SetType.isSet(returnType)) {
+ return ContributionType.SET;
+ } else {
+ throw new IllegalArgumentException("Must be Map or Set: " + returnType);
+ }
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/MultibindingExpression.java b/java/dagger/internal/codegen/MultibindingExpression.java
new file mode 100644
index 0000000..f0523eb
--- /dev/null
+++ b/java/dagger/internal/codegen/MultibindingExpression.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import java.util.Set;
+
+/** An abstract base class for multibinding {@link BindingExpression}s. */
+abstract class MultibindingExpression extends SimpleInvocationBindingExpression {
+ private final ProvisionBinding binding;
+ private final ComponentImplementation componentImplementation;
+
+ MultibindingExpression(
+ ResolvedBindings resolvedBindings, ComponentImplementation componentImplementation) {
+ super(resolvedBindings);
+ this.componentImplementation = componentImplementation;
+ this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ Expression expression = buildDependencyExpression(requestingClass);
+ componentImplementation.registerImplementedMultibinding(binding, bindingRequest());
+ return expression;
+ }
+
+ /**
+ * Returns an expression that evaluates to the value of a multibinding request for the given
+ * requesting class.
+ */
+ protected abstract Expression buildDependencyExpression(ClassName requestingClass);
+
+ /**
+ * Returns the subset of {@code dependencies} that represent multibinding contributions that were
+ * not included in a superclass implementation of this multibinding method. This is relevant only
+ * for ahead-of-time subcomponents. When not generating ahead-of-time subcomponents there is only
+ * one implementation of a multibinding expression and all {@link DependencyRequest}s from the
+ * argment are returned.
+ */
+ protected Set<DependencyRequest> getNewContributions(
+ ImmutableSet<DependencyRequest> dependencies) {
+ ImmutableSet<Key> superclassContributions = superclassContributions();
+ return Sets.filter(
+ dependencies, dependency -> !superclassContributions.contains(dependency.key()));
+ }
+
+ /**
+ * Returns the {@link CodeBlock} representing a call to a superclass implementation of the
+ * modifiable binding method that encapsulates this binding, if it exists. This is only possible
+ * when generating ahead-of-time subcomponents.
+ */
+ protected Optional<CodeBlock> superMethodCall() {
+ if (componentImplementation.superclassImplementation().isPresent()) {
+ Optional<ModifiableBindingMethod> method =
+ componentImplementation.getModifiableBindingMethod(bindingRequest());
+ if (method.isPresent()) {
+ if (!superclassContributions().isEmpty()) {
+ return Optional.of(CodeBlock.of("super.$L()", method.get().methodSpec().name));
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ private BindingRequest bindingRequest() {
+ return BindingRequest.bindingRequest(binding.key(), RequestKind.INSTANCE);
+ }
+
+ private ImmutableSet<Key> superclassContributions() {
+ return componentImplementation.superclassContributionsMade(bindingRequest());
+ }
+}
diff --git a/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java b/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java
new file mode 100644
index 0000000..abe161a
--- /dev/null
+++ b/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.Optional;
+
+/** An abstract factory creation expression for multibindings. */
+abstract class MultibindingFactoryCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ContributionBinding binding;
+
+ MultibindingFactoryCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ /** Returns the expression for a dependency of this multibinding. */
+ protected final CodeBlock multibindingDependencyExpression(DependencyRequest dependency) {
+ CodeBlock expression =
+ componentBindingExpressions
+ .getDependencyExpression(
+ BindingRequest.bindingRequest(dependency.key(), binding.frameworkType()),
+ componentImplementation.name())
+ .codeBlock();
+
+ return useRawType()
+ ? CodeBlocks.cast(expression, binding.frameworkType().frameworkClass())
+ : expression;
+ }
+
+ protected final ImmutableSet<DependencyRequest> dependenciesToImplement() {
+ ImmutableSet<Key> alreadyImplementedKeys =
+ componentImplementation.superclassContributionsMade(bindingRequest());
+ return binding.dependencies().stream()
+ .filter(dependency -> !alreadyImplementedKeys.contains(dependency.key()))
+ .collect(toImmutableSet());
+ }
+
+ protected Optional<CodeBlock> superContributions() {
+ if (dependenciesToImplement().size() == binding.dependencies().size()) {
+ return Optional.empty();
+ }
+ ModifiableBindingMethod superMethod =
+ componentImplementation.getModifiableBindingMethod(bindingRequest()).get();
+ return Optional.of(CodeBlock.of("super.$N()", superMethod.methodSpec().name));
+ }
+
+ /** The binding request for this framework instance. */
+ protected final BindingRequest bindingRequest() {
+ return BindingRequest.bindingRequest(binding.key(), binding.frameworkType());
+ }
+
+ /**
+ * Returns true if the {@linkplain ContributionBinding#key() key type} is inaccessible from the
+ * component, and therefore a raw type must be used.
+ */
+ protected final boolean useRawType() {
+ return !componentImplementation.isTypeAccessible(binding.key().type());
+ }
+
+ @Override
+ public final boolean useInnerSwitchingProvider() {
+ return !binding.dependencies().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/MultibindsMethodValidator.java b/java/dagger/internal/codegen/MultibindsMethodValidator.java
new file mode 100644
index 0000000..bc97d30
--- /dev/null
+++ b/java/dagger/internal/codegen/MultibindsMethodValidator.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
+import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
+import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
+import static dagger.internal.codegen.FrameworkTypes.isFrameworkType;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.multibindings.Multibinds;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link Multibinds} methods. */
+class MultibindsMethodValidator extends BindingMethodValidator {
+
+ /** Creates a validator for {@link Multibinds @Multibinds} methods. */
+ @Inject
+ MultibindsMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestValidator dependencyRequestValidator) {
+ super(
+ elements,
+ types,
+ Multibinds.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_ABSTRACT,
+ NO_EXCEPTIONS,
+ NO_MULTIBINDINGS,
+ NO_SCOPING);
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkParameters() {
+ if (!element.getParameters().isEmpty()) {
+ report.addError(bindingMethods("cannot have parameters"));
+ }
+ }
+
+ /** Adds an error unless the method returns a {@code Map<K, V>} or {@code Set<T>}. */
+ @Override
+ protected void checkType() {
+ if (!isPlainMap(element.getReturnType())
+ && !isPlainSet(element.getReturnType())) {
+ report.addError(bindingMethods("must return Map<K, V> or Set<T>"));
+ }
+ }
+
+ private boolean isPlainMap(TypeMirror returnType) {
+ if (!MapType.isMap(returnType)) {
+ return false;
+ }
+ MapType mapType = MapType.from(returnType);
+ return !mapType.isRawType()
+ && MoreTypes.isType(mapType.valueType()) // No wildcards.
+ && !isFrameworkType(mapType.valueType());
+ }
+
+ private boolean isPlainSet(TypeMirror returnType) {
+ if (!SetType.isSet(returnType)) {
+ return false;
+ }
+ SetType setType = SetType.from(returnType);
+ return !setType.isRawType()
+ && MoreTypes.isType(setType.elementType()) // No wildcards.
+ && !isFrameworkType(setType.elementType());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/NullableBindingValidator.java b/java/dagger/internal/codegen/NullableBindingValidator.java
new file mode 100644
index 0000000..1452c3f
--- /dev/null
+++ b/java/dagger/internal/codegen/NullableBindingValidator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+
+/**
+ * Reports errors or warnings (depending on the {@code -Adagger.nullableValidation} value) for each
+ * non-nullable dependency request that is satisfied by a nullable binding.
+ */
+final class NullableBindingValidator implements BindingGraphPlugin {
+
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ NullableBindingValidator(CompilerOptions compilerOptions) {
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ for (dagger.model.Binding binding : nullableBindings(bindingGraph)) {
+ for (DependencyEdge dependencyEdge : nonNullableDependencies(bindingGraph, binding)) {
+ diagnosticReporter.reportDependency(
+ compilerOptions.nullableValidationKind(),
+ dependencyEdge,
+ nullableToNonNullable(
+ binding.key().toString(),
+ binding.toString())); // binding.toString() will include the @Nullable
+ }
+ }
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/Nullable";
+ }
+
+ private ImmutableList<dagger.model.Binding> nullableBindings(BindingGraph bindingGraph) {
+ return bindingGraph.bindings().stream()
+ .filter(binding -> binding.isNullable())
+ .collect(toImmutableList());
+ }
+
+ private ImmutableSet<DependencyEdge> nonNullableDependencies(
+ BindingGraph bindingGraph, dagger.model.Binding binding) {
+ return bindingGraph.network().inEdges(binding).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .filter(edge -> !edge.dependencyRequest().isNullable())
+ .collect(toImmutableSet());
+ }
+
+ @VisibleForTesting
+ static String nullableToNonNullable(String key, String binding) {
+ return String.format("%s is not nullable, but is being provided by %s", key, binding);
+ }
+}
diff --git a/java/dagger/internal/codegen/OptionalBindingDeclaration.java b/java/dagger/internal/codegen/OptionalBindingDeclaration.java
new file mode 100644
index 0000000..b26ab91
--- /dev/null
+++ b/java/dagger/internal/codegen/OptionalBindingDeclaration.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import dagger.BindsOptionalOf;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/** A {@link BindsOptionalOf} declaration. */
+@AutoValue
+abstract class OptionalBindingDeclaration extends BindingDeclaration {
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The key's type is the method's return type, even though the synthetic bindings will be for
+ * {@code Optional} of derived types.
+ */
+ @Override
+ public abstract Key key();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ static class Factory {
+ private final KeyFactory keyFactory;
+
+ @Inject
+ Factory(KeyFactory keyFactory) {
+ this.keyFactory = keyFactory;
+ }
+
+ OptionalBindingDeclaration forMethod(ExecutableElement method, TypeElement contributingModule) {
+ checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
+ return new AutoValue_OptionalBindingDeclaration(
+ Optional.<Element>of(method),
+ Optional.of(contributingModule),
+ keyFactory.forBindsOptionalOfMethod(method, contributingModule));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/OptionalBindingExpression.java b/java/dagger/internal/codegen/OptionalBindingExpression.java
new file mode 100644
index 0000000..dbb0e37
--- /dev/null
+++ b/java/dagger/internal/codegen/OptionalBindingExpression.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.OptionalType.OptionalKind;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+
+/** A binding expression for optional bindings. */
+final class OptionalBindingExpression extends SimpleInvocationBindingExpression {
+ private final ProvisionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final SourceVersion sourceVersion;
+
+ @Inject
+ OptionalBindingExpression(
+ ResolvedBindings resolvedBindings,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ SourceVersion sourceVersion) {
+ super(resolvedBindings);
+ this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ this.sourceVersion = sourceVersion;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ OptionalType optionalType = OptionalType.from(binding.key());
+ OptionalKind optionalKind = optionalType.kind();
+ if (binding.dependencies().isEmpty()) {
+ if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+ // When compiling with -source 7, javac's type inference isn't strong enough to detect
+ // Futures.immediateFuture(Optional.absent()) for keys that aren't Object. It also has
+ // issues
+ // when used as an argument to some members injection proxy methods (see
+ // https://github.com/google/dagger/issues/916)
+ if (isTypeAccessibleFrom(binding.key().type(), requestingClass.packageName())) {
+ return Expression.create(
+ binding.key().type(), optionalKind.parameterizedAbsentValueExpression(optionalType));
+ }
+ }
+ return Expression.create(binding.key().type(), optionalKind.absentValueExpression());
+ }
+ DependencyRequest dependency = getOnlyElement(binding.dependencies());
+
+ CodeBlock dependencyExpression =
+ componentBindingExpressions
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock();
+
+ // If the dependency type is inaccessible, then we have to use Optional.<Object>of(...), or else
+ // we will get "incompatible types: inference variable has incompatible bounds.
+ return isTypeAccessibleFrom(dependency.key().type(), requestingClass.packageName())
+ ? Expression.create(
+ binding.key().type(), optionalKind.presentExpression(dependencyExpression))
+ : Expression.create(
+ types.erasure(binding.key().type()),
+ optionalKind.presentObjectExpression(dependencyExpression));
+ }
+
+ @Override
+ boolean requiresMethodEncapsulation() {
+ // TODO(dpb): Maybe require it for present bindings.
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/OptionalFactories.java b/java/dagger/internal/codegen/OptionalFactories.java
new file mode 100644
index 0000000..51c9939
--- /dev/null
+++ b/java/dagger/internal/codegen/OptionalFactories.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.ABSENT_OPTIONAL_FIELD;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.ABSENT_OPTIONAL_METHOD;
+import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.PRESENT_FACTORY;
+import static dagger.internal.codegen.RequestKinds.requestTypeName;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
+import static dagger.internal.codegen.javapoet.TypeNames.abstractProducerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.InstanceFactory;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.OptionalType.OptionalKind;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import dagger.producers.internal.Producers;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+import java.util.concurrent.Executor;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/** The nested class and static methods required by the component to implement optional bindings. */
+// TODO(dpb): Name members simply if a component uses only one of Guava or JDK Optional.
+@PerGeneratedFile
+final class OptionalFactories {
+ private final ComponentImplementation componentImplementation;
+
+ @Inject OptionalFactories(@TopLevel ComponentImplementation componentImplementation) {
+ this.componentImplementation = componentImplementation;
+ }
+
+ /**
+ * The factory classes that implement {@code Provider<Optional<T>>} or {@code
+ * Producer<Optional<T>>} for present optional bindings for a given kind of dependency request
+ * within the component.
+ *
+ * <p>The key is the {@code Provider<Optional<T>>} type.
+ */
+ private final Map<PresentFactorySpec, TypeSpec> presentFactoryClasses =
+ new TreeMap<>(
+ Comparator.comparing(PresentFactorySpec::valueKind)
+ .thenComparing(PresentFactorySpec::frameworkType)
+ .thenComparing(PresentFactorySpec::optionalKind));
+
+ /**
+ * The static methods that return a {@code Provider<Optional<T>>} that always returns an absent
+ * value.
+ */
+ private final Map<OptionalKind, MethodSpec> absentOptionalProviderMethods = new TreeMap<>();
+
+ /**
+ * The static fields for {@code Provider<Optional<T>>} objects that always return an absent value.
+ */
+ private final Map<OptionalKind, FieldSpec> absentOptionalProviderFields = new TreeMap<>();
+
+ /**
+ * Returns an expression that calls a static method that returns a {@code Provider<Optional<T>>}
+ * for absent optional bindings.
+ */
+ CodeBlock absentOptionalProvider(ContributionBinding binding) {
+ verify(
+ binding.bindingType().equals(BindingType.PROVISION),
+ "Absent optional bindings should be provisions: %s",
+ binding);
+ OptionalKind optionalKind = OptionalType.from(binding.key()).kind();
+ return CodeBlock.of(
+ "$N()",
+ absentOptionalProviderMethods.computeIfAbsent(
+ optionalKind,
+ kind -> {
+ MethodSpec method = absentOptionalProviderMethod(kind);
+ componentImplementation.addMethod(ABSENT_OPTIONAL_METHOD, method);
+ return method;
+ }));
+ }
+
+ /**
+ * Creates a method specification for a {@code Provider<Optional<T>>} that always returns an
+ * absent value.
+ */
+ private MethodSpec absentOptionalProviderMethod(OptionalKind optionalKind) {
+ TypeVariableName typeVariable = TypeVariableName.get("T");
+ return methodBuilder(
+ String.format(
+ "absent%sProvider", UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind.name())))
+ .addModifiers(PRIVATE, STATIC)
+ .addTypeVariable(typeVariable)
+ .returns(providerOf(optionalKind.of(typeVariable)))
+ .addJavadoc(
+ "Returns a {@link $T} that returns {@code $L}.",
+ Provider.class,
+ optionalKind.absentValueExpression())
+ .addCode("$L // safe covariant cast\n", AnnotationSpecs.suppressWarnings(UNCHECKED))
+ .addCode(
+ "$1T provider = ($1T) $2N;",
+ providerOf(optionalKind.of(typeVariable)),
+ absentOptionalProviderFields.computeIfAbsent(
+ optionalKind,
+ kind -> {
+ FieldSpec field = absentOptionalProviderField(kind);
+ componentImplementation.addField(ABSENT_OPTIONAL_FIELD, field);
+ return field;
+ }))
+ .addCode("return provider;")
+ .build();
+ }
+
+ /**
+ * Creates a field specification for a {@code Provider<Optional<T>>} that always returns an absent
+ * value.
+ */
+ private FieldSpec absentOptionalProviderField(OptionalKind optionalKind) {
+ return FieldSpec.builder(
+ PROVIDER,
+ String.format("ABSENT_%s_PROVIDER", optionalKind.name()),
+ PRIVATE,
+ STATIC,
+ FINAL)
+ .addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES))
+ .initializer("$T.create($L)", InstanceFactory.class, optionalKind.absentValueExpression())
+ .addJavadoc(
+ "A {@link $T} that returns {@code $L}.",
+ Provider.class,
+ optionalKind.absentValueExpression())
+ .build();
+ }
+
+ /** Information about the type of a factory for present bindings. */
+ @AutoValue
+ abstract static class PresentFactorySpec {
+ /** Whether the factory is a {@link Provider} or a {@link Producer}. */
+ abstract FrameworkType frameworkType();
+
+ /** What kind of {@code Optional} is returned. */
+ abstract OptionalKind optionalKind();
+
+ /** The kind of request satisfied by the value of the {@code Optional}. */
+ abstract RequestKind valueKind();
+
+ /** The type variable for the factory class. */
+ TypeVariableName typeVariable() {
+ return TypeVariableName.get("T");
+ }
+
+ /** The type contained by the {@code Optional}. */
+ TypeName valueType() {
+ return requestTypeName(valueKind(), typeVariable());
+ }
+
+ /** The type provided or produced by the factory. */
+ ParameterizedTypeName optionalType() {
+ return optionalKind().of(valueType());
+ }
+
+ /** The type of the factory. */
+ ParameterizedTypeName factoryType() {
+ return frameworkType().frameworkClassOf(optionalType());
+ }
+
+ /** The type of the delegate provider or producer. */
+ ParameterizedTypeName delegateType() {
+ return frameworkType().frameworkClassOf(typeVariable());
+ }
+
+ /** Returns the superclass the generated factory should have, if any. */
+ Optional<ParameterizedTypeName> superclass() {
+ switch (frameworkType()) {
+ case PRODUCER_NODE:
+ // TODO(cgdecker): This probably isn't a big issue for now, but it's possible this
+ // shouldn't be an AbstractProducer:
+ // - As AbstractProducer, it'll only call the delegate's get() method once and then cache
+ // that result (essentially) rather than calling the delegate's get() method each time
+ // its get() method is called (which was what it did before the cancellation change).
+ // - It's not 100% clear to me whether the view-creation methods should return a view of
+ // the same view created by the delegate or if they should just return their own views.
+ return Optional.of(abstractProducerOf(optionalType()));
+ default:
+ return Optional.empty();
+ }
+ }
+
+ /** Returns the superinterface the generated factory should have, if any. */
+ Optional<ParameterizedTypeName> superinterface() {
+ switch (frameworkType()) {
+ case PROVIDER:
+ return Optional.of(factoryType());
+ default:
+ return Optional.empty();
+ }
+ }
+
+ /** Returns the name of the factory method to generate. */
+ String factoryMethodName() {
+ switch (frameworkType()) {
+ case PROVIDER:
+ return "get";
+ case PRODUCER_NODE:
+ return "compute";
+ }
+ throw new AssertionError(frameworkType());
+ }
+
+ /** The name of the factory class. */
+ String factoryClassName() {
+ return new StringBuilder("Present")
+ .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind().name()))
+ .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, valueKind().toString()))
+ .append(frameworkType().frameworkClass().getSimpleName())
+ .toString();
+ }
+
+ private static PresentFactorySpec of(ContributionBinding binding) {
+ return new AutoValue_OptionalFactories_PresentFactorySpec(
+ FrameworkType.forBindingType(binding.bindingType()),
+ OptionalType.from(binding.key()).kind(),
+ getOnlyElement(binding.dependencies()).kind());
+ }
+ }
+
+ /**
+ * Returns an expression for an instance of a nested class that implements {@code
+ * Provider<Optional<T>>} or {@code Producer<Optional<T>>} for a present optional binding, where
+ * {@code T} represents dependency requests of that kind.
+ *
+ * <ul>
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#INSTANCE}, the class implements
+ * {@code ProviderOrProducer<Optional<T>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER}, the class implements
+ * {@code Provider<Optional<Provider<T>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#LAZY}, the class implements {@code
+ * Provider<Optional<Lazy<T>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER_OF_LAZY}, the class
+ * implements {@code Provider<Optional<Provider<Lazy<T>>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCER}, the class implements
+ * {@code Producer<Optional<Producer<T>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCED}, the class implements
+ * {@code Producer<Optional<Produced<T>>>}.
+ * </ul>
+ *
+ * @param delegateFactory an expression for a {@link Provider} or {@link Producer} of the
+ * underlying type
+ */
+ CodeBlock presentOptionalFactory(ContributionBinding binding, CodeBlock delegateFactory) {
+ return CodeBlock.of(
+ "$N.of($L)",
+ presentFactoryClasses.computeIfAbsent(
+ PresentFactorySpec.of(binding),
+ spec -> {
+ TypeSpec type = presentOptionalFactoryClass(spec);
+ componentImplementation.addType(PRESENT_FACTORY, type);
+ return type;
+ }),
+ delegateFactory);
+ }
+
+ private TypeSpec presentOptionalFactoryClass(PresentFactorySpec spec) {
+ FieldSpec delegateField =
+ FieldSpec.builder(spec.delegateType(), "delegate", PRIVATE, FINAL).build();
+ ParameterSpec delegateParameter = ParameterSpec.builder(delegateField.type, "delegate").build();
+ TypeSpec.Builder factoryClassBuilder =
+ classBuilder(spec.factoryClassName())
+ .addTypeVariable(spec.typeVariable())
+ .addModifiers(PRIVATE, STATIC, FINAL)
+ .addJavadoc(
+ "A {@code $T} that uses a delegate {@code $T}.",
+ spec.factoryType(),
+ delegateField.type);
+
+ spec.superclass().ifPresent(factoryClassBuilder::superclass);
+ spec.superinterface().ifPresent(factoryClassBuilder::addSuperinterface);
+
+ return factoryClassBuilder
+ .addField(delegateField)
+ .addMethod(
+ constructorBuilder()
+ .addModifiers(PRIVATE)
+ .addParameter(delegateParameter)
+ .addCode(
+ "this.$N = $T.checkNotNull($N);",
+ delegateField,
+ Preconditions.class,
+ delegateParameter)
+ .build())
+ .addMethod(presentOptionalFactoryGetMethod(spec, delegateField))
+ .addMethod(
+ methodBuilder("of")
+ .addModifiers(PRIVATE, STATIC)
+ .addTypeVariable(spec.typeVariable())
+ .returns(spec.factoryType())
+ .addParameter(delegateParameter)
+ .addCode(
+ "return new $L<$T>($N);",
+ spec.factoryClassName(),
+ spec.typeVariable(),
+ delegateParameter)
+ .build())
+ .build();
+ }
+
+ private MethodSpec presentOptionalFactoryGetMethod(
+ PresentFactorySpec spec, FieldSpec delegateField) {
+ MethodSpec.Builder getMethodBuilder =
+ methodBuilder(spec.factoryMethodName()).addAnnotation(Override.class).addModifiers(PUBLIC);
+
+ switch (spec.frameworkType()) {
+ case PROVIDER:
+ return getMethodBuilder
+ .returns(spec.optionalType())
+ .addCode(
+ "return $L;",
+ spec.optionalKind()
+ .presentExpression(
+ FrameworkType.PROVIDER.to(
+ spec.valueKind(), CodeBlock.of("$N", delegateField))))
+ .build();
+
+ case PRODUCER_NODE:
+ getMethodBuilder.returns(listenableFutureOf(spec.optionalType()));
+
+ switch (spec.valueKind()) {
+ case FUTURE: // return a ListenableFuture<Optional<ListenableFuture<T>>>
+ case PRODUCER: // return a ListenableFuture<Optional<Producer<T>>>
+ return getMethodBuilder
+ .addCode(
+ "return $T.immediateFuture($L);",
+ Futures.class,
+ spec.optionalKind()
+ .presentExpression(
+ FrameworkType.PRODUCER_NODE.to(
+ spec.valueKind(), CodeBlock.of("$N", delegateField))))
+ .build();
+
+ case INSTANCE: // return a ListenableFuture<Optional<T>>
+ return getMethodBuilder
+ .addCode(
+ "return $L;",
+ transformFutureToOptional(
+ spec.optionalKind(),
+ spec.typeVariable(),
+ CodeBlock.of("$N.get()", delegateField)))
+ .build();
+
+ case PRODUCED: // return a ListenableFuture<Optional<Produced<T>>>
+ return getMethodBuilder
+ .addCode(
+ "return $L;",
+ transformFutureToOptional(
+ spec.optionalKind(),
+ spec.valueType(),
+ CodeBlock.of(
+ "$T.createFutureProduced($N.get())", Producers.class, delegateField)))
+ .build();
+
+ default:
+ throw new UnsupportedOperationException(
+ spec.factoryType() + " objects are not supported");
+ }
+ }
+ throw new AssertionError(spec.frameworkType());
+ }
+
+ /**
+ * An expression that uses {@link Futures#transform(ListenableFuture, Function, Executor)} to
+ * transform a {@code ListenableFuture<inputType>} into a {@code
+ * ListenableFuture<Optional<inputType>>}.
+ *
+ * @param inputFuture an expression of type {@code ListenableFuture<inputType>}
+ */
+ private static CodeBlock transformFutureToOptional(
+ OptionalKind optionalKind, TypeName inputType, CodeBlock inputFuture) {
+ return CodeBlock.of(
+ "$T.transform($L, $L, $T.directExecutor())",
+ Futures.class,
+ inputFuture,
+ anonymousClassBuilder("")
+ .addSuperinterface(
+ ParameterizedTypeName.get(
+ ClassName.get(Function.class), inputType, optionalKind.of(inputType)))
+ .addMethod(
+ methodBuilder("apply")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(optionalKind.of(inputType))
+ .addParameter(inputType, "input")
+ .addCode("return $L;", optionalKind.presentExpression(CodeBlock.of("input")))
+ .build())
+ .build(),
+ MoreExecutors.class);
+ }
+}
diff --git a/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java b/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java
new file mode 100644
index 0000000..ba9e25f
--- /dev/null
+++ b/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/**
+ * A {@link FrameworkInstanceCreationExpression} for {@link dagger.model.BindingKind#OPTIONAL
+ * optional bindings}.
+ */
+final class OptionalFactoryInstanceCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final OptionalFactories optionalFactories;
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ OptionalFactoryInstanceCreationExpression(
+ OptionalFactories optionalFactories,
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.optionalFactories = optionalFactories;
+ this.binding = binding;
+ this.componentImplementation = componentImplementation;
+ this.componentBindingExpressions = componentBindingExpressions;
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return binding.dependencies().isEmpty()
+ ? optionalFactories.absentOptionalProvider(binding)
+ : optionalFactories.presentOptionalFactory(
+ binding,
+ componentBindingExpressions
+ .getDependencyExpression(
+ bindingRequest(
+ getOnlyElement(binding.dependencies()).key(), binding.frameworkType()),
+ componentImplementation.name())
+ .codeBlock());
+ }
+
+ @Override
+ public boolean useInnerSwitchingProvider() {
+ // Share providers for empty optionals from OptionalFactories so we don't have numerous
+ // switch cases that all return Optional.empty().
+ return !binding.dependencies().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/OptionalType.java b/java/dagger/internal/codegen/OptionalType.java
new file mode 100644
index 0000000..0fdbf68
--- /dev/null
+++ b/java/dagger/internal/codegen/OptionalType.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.lang.model.element.Name;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Information about an {@code Optional} {@link TypeMirror}.
+ *
+ * <p>{@link com.google.common.base.Optional} and {@link java.util.Optional} are supported.
+ */
+@AutoValue
+abstract class OptionalType {
+
+ /** A variant of {@code Optional}. */
+ enum OptionalKind {
+ /** {@link com.google.common.base.Optional}. */
+ GUAVA_OPTIONAL(com.google.common.base.Optional.class, "absent"),
+
+ /** {@link java.util.Optional}. */
+ JDK_OPTIONAL(java.util.Optional.class, "empty"),
+ ;
+
+ private final Class<?> clazz;
+ private final String absentFactoryMethodName;
+
+ OptionalKind(Class<?> clazz, String absentFactoryMethodName) {
+ this.clazz = clazz;
+ this.absentFactoryMethodName = absentFactoryMethodName;
+ }
+
+ /** Returns {@code valueType} wrapped in the correct class. */
+ ParameterizedTypeName of(TypeName valueType) {
+ return ParameterizedTypeName.get(ClassName.get(clazz), valueType);
+ }
+
+ /** Returns an expression for the absent/empty value. */
+ CodeBlock absentValueExpression() {
+ return CodeBlock.of("$T.$L()", clazz, absentFactoryMethodName);
+ }
+
+ /**
+ * Returns an expression for the absent/empty value, parameterized with {@link #valueType()}.
+ */
+ CodeBlock parameterizedAbsentValueExpression(OptionalType optionalType) {
+ return CodeBlock.of("$T.<$T>$L()", clazz, optionalType.valueType(), absentFactoryMethodName);
+ }
+
+ /** Returns an expression for the present {@code value}. */
+ CodeBlock presentExpression(CodeBlock value) {
+ return CodeBlock.of("$T.of($L)", clazz, value);
+ }
+
+ /**
+ * Returns an expression for the present {@code value}, returning {@code Optional<Object>} no
+ * matter what type the value is.
+ */
+ CodeBlock presentObjectExpression(CodeBlock value) {
+ return CodeBlock.of("$T.<$T>of($L)", clazz, Object.class, value);
+ }
+ }
+
+ private static final TypeVisitor<Optional<OptionalKind>, Void> OPTIONAL_KIND =
+ new SimpleTypeVisitor8<Optional<OptionalKind>, Void>(Optional.empty()) {
+ @Override
+ public Optional<OptionalKind> visitDeclared(DeclaredType t, Void p) {
+ for (OptionalKind optionalKind : OptionalKind.values()) {
+ Name qualifiedName = MoreElements.asType(t.asElement()).getQualifiedName();
+ if (qualifiedName.contentEquals(optionalKind.clazz.getCanonicalName())) {
+ return Optional.of(optionalKind);
+ }
+ }
+ return Optional.empty();
+ }
+ };
+
+ /**
+ * The optional type itself, wrapped using {@link MoreTypes#equivalence()}.
+ *
+ * @deprecated Use {@link #declaredOptionalType()} instead.
+ */
+ @Deprecated
+ protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredOptionalType();
+
+ /** The optional type itself. */
+ @SuppressWarnings("deprecation")
+ DeclaredType declaredOptionalType() {
+ return wrappedDeclaredOptionalType().get();
+ }
+
+ /** Which {@code Optional} type is used. */
+ OptionalKind kind() {
+ return declaredOptionalType().accept(OPTIONAL_KIND, null).get();
+ }
+
+ /** The value type. */
+ TypeMirror valueType() {
+ return declaredOptionalType().getTypeArguments().get(0);
+ }
+
+ /** Returns {@code true} if {@code type} is an {@code Optional} type. */
+ static boolean isOptional(TypeMirror type) {
+ return type.accept(OPTIONAL_KIND, null).isPresent();
+ }
+
+ /** Returns {@code true} if {@code key.type()} is an {@code Optional} type. */
+ static boolean isOptional(Key key) {
+ return isOptional(key.type());
+ }
+
+ /**
+ * Returns a {@link OptionalType} for {@code type}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not an {@code Optional} type
+ */
+ static OptionalType from(TypeMirror type) {
+ checkArgument(isOptional(type), "%s must be an Optional", type);
+ return new AutoValue_OptionalType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+ }
+
+ /**
+ * Returns a {@link OptionalType} for {@code key}'s {@link Key#type() type}.
+ *
+ * @throws IllegalArgumentException if {@code key.type()} is not an {@code Optional} type
+ */
+ static OptionalType from(Key key) {
+ return from(key.type());
+ }
+}
diff --git a/java/dagger/internal/codegen/Optionals.java b/java/dagger/internal/codegen/Optionals.java
new file mode 100644
index 0000000..1021c35
--- /dev/null
+++ b/java/dagger/internal/codegen/Optionals.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.asList;
+
+import java.util.Comparator;
+import java.util.Optional;
+import java.util.function.Function;
+
+/** Utilities for {@link Optional}s. */
+final class Optionals {
+ /**
+ * A {@link Comparator} that puts empty {@link Optional}s before present ones, and compares
+ * present {@link Optional}s by their values.
+ */
+ static <C extends Comparable<C>> Comparator<Optional<C>> optionalComparator() {
+ return Comparator.comparing((Optional<C> optional) -> optional.isPresent())
+ .thenComparing(Optional::get);
+ }
+
+ static <T> Comparator<Optional<T>> emptiesLast(Comparator<? super T> valueComparator) {
+ checkNotNull(valueComparator);
+ return Comparator.comparing(o -> o.orElse(null), Comparator.nullsLast(valueComparator));
+ }
+
+ /** Returns the first argument that is present, or empty if none are. */
+ @SafeVarargs
+ static <T> Optional<T> firstPresent(Optional<T> first, Optional<T> second, Optional<T>... rest) {
+ return asList(first, second, rest)
+ .stream()
+ .filter(Optional::isPresent)
+ .findFirst()
+ .orElse(Optional.empty());
+ }
+
+ /**
+ * Walks a chain of present optionals as defined by successive calls to {@code nextFunction},
+ * returning the value of the final optional that is present. The first optional in the chain is
+ * the result of {@code nextFunction(start)}.
+ */
+ static <T> T rootmostValue(T start, Function<T, Optional<T>> nextFunction) {
+ T current = start;
+ for (Optional<T> next = nextFunction.apply(start);
+ next.isPresent();
+ next = nextFunction.apply(current)) {
+ current = next.get();
+ }
+ return current;
+ }
+
+ private Optionals() {}
+}
diff --git a/java/dagger/internal/codegen/ParentComponent.java b/java/dagger/internal/codegen/ParentComponent.java
new file mode 100644
index 0000000..2d2b583
--- /dev/null
+++ b/java/dagger/internal/codegen/ParentComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A {@link Qualifier} for bindings that are associated with a component implementation's
+ * parent component.
+ */
+@Retention(RUNTIME)
+@Qualifier
+@interface ParentComponent {}
diff --git a/java/dagger/internal/codegen/PerComponentImplementation.java b/java/dagger/internal/codegen/PerComponentImplementation.java
new file mode 100644
index 0000000..5d4ba18
--- /dev/null
+++ b/java/dagger/internal/codegen/PerComponentImplementation.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/** A {@link Scope} that encompasses a single component implementation. */
+@Retention(RUNTIME)
+@Scope
+@interface PerComponentImplementation {}
diff --git a/java/dagger/internal/codegen/PerGeneratedFile.java b/java/dagger/internal/codegen/PerGeneratedFile.java
new file mode 100644
index 0000000..c30e67a
--- /dev/null
+++ b/java/dagger/internal/codegen/PerGeneratedFile.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * A {@link Scope} that encompasses a top-level component implementation and any of its inner
+ * descendant component implementations in the same generated file.
+ */
+@Retention(RUNTIME)
+@Scope
+@interface PerGeneratedFile {}
diff --git a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
new file mode 100644
index 0000000..482c123
--- /dev/null
+++ b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.PRIVATE_METHOD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+
+/**
+ * A binding expression that wraps the dependency expressions in a private, no-arg method.
+ *
+ * <p>Dependents of this binding expression will just call the no-arg private method.
+ */
+final class PrivateMethodBindingExpression extends MethodBindingExpression {
+ private final BindingRequest request;
+ private final ComponentImplementation componentImplementation;
+ private String methodName;
+
+ PrivateMethodBindingExpression(
+ BindingRequest request,
+ ResolvedBindings resolvedBindings,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression,
+ ComponentImplementation componentImplementation,
+ DaggerTypes types) {
+ super(
+ request,
+ resolvedBindings,
+ methodImplementationStrategy,
+ wrappedBindingExpression,
+ componentImplementation,
+ types);
+ this.request = checkNotNull(request);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ }
+
+ @Override
+ protected void addMethod() {
+ if (methodName == null) {
+ // Have to set methodName field before implementing the method in order to handle recursion.
+ methodName = componentImplementation.getUniqueMethodName(request);
+ // TODO(user): Fix the order that these generated methods are written to the component.
+ componentImplementation.addMethod(
+ PRIVATE_METHOD,
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ .returns(TypeName.get(returnType()))
+ .addCode(methodBody())
+ .build());
+ }
+ }
+
+ @Override
+ protected String methodName() {
+ checkState(methodName != null, "addMethod() must be called before methodName()");
+ return methodName;
+ }
+}
diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java b/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java
new file mode 100644
index 0000000..cf2475d
--- /dev/null
+++ b/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.FeatureStatus.DISABLED;
+import static dagger.internal.codegen.FeatureStatus.ENABLED;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EMIT_MODIFIABLE_METADATA_ANNOTATIONS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_ANDROID_MODE;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FAST_INIT;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FLOATING_BINDS_METHODS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FORMAT_GENERATED_SOURCE;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.WRITE_PRODUCER_NAME_IN_TOKEN;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.HEADER_COMPILATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.USE_GRADLE_INCREMENTAL_PROCESSING;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.DISABLE_INTER_COMPONENT_SCOPE_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.EXPLICIT_BINDING_CONFLICTS_WITH_INJECT;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.FULL_BINDING_GRAPH_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.MODULE_HAS_DIFFERENT_SCOPES_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.NULLABLE_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.PRIVATE_MEMBER_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.STATIC_MEMBER_VALIDATION;
+import static dagger.internal.codegen.ValidationType.ERROR;
+import static dagger.internal.codegen.ValidationType.NONE;
+import static dagger.internal.codegen.ValidationType.WARNING;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Stream.concat;
+
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.producers.Produces;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+final class ProcessingEnvironmentCompilerOptions extends CompilerOptions {
+ /** Returns a valid {@link CompilerOptions} parsed from the processing environment. */
+ static CompilerOptions create(ProcessingEnvironment processingEnvironment) {
+ return new ProcessingEnvironmentCompilerOptions(processingEnvironment).checkValid();
+ }
+
+ private final ProcessingEnvironment processingEnvironment;
+ private final Map<EnumOption<?>, Object> enumOptions = new HashMap<>();
+ private final Map<EnumOption<?>, ImmutableMap<String, ? extends Enum<?>>> allCommandLineOptions =
+ new HashMap<>();
+
+ private ProcessingEnvironmentCompilerOptions(ProcessingEnvironment processingEnvironment) {
+ this.processingEnvironment = processingEnvironment;
+ }
+
+ @Override
+ boolean usesProducers() {
+ return processingEnvironment.getElementUtils().getTypeElement(Produces.class.getCanonicalName())
+ != null;
+ }
+
+ @Override
+ boolean headerCompilation() {
+ return isEnabled(HEADER_COMPILATION);
+ }
+
+ @Override
+ boolean fastInit() {
+ return isEnabled(FAST_INIT);
+ }
+
+ @Override
+ boolean formatGeneratedSource() {
+ return isEnabled(FORMAT_GENERATED_SOURCE);
+ }
+
+ @Override
+ boolean writeProducerNameInToken() {
+ return isEnabled(WRITE_PRODUCER_NAME_IN_TOKEN);
+ }
+
+ @Override
+ Diagnostic.Kind nullableValidationKind() {
+ return diagnosticKind(NULLABLE_VALIDATION);
+ }
+
+ @Override
+ Diagnostic.Kind privateMemberValidationKind() {
+ return diagnosticKind(PRIVATE_MEMBER_VALIDATION);
+ }
+
+ @Override
+ Diagnostic.Kind staticMemberValidationKind() {
+ return diagnosticKind(STATIC_MEMBER_VALIDATION);
+ }
+
+ @Override
+ boolean ignorePrivateAndStaticInjectionForComponent() {
+ return isEnabled(IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT);
+ }
+
+ @Override
+ ValidationType scopeCycleValidationType() {
+ return parseOption(DISABLE_INTER_COMPONENT_SCOPE_VALIDATION);
+ }
+
+ @Override
+ boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+ return isEnabled(WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM);
+ }
+
+ @Override
+ boolean aheadOfTimeSubcomponents() {
+ return isEnabled(EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS);
+ }
+
+ @Override
+ boolean forceUseSerializedComponentImplementations() {
+ return isEnabled(FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS);
+ }
+
+ @Override
+ boolean emitModifiableMetadataAnnotations() {
+ return isEnabled(EMIT_MODIFIABLE_METADATA_ANNOTATIONS);
+ }
+
+ @Override
+ boolean useGradleIncrementalProcessing() {
+ return isEnabled(USE_GRADLE_INCREMENTAL_PROCESSING);
+ }
+
+ @Override
+ ValidationType fullBindingGraphValidationType(TypeElement element) {
+ return fullBindingGraphValidationType();
+ }
+
+ private ValidationType fullBindingGraphValidationType() {
+ return parseOption(FULL_BINDING_GRAPH_VALIDATION);
+ }
+
+ @Override
+ Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+ return diagnosticKind(MODULE_HAS_DIFFERENT_SCOPES_VALIDATION);
+ }
+
+ @Override
+ ValidationType explicitBindingConflictsWithInjectValidationType() {
+ return parseOption(EXPLICIT_BINDING_CONFLICTS_WITH_INJECT);
+ }
+
+ private boolean isEnabled(KeyOnlyOption keyOnlyOption) {
+ return processingEnvironment.getOptions().containsKey(keyOnlyOption.toString());
+ }
+
+ private boolean isEnabled(Feature feature) {
+ return parseOption(feature).equals(ENABLED);
+ }
+
+ private Diagnostic.Kind diagnosticKind(Validation validation) {
+ return parseOption(validation).diagnosticKind().get();
+ }
+
+ @SuppressWarnings("CheckReturnValue")
+ private ProcessingEnvironmentCompilerOptions checkValid() {
+ for (KeyOnlyOption keyOnlyOption : KeyOnlyOption.values()) {
+ isEnabled(keyOnlyOption);
+ }
+ for (Feature feature : Feature.values()) {
+ parseOption(feature);
+ }
+ for (Validation validation : Validation.values()) {
+ parseOption(validation);
+ }
+ noLongerRecognized(EXPERIMENTAL_ANDROID_MODE);
+ noLongerRecognized(FLOATING_BINDS_METHODS);
+ return this;
+ }
+
+ private void noLongerRecognized(CommandLineOption commandLineOption) {
+ if (processingEnvironment.getOptions().containsKey(commandLineOption.toString())) {
+ processingEnvironment
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.WARNING, commandLineOption + " is no longer recognized by Dagger");
+ }
+ }
+
+ private interface CommandLineOption {
+ /** The key of the option (appears after "-A"). */
+ @Override
+ String toString();
+
+ /**
+ * Returns all aliases besides {@link #toString()}, such as old names for an option, in order of
+ * precedence.
+ */
+ default ImmutableList<String> aliases() {
+ return ImmutableList.of();
+ }
+
+ /** All the command-line names for this option, in order of precedence. */
+ default Stream<String> allNames() {
+ return concat(Stream.of(toString()), aliases().stream());
+ }
+ }
+
+ /** An option that can be set on the command line. */
+ private interface EnumOption<E extends Enum<E>> extends CommandLineOption {
+ /** The default value for this option. */
+ E defaultValue();
+
+ /** The valid values for this option. */
+ Set<E> validValues();
+ }
+
+ enum KeyOnlyOption implements CommandLineOption {
+ HEADER_COMPILATION {
+ @Override
+ public String toString() {
+ return "experimental_turbine_hjar";
+ }
+ },
+
+ USE_GRADLE_INCREMENTAL_PROCESSING {
+ @Override
+ public String toString() {
+ return "dagger.gradle.incremental";
+ }
+ },
+ }
+
+ /**
+ * A feature that can be enabled or disabled on the command line by setting {@code -Akey=ENABLED}
+ * or {@code -Akey=DISABLED}.
+ */
+ enum Feature implements EnumOption<FeatureStatus> {
+ FAST_INIT,
+
+ EXPERIMENTAL_ANDROID_MODE,
+
+ FORMAT_GENERATED_SOURCE,
+
+ WRITE_PRODUCER_NAME_IN_TOKEN,
+
+ WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM,
+
+ IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT,
+
+ EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS,
+
+ FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS,
+
+ EMIT_MODIFIABLE_METADATA_ANNOTATIONS(ENABLED),
+
+ FLOATING_BINDS_METHODS,
+ ;
+
+ final FeatureStatus defaultValue;
+
+ Feature() {
+ this(DISABLED);
+ }
+
+ Feature(FeatureStatus defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ @Override
+ public FeatureStatus defaultValue() {
+ return defaultValue;
+ }
+
+ @Override
+ public Set<FeatureStatus> validValues() {
+ return EnumSet.allOf(FeatureStatus.class);
+ }
+
+ @Override
+ public String toString() {
+ return optionName(this);
+ }
+ }
+
+ /** The diagnostic kind or validation type for a kind of validation. */
+ enum Validation implements EnumOption<ValidationType> {
+ DISABLE_INTER_COMPONENT_SCOPE_VALIDATION(),
+
+ NULLABLE_VALIDATION(ERROR, WARNING),
+
+ PRIVATE_MEMBER_VALIDATION(ERROR, WARNING),
+
+ STATIC_MEMBER_VALIDATION(ERROR, WARNING),
+
+ /** Whether to validate full binding graphs for components, subcomponents, and modules. */
+ FULL_BINDING_GRAPH_VALIDATION(NONE, ERROR, WARNING) {
+ @Override
+ public ImmutableList<String> aliases() {
+ return ImmutableList.of("dagger.moduleBindingValidation");
+ }
+ },
+
+ /**
+ * How to report conflicting scoped bindings when validating partial binding graphs associated
+ * with modules.
+ */
+ MODULE_HAS_DIFFERENT_SCOPES_VALIDATION(ERROR, WARNING),
+
+ /**
+ * How to report that an explicit binding in a subcomponent conflicts with an {@code @Inject}
+ * constructor used in an ancestor component.
+ */
+ EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(WARNING, ERROR, NONE),
+ ;
+
+ final ValidationType defaultType;
+ final ImmutableSet<ValidationType> validTypes;
+
+ Validation() {
+ this(ERROR, WARNING, NONE);
+ }
+
+ Validation(ValidationType defaultType, ValidationType... moreValidTypes) {
+ this.defaultType = defaultType;
+ this.validTypes = immutableEnumSet(defaultType, moreValidTypes);
+ }
+
+ @Override
+ public ValidationType defaultValue() {
+ return defaultType;
+ }
+
+ @Override
+ public Set<ValidationType> validValues() {
+ return validTypes;
+ }
+
+ @Override
+ public String toString() {
+ return optionName(this);
+ }
+ }
+
+ private static String optionName(Enum<? extends EnumOption<?>> option) {
+ return "dagger." + UPPER_UNDERSCORE.to(LOWER_CAMEL, option.name());
+ }
+
+ /** The supported command-line options. */
+ static ImmutableSet<String> supportedOptions() {
+ // need explicit type parameter to avoid a runtime stream error
+ return Stream.<CommandLineOption[]>of(
+ KeyOnlyOption.values(), Feature.values(), Validation.values())
+ .flatMap(Arrays::stream)
+ .flatMap(CommandLineOption::allNames)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the value for the option as set on the command line by any name, or the default value
+ * if not set.
+ *
+ * <p>If more than one name is used to set the value, but all names specify the same value,
+ * reports a warning and returns that value.
+ *
+ * <p>If more than one name is used to set the value, and not all names specify the same value,
+ * reports an error and returns the default value.
+ */
+ private <T extends Enum<T>> T parseOption(EnumOption<T> option) {
+ @SuppressWarnings("unchecked") // we only put covariant values into the map
+ T value = (T) enumOptions.computeIfAbsent(option, this::parseOptionUncached);
+ return value;
+ }
+
+ private <T extends Enum<T>> T parseOptionUncached(EnumOption<T> option) {
+ ImmutableMap<String, T> values = parseOptionWithAllNames(option);
+
+ // If no value is specified, return the default value.
+ if (values.isEmpty()) {
+ return option.defaultValue();
+ }
+
+ // If all names have the same value, return that.
+ if (values.asMultimap().inverse().keySet().size() == 1) {
+ // Warn if an option was set with more than one name. That would be an error if the values
+ // differed.
+ if (values.size() > 1) {
+ reportUseOfDifferentNamesForOption(Diagnostic.Kind.WARNING, option, values.keySet());
+ }
+ return values.values().asList().get(0);
+ }
+
+ // If different names have different values, report an error and return the default
+ // value.
+ reportUseOfDifferentNamesForOption(Diagnostic.Kind.ERROR, option, values.keySet());
+ return option.defaultValue();
+ }
+
+ private void reportUseOfDifferentNamesForOption(
+ Diagnostic.Kind diagnosticKind, EnumOption<?> option, ImmutableSet<String> usedNames) {
+ processingEnvironment
+ .getMessager()
+ .printMessage(
+ diagnosticKind,
+ String.format(
+ "Only one of the equivalent options (%s) should be used; prefer -A%s",
+ usedNames.stream().map(name -> "-A" + name).collect(joining(", ")), option));
+ }
+
+ private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNames(
+ EnumOption<T> option) {
+ @SuppressWarnings("unchecked") // map is covariant
+ ImmutableMap<String, T> aliasValues =
+ (ImmutableMap<String, T>)
+ allCommandLineOptions.computeIfAbsent(option, this::parseOptionWithAllNamesUncached);
+ return aliasValues;
+ }
+
+ private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNamesUncached(
+ EnumOption<T> option) {
+ ImmutableMap.Builder<String, T> values = ImmutableMap.builder();
+ getUsedNames(option)
+ .forEach(
+ name -> parseOptionWithName(option, name).ifPresent(value -> values.put(name, value)));
+ return values.build();
+ }
+
+ private <T extends Enum<T>> Optional<T> parseOptionWithName(EnumOption<T> option, String key) {
+ checkArgument(processingEnvironment.getOptions().containsKey(key), "key %s not found", key);
+ String stringValue = processingEnvironment.getOptions().get(key);
+ if (stringValue == null) {
+ processingEnvironment
+ .getMessager()
+ .printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
+ } else {
+ try {
+ T value =
+ Enum.valueOf(option.defaultValue().getDeclaringClass(), Ascii.toUpperCase(stringValue));
+ if (option.validValues().contains(value)) {
+ return Optional.of(value);
+ }
+ } catch (IllegalArgumentException e) {
+ // handled below
+ }
+ processingEnvironment
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Processor option -A%s may only have the values %s "
+ + "(case insensitive), found: %s",
+ key, option.validValues(), stringValue));
+ }
+ return Optional.empty();
+ }
+
+ private Stream<String> getUsedNames(CommandLineOption option) {
+ return option.allNames().filter(name -> processingEnvironment.getOptions().containsKey(name));
+ }
+}
diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
new file mode 100644
index 0000000..1730574
--- /dev/null
+++ b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.googlejavaformat.java.filer.FormattingFiler;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Reusable;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.util.Types;
+
+/** Bindings that depend on the {@link ProcessingEnvironment}. */
+@Module
+final class ProcessingEnvironmentModule {
+
+ private final ProcessingEnvironment processingEnvironment;
+
+ ProcessingEnvironmentModule(ProcessingEnvironment processingEnvironment) {
+ this.processingEnvironment = checkNotNull(processingEnvironment);
+ }
+
+ @Provides
+ @ProcessingOptions
+ Map<String, String> processingOptions() {
+ return processingEnvironment.getOptions();
+ }
+
+ @Provides
+ Messager messager() {
+ return processingEnvironment.getMessager();
+ }
+
+ @Provides
+ Filer filer(CompilerOptions compilerOptions) {
+ if (compilerOptions.headerCompilation() || !compilerOptions.formatGeneratedSource()) {
+ return processingEnvironment.getFiler();
+ } else {
+ return new FormattingFiler(processingEnvironment.getFiler());
+ }
+ }
+
+ @Provides
+ Types types() {
+ return processingEnvironment.getTypeUtils();
+ }
+
+ @Provides
+ SourceVersion sourceVersion() {
+ return processingEnvironment.getSourceVersion();
+ }
+
+ @Provides
+ DaggerElements daggerElements() {
+ return new DaggerElements(processingEnvironment);
+ }
+
+ @Provides
+ @Reusable // to avoid parsing options more than once
+ CompilerOptions compilerOptions() {
+ return ProcessingEnvironmentCompilerOptions.create(processingEnvironment);
+ }
+
+ @Provides
+ Optional<DaggerStatisticsRecorder> daggerStatisticsRecorder() {
+ return Optional.empty();
+ }
+}
diff --git a/java/dagger/internal/codegen/ProcessingOptions.java b/java/dagger/internal/codegen/ProcessingOptions.java
new file mode 100644
index 0000000..105452a
--- /dev/null
+++ b/java/dagger/internal/codegen/ProcessingOptions.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A qualifier for the {@link javax.annotation.processing.ProcessingEnvironment#getOptions()
+ * processing options} passed to the current invocation of {@code javac}.
+ */
+@Retention(RUNTIME)
+@Qualifier
+@interface ProcessingOptions {}
diff --git a/java/dagger/internal/codegen/ProcessingRoundCacheModule.java b/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
new file mode 100644
index 0000000..b56cc30
--- /dev/null
+++ b/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoSet;
+
+/**
+ * Binding contributions to a set of {@link ClearableCache}s that will be cleared at the end of each
+ * processing round.
+ */
+@Module
+interface ProcessingRoundCacheModule {
+ @Binds
+ @IntoSet
+ ClearableCache moduleDescriptorFactory(ModuleDescriptor.Factory cache);
+
+ @Binds
+ @IntoSet
+ ClearableCache bindingGraphFactory(BindingGraphFactory cache);
+
+ @Binds
+ @IntoSet
+ ClearableCache componentImplementationFactory(ComponentImplementationFactory cache);
+}
diff --git a/java/dagger/internal/codegen/ProducerCreationExpression.java b/java/dagger/internal/codegen/ProducerCreationExpression.java
new file mode 100644
index 0000000..1dd7a1c
--- /dev/null
+++ b/java/dagger/internal/codegen/ProducerCreationExpression.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/**
+ * A {@link dagger.producers.Producer} creation expression for a {@link
+ * dagger.producers.Produces @Produces}-annotated module method.
+ */
+// TODO(dpb): Resolve with InjectionOrProvisionProviderCreationExpression.
+final class ProducerCreationExpression implements FrameworkInstanceCreationExpression {
+
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ContributionBinding binding;
+
+ ProducerCreationExpression(
+ ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return CodeBlock.of(
+ "$T.create($L)",
+ generatedClassNameForBinding(binding),
+ componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+ }
+}
diff --git a/java/dagger/internal/codegen/ProducerEntryPointView.java b/java/dagger/internal/codegen/ProducerEntryPointView.java
new file mode 100644
index 0000000..87b5a4a
--- /dev/null
+++ b/java/dagger/internal/codegen/ProducerEntryPointView.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import dagger.producers.internal.CancellationListener;
+import dagger.producers.internal.Producers;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A factory of {@linkplain Producers#entryPointViewOf(Producer, CancellationListener) entry point
+ * views} of {@link Producer}s.
+ */
+final class ProducerEntryPointView {
+ private final DaggerTypes types;
+
+ ProducerEntryPointView(DaggerTypes types) {
+ this.types = types;
+ }
+
+ /**
+ * Returns an expression for an {@linkplain Producers#entryPointViewOf(Producer,
+ * CancellationListener) entry point view} of a producer if the component method returns a {@link
+ * Producer} or {@link com.google.common.util.concurrent.ListenableFuture}.
+ *
+ * <p>This is intended to be a replacement implementation for {@link
+ * BindingExpression#getDependencyExpressionForComponentMethod(ComponentMethodDescriptor,
+ * ComponentImplementation)}, and in cases where {@link Optional#empty()} is returned, callers
+ * should call {@code super.getDependencyExpressionForComponentMethod()}.
+ */
+ Optional<Expression> getProducerEntryPointField(
+ BindingExpression producerExpression,
+ ComponentMethodDescriptor componentMethod,
+ ComponentImplementation component) {
+ if (component.componentDescriptor().isProduction()
+ && (componentMethod.dependencyRequest().get().kind().equals(RequestKind.FUTURE)
+ || componentMethod.dependencyRequest().get().kind().equals(RequestKind.PRODUCER))) {
+ return Optional.of(
+ Expression.create(
+ fieldType(componentMethod),
+ "$N",
+ createField(producerExpression, componentMethod, component)));
+ } else {
+ // If the component isn't a production component, it won't implement CancellationListener and
+ // as such we can't create an entry point. But this binding must also just be a Producer from
+ // Provider anyway in that case, so there shouldn't be an issue.
+ // TODO(b/116855531): Is it really intended that a non-production component can have Producer
+ // entry points?
+ return Optional.empty();
+ }
+ }
+
+ private FieldSpec createField(
+ BindingExpression producerExpression,
+ ComponentMethodDescriptor componentMethod,
+ ComponentImplementation component) {
+ // TODO(cgdecker): Use a FrameworkFieldInitializer for this?
+ // Though I don't think we need the once-only behavior of that, since I think
+ // getComponentMethodImplementation will only be called once anyway
+ String methodName = componentMethod.methodElement().getSimpleName().toString();
+ FieldSpec field =
+ FieldSpec.builder(
+ TypeName.get(fieldType(componentMethod)),
+ component.getUniqueFieldName(methodName + "EntryPoint"),
+ PRIVATE)
+ .build();
+ component.addField(FRAMEWORK_FIELD, field);
+
+ CodeBlock fieldInitialization =
+ CodeBlock.of(
+ "this.$N = $T.entryPointViewOf($L, this);",
+ field,
+ Producers.class,
+ producerExpression.getDependencyExpression(component.name()).codeBlock());
+ component.addInitialization(fieldInitialization);
+
+ return field;
+ }
+
+ // TODO(cgdecker): Can we use producerExpression.getDependencyExpression().type() instead of
+ // needing to (re)compute this?
+ private TypeMirror fieldType(ComponentMethodDescriptor componentMethod) {
+ return types.wrapType(componentMethod.dependencyRequest().get().key().type(), Producer.class);
+ }
+}
diff --git a/java/dagger/internal/codegen/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/ProducerFactoryGenerator.java
new file mode 100644
index 0000000..3f2bef4
--- /dev/null
+++ b/java/dagger/internal/codegen/ProducerFactoryGenerator.java
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verifyNotNull;
+import static com.squareup.javapoet.ClassName.OBJECT;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
+import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.FUTURES;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER_TOKEN;
+import static dagger.internal.codegen.javapoet.TypeNames.VOID_CLASS;
+import static dagger.internal.codegen.javapoet.TypeNames.listOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PROTECTED;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import dagger.producers.internal.AbstractProducesMethodProducer;
+import dagger.producers.internal.Producers;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Generates {@link Producer} implementations from {@link ProductionBinding} instances.
+ */
+final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBinding> {
+ private final CompilerOptions compilerOptions;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ ProducerFactoryGenerator(
+ Filer filer,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ CompilerOptions compilerOptions,
+ KeyFactory keyFactory) {
+ super(filer, elements, sourceVersion);
+ this.compilerOptions = compilerOptions;
+ this.keyFactory = keyFactory;
+ }
+
+ @Override
+ ClassName nameGeneratedType(ProductionBinding binding) {
+ return generatedClassNameForBinding(binding);
+ }
+
+ @Override
+ Element originatingElement(ProductionBinding binding) {
+ // we only create factories for bindings that have a binding element
+ return binding.bindingElement().get();
+ }
+
+ @Override
+ Optional<TypeSpec.Builder> write(ClassName generatedTypeName, ProductionBinding binding) {
+ // We don't want to write out resolved bindings -- we want to write out the generic version.
+ checkArgument(!binding.unresolved().isPresent());
+ checkArgument(binding.bindingElement().isPresent());
+
+ TypeName providedTypeName = TypeName.get(binding.contributedType());
+ TypeName futureTypeName = listenableFutureOf(providedTypeName);
+
+ TypeSpec.Builder factoryBuilder =
+ classBuilder(generatedTypeName)
+ .addAnnotation(
+ // TODO(beder): examine if we can remove this or prevent subtypes of Future from
+ // being produced
+ AnnotationSpec.builder(SuppressWarnings.class)
+ .addMember("value", "$S", "FutureReturnValueIgnored")
+ .build())
+ .addModifiers(PUBLIC, FINAL)
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
+
+ UniqueNameSet uniqueFieldNames = new UniqueNameSet();
+ ImmutableMap.Builder<Key, FieldSpec> fieldsBuilder = ImmutableMap.builder();
+
+ MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PRIVATE);
+
+ Optional<FieldSpec> moduleField =
+ binding.requiresModuleInstance()
+ ? Optional.of(
+ addFieldAndConstructorParameter(
+ factoryBuilder,
+ constructorBuilder,
+ uniqueFieldNames.getUniqueName("module"),
+ TypeName.get(binding.bindingTypeElement().get().asType())))
+ : Optional.empty();
+
+ String[] executorParameterName = new String[1];
+ String[] monitorParameterName = new String[1];
+ Map<Key, FrameworkField> bindingFieldsForDependencies =
+ generateBindingFieldsForDependencies(binding);
+ bindingFieldsForDependencies.forEach(
+ (key, bindingField) -> {
+ String fieldName = uniqueFieldNames.getUniqueName(bindingField.name());
+ if (key.equals(keyFactory.forProductionImplementationExecutor())) {
+ executorParameterName[0] = fieldName;
+ constructorBuilder.addParameter(bindingField.type(), executorParameterName[0]);
+ } else if (key.equals(keyFactory.forProductionComponentMonitor())) {
+ monitorParameterName[0] = fieldName;
+ constructorBuilder.addParameter(bindingField.type(), monitorParameterName[0]);
+ } else {
+ FieldSpec field =
+ addFieldAndConstructorParameter(
+ factoryBuilder, constructorBuilder, fieldName, bindingField.type());
+ fieldsBuilder.put(key, field);
+ }
+ });
+ ImmutableMap<Key, FieldSpec> fields = fieldsBuilder.build();
+
+ constructorBuilder.addStatement(
+ "super($N, $L, $N)",
+ verifyNotNull(monitorParameterName[0]),
+ producerTokenConstruction(generatedTypeName, binding),
+ verifyNotNull(executorParameterName[0]));
+
+ if (binding.requiresModuleInstance()) {
+ assignField(constructorBuilder, moduleField.get(), null);
+ }
+
+ fields.forEach(
+ (key, field) -> {
+ ParameterizedTypeName type = bindingFieldsForDependencies.get(key).type();
+ assignField(constructorBuilder, field, type);
+ });
+
+ MethodSpec.Builder collectDependenciesBuilder =
+ methodBuilder("collectDependencies")
+ .addAnnotation(Override.class)
+ .addModifiers(PROTECTED);
+
+ ImmutableList<DependencyRequest> asyncDependencies = asyncDependencies(binding);
+ for (DependencyRequest dependency : asyncDependencies) {
+ TypeName futureType = listenableFutureOf(asyncDependencyType(dependency));
+ CodeBlock futureAccess = CodeBlock.of("$N.get()", fields.get(dependency.key()));
+ collectDependenciesBuilder.addStatement(
+ "$T $L = $L",
+ futureType,
+ dependencyFutureName(dependency),
+ dependency.kind().equals(RequestKind.PRODUCED)
+ ? CodeBlock.of("$T.createFutureProduced($L)", PRODUCERS, futureAccess)
+ : futureAccess);
+ }
+ FutureTransform futureTransform = FutureTransform.create(fields, binding, asyncDependencies);
+
+ collectDependenciesBuilder
+ .returns(listenableFutureOf(futureTransform.applyArgType()))
+ .addStatement("return $L", futureTransform.futureCodeBlock());
+
+ MethodSpec.Builder callProducesMethod =
+ methodBuilder("callProducesMethod")
+ .returns(futureTypeName)
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .addParameter(futureTransform.applyArgType(), futureTransform.applyArgName())
+ .addExceptions(getThrownTypeNames(binding.thrownTypes()))
+ .addCode(
+ getInvocationCodeBlock(
+ binding, providedTypeName, futureTransform.parameterCodeBlocks()));
+ if (futureTransform.hasUncheckedCast()) {
+ callProducesMethod.addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED));
+ }
+
+ MethodSpec constructor = constructorBuilder.build();
+ factoryBuilder
+ .superclass(
+ ParameterizedTypeName.get(
+ ClassName.get(AbstractProducesMethodProducer.class),
+ futureTransform.applyArgType(),
+ providedTypeName))
+ .addMethod(constructor)
+ .addMethod(staticFactoryMethod(binding, constructor))
+ .addMethod(collectDependenciesBuilder.build())
+ .addMethod(callProducesMethod.build());
+
+ gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
+
+ // TODO(gak): write a sensible toString
+ return Optional.of(factoryBuilder);
+ }
+
+ private MethodSpec staticFactoryMethod(ProductionBinding binding, MethodSpec constructor) {
+ return MethodSpec.methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(parameterizedGeneratedTypeNameForBinding(binding))
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding))
+ .addParameters(constructor.parameters)
+ .addStatement(
+ "return new $T($L)",
+ parameterizedGeneratedTypeNameForBinding(binding),
+ constructor.parameters.stream()
+ .map(p -> CodeBlock.of("$N", p.name))
+ .collect(toParametersCodeBlock()))
+ .build();
+ }
+
+ // TODO(ronshapiro): consolidate versions of these
+ private static FieldSpec addFieldAndConstructorParameter(
+ TypeSpec.Builder typeBuilder,
+ MethodSpec.Builder constructorBuilder,
+ String variableName,
+ TypeName variableType) {
+ FieldSpec field = FieldSpec.builder(variableType, variableName, PRIVATE, FINAL).build();
+ typeBuilder.addField(field);
+ constructorBuilder.addParameter(field.type, field.name);
+ return field;
+ }
+
+ private static void assignField(
+ MethodSpec.Builder constructorBuilder, FieldSpec field, ParameterizedTypeName type) {
+ if (type != null && type.rawType.equals(TypeNames.PRODUCER)) {
+ constructorBuilder.addStatement(
+ "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, Producers.class);
+ } else {
+ constructorBuilder.addStatement("this.$1N = $1N", field);
+ }
+ }
+
+ /** Returns a list of dependencies that are generated asynchronously. */
+ private static ImmutableList<DependencyRequest> asyncDependencies(Binding binding) {
+ final ImmutableMap<DependencyRequest, FrameworkDependency> frameworkDependencies =
+ binding.dependenciesToFrameworkDependenciesMap();
+ return FluentIterable.from(binding.dependencies())
+ .filter(
+ dependency ->
+ isAsyncDependency(dependency)
+ && frameworkDependencies
+ .get(dependency)
+ .frameworkClass()
+ .equals(Producer.class))
+ .toList();
+ }
+
+ private CodeBlock producerTokenConstruction(
+ ClassName generatedTypeName, ProductionBinding binding) {
+ CodeBlock producerTokenArgs =
+ compilerOptions.writeProducerNameInToken()
+ ? CodeBlock.of(
+ "$S",
+ String.format(
+ "%s#%s",
+ ClassName.get(binding.bindingTypeElement().get()),
+ binding.bindingElement().get().getSimpleName()))
+ : CodeBlock.of("$T.class", generatedTypeName);
+ return CodeBlock.of("$T.create($L)", PRODUCER_TOKEN, producerTokenArgs);
+ }
+
+ /** Returns a name of the variable representing this dependency's future. */
+ private static String dependencyFutureName(DependencyRequest dependency) {
+ return dependency.requestElement().get().getSimpleName() + "Future";
+ }
+
+ /** Represents the transformation of an input future by a producer method. */
+ abstract static class FutureTransform {
+ protected final ImmutableMap<Key, FieldSpec> fields;
+ protected final ProductionBinding binding;
+
+ FutureTransform(ImmutableMap<Key, FieldSpec> fields, ProductionBinding binding) {
+ this.fields = fields;
+ this.binding = binding;
+ }
+
+ /** The code block representing the future that should be transformed. */
+ abstract CodeBlock futureCodeBlock();
+
+ /** The type of the argument to the apply method. */
+ abstract TypeName applyArgType();
+
+ /** The name of the argument to the apply method */
+ abstract String applyArgName();
+
+ /** The code blocks to be passed to the produces method itself. */
+ abstract ImmutableList<CodeBlock> parameterCodeBlocks();
+
+ /** Whether the transform method has an unchecked cast. */
+ boolean hasUncheckedCast() {
+ return false;
+ }
+
+ CodeBlock frameworkTypeUsageStatement(DependencyRequest dependency) {
+ return SourceFiles.frameworkTypeUsageStatement(
+ CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind());
+ }
+
+ static FutureTransform create(
+ ImmutableMap<Key, FieldSpec> fields,
+ ProductionBinding binding,
+ ImmutableList<DependencyRequest> asyncDependencies) {
+ if (asyncDependencies.isEmpty()) {
+ return new NoArgFutureTransform(fields, binding);
+ } else if (asyncDependencies.size() == 1) {
+ return new SingleArgFutureTransform(
+ fields, binding, Iterables.getOnlyElement(asyncDependencies));
+ } else {
+ return new MultiArgFutureTransform(fields, binding, asyncDependencies);
+ }
+ }
+ }
+
+ static final class NoArgFutureTransform extends FutureTransform {
+ NoArgFutureTransform(ImmutableMap<Key, FieldSpec> fields, ProductionBinding binding) {
+ super(fields, binding);
+ }
+
+ @Override
+ CodeBlock futureCodeBlock() {
+ return CodeBlock.of("$T.<$T>immediateFuture(null)", FUTURES, VOID_CLASS);
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return VOID_CLASS;
+ }
+
+ @Override
+ String applyArgName() {
+ return "ignoredVoidArg";
+ }
+
+ @Override
+ ImmutableList<CodeBlock> parameterCodeBlocks() {
+ return binding.explicitDependencies().stream()
+ .map(this::frameworkTypeUsageStatement)
+ .collect(toImmutableList());
+ }
+ }
+
+ static final class SingleArgFutureTransform extends FutureTransform {
+ private final DependencyRequest asyncDependency;
+
+ SingleArgFutureTransform(
+ ImmutableMap<Key, FieldSpec> fields,
+ ProductionBinding binding,
+ DependencyRequest asyncDependency) {
+ super(fields, binding);
+ this.asyncDependency = asyncDependency;
+ }
+
+ @Override
+ CodeBlock futureCodeBlock() {
+ return CodeBlock.of("$L", dependencyFutureName(asyncDependency));
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return asyncDependencyType(asyncDependency);
+ }
+
+ @Override
+ String applyArgName() {
+ String argName = asyncDependency.requestElement().get().getSimpleName().toString();
+ if (argName.equals("module")) {
+ return "moduleArg";
+ }
+ return argName;
+ }
+
+ @Override
+ ImmutableList<CodeBlock> parameterCodeBlocks() {
+ ImmutableList.Builder<CodeBlock> parameterCodeBlocks = ImmutableList.builder();
+ for (DependencyRequest dependency : binding.explicitDependencies()) {
+ // We really want to compare instances here, because asyncDependency is an element in the
+ // set binding.dependencies().
+ if (dependency == asyncDependency) {
+ parameterCodeBlocks.add(CodeBlock.of("$L", applyArgName()));
+ } else {
+ parameterCodeBlocks.add(frameworkTypeUsageStatement(dependency));
+ }
+ }
+ return parameterCodeBlocks.build();
+ }
+ }
+
+ static final class MultiArgFutureTransform extends FutureTransform {
+ private final ImmutableList<DependencyRequest> asyncDependencies;
+
+ MultiArgFutureTransform(
+ ImmutableMap<Key, FieldSpec> fields,
+ ProductionBinding binding,
+ ImmutableList<DependencyRequest> asyncDependencies) {
+ super(fields, binding);
+ this.asyncDependencies = asyncDependencies;
+ }
+
+ @Override
+ CodeBlock futureCodeBlock() {
+ return CodeBlock.of(
+ "$T.<$T>allAsList($L)",
+ FUTURES,
+ OBJECT,
+ asyncDependencies
+ .stream()
+ .map(ProducerFactoryGenerator::dependencyFutureName)
+ .collect(joining(", ")));
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return listOf(OBJECT);
+ }
+
+ @Override
+ String applyArgName() {
+ return "args";
+ }
+
+ @Override
+ ImmutableList<CodeBlock> parameterCodeBlocks() {
+ int argIndex = 0;
+ ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
+ for (DependencyRequest dependency : binding.explicitDependencies()) {
+ if (isAsyncDependency(dependency)) {
+ codeBlocks.add(
+ CodeBlock.of(
+ "($T) $L.get($L)", asyncDependencyType(dependency), applyArgName(), argIndex));
+ argIndex++;
+ } else {
+ codeBlocks.add(frameworkTypeUsageStatement(dependency));
+ }
+ }
+ return codeBlocks.build();
+ }
+
+ @Override
+ boolean hasUncheckedCast() {
+ return true;
+ }
+ }
+
+ private static boolean isAsyncDependency(DependencyRequest dependency) {
+ switch (dependency.kind()) {
+ case INSTANCE:
+ case PRODUCED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static TypeName asyncDependencyType(DependencyRequest dependency) {
+ TypeName keyName = TypeName.get(dependency.key().type());
+ switch (dependency.kind()) {
+ case INSTANCE:
+ return keyName;
+ case PRODUCED:
+ return producedOf(keyName);
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Creates a code block for the invocation of the producer method from the module, which should be
+ * used entirely within a method body.
+ *
+ * @param binding The binding to generate the invocation code block for.
+ * @param providedTypeName The type name that should be provided by this producer.
+ * @param parameterCodeBlocks The code blocks for all the parameters to the producer method.
+ */
+ private CodeBlock getInvocationCodeBlock(
+ ProductionBinding binding,
+ TypeName providedTypeName,
+ ImmutableList<CodeBlock> parameterCodeBlocks) {
+ CodeBlock moduleCodeBlock =
+ CodeBlock.of(
+ "$L.$L($L)",
+ binding.requiresModuleInstance()
+ ? "module"
+ : CodeBlock.of("$T", ClassName.get(binding.bindingTypeElement().get())),
+ binding.bindingElement().get().getSimpleName(),
+ makeParametersCodeBlock(parameterCodeBlocks));
+
+ final CodeBlock returnCodeBlock;
+ switch (binding.productionKind().get()) {
+ case IMMEDIATE:
+ returnCodeBlock =
+ CodeBlock.of("$T.<$T>immediateFuture($L)", FUTURES, providedTypeName, moduleCodeBlock);
+ break;
+ case FUTURE:
+ returnCodeBlock = moduleCodeBlock;
+ break;
+ case SET_OF_FUTURE:
+ returnCodeBlock = CodeBlock.of("$T.allAsSet($L)", PRODUCERS, moduleCodeBlock);
+ break;
+ default:
+ throw new AssertionError();
+ }
+ return CodeBlock.of("return $L;", returnCodeBlock);
+ }
+
+ /**
+ * Converts the list of thrown types into type names.
+ *
+ * @param thrownTypes the list of thrown types.
+ */
+ private FluentIterable<? extends TypeName> getThrownTypeNames(
+ Iterable<? extends TypeMirror> thrownTypes) {
+ return FluentIterable.from(thrownTypes).transform(TypeName::get);
+ }
+}
diff --git a/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java b/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java
new file mode 100644
index 0000000..aca9756
--- /dev/null
+++ b/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import java.util.Optional;
+
+/** An {@link Producer} creation expression for provision bindings. */
+final class ProducerFromProviderCreationExpression implements FrameworkInstanceCreationExpression {
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ ProducerFromProviderCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return FrameworkType.PROVIDER.to(
+ RequestKind.PRODUCER,
+ componentBindingExpressions
+ .getDependencyExpression(
+ bindingRequest(binding.key(), FrameworkType.PROVIDER),
+ componentImplementation.name())
+ .codeBlock());
+ }
+
+ @Override
+ public Optional<ClassName> alternativeFrameworkClass() {
+ return Optional.of(TypeNames.PRODUCER);
+ }
+
+ // TODO(ronshapiro): should this have a simple factory if the delegate expression is simple?
+}
diff --git a/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java b/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java
new file mode 100644
index 0000000..18818d5
--- /dev/null
+++ b/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+
+/** Binding expression for producer node instances. */
+final class ProducerNodeInstanceBindingExpression extends FrameworkInstanceBindingExpression {
+ /** The component defining this binding. */
+ private final ComponentImplementation componentImplementation;
+ private final Key key;
+ private final ProducerEntryPointView producerEntryPointView;
+
+ ProducerNodeInstanceBindingExpression(
+ ResolvedBindings resolvedBindings,
+ FrameworkInstanceSupplier frameworkInstanceSupplier,
+ DaggerTypes types,
+ DaggerElements elements,
+ ComponentImplementation componentImplementation) {
+ super(resolvedBindings, frameworkInstanceSupplier, types, elements);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.key = resolvedBindings.key();
+ this.producerEntryPointView = new ProducerEntryPointView(types);
+ }
+
+ @Override
+ protected FrameworkType frameworkType() {
+ return FrameworkType.PRODUCER_NODE;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ Expression result = super.getDependencyExpression(requestingClass);
+ componentImplementation.addCancellableProducerKey(key);
+ return result;
+ }
+
+ @Override
+ Expression getDependencyExpressionForComponentMethod(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ return producerEntryPointView
+ .getProducerEntryPointField(this, componentMethod, component)
+ .orElseGet(
+ () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
+ }
+}
diff --git a/java/dagger/internal/codegen/ProducesMethodValidator.java b/java/dagger/internal/codegen/ProducesMethodValidator.java
new file mode 100644
index 0000000..bf45948
--- /dev/null
+++ b/java/dagger/internal/codegen/ProducesMethodValidator.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
+import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
+import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.EXCEPTION;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link Produces} methods. */
+final class ProducesMethodValidator extends BindingMethodValidator {
+
+ @Inject
+ ProducesMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestValidator dependencyRequestValidator) {
+ super(
+ elements,
+ types,
+ dependencyRequestValidator,
+ Produces.class,
+ ProducerModule.class,
+ MUST_BE_CONCRETE,
+ EXCEPTION,
+ ALLOWS_MULTIBINDINGS,
+ NO_SCOPING);
+ }
+
+ @Override
+ protected String elementsIntoSetNotASetMessage() {
+ return "@Produces methods of type set values must return a Set or ListenableFuture of Set";
+ }
+
+ @Override
+ protected String badTypeMessage() {
+ return "@Produces methods can return only a primitive, an array, a type variable, "
+ + "a declared type, or a ListenableFuture of one of those types";
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalMethodProperties() {
+ checkNullable();
+ }
+
+ /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */
+ // TODO(beder): Properly handle nullable with producer methods.
+ private void checkNullable() {
+ if (ConfigurationAnnotations.getNullableType(element).isPresent()) {
+ report.addWarning("@Nullable on @Produces methods does not do anything");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Allows {@code keyType} to be a {@link ListenableFuture} of an otherwise-valid key type.
+ */
+ @Override
+ protected void checkKeyType(TypeMirror keyType) {
+ Optional<TypeMirror> typeToCheck = unwrapListenableFuture(keyType);
+ if (typeToCheck.isPresent()) {
+ super.checkKeyType(typeToCheck.get());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return
+ * a {@link ListenableFuture} of a {@link Set} as well.
+ */
+ @Override
+ protected void checkSetValuesType() {
+ Optional<TypeMirror> typeToCheck = unwrapListenableFuture(element.getReturnType());
+ if (typeToCheck.isPresent()) {
+ checkSetValuesType(typeToCheck.get());
+ }
+ }
+
+ private Optional<TypeMirror> unwrapListenableFuture(TypeMirror type) {
+ if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
+ DeclaredType declaredType = MoreTypes.asDeclared(type);
+ if (declaredType.getTypeArguments().isEmpty()) {
+ report.addError("@Produces methods cannot return a raw ListenableFuture");
+ return Optional.empty();
+ } else {
+ return Optional.of((TypeMirror) getOnlyElement(declaredType.getTypeArguments()));
+ }
+ }
+ return Optional.of(type);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ProductionBinding.java b/java/dagger/internal/codegen/ProductionBinding.java
new file mode 100644
index 0000000..a22f21c
--- /dev/null
+++ b/java/dagger/internal/codegen/ProductionBinding.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A value object representing the mechanism by which a {@link Key} can be produced.
+ */
+@AutoValue
+abstract class ProductionBinding extends ContributionBinding {
+
+ @Override
+ public BindingType bindingType() {
+ return BindingType.PRODUCTION;
+ }
+
+ @Override
+ abstract Optional<ProductionBinding> unresolved();
+
+ @Override
+ ImmutableSet<DependencyRequest> implicitDependencies() {
+ return Stream.of(executorRequest(), monitorRequest())
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toImmutableSet());
+ }
+
+ /** What kind of object a {@code @Produces}-annotated method returns. */
+ enum ProductionKind {
+ /** A value. */
+ IMMEDIATE,
+ /** A {@code ListenableFuture<T>}. */
+ FUTURE,
+ /** A {@code Set<ListenableFuture<T>>}. */
+ SET_OF_FUTURE;
+
+ /** Returns the kind of object a {@code @Produces}-annotated method returns. */
+ static ProductionKind fromProducesMethod(ExecutableElement producesMethod) {
+ if (isFutureType(producesMethod.getReturnType())) {
+ return FUTURE;
+ } else if (ContributionType.fromBindingElement(producesMethod)
+ .equals(ContributionType.SET_VALUES)
+ && isFutureType(SetType.from(producesMethod.getReturnType()).elementType())) {
+ return SET_OF_FUTURE;
+ } else {
+ return IMMEDIATE;
+ }
+ }
+ }
+
+ /**
+ * Returns the kind of object the produces method returns. All production bindings from
+ * {@code @Produces} methods will have a production kind, but synthetic production bindings may
+ * not.
+ */
+ abstract Optional<ProductionKind> productionKind();
+
+ /** Returns the list of types in the throws clause of the method. */
+ abstract ImmutableList<? extends TypeMirror> thrownTypes();
+
+ /**
+ * If this production requires an executor, this will be the corresponding request. All
+ * production bindings from {@code @Produces} methods will have an executor request, but
+ * synthetic production bindings may not.
+ */
+ abstract Optional<DependencyRequest> executorRequest();
+
+ /** If this production requires a monitor, this will be the corresponding request. All
+ * production bindings from {@code @Produces} methods will have a monitor request, but synthetic
+ * production bindings may not.
+ */
+ abstract Optional<DependencyRequest> monitorRequest();
+
+ // Profiling determined that this method is called enough times that memoizing it had a measurable
+ // performance improvement for large components.
+ @Memoized
+ @Override
+ boolean requiresModuleInstance() {
+ return super.requiresModuleInstance();
+ }
+
+ static Builder builder() {
+ return new AutoValue_ProductionBinding.Builder()
+ .explicitDependencies(ImmutableList.<DependencyRequest>of())
+ .thrownTypes(ImmutableList.<TypeMirror>of());
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // TODO(ronshapiro,dpb): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @AutoValue.Builder
+ @CanIgnoreReturnValue
+ abstract static class Builder extends ContributionBinding.Builder<ProductionBinding, Builder> {
+
+ @Override
+ Builder dependencies(Iterable<DependencyRequest> dependencies) {
+ return explicitDependencies(dependencies);
+ }
+
+ abstract Builder explicitDependencies(Iterable<DependencyRequest> dependencies);
+
+ abstract Builder productionKind(ProductionKind productionKind);
+
+ @Override
+ abstract Builder unresolved(ProductionBinding unresolved);
+
+ abstract Builder thrownTypes(Iterable<? extends TypeMirror> thrownTypes);
+
+ abstract Builder executorRequest(DependencyRequest executorRequest);
+
+ abstract Builder monitorRequest(DependencyRequest monitorRequest);
+ }
+}
diff --git a/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java b/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java
new file mode 100644
index 0000000..60166de
--- /dev/null
+++ b/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+
+/** Binding expression for provider instances. */
+final class ProviderInstanceBindingExpression extends FrameworkInstanceBindingExpression {
+
+ ProviderInstanceBindingExpression(
+ ResolvedBindings resolvedBindings,
+ FrameworkInstanceSupplier frameworkInstanceSupplier,
+ DaggerTypes types,
+ DaggerElements elements) {
+ super(
+ resolvedBindings,
+ frameworkInstanceSupplier,
+ types,
+ elements);
+ }
+
+ @Override
+ protected FrameworkType frameworkType() {
+ return FrameworkType.PROVIDER;
+ }
+}
diff --git a/java/dagger/internal/codegen/ProvidesMethodValidator.java b/java/dagger/internal/codegen/ProvidesMethodValidator.java
new file mode 100644
index 0000000..01e71ae
--- /dev/null
+++ b/java/dagger/internal/codegen/ProvidesMethodValidator.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
+import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
+import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+/** A validator for {@link Provides} methods. */
+final class ProvidesMethodValidator extends BindingMethodValidator {
+
+ private final DependencyRequestValidator dependencyRequestValidator;
+
+ @Inject
+ ProvidesMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestValidator dependencyRequestValidator) {
+ super(
+ elements,
+ types,
+ Provides.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_CONCRETE,
+ RUNTIME_EXCEPTION,
+ ALLOWS_MULTIBINDINGS,
+ ALLOWS_SCOPING);
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalMethodProperties() {
+ }
+
+ /** Adds an error if a {@link Provides @Provides} method depends on a producer type. */
+ @Override
+ protected void checkParameter(VariableElement parameter) {
+ super.checkParameter(parameter);
+ dependencyRequestValidator.checkNotProducer(report, parameter);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ProvisionBinding.java b/java/dagger/internal/codegen/ProvisionBinding.java
new file mode 100644
index 0000000..306cb13
--- /dev/null
+++ b/java/dagger/internal/codegen/ProvisionBinding.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.model.BindingKind.COMPONENT_PROVISION;
+import static dagger.model.BindingKind.PROVISION;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Scope;
+import java.util.Optional;
+
+/**
+ * A value object representing the mechanism by which a {@link Key} can be provided.
+ */
+@AutoValue
+abstract class ProvisionBinding extends ContributionBinding {
+
+ @Override
+ @Memoized
+ ImmutableSet<DependencyRequest> explicitDependencies() {
+ return ImmutableSet.<DependencyRequest>builder()
+ .addAll(provisionDependencies())
+ .addAll(membersInjectionDependencies())
+ .build();
+ }
+
+ /**
+ * Dependencies necessary to invoke an {@code @Inject} constructor or {@code @Provides} method.
+ */
+ abstract ImmutableSet<DependencyRequest> provisionDependencies();
+
+ @Memoized
+ ImmutableSet<DependencyRequest> membersInjectionDependencies() {
+ return injectionSites()
+ .stream()
+ .flatMap(i -> i.dependencies().stream())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * {@link InjectionSite}s for all {@code @Inject} members if {@link #kind()} is {@link
+ * BindingKind#INJECTION}, otherwise empty.
+ */
+ abstract ImmutableSortedSet<InjectionSite> injectionSites();
+
+ @Override
+ public BindingType bindingType() {
+ return BindingType.PROVISION;
+ }
+
+ @Override
+ abstract Optional<ProvisionBinding> unresolved();
+
+ // TODO(ronshapiro): we should be able to remove this, but AutoValue barks on the Builder's scope
+ // method, saying that the method doesn't correspond to a property of ProvisionBinding
+ @Override
+ public abstract Optional<Scope> scope();
+
+ static Builder builder() {
+ return new AutoValue_ProvisionBinding.Builder()
+ .provisionDependencies(ImmutableSet.of())
+ .injectionSites(ImmutableSortedSet.of());
+ }
+
+ abstract Builder toBuilder();
+
+ private static final ImmutableSet<BindingKind> KINDS_TO_CHECK_FOR_NULL =
+ ImmutableSet.of(PROVISION, COMPONENT_PROVISION);
+
+ boolean shouldCheckForNull(CompilerOptions compilerOptions) {
+ return KINDS_TO_CHECK_FOR_NULL.contains(kind())
+ && !contributedPrimitiveType().isPresent()
+ && !nullableType().isPresent()
+ && compilerOptions.doCheckForNulls();
+ }
+
+ // Profiling determined that this method is called enough times that memoizing it had a measurable
+ // performance improvement for large components.
+ @Memoized
+ @Override
+ boolean requiresModuleInstance() {
+ return super.requiresModuleInstance();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // TODO(ronshapiro,dpb): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @AutoValue.Builder
+ @CanIgnoreReturnValue
+ abstract static class Builder extends ContributionBinding.Builder<ProvisionBinding, Builder> {
+
+ @Override
+ Builder dependencies(Iterable<DependencyRequest> dependencies) {
+ return provisionDependencies(dependencies);
+ }
+
+ abstract Builder provisionDependencies(Iterable<DependencyRequest> provisionDependencies);
+
+ abstract Builder injectionSites(ImmutableSortedSet<InjectionSite> injectionSites);
+
+ @Override
+ abstract Builder unresolved(ProvisionBinding unresolved);
+
+ abstract Builder scope(Optional<Scope> scope);
+ }
+
+}
diff --git a/java/dagger/internal/codegen/ProvisionDependencyOnProducerBindingValidator.java b/java/dagger/internal/codegen/ProvisionDependencyOnProducerBindingValidator.java
new file mode 100644
index 0000000..2fb1a0e
--- /dev/null
+++ b/java/dagger/internal/codegen/ProvisionDependencyOnProducerBindingValidator.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.RequestKinds.canBeSatisfiedByProductionBinding;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Node;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+
+/**
+ * Reports an error for each provision-only dependency request that is satisfied by a production
+ * binding.
+ */
+// TODO(b/29509141): Clarify the error.
+final class ProvisionDependencyOnProducerBindingValidator implements BindingGraphPlugin {
+
+ @Inject
+ ProvisionDependencyOnProducerBindingValidator() {}
+
+ @Override
+ public String pluginName() {
+ return "Dagger/ProviderDependsOnProducer";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ provisionDependenciesOnProductionBindings(bindingGraph)
+ .forEach(
+ provisionDependent ->
+ diagnosticReporter.reportDependency(
+ ERROR,
+ provisionDependent,
+ provisionDependent.isEntryPoint()
+ ? entryPointErrorMessage(provisionDependent)
+ : dependencyErrorMessage(provisionDependent, bindingGraph)));
+ }
+
+ private Stream<DependencyEdge> provisionDependenciesOnProductionBindings(
+ BindingGraph bindingGraph) {
+ return bindingGraph.bindings().stream()
+ .filter(binding -> binding.isProduction())
+ .flatMap(binding -> incomingDependencies(binding, bindingGraph))
+ .filter(edge -> !dependencyCanUseProduction(edge, bindingGraph));
+ }
+
+ /** Returns the dependencies on {@code binding}. */
+ // TODO(dpb): Move to BindingGraph.
+ private Stream<DependencyEdge> incomingDependencies(
+ dagger.model.Binding binding, BindingGraph bindingGraph) {
+ return bindingGraph.network().inEdges(binding).stream()
+ .flatMap(instancesOf(DependencyEdge.class));
+ }
+
+ // TODO(ronshapiro): merge with MissingBindingValidator.dependencyCanUseProduction
+ private boolean dependencyCanUseProduction(DependencyEdge edge, BindingGraph bindingGraph) {
+ return edge.isEntryPoint()
+ ? canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind())
+ : bindingRequestingDependency(edge, bindingGraph).isProduction();
+ }
+
+ /**
+ * Returns the binding that requests a dependency.
+ *
+ * @throws IllegalArgumentException if {@code dependency} is an {@linkplain
+ * DependencyEdge#isEntryPoint() entry point}.
+ */
+ // TODO(dpb): Move to BindingGraph.
+ private dagger.model.Binding bindingRequestingDependency(
+ DependencyEdge dependency, BindingGraph bindingGraph) {
+ checkArgument(!dependency.isEntryPoint());
+ Node source = bindingGraph.network().incidentNodes(dependency).source();
+ verify(
+ source instanceof dagger.model.Binding,
+ "expected source of %s to be a binding, but was: %s",
+ dependency,
+ source);
+ return (dagger.model.Binding) source;
+ }
+
+ private String entryPointErrorMessage(DependencyEdge entryPoint) {
+ return String.format(
+ "%s is a provision entry-point, which cannot depend on a production.",
+ entryPoint.dependencyRequest().key());
+ }
+
+ private String dependencyErrorMessage(
+ DependencyEdge dependencyOnProduction, BindingGraph bindingGraph) {
+ return String.format(
+ "%s is a provision, which cannot depend on a production.",
+ bindingRequestingDependency(dependencyOnProduction, bindingGraph).key());
+ }
+}
diff --git a/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java b/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java
new file mode 100644
index 0000000..6eb92ac
--- /dev/null
+++ b/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.MissingBindingFactory;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.internal.MissingBindingProducer;
+import java.util.Optional;
+
+/**
+ * A {@link BindingExpression} that implements a method that encapsulates a binding that is not part
+ * of the binding graph when generating a final concrete implementation of a subcomponent. The
+ * implementation throws an exception. It is assumed that a binding may remain missing in a valid
+ * binding graph, because it's possible for there to be dependencies that are passively pruned when
+ * a non-leaf binding is re-defined (such as when {@code @Provides} bindings override
+ * {@code @Inject} bindings).
+ *
+ * <p>This method should never be invoked. If it is the exception indicates an issue within Dagger
+ * itself.
+ */
+final class PrunedConcreteMethodBindingExpression extends BindingExpression {
+ private static final CodeBlock METHOD_IMPLEMENTATION =
+ CodeBlock.of(
+ "throw new $T($S);",
+ UnsupportedOperationException.class,
+ "This binding is not part of the final binding graph. The key was requested by a binding "
+ + "that was believed to possibly be part of the graph, but is no longer requested. "
+ + "If this exception is thrown, it is the result of a Dagger bug.");
+
+ PrunedConcreteMethodBindingExpression() {}
+
+ @Override
+ CodeBlock getModifiableBindingMethodImplementation(
+ ModifiableBindingMethod modifiableBindingMethod,
+ ComponentImplementation component,
+ DaggerTypes types) {
+ Optional<FrameworkType> frameworkType = modifiableBindingMethod.request().frameworkType();
+ if (frameworkType.isPresent()) {
+ // If we make initializations replaceable, we can do away with these classes and this logic
+ // since the pruned framework instances will no longer be initialized
+ switch (frameworkType.get()) {
+ case PROVIDER:
+ return missingFrameworkInstance(MissingBindingFactory.class);
+ case PRODUCER_NODE:
+ return missingFrameworkInstance(MissingBindingProducer.class);
+ }
+ throw new AssertionError(frameworkType);
+ }
+ return METHOD_IMPLEMENTATION;
+ }
+
+ private static CodeBlock missingFrameworkInstance(Class<?> factoryClass) {
+ return CodeBlock.builder().addStatement("return $T.create()", factoryClass).build();
+ }
+
+ @Override
+ final Expression getDependencyExpression(ClassName requestingClass) {
+ throw new UnsupportedOperationException(
+ "Requesting a dependency expression for a pruned binding.");
+ }
+}
diff --git a/java/dagger/internal/codegen/RequestKinds.java b/java/dagger/internal/codegen/RequestKinds.java
new file mode 100644
index 0000000..aa17f5e
--- /dev/null
+++ b/java/dagger/internal/codegen/RequestKinds.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.isType;
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.javapoet.TypeNames.lazyOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.langmodel.DaggerTypes.checkTypePresent;
+import static dagger.model.RequestKind.INSTANCE;
+import static dagger.model.RequestKind.LAZY;
+import static dagger.model.RequestKind.PRODUCED;
+import static dagger.model.RequestKind.PRODUCER;
+import static dagger.model.RequestKind.PROVIDER;
+import static dagger.model.RequestKind.PROVIDER_OF_LAZY;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.TypeName;
+import dagger.Lazy;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** Utility methods for {@link RequestKind}s. */
+final class RequestKinds {
+ /** Returns the type of a request of this kind for a key with a given type. */
+ static TypeMirror requestType(RequestKind requestKind, TypeMirror type, DaggerTypes types) {
+ switch (requestKind) {
+ case INSTANCE:
+ return type;
+
+ case PROVIDER_OF_LAZY:
+ return types.wrapType(requestType(LAZY, type, types), Provider.class);
+
+ case FUTURE:
+ return types.wrapType(type, ListenableFuture.class);
+
+ default:
+ return types.wrapType(type, frameworkClass(requestKind));
+ }
+ }
+
+ /** Returns the type of a request of this kind for a key with a given type. */
+ static TypeName requestTypeName(RequestKind requestKind, TypeName keyType) {
+ switch (requestKind) {
+ case INSTANCE:
+ return keyType;
+
+ case PROVIDER:
+ return providerOf(keyType);
+
+ case LAZY:
+ return lazyOf(keyType);
+
+ case PROVIDER_OF_LAZY:
+ return providerOf(lazyOf(keyType));
+
+ case PRODUCER:
+ return producerOf(keyType);
+
+ case PRODUCED:
+ return producedOf(keyType);
+
+ case FUTURE:
+ return listenableFutureOf(keyType);
+
+ default:
+ throw new AssertionError(requestKind);
+ }
+ }
+
+ private static final ImmutableMap<RequestKind, Class<?>> FRAMEWORK_CLASSES =
+ ImmutableMap.of(
+ PROVIDER, Provider.class,
+ LAZY, Lazy.class,
+ PRODUCER, Producer.class,
+ PRODUCED, Produced.class);
+
+ /** Returns the {@link RequestKind} that matches the wrapping types (if any) of {@code type}. */
+ static RequestKind getRequestKind(TypeMirror type) {
+ checkTypePresent(type);
+ for (RequestKind kind : FRAMEWORK_CLASSES.keySet()) {
+ if (matchesKind(kind, type)) {
+ if (kind.equals(PROVIDER) && matchesKind(LAZY, extractKeyType(kind, type))) {
+ return PROVIDER_OF_LAZY;
+ }
+ return kind;
+ }
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * Returns {@code true} if {@code type} is a parameterized type of {@code kind}'s {@link
+ * #frameworkClass(RequestKind) framework class}.
+ */
+ private static boolean matchesKind(RequestKind kind, TypeMirror type) {
+ return isType(type)
+ && isTypeOf(frameworkClass(kind), type)
+ && !asDeclared(type).getTypeArguments().isEmpty();
+ }
+
+ /**
+ * Unwraps the framework class(es) of {@code requestKind} from {@code type}. If {@code
+ * requestKind} is {@link RequestKind#INSTANCE}, this acts as an identity function.
+ *
+ * @throws TypeNotPresentException if {@code type} is an {@link javax.lang.model.type.ErrorType},
+ * which may mean that the type will be generated in a later round of processing
+ * @throws IllegalArgumentException if {@code type} is not wrapped with {@code requestKind}'s
+ * framework class(es).
+ */
+ static TypeMirror extractKeyType(RequestKind requestKind, TypeMirror type) {
+ checkTypePresent(type);
+ switch (requestKind) {
+ case INSTANCE:
+ return type;
+ case PROVIDER_OF_LAZY:
+ return extractKeyType(LAZY, extractKeyType(PROVIDER, type));
+ default:
+ checkArgument(isType(type) && isTypeOf(frameworkClass(requestKind), type));
+ return getOnlyElement(MoreTypes.asDeclared(type).getTypeArguments());
+ }
+ }
+
+ /**
+ * A dagger- or {@code javax.inject}-defined class for {@code requestKind} that that can wrap
+ * another type but share the same {@link dagger.model.Key}.
+ *
+ * <p>For example, {@code Provider<String>} and {@code Lazy<String>} can both be requested if a
+ * key exists for {@code String}; they all share the same key.
+ *
+ * <p>This concept is not well defined and should probably be removed and inlined into the cases
+ * that need it. For example, {@link RequestKind#PROVIDER_OF_LAZY} has <em>2</em> wrapping
+ * classes, and {@link RequestKind#FUTURE} is wrapped with a {@link ListenableFuture}, but for
+ * historical/implementation reasons has not had an associated framework class.
+ */
+ static Class<?> frameworkClass(RequestKind requestKind) {
+ Class<?> result = FRAMEWORK_CLASSES.get(requestKind);
+ checkArgument(result != null, "no framework class for %s", requestKind);
+ return result;
+ }
+
+ /**
+ * Returns {@code true} if requests for {@code requestKind} can be satisfied by a production
+ * binding.
+ */
+ static boolean canBeSatisfiedByProductionBinding(RequestKind requestKind) {
+ switch (requestKind) {
+ case INSTANCE:
+ case PROVIDER:
+ case LAZY:
+ case PROVIDER_OF_LAZY:
+ case MEMBERS_INJECTION:
+ return false;
+ case PRODUCER:
+ case PRODUCED:
+ case FUTURE:
+ return true;
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns true if {@code requestKind} is always derived from a {@link RequestKind#PROVIDER}
+ * instance.
+ */
+ static boolean isDerivedFromProvider(RequestKind requestKind) {
+ switch (requestKind) {
+ case LAZY:
+ case PROVIDER_OF_LAZY:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private RequestKinds() {}
+}
diff --git a/java/dagger/internal/codegen/ResolvedBindings.java b/java/dagger/internal/codegen/ResolvedBindings.java
new file mode 100644
index 0000000..814995a
--- /dev/null
+++ b/java/dagger/internal/codegen/ResolvedBindings.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import dagger.internal.codegen.ContributionType.HasContributionType;
+import dagger.model.Key;
+import dagger.model.Scope;
+import java.util.Optional;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * The collection of bindings that have been resolved for a key. For valid graphs, contains exactly
+ * one binding.
+ *
+ * <p>Separate {@link ResolvedBindings} instances should be used if a {@link
+ * MembersInjectionBinding} and a {@link ProvisionBinding} for the same key exist in the same
+ * component. (This will only happen if a type has an {@code @Inject} constructor and members, the
+ * component has a members injection method, and the type is also requested normally.)
+ */
+@AutoValue
+abstract class ResolvedBindings implements HasContributionType {
+ /**
+ * The binding key for which the {@link #bindings()} have been resolved.
+ */
+ abstract Key key();
+
+ /**
+ * The {@link ContributionBinding}s for {@link #key()} indexed by the component that owns the
+ * binding. Each key in the multimap is a part of the same component ancestry.
+ */
+ abstract ImmutableSetMultimap<TypeElement, ContributionBinding> allContributionBindings();
+
+ /**
+ * The {@link MembersInjectionBinding}s for {@link #key()} indexed by the component that owns the
+ * binding. Each key in the map is a part of the same component ancestry.
+ */
+ abstract ImmutableMap<TypeElement, MembersInjectionBinding> allMembersInjectionBindings();
+
+ /** The multibinding declarations for {@link #key()}. */
+ abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
+
+ /** The subcomponent declarations for {@link #key()}. */
+ abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
+
+ /**
+ * The optional binding declarations for {@link #key()}.
+ */
+ abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();
+
+ // Computing the hash code is an expensive operation.
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // Suppresses ErrorProne warning that hashCode was overridden w/o equals
+ @Override
+ public abstract boolean equals(Object other);
+
+ /** All bindings for {@link #key()}, indexed by the component that owns the binding. */
+ final ImmutableSetMultimap<TypeElement, ? extends Binding> allBindings() {
+ return !allMembersInjectionBindings().isEmpty()
+ ? allMembersInjectionBindings().asMultimap()
+ : allContributionBindings();
+ }
+
+ /** All bindings for {@link #key()}, regardless of which component owns them. */
+ final ImmutableCollection<? extends Binding> bindings() {
+ return allBindings().values();
+ }
+
+ /**
+ * Returns the single binding.
+ *
+ * @throws IllegalStateException if there is not exactly one element in {@link #bindings()}, which
+ * will never happen for contributions in valid graphs
+ */
+ final Binding binding() {
+ return getOnlyElement(bindings());
+ }
+
+ /**
+ * {@code true} if there are no {@link #bindings()}, {@link #multibindingDeclarations()}, {@link
+ * #optionalBindingDeclarations()}, or {@link #subcomponentDeclarations()}.
+ */
+ final boolean isEmpty() {
+ return allMembersInjectionBindings().isEmpty()
+ && allContributionBindings().isEmpty()
+ && multibindingDeclarations().isEmpty()
+ && optionalBindingDeclarations().isEmpty()
+ && subcomponentDeclarations().isEmpty();
+ }
+
+ /** All bindings for {@link #key()} that are owned by a component. */
+ ImmutableSet<? extends Binding> bindingsOwnedBy(ComponentDescriptor component) {
+ return allBindings().get(component.typeElement());
+ }
+
+ /**
+ * All contribution bindings, regardless of owning component. Empty if this is a members-injection
+ * binding.
+ */
+ @Memoized
+ ImmutableSet<ContributionBinding> contributionBindings() {
+ // TODO(ronshapiro): consider optimizing ImmutableSet.copyOf(Collection) for small immutable
+ // collections so that it doesn't need to call toArray(). Even though this method is memoized,
+ // toArray() can take ~150ms for large components, and there are surely other places in the
+ // processor that can benefit from this.
+ return ImmutableSet.copyOf(allContributionBindings().values());
+ }
+
+ /** The component that owns {@code binding}. */
+ final TypeElement owningComponent(ContributionBinding binding) {
+ checkArgument(
+ contributionBindings().contains(binding),
+ "binding is not resolved for %s: %s",
+ key(),
+ binding);
+ return getOnlyElement(allContributionBindings().inverse().get(binding));
+ }
+
+ /**
+ * The members-injection binding, regardless of owning component. Absent if these are contribution
+ * bindings, or if there is no members-injection binding because the type fails validation.
+ */
+ final Optional<MembersInjectionBinding> membersInjectionBinding() {
+ return allMembersInjectionBindings().isEmpty()
+ ? Optional.empty()
+ : Optional.of(Iterables.getOnlyElement(allMembersInjectionBindings().values()));
+ }
+
+ /** Creates a {@link ResolvedBindings} for contribution bindings. */
+ static ResolvedBindings forContributionBindings(
+ Key key,
+ Multimap<TypeElement, ContributionBinding> contributionBindings,
+ Iterable<MultibindingDeclaration> multibindings,
+ Iterable<SubcomponentDeclaration> subcomponentDeclarations,
+ Iterable<OptionalBindingDeclaration> optionalBindingDeclarations) {
+ return new AutoValue_ResolvedBindings(
+ key,
+ ImmutableSetMultimap.copyOf(contributionBindings),
+ ImmutableMap.of(),
+ ImmutableSet.copyOf(multibindings),
+ ImmutableSet.copyOf(subcomponentDeclarations),
+ ImmutableSet.copyOf(optionalBindingDeclarations));
+ }
+
+ /**
+ * Creates a {@link ResolvedBindings} for members injection bindings.
+ */
+ static ResolvedBindings forMembersInjectionBinding(
+ Key key,
+ ComponentDescriptor owningComponent,
+ MembersInjectionBinding ownedMembersInjectionBinding) {
+ return new AutoValue_ResolvedBindings(
+ key,
+ ImmutableSetMultimap.of(),
+ ImmutableMap.of(owningComponent.typeElement(), ownedMembersInjectionBinding),
+ ImmutableSet.of(),
+ ImmutableSet.of(),
+ ImmutableSet.of());
+ }
+
+ /**
+ * Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
+ */
+ static ResolvedBindings noBindings(Key key) {
+ return new AutoValue_ResolvedBindings(
+ key,
+ ImmutableSetMultimap.of(),
+ ImmutableMap.of(),
+ ImmutableSet.of(),
+ ImmutableSet.of(),
+ ImmutableSet.of());
+ }
+
+ /**
+ * {@code true} if this is a multibinding contribution.
+ */
+ boolean isMultibindingContribution() {
+ return contributionBindings().size() == 1
+ && contributionBinding().contributionType().isMultibinding();
+ }
+
+ /**
+ * Returns the single contribution binding.
+ *
+ * @throws IllegalStateException if there is not exactly one element in
+ * {@link #contributionBindings()}, which will never happen for contributions in valid graphs
+ */
+ ContributionBinding contributionBinding() {
+ return getOnlyElement(contributionBindings());
+ }
+
+ /**
+ * The binding type for these bindings. If there are {@link #multibindingDeclarations()} or {@link
+ * #subcomponentDeclarations()} but no {@link #bindings()}, returns {@link BindingType#PROVISION}.
+ *
+ * @throws IllegalStateException if {@link #isEmpty()} or the binding types conflict
+ */
+ final BindingType bindingType() {
+ checkState(!isEmpty(), "empty bindings for %s", key());
+ if (allBindings().isEmpty()
+ && (!multibindingDeclarations().isEmpty() || !subcomponentDeclarations().isEmpty())) {
+ // Only multibinding declarations, so assume provision.
+ return BindingType.PROVISION;
+ }
+ ImmutableSet<BindingType> bindingTypes = bindingTypes();
+ checkState(bindingTypes.size() == 1, "conflicting binding types: %s", bindings());
+ return getOnlyElement(bindingTypes);
+ }
+
+ /** The binding types for {@link #bindings()}. */
+ @Memoized
+ ImmutableSet<BindingType> bindingTypes() {
+ return bindings().stream().map(Binding::bindingType).collect(toImmutableSet());
+ }
+
+ /**
+ * The contribution type for these bindings.
+ *
+ * @throws IllegalStateException if there is not exactly one element in {@link
+ * #contributionBindings()}, which will never happen for contributions in valid graphs
+ */
+ @Override
+ public ContributionType contributionType() {
+ return contributionBinding().contributionType();
+ }
+
+ /**
+ * The scope associated with the single binding.
+ *
+ * @throws IllegalStateException if {@link #bindings()} does not have exactly one element
+ */
+ Optional<Scope> scope() {
+ return binding().scope();
+ }
+}
diff --git a/java/dagger/internal/codegen/Scopes.java b/java/dagger/internal/codegen/Scopes.java
new file mode 100644
index 0000000..252f712
--- /dev/null
+++ b/java/dagger/internal/codegen/Scopes.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.Scope;
+import dagger.producers.ProductionScope;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.inject.Singleton;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Common names and convenience methods for {@link Scope}s. */
+final class Scopes {
+ /**
+ * Creates a {@link Scope} object from the {@link javax.inject.Scope}-annotated annotation type.
+ */
+ static Scope scope(TypeElement scopeType) {
+ return Scope.scope(SimpleAnnotationMirror.of(scopeType));
+ }
+
+ /** Returns a representation for {@link ProductionScope @ProductionScope} scope. */
+ static Scope productionScope(DaggerElements elements) {
+ return scope(elements, ProductionScope.class);
+ }
+
+ /** Returns a representation for {@link Singleton @Singleton} scope. */
+ static Scope singletonScope(DaggerElements elements) {
+ return scope(elements, Singleton.class);
+ }
+
+ private static Scope scope(
+ DaggerElements elements, Class<? extends Annotation> scopeAnnotationClass) {
+ return scope(elements.getTypeElement(scopeAnnotationClass));
+ }
+
+ /**
+ * Returns at most one associated scoped annotation from the source code element, throwing an
+ * exception if there are more than one.
+ */
+ static Optional<Scope> uniqueScopeOf(Element element) {
+ // TODO(ronshapiro): Use MoreCollectors.toOptional() once we can use guava-jre
+ return Optional.ofNullable(getOnlyElement(scopesOf(element), null));
+ }
+
+ /**
+ * Returns the readable source representation (name with @ prefix) of the scope's annotation type.
+ *
+ * <p>It's readable source because it has had common package prefixes removed, e.g.
+ * {@code @javax.inject.Singleton} is returned as {@code @Singleton}.
+ */
+ static String getReadableSource(Scope scope) {
+ return stripCommonTypePrefixes(scope.toString());
+ }
+
+ /** Returns all of the associated scopes for a source code element. */
+ static ImmutableSet<Scope> scopesOf(Element element) {
+ return AnnotationMirrors.getAnnotatedAnnotations(element, javax.inject.Scope.class)
+ .stream()
+ .map(Scope::scope)
+ .collect(toImmutableSet());
+ }
+}
diff --git a/java/dagger/internal/codegen/SetBindingExpression.java b/java/dagger/internal/codegen/SetBindingExpression.java
new file mode 100644
index 0000000..5efb85f
--- /dev/null
+++ b/java/dagger/internal/codegen/SetBindingExpression.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.SetBuilder;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import java.util.Collections;
+import java.util.Optional;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression for multibound sets. */
+final class SetBindingExpression extends MultibindingExpression {
+ private final ProvisionBinding binding;
+ private final BindingGraph graph;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ SetBindingExpression(
+ ResolvedBindings resolvedBindings,
+ ComponentImplementation componentImplementation,
+ BindingGraph graph,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ DaggerElements elements) {
+ super(resolvedBindings, componentImplementation);
+ this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
+ this.graph = graph;
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ this.elements = elements;
+ }
+
+ @Override
+ protected Expression buildDependencyExpression(ClassName requestingClass) {
+ Optional<CodeBlock> superMethodCall = superMethodCall();
+ // TODO(ronshapiro): We should also make an ImmutableSet version of SetFactory
+ boolean isImmutableSetAvailable = isImmutableSetAvailable();
+ // TODO(ronshapiro, gak): Use Sets.immutableEnumSet() if it's available?
+ if (isImmutableSetAvailable
+ && binding.dependencies().stream().allMatch(this::isSingleValue)
+ && !superMethodCall.isPresent()) {
+ return Expression.create(
+ immutableSetType(),
+ CodeBlock.builder()
+ .add("$T.", ImmutableSet.class)
+ .add(maybeTypeParameter(requestingClass))
+ .add(
+ "of($L)",
+ binding
+ .dependencies()
+ .stream()
+ .map(dependency -> getContributionExpression(dependency, requestingClass))
+ .collect(toParametersCodeBlock()))
+ .build());
+ }
+ switch (binding.dependencies().size()) {
+ case 0:
+ return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptySet()"));
+ case 1:
+ {
+ DependencyRequest dependency = getOnlyElement(binding.dependencies());
+ CodeBlock contributionExpression = getContributionExpression(dependency, requestingClass);
+ if (isSingleValue(dependency)) {
+ return collectionsStaticFactoryInvocation(
+ requestingClass, CodeBlock.of("singleton($L)", contributionExpression));
+ } else if (isImmutableSetAvailable) {
+ return Expression.create(
+ immutableSetType(),
+ CodeBlock.builder()
+ .add("$T.", ImmutableSet.class)
+ .add(maybeTypeParameter(requestingClass))
+ .add("copyOf($L)", contributionExpression)
+ .build());
+ }
+ }
+ // fall through
+ default:
+ CodeBlock.Builder instantiation = CodeBlock.builder();
+ instantiation
+ .add("$T.", isImmutableSetAvailable ? ImmutableSet.class : SetBuilder.class)
+ .add(maybeTypeParameter(requestingClass));
+ if (isImmutableSetBuilderWithExpectedSizeAvailable()) {
+ instantiation.add("builderWithExpectedSize($L)", binding.dependencies().size());
+ } else if (isImmutableSetAvailable) {
+ instantiation.add("builder()");
+ } else {
+ instantiation.add("newSetBuilder($L)", binding.dependencies().size());
+ }
+ for (DependencyRequest dependency : getNewContributions(binding.dependencies())) {
+ String builderMethod = isSingleValue(dependency) ? "add" : "addAll";
+ instantiation.add(
+ ".$L($L)", builderMethod, getContributionExpression(dependency, requestingClass));
+ }
+ if (superMethodCall.isPresent()) {
+ instantiation.add(CodeBlock.of(".addAll($L)", superMethodCall.get()));
+ }
+ instantiation.add(".build()");
+ return Expression.create(
+ isImmutableSetAvailable ? immutableSetType() : binding.key().type(),
+ instantiation.build());
+ }
+ }
+
+ private DeclaredType immutableSetType() {
+ return types.getDeclaredType(
+ elements.getTypeElement(ImmutableSet.class), SetType.from(binding.key()).elementType());
+ }
+
+ private CodeBlock getContributionExpression(
+ DependencyRequest dependency, ClassName requestingClass) {
+ return componentBindingExpressions
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock();
+ }
+
+ private Expression collectionsStaticFactoryInvocation(
+ ClassName requestingClass, CodeBlock methodInvocation) {
+ return Expression.create(
+ binding.key().type(),
+ CodeBlock.builder()
+ .add("$T.", Collections.class)
+ .add(maybeTypeParameter(requestingClass))
+ .add(methodInvocation)
+ .build());
+ }
+
+ private CodeBlock maybeTypeParameter(ClassName requestingClass) {
+ TypeMirror elementType = SetType.from(binding.key()).elementType();
+ return isTypeAccessibleFrom(elementType, requestingClass.packageName())
+ ? CodeBlock.of("<$T>", elementType)
+ : CodeBlock.of("");
+ }
+
+ private boolean isSingleValue(DependencyRequest dependency) {
+ return graph
+ .contributionBindings()
+ .get(dependency.key())
+ .contributionBinding()
+ .contributionType()
+ .equals(ContributionType.SET);
+ }
+
+ private boolean isImmutableSetBuilderWithExpectedSizeAvailable() {
+ if (isImmutableSetAvailable()) {
+ return methodsIn(elements.getTypeElement(ImmutableSet.class).getEnclosedElements())
+ .stream()
+ .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
+ }
+ return false;
+ }
+
+ private boolean isImmutableSetAvailable() {
+ return elements.getTypeElement(ImmutableSet.class) != null;
+ }
+}
diff --git a/java/dagger/internal/codegen/SetFactoryCreationExpression.java b/java/dagger/internal/codegen/SetFactoryCreationExpression.java
new file mode 100644
index 0000000..9712091
--- /dev/null
+++ b/java/dagger/internal/codegen/SetFactoryCreationExpression.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.model.DependencyRequest;
+import dagger.producers.Produced;
+import java.util.Optional;
+
+/** A factory creation expression for a multibound set. */
+final class SetFactoryCreationExpression extends MultibindingFactoryCreationExpression {
+
+ private final ComponentImplementation componentImplementation;
+ private final BindingGraph graph;
+ private final ContributionBinding binding;
+
+ SetFactoryCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions,
+ BindingGraph graph) {
+ super(binding, componentImplementation, componentBindingExpressions);
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.graph = checkNotNull(graph);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ CodeBlock.Builder builder = CodeBlock.builder().add("$T.", setFactoryClassName(binding));
+ if (!useRawType()) {
+ SetType setType = SetType.from(binding.key());
+ builder.add(
+ "<$T>",
+ setType.elementsAreTypeOf(Produced.class)
+ ? setType.unwrappedElementType(Produced.class)
+ : setType.elementType());
+ }
+
+ int individualProviders = 0;
+ int setProviders = 0;
+ CodeBlock.Builder builderMethodCalls = CodeBlock.builder();
+ String methodNameSuffix =
+ binding.bindingType().equals(BindingType.PROVISION) ? "Provider" : "Producer";
+
+ Optional<CodeBlock> superContributions = superContributions();
+ if (superContributions.isPresent()) {
+ // TODO(b/117833324): consider decomposing the Provider<Set<Provider>> and adding the
+ // individual contributions separately from the collection contributions. Though this may
+ // actually not be doable/desirable if the super provider instance is a DelegateFactory or
+ // another internal type that is not SetFactory
+ builderMethodCalls.add(".addCollection$N($L)", methodNameSuffix, superContributions.get());
+ setProviders++;
+ }
+
+ for (DependencyRequest dependency : dependenciesToImplement()) {
+ ContributionType contributionType =
+ graph.contributionBindings().get(dependency.key()).contributionType();
+ String methodNamePrefix;
+ switch (contributionType) {
+ case SET:
+ individualProviders++;
+ methodNamePrefix = "add";
+ break;
+ case SET_VALUES:
+ setProviders++;
+ methodNamePrefix = "addCollection";
+ break;
+ default:
+ throw new AssertionError(dependency + " is not a set multibinding");
+ }
+
+ builderMethodCalls.add(
+ ".$N$N($L)",
+ methodNamePrefix,
+ methodNameSuffix,
+ multibindingDependencyExpression(dependency));
+ }
+ builder.add("builder($L, $L)", individualProviders, setProviders);
+ builder.add(builderMethodCalls.build());
+
+ componentImplementation.registerImplementedMultibinding(binding, bindingRequest());
+
+ return builder.add(".build()").build();
+ }
+}
diff --git a/java/dagger/internal/codegen/SetType.java b/java/dagger/internal/codegen/SetType.java
new file mode 100644
index 0000000..e4fa584
--- /dev/null
+++ b/java/dagger/internal/codegen/SetType.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import dagger.model.Key;
+import java.util.Set;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Information about a {@link Set} {@link TypeMirror}.
+ */
+@AutoValue
+abstract class SetType {
+ /**
+ * The set type itself, wrapped using {@link MoreTypes#equivalence()}. Use
+ * {@link #declaredSetType()} instead.
+ */
+ protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredSetType();
+
+ /**
+ * The set type itself.
+ */
+ DeclaredType declaredSetType() {
+ return wrappedDeclaredSetType().get();
+ }
+
+ /**
+ * {@code true} if the set type is the raw {@link Set} type.
+ */
+ boolean isRawType() {
+ return declaredSetType().getTypeArguments().isEmpty();
+ }
+
+ /**
+ * The element type.
+ */
+ TypeMirror elementType() {
+ return declaredSetType().getTypeArguments().get(0);
+ }
+
+ /**
+ * {@code true} if {@link #elementType()} is a {@code clazz}.
+ */
+ boolean elementsAreTypeOf(Class<?> clazz) {
+ return MoreTypes.isType(elementType()) && MoreTypes.isTypeOf(clazz, elementType());
+ }
+
+ /**
+ * {@code T} if {@link #elementType()} is a {@code WrappingClass<T>}.
+ *
+ * @throws IllegalStateException if {@link #elementType()} is not a {@code WrappingClass<T>}
+ * @throws IllegalArgumentException if {@code wrappingClass} does not have exactly one type
+ * parameter
+ */
+ TypeMirror unwrappedElementType(Class<?> wrappingClass) {
+ checkArgument(
+ wrappingClass.getTypeParameters().length == 1,
+ "%s must have exactly one type parameter",
+ wrappingClass);
+ checkArgument(
+ elementsAreTypeOf(wrappingClass),
+ "expected elements to be %s, but this type is %s",
+ wrappingClass,
+ declaredSetType());
+ return MoreTypes.asDeclared(elementType()).getTypeArguments().get(0);
+ }
+
+ /**
+ * {@code true} if {@code type} is a {@link Set} type.
+ */
+ static boolean isSet(TypeMirror type) {
+ return MoreTypes.isType(type) && MoreTypes.isTypeOf(Set.class, type);
+ }
+
+ /**
+ * {@code true} if {@code key.type()} is a {@link Set} type.
+ */
+ static boolean isSet(Key key) {
+ return isSet(key.type());
+ }
+
+ /**
+ * Returns a {@link SetType} for {@code type}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not a {@link Set} type
+ */
+ static SetType from(TypeMirror type) {
+ checkArgument(isSet(type), "%s must be a Set", type);
+ return new AutoValue_SetType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+ }
+
+ /**
+ * Returns a {@link SetType} for {@code key}'s {@link Key#type() type}.
+ *
+ * @throws IllegalArgumentException if {@code key.type()} is not a {@link Set} type
+ */
+ static SetType from(Key key) {
+ return from (key.type());
+ }
+}
diff --git a/java/dagger/internal/codegen/SimpleAnnotationMirror.java b/java/dagger/internal/codegen/SimpleAnnotationMirror.java
new file mode 100644
index 0000000..505c8ea
--- /dev/null
+++ b/java/dagger/internal/codegen/SimpleAnnotationMirror.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Functions;
+import com.google.common.base.Joiner;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+
+/** A representation of an annotation. */
+final class SimpleAnnotationMirror implements AnnotationMirror {
+ private final TypeElement annotationType;
+ private final ImmutableMap<String, ? extends AnnotationValue> namedValues;
+ private final ImmutableMap<ExecutableElement, ? extends AnnotationValue> elementValues;
+
+ private SimpleAnnotationMirror(
+ TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
+ checkArgument(
+ annotationType.getKind().equals(ElementKind.ANNOTATION_TYPE),
+ "annotationType must be an annotation: %s",
+ annotationType);
+ checkArgument(
+ FluentIterable.from(methodsIn(annotationType.getEnclosedElements()))
+ .transform(element -> element.getSimpleName().toString())
+ .toSet()
+ .equals(namedValues.keySet()),
+ "namedValues must have values for exactly the members in %s: %s",
+ annotationType,
+ namedValues);
+ this.annotationType = annotationType;
+ this.namedValues = ImmutableMap.copyOf(namedValues);
+ this.elementValues =
+ Maps.toMap(
+ methodsIn(annotationType.getEnclosedElements()),
+ Functions.compose(
+ Functions.forMap(namedValues), element -> element.getSimpleName().toString()));
+ }
+
+ @Override
+ public DeclaredType getAnnotationType() {
+ return MoreTypes.asDeclared(annotationType.asType());
+ }
+
+ @Override
+ public Map<ExecutableElement, ? extends AnnotationValue> getElementValues() {
+ return elementValues;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("@").append(annotationType.getQualifiedName());
+ if (!namedValues.isEmpty()) {
+ builder
+ .append('(')
+ .append(Joiner.on(", ").withKeyValueSeparator(" = ").join(namedValues))
+ .append(')');
+ }
+ return builder.toString();
+ }
+
+ /**
+ * An object representing an annotation instance.
+ *
+ * @param annotationType must be an annotation type with no members
+ */
+ static AnnotationMirror of(TypeElement annotationType) {
+ return of(annotationType, ImmutableMap.<String, AnnotationValue>of());
+ }
+
+ /**
+ * An object representing an annotation instance.
+ *
+ * @param annotationType must be an annotation type
+ * @param namedValues a value for every annotation member, including those with defaults, indexed
+ * by simple name
+ */
+ static AnnotationMirror of(
+ TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
+ return new SimpleAnnotationMirror(annotationType, namedValues);
+ }
+}
diff --git a/java/dagger/internal/codegen/SimpleInvocationBindingExpression.java b/java/dagger/internal/codegen/SimpleInvocationBindingExpression.java
new file mode 100644
index 0000000..909d7ae
--- /dev/null
+++ b/java/dagger/internal/codegen/SimpleInvocationBindingExpression.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/** A simple binding expression for instance requests. Does not scope. */
+abstract class SimpleInvocationBindingExpression extends BindingExpression {
+ // TODO(dpb): Take ContributionBinding instead of ResolvedBindings.
+ private final ResolvedBindings resolvedBindings;
+
+ SimpleInvocationBindingExpression(ResolvedBindings resolvedBindings) {
+ this.resolvedBindings = checkNotNull(resolvedBindings);
+ }
+
+ @Override
+ boolean requiresMethodEncapsulation() {
+ return !resolvedBindings.contributionBinding().dependencies().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/SimpleMethodBindingExpression.java b/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
new file mode 100644
index 0000000..805ac7b
--- /dev/null
+++ b/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A binding expression that invokes methods or constructors directly (without attempting to scope)
+ * {@link dagger.model.RequestKind#INSTANCE} requests.
+ */
+final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression {
+ private final CompilerOptions compilerOptions;
+ private final ProvisionBinding provisionBinding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final MembersInjectionMethods membersInjectionMethods;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final SourceVersion sourceVersion;
+
+ SimpleMethodBindingExpression(
+ ResolvedBindings resolvedBindings,
+ CompilerOptions compilerOptions,
+ ComponentBindingExpressions componentBindingExpressions,
+ MembersInjectionMethods membersInjectionMethods,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ DaggerTypes types,
+ DaggerElements elements,
+ SourceVersion sourceVersion) {
+ super(resolvedBindings);
+ this.compilerOptions = compilerOptions;
+ this.provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding();
+ checkArgument(
+ provisionBinding.implicitDependencies().isEmpty(),
+ "framework deps are not currently supported");
+ checkArgument(provisionBinding.bindingElement().isPresent());
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.membersInjectionMethods = membersInjectionMethods;
+ this.componentRequirementExpressions = componentRequirementExpressions;
+ this.types = types;
+ this.elements = elements;
+ this.sourceVersion = sourceVersion;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ ImmutableMap<DependencyRequest, Expression> arguments =
+ ImmutableMap.copyOf(
+ Maps.asMap(
+ provisionBinding.dependencies(),
+ request -> dependencyArgument(request, requestingClass)));
+ Function<DependencyRequest, CodeBlock> argumentsFunction =
+ request -> arguments.get(request).codeBlock();
+ return requiresInjectionMethod(
+ provisionBinding,
+ arguments.values().asList(),
+ compilerOptions,
+ requestingClass.packageName(),
+ types)
+ ? invokeInjectionMethod(argumentsFunction, requestingClass)
+ : invokeMethod(argumentsFunction, requestingClass);
+ }
+
+ private Expression invokeMethod(
+ Function<DependencyRequest, CodeBlock> argumentsFunction,
+ ClassName requestingClass) {
+ // TODO(dpb): align this with the contents of InlineMethods.create
+ CodeBlock arguments =
+ provisionBinding.dependencies().stream()
+ .map(argumentsFunction)
+ .collect(toParametersCodeBlock());
+ ExecutableElement method = asExecutable(provisionBinding.bindingElement().get());
+ CodeBlock invocation;
+ switch (method.getKind()) {
+ case CONSTRUCTOR:
+ invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments);
+ break;
+ case METHOD:
+ CodeBlock module =
+ moduleReference(requestingClass)
+ .orElse(CodeBlock.of("$T", provisionBinding.bindingTypeElement().get()));
+ invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+
+ return Expression.create(simpleMethodReturnType(), invocation);
+ }
+
+ private TypeName constructorTypeName(ClassName requestingClass) {
+ DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type());
+ TypeName typeName = TypeName.get(type);
+ if (type.getTypeArguments()
+ .stream()
+ .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) {
+ return typeName;
+ }
+ return rawTypeName(typeName);
+ }
+
+ private Expression invokeInjectionMethod(
+ Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass) {
+ return injectMembers(
+ ProvisionMethod.invoke(
+ provisionBinding,
+ argumentsFunction,
+ requestingClass,
+ moduleReference(requestingClass),
+ compilerOptions,
+ elements));
+ }
+
+ private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
+ return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass);
+ }
+
+ private Expression injectMembers(CodeBlock instance) {
+ if (provisionBinding.injectionSites().isEmpty()) {
+ return Expression.create(simpleMethodReturnType(), instance);
+ }
+ if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+ // Java 7 type inference can't figure out that instance in
+ // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not
+ // Parameterized<Object>
+ if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) {
+ TypeName keyType = TypeName.get(provisionBinding.key().type());
+ instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
+ }
+ }
+ MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key());
+ TypeMirror returnType =
+ membersInjectionMethod.returnType.equals(TypeName.OBJECT)
+ ? elements.getTypeElement(Object.class).asType()
+ : provisionBinding.key().type();
+ return Expression.create(returnType, CodeBlock.of("$N($L)", membersInjectionMethod, instance));
+ }
+
+ private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
+ return provisionBinding.requiresModuleInstance()
+ ? provisionBinding
+ .contributingModule()
+ .map(Element::asType)
+ .map(ComponentRequirement::forModule)
+ .map(module -> componentRequirementExpressions.getExpression(module, requestingClass))
+ : Optional.empty();
+ }
+
+ private TypeMirror simpleMethodReturnType() {
+ return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type());
+ }
+}
diff --git a/java/dagger/internal/codegen/SimpleTypeAnnotationValue.java b/java/dagger/internal/codegen/SimpleTypeAnnotationValue.java
new file mode 100644
index 0000000..7f043db
--- /dev/null
+++ b/java/dagger/internal/codegen/SimpleTypeAnnotationValue.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.type.TypeMirror;
+
+/** An {@link AnnotationValue} that contains a {@link TypeMirror}. */
+final class SimpleTypeAnnotationValue implements AnnotationValue {
+ private final TypeMirror value;
+
+ SimpleTypeAnnotationValue(TypeMirror value) {
+ this.value = value;
+ }
+
+ @Override
+ public TypeMirror getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return value + ".class";
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> visitor, P parameter) {
+ return visitor.visitType(getValue(), parameter);
+ }
+}
diff --git a/java/dagger/internal/codegen/SourceFileGenerationException.java b/java/dagger/internal/codegen/SourceFileGenerationException.java
new file mode 100644
index 0000000..07c1c68
--- /dev/null
+++ b/java/dagger/internal/codegen/SourceFileGenerationException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.squareup.javapoet.ClassName;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.Element;
+
+/**
+ * An exception thrown to indicate that a source file could not be generated.
+ *
+ * <p>This exception <b>should not</b> be used to report detectable, logical errors as it may mask
+ * other errors that might have been caught upon further processing. Use a {@link ValidationReport}
+ * for that.
+ */
+final class SourceFileGenerationException extends Exception {
+ private final Element associatedElement;
+
+ SourceFileGenerationException(
+ Optional<ClassName> generatedClassName, Throwable cause, Element associatedElement) {
+ super(createMessage(generatedClassName, cause.getMessage()), cause);
+ this.associatedElement = checkNotNull(associatedElement);
+ }
+
+ private static String createMessage(Optional<ClassName> generatedClassName, String message) {
+ return String.format("Could not generate %s: %s.",
+ generatedClassName.isPresent()
+ ? generatedClassName.get()
+ : "unknown file",
+ message);
+ }
+
+ void printMessageTo(Messager messager) {
+ messager.printMessage(ERROR, getMessage(), associatedElement);
+ }
+}
diff --git a/java/dagger/internal/codegen/SourceFileGenerator.java b/java/dagger/internal/codegen/SourceFileGenerator.java
new file mode 100644
index 0000000..7dddc2f
--- /dev/null
+++ b/java/dagger/internal/codegen/SourceFileGenerator.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Throwables;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * A template class that provides a framework for properly handling IO while generating source files
+ * from an annotation processor. Particularly, it makes a best effort to ensure that files that
+ * fail to write successfully are deleted.
+ *
+ * @param <T> The input type from which source is to be generated.
+ */
+abstract class SourceFileGenerator<T> {
+ private static final String GENERATED_COMMENTS = "https://dagger.dev";
+
+ private final Filer filer;
+ private final DaggerElements elements;
+ private final SourceVersion sourceVersion;
+
+ SourceFileGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ this.filer = checkNotNull(filer);
+ this.elements = checkNotNull(elements);
+ this.sourceVersion = checkNotNull(sourceVersion);
+ }
+
+ SourceFileGenerator(SourceFileGenerator<T> delegate) {
+ this(delegate.filer, delegate.elements, delegate.sourceVersion);
+ }
+
+ /**
+ * Generates a source file to be compiled for {@code T}. Writes any generation exception to {@code
+ * messager} and does not throw.
+ */
+ void generate(T input, Messager messager) {
+ try {
+ generate(input);
+ } catch (SourceFileGenerationException e) {
+ e.printMessageTo(messager);
+ }
+ }
+
+ /** Generates a source file to be compiled for {@code T}. */
+ void generate(T input) throws SourceFileGenerationException {
+ ClassName generatedTypeName = nameGeneratedType(input);
+ Optional<TypeSpec.Builder> type = write(generatedTypeName, input);
+ if (!type.isPresent()) {
+ return;
+ }
+ try {
+ buildJavaFile(generatedTypeName, input, type.get()).writeTo(filer);
+ } catch (Exception e) {
+ // if the code above threw a SFGE, use that
+ Throwables.propagateIfPossible(e, SourceFileGenerationException.class);
+ // otherwise, throw a new one
+ throw new SourceFileGenerationException(
+ Optional.empty(), e, originatingElement(input));
+ }
+ }
+
+ private JavaFile buildJavaFile(
+ ClassName generatedTypeName, T input, TypeSpec.Builder typeSpecBuilder) {
+ typeSpecBuilder.addOriginatingElement(originatingElement(input));
+ Optional<AnnotationSpec> generatedAnnotation =
+ generatedAnnotation(elements, sourceVersion)
+ .map(
+ annotation ->
+ AnnotationSpec.builder(ClassName.get(annotation))
+ .addMember("value", "$S", "dagger.internal.codegen.ComponentProcessor")
+ .addMember("comments", "$S", GENERATED_COMMENTS)
+ .build());
+ generatedAnnotation.ifPresent(typeSpecBuilder::addAnnotation);
+ JavaFile.Builder javaFileBuilder =
+ JavaFile.builder(generatedTypeName.packageName(), typeSpecBuilder.build())
+ .skipJavaLangImports(true);
+ if (!generatedAnnotation.isPresent()) {
+ javaFileBuilder.addFileComment("Generated by Dagger ($L).", GENERATED_COMMENTS);
+ }
+ return javaFileBuilder.build();
+ }
+
+ /**
+ * Implementations should return the {@link ClassName} for the top-level type to be generated.
+ */
+ abstract ClassName nameGeneratedType(T input);
+
+ /** Returns the originating element of the generating type. */
+ abstract Element originatingElement(T input);
+
+ /**
+ * Returns a {@link TypeSpec.Builder type} to be generated for {@code T}, or {@link
+ * Optional#empty()} if no file should be generated.
+ */
+ // TODO(ronshapiro): write() makes more sense in JavaWriter where all writers are mutable.
+ // consider renaming to something like typeBuilder() which conveys the mutability of the result
+ abstract Optional<TypeSpec.Builder> write(ClassName generatedTypeName, T input);
+}
diff --git a/java/dagger/internal/codegen/SourceFileGeneratorsModule.java b/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
new file mode 100644
index 0000000..c32262a
--- /dev/null
+++ b/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.SourceFileGeneratorsModule.ComponentModule;
+import dagger.internal.codegen.SourceFileGeneratorsModule.MembersInjectionModule;
+import dagger.internal.codegen.SourceFileGeneratorsModule.ProductionModule;
+import dagger.internal.codegen.SourceFileGeneratorsModule.ProvisionModule;
+import javax.lang.model.element.TypeElement;
+
+@Module(
+ includes = {
+ ProvisionModule.class,
+ ProductionModule.class,
+ MembersInjectionModule.class,
+ ComponentModule.class
+ })
+interface SourceFileGeneratorsModule {
+ @Module
+ abstract class GeneratorModule<T, G extends SourceFileGenerator<T>> {
+ @Provides
+ SourceFileGenerator<T> generator(G generator, CompilerOptions compilerOptions) {
+ return compilerOptions.headerCompilation()
+ ? HjarSourceFileGenerator.wrap(generator)
+ : generator;
+ }
+ }
+
+ @Module
+ class ProvisionModule extends GeneratorModule<ProvisionBinding, FactoryGenerator> {}
+
+ @Module
+ class ProductionModule extends GeneratorModule<ProductionBinding, ProducerFactoryGenerator> {}
+
+ @Module
+ class MembersInjectionModule
+ extends GeneratorModule<MembersInjectionBinding, MembersInjectorGenerator> {}
+
+ @Module
+ class ComponentModule extends GeneratorModule<BindingGraph, ComponentGenerator> {}
+
+ // the abstract module is not available because we're using a qualifier
+ @Provides
+ @ModuleGenerator
+ static SourceFileGenerator<TypeElement> generator(
+ ModuleConstructorProxyGenerator generator, CompilerOptions compilerOptions) {
+ return compilerOptions.headerCompilation()
+ ? HjarSourceFileGenerator.wrap(generator)
+ : generator;
+ }
+}
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
new file mode 100644
index 0000000..fd93d0d
--- /dev/null
+++ b/java/dagger/internal/codegen/SourceFiles.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.Optionals.optionalComparator;
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCED_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCER_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_PROVIDER_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER_OF_LAZY;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_OF_PRODUCED_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_PRODUCER;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static dagger.model.BindingKind.MULTIBOUND_SET;
+import static java.util.Comparator.comparing;
+import static javax.lang.model.SourceVersion.isName;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.SetFactory;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.internal.SetOfProducedProducer;
+import dagger.producers.internal.SetProducer;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import javax.inject.Provider;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+
+/**
+ * Utilities for generating files.
+ */
+class SourceFiles {
+
+ private static final Joiner CLASS_FILE_NAME_JOINER = Joiner.on('_');
+
+ /**
+ * Sorts {@link DependencyRequest} instances in an order likely to reflect their logical
+ * importance.
+ */
+ static final Comparator<DependencyRequest> DEPENDENCY_ORDERING =
+ // put fields before parameters
+ comparing(
+ (DependencyRequest request) -> request.requestElement().map(Element::getKind),
+ optionalComparator())
+ // order by dependency kind
+ .thenComparing(DependencyRequest::kind)
+ // then sort by name
+ .thenComparing(
+ request ->
+ request.requestElement().map(element -> element.getSimpleName().toString()),
+ optionalComparator());
+
+ /**
+ * Generates names and keys for the factory class fields needed to hold the framework classes for
+ * all of the dependencies of {@code binding}. It is responsible for choosing a name that
+ *
+ * <ul>
+ * <li>represents all of the dependency requests for this key
+ * <li>is <i>probably</i> associated with the type being bound
+ * <li>is unique within the class
+ * </ul>
+ *
+ * @param binding must be an unresolved binding (type parameters must match its type element's)
+ */
+ static ImmutableMap<Key, FrameworkField> generateBindingFieldsForDependencies(
+ Binding binding) {
+ checkArgument(!binding.unresolved().isPresent(), "binding must be unresolved: %s", binding);
+
+ ImmutableMap.Builder<Key, FrameworkField> bindingFields = ImmutableMap.builder();
+ for (Binding.DependencyAssociation dependencyAssociation : binding.dependencyAssociations()) {
+ FrameworkDependency frameworkDependency = dependencyAssociation.frameworkDependency();
+ bindingFields.put(
+ frameworkDependency.key(),
+ FrameworkField.create(
+ ClassName.get(frameworkDependency.frameworkClass()),
+ TypeName.get(frameworkDependency.key().type()),
+ fieldNameForDependency(dependencyAssociation.dependencyRequests())));
+ }
+ return bindingFields.build();
+ }
+
+ private static String fieldNameForDependency(ImmutableSet<DependencyRequest> dependencyRequests) {
+ // collect together all of the names that we would want to call the provider
+ ImmutableSet<String> dependencyNames =
+ dependencyRequests.stream().map(DependencyVariableNamer::name).collect(toImmutableSet());
+
+ if (dependencyNames.size() == 1) {
+ // if there's only one name, great! use it!
+ return Iterables.getOnlyElement(dependencyNames);
+ } else {
+ // in the event that a field is being used for a bunch of deps with different names,
+ // add all the names together with "And"s in the middle. E.g.: stringAndS
+ Iterator<String> namesIterator = dependencyNames.iterator();
+ String first = namesIterator.next();
+ StringBuilder compositeNameBuilder = new StringBuilder(first);
+ while (namesIterator.hasNext()) {
+ compositeNameBuilder
+ .append("And")
+ .append(CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, namesIterator.next()));
+ }
+ return compositeNameBuilder.toString();
+ }
+ }
+
+ static CodeBlock frameworkTypeUsageStatement(
+ CodeBlock frameworkTypeMemberSelect, RequestKind dependencyKind) {
+ switch (dependencyKind) {
+ case LAZY:
+ return CodeBlock.of("$T.lazy($L)", DOUBLE_CHECK, frameworkTypeMemberSelect);
+ case INSTANCE:
+ case FUTURE:
+ return CodeBlock.of("$L.get()", frameworkTypeMemberSelect);
+ case PROVIDER:
+ case PRODUCER:
+ return frameworkTypeMemberSelect;
+ case PROVIDER_OF_LAZY:
+ return CodeBlock.of("$T.create($L)", PROVIDER_OF_LAZY, frameworkTypeMemberSelect);
+ default: // including PRODUCED
+ throw new AssertionError(dependencyKind);
+ }
+ }
+
+ /**
+ * Returns a mapping of {@link DependencyRequest}s to {@link CodeBlock}s that {@linkplain
+ * #frameworkTypeUsageStatement(CodeBlock, RequestKind) use them}.
+ */
+ static ImmutableMap<DependencyRequest, CodeBlock> frameworkFieldUsages(
+ ImmutableSet<DependencyRequest> dependencies, ImmutableMap<Key, FieldSpec> fields) {
+ return Maps.toMap(
+ dependencies,
+ dep ->
+ frameworkTypeUsageStatement(CodeBlock.of("$N", fields.get(dep.key())), dep.kind()));
+ }
+
+ /**
+ * Returns the generated factory or members injector name for a binding.
+ */
+ static ClassName generatedClassNameForBinding(Binding binding) {
+ switch (binding.bindingType()) {
+ case PROVISION:
+ case PRODUCTION:
+ ContributionBinding contribution = (ContributionBinding) binding;
+ switch (contribution.kind()) {
+ case INJECTION:
+ case PROVISION:
+ case PRODUCTION:
+ return elementBasedClassName(
+ MoreElements.asExecutable(binding.bindingElement().get()), "Factory");
+
+ default:
+ throw new AssertionError();
+ }
+
+ case MEMBERS_INJECTION:
+ return membersInjectorNameForType(
+ ((MembersInjectionBinding) binding).membersInjectedType());
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Calculates an appropriate {@link ClassName} for a generated class that is based on {@code
+ * element}, appending {@code suffix} at the end.
+ *
+ * <p>This will always return a {@linkplain ClassName#topLevelClassName() top level class name},
+ * even if {@code element}'s enclosing class is a nested type.
+ */
+ static ClassName elementBasedClassName(ExecutableElement element, String suffix) {
+ ClassName enclosingClassName =
+ ClassName.get(MoreElements.asType(element.getEnclosingElement()));
+ String methodName =
+ element.getKind().equals(ElementKind.CONSTRUCTOR)
+ ? ""
+ : LOWER_CAMEL.to(UPPER_CAMEL, element.getSimpleName().toString());
+ return ClassName.get(
+ enclosingClassName.packageName(),
+ classFileName(enclosingClassName) + "_" + methodName + suffix);
+ }
+
+ static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
+ ClassName className = generatedClassNameForBinding(binding);
+ ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
+ return typeParameters.isEmpty()
+ ? className
+ : ParameterizedTypeName.get(className, Iterables.toArray(typeParameters, TypeName.class));
+ }
+
+ static ClassName membersInjectorNameForType(TypeElement typeElement) {
+ return siblingClassName(typeElement, "_MembersInjector");
+ }
+
+ static String classFileName(ClassName className) {
+ return CLASS_FILE_NAME_JOINER.join(className.simpleNames());
+ }
+
+ static ClassName generatedMonitoringModuleName(
+ TypeElement componentElement) {
+ return siblingClassName(componentElement, "_MonitoringModule");
+ }
+
+ // TODO(ronshapiro): when JavaPoet migration is complete, replace the duplicated code
+ // which could use this.
+ private static ClassName siblingClassName(TypeElement typeElement, String suffix) {
+ ClassName className = ClassName.get(typeElement);
+ return className.topLevelClassName().peerClass(classFileName(className) + suffix);
+ }
+
+ /**
+ * The {@link java.util.Set} factory class name appropriate for set bindings.
+ *
+ * <ul>
+ * <li>{@link SetFactory} for provision bindings.
+ * <li>{@link SetProducer} for production bindings for {@code Set<T>}.
+ * <li>{@link SetOfProducedProducer} for production bindings for {@code Set<Produced<T>>}.
+ * </ul>
+ */
+ static ClassName setFactoryClassName(ContributionBinding binding) {
+ checkArgument(binding.kind().equals(MULTIBOUND_SET));
+ if (binding.bindingType().equals(BindingType.PROVISION)) {
+ return SET_FACTORY;
+ } else {
+ SetType setType = SetType.from(binding.key());
+ return setType.elementsAreTypeOf(Produced.class) ? SET_OF_PRODUCED_PRODUCER : SET_PRODUCER;
+ }
+ }
+
+ /** The {@link java.util.Map} factory class name appropriate for map bindings. */
+ static ClassName mapFactoryClassName(ContributionBinding binding) {
+ checkState(binding.kind().equals(MULTIBOUND_MAP), binding.kind());
+ MapType mapType = MapType.from(binding.key());
+ switch (binding.bindingType()) {
+ case PROVISION:
+ return mapType.valuesAreTypeOf(Provider.class) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
+ case PRODUCTION:
+ return mapType.valuesAreFrameworkType()
+ ? mapType.valuesAreTypeOf(Producer.class)
+ ? MAP_OF_PRODUCER_PRODUCER
+ : MAP_OF_PRODUCED_PRODUCER
+ : MAP_PRODUCER;
+ default:
+ throw new IllegalArgumentException(binding.bindingType().toString());
+ }
+ }
+
+ static ImmutableList<TypeVariableName> bindingTypeElementTypeVariableNames(Binding binding) {
+ if (binding instanceof ContributionBinding) {
+ ContributionBinding contributionBinding = (ContributionBinding) binding;
+ if (!contributionBinding.kind().equals(INJECTION)
+ && !contributionBinding.requiresModuleInstance()) {
+ return ImmutableList.of();
+ }
+ }
+ List<? extends TypeParameterElement> typeParameters =
+ binding.bindingTypeElement().get().getTypeParameters();
+ return typeParameters.stream().map(TypeVariableName::get).collect(toImmutableList());
+ }
+
+ /**
+ * Returns a name to be used for variables of the given {@linkplain TypeElement type}. Prefer
+ * semantically meaningful variable names, but if none can be derived, this will produce something
+ * readable.
+ */
+ // TODO(gak): maybe this should be a function of TypeMirrors instead of Elements?
+ static String simpleVariableName(TypeElement typeElement) {
+ String candidateName = UPPER_CAMEL.to(LOWER_CAMEL, typeElement.getSimpleName().toString());
+ String variableName = protectAgainstKeywords(candidateName);
+ verify(isName(variableName), "'%s' was expected to be a valid variable name");
+ return variableName;
+ }
+
+ static String protectAgainstKeywords(String candidateName) {
+ switch (candidateName) {
+ case "package":
+ return "pkg";
+ case "boolean":
+ return "b";
+ case "double":
+ return "d";
+ case "byte":
+ return "b";
+ case "int":
+ return "i";
+ case "short":
+ return "s";
+ case "char":
+ return "c";
+ case "void":
+ return "v";
+ case "class":
+ return "clazz";
+ case "float":
+ return "f";
+ case "long":
+ return "l";
+ default:
+ return SourceVersion.isKeyword(candidateName) ? candidateName + '_' : candidateName;
+ }
+ }
+
+ private SourceFiles() {}
+}
diff --git a/java/dagger/internal/codegen/SpiModule.java b/java/dagger/internal/codegen/SpiModule.java
new file mode 100644
index 0000000..a8f13e1
--- /dev/null
+++ b/java/dagger/internal/codegen/SpiModule.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.Provides;
+import dagger.spi.BindingGraphPlugin;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+
+/** Contains the bindings for {@link BindingGraphValidator} from external SPI providers. */
+@Module
+abstract class SpiModule {
+ private SpiModule() {}
+
+ @Provides
+ @Singleton
+ static ImmutableSet<BindingGraphPlugin> externalPlugins(
+ @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins) {
+ return testingPlugins.orElseGet(
+ () ->
+ ImmutableSet.copyOf(
+ ServiceLoader.load(
+ BindingGraphPlugin.class, BindingGraphValidator.class.getClassLoader())));
+ }
+
+ @Qualifier
+ @Retention(RUNTIME)
+ @Target({FIELD, PARAMETER, METHOD})
+ @interface TestingPlugins {}
+}
diff --git a/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java b/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java
new file mode 100644
index 0000000..c97024e
--- /dev/null
+++ b/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.presentValues;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.model.BindingGraph.SubcomponentCreatorBindingEdge;
+import javax.lang.model.element.TypeElement;
+
+/** An implementation of {@link SubcomponentCreatorBindingEdge}. */
+final class SubcomponentCreatorBindingEdgeImpl implements SubcomponentCreatorBindingEdge {
+
+ private final ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations;
+
+ SubcomponentCreatorBindingEdgeImpl(
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
+ this.subcomponentDeclarations = subcomponentDeclarations;
+ }
+
+ @Override
+ public ImmutableSet<TypeElement> declaringModules() {
+ return subcomponentDeclarations.stream()
+ .map(SubcomponentDeclaration::contributingModule)
+ .flatMap(presentValues())
+ .collect(toImmutableSet());
+ }
+
+ @Override
+ public String toString() {
+ return "subcomponent declared by "
+ + (subcomponentDeclarations.size() == 1
+ ? getOnlyElement(declaringModules()).getQualifiedName()
+ : declaringModules().stream()
+ .map(TypeElement::getQualifiedName)
+ .collect(joining(", ", "{", "}")));
+ }
+}
diff --git a/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java b/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java
new file mode 100644
index 0000000..b415d3f
--- /dev/null
+++ b/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.Expression;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression for a subcomponent creator that just invokes the constructor. */
+final class SubcomponentCreatorBindingExpression extends SimpleInvocationBindingExpression {
+ private final TypeMirror creatorType;
+ private final String creatorImplementationName;
+
+ SubcomponentCreatorBindingExpression(
+ ResolvedBindings resolvedBindings, String creatorImplementationName) {
+ super(resolvedBindings);
+ this.creatorType = resolvedBindings.key().type();
+ this.creatorImplementationName = creatorImplementationName;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(creatorType, "new $L()", creatorImplementationName);
+ }
+}
diff --git a/java/dagger/internal/codegen/SubcomponentDeclaration.java b/java/dagger/internal/codegen/SubcomponentDeclaration.java
new file mode 100644
index 0000000..5677857
--- /dev/null
+++ b/java/dagger/internal/codegen/SubcomponentDeclaration.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue;
+import static dagger.internal.codegen.ConfigurationAnnotations.getSubcomponentCreator;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A declaration for a subcomponent that is included in a module via {@link
+ * dagger.Module#subcomponents()}.
+ */
+@AutoValue
+abstract class SubcomponentDeclaration extends BindingDeclaration {
+ /**
+ * Key for the {@link dagger.Subcomponent.Builder} or {@link
+ * dagger.producers.ProductionSubcomponent.Builder} of {@link #subcomponentType()}.
+ */
+ @Override
+ public abstract Key key();
+
+ /**
+ * The type element that defines the {@link dagger.Subcomponent} or {@link
+ * dagger.producers.ProductionSubcomponent} for this declaration.
+ */
+ abstract TypeElement subcomponentType();
+
+ /** The module annotation. */
+ abstract ModuleAnnotation moduleAnnotation();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ static class Factory {
+ private final KeyFactory keyFactory;
+
+ @Inject
+ Factory(KeyFactory keyFactory) {
+ this.keyFactory = keyFactory;
+ }
+
+ ImmutableSet<SubcomponentDeclaration> forModule(TypeElement module) {
+ ImmutableSet.Builder<SubcomponentDeclaration> declarations = ImmutableSet.builder();
+ ModuleAnnotation moduleAnnotation = ModuleAnnotation.moduleAnnotation(module).get();
+ Element subcomponentAttribute =
+ getAnnotationElementAndValue(moduleAnnotation.annotation(), "subcomponents").getKey();
+ for (TypeElement subcomponent : moduleAnnotation.subcomponents()) {
+ declarations.add(
+ new AutoValue_SubcomponentDeclaration(
+ Optional.of(subcomponentAttribute),
+ Optional.of(module),
+ keyFactory.forSubcomponentCreator(
+ getSubcomponentCreator(subcomponent).get().asType()),
+ subcomponent,
+ moduleAnnotation));
+ }
+ return declarations.build();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java b/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java
new file mode 100644
index 0000000..0c6a006
--- /dev/null
+++ b/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.auto.common.MoreTypes.asTypeElements;
+import static com.google.common.collect.Sets.union;
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+
+/** Reports an error if a subcomponent factory method is missing required modules. */
+final class SubcomponentFactoryMethodValidator implements BindingGraphPlugin {
+
+ private final DaggerTypes types;
+ private final Map<ComponentNode, Set<TypeElement>> inheritedModulesCache = new HashMap<>();
+
+ @Inject
+ SubcomponentFactoryMethodValidator(DaggerTypes types) {
+ this.types = types;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/SubcomponentFactoryMethodMissingModule";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ if (!bindingGraph.rootComponentNode().isRealComponent()
+ || bindingGraph.rootComponentNode().isSubcomponent()) {
+ // We don't know all the modules that might be owned by the child until we know the real root
+ // component, which we don't if the root component node is really a module or a subcomponent.
+ return;
+ }
+ bindingGraph.network().edges().stream()
+ .flatMap(instancesOf(ChildFactoryMethodEdge.class))
+ .forEach(
+ edge -> {
+ ImmutableSet<TypeElement> missingModules = findMissingModules(edge, bindingGraph);
+ if (!missingModules.isEmpty()) {
+ reportMissingModuleParameters(
+ edge, missingModules, bindingGraph, diagnosticReporter);
+ }
+ });
+ }
+
+ private ImmutableSet<TypeElement> findMissingModules(
+ ChildFactoryMethodEdge edge, BindingGraph graph) {
+ ImmutableSet<TypeElement> factoryMethodParameters =
+ subgraphFactoryMethodParameters(edge, graph);
+ ComponentNode child = (ComponentNode) graph.network().incidentNodes(edge).target();
+ SetView<TypeElement> modulesOwnedByChild = ownedModules(child, graph);
+ return graph.bindings().stream()
+ // bindings owned by child
+ .filter(binding -> binding.componentPath().equals(child.componentPath()))
+ // that require a module instance
+ .filter(binding -> binding.requiresModuleInstance())
+ .map(binding -> binding.contributingModule().get())
+ .distinct()
+ // module owned by child
+ .filter(module -> modulesOwnedByChild.contains(module))
+ // module not in the method parameters
+ .filter(module -> !factoryMethodParameters.contains(module))
+ // module doesn't have an accessible no-arg constructor
+ .filter(moduleType -> !componentCanMakeNewInstances(moduleType))
+ .collect(toImmutableSet());
+ }
+
+ private ImmutableSet<TypeElement> subgraphFactoryMethodParameters(
+ ChildFactoryMethodEdge edge, BindingGraph bindingGraph) {
+ ComponentNode parent = (ComponentNode) bindingGraph.network().incidentNodes(edge).source();
+ DeclaredType parentType = asDeclared(parent.componentPath().currentComponent().asType());
+ ExecutableType factoryMethodType =
+ asExecutable(types.asMemberOf(parentType, edge.factoryMethod()));
+ return asTypeElements(factoryMethodType.getParameterTypes());
+ }
+
+ private SetView<TypeElement> ownedModules(ComponentNode component, BindingGraph graph) {
+ return Sets.difference(
+ ((ComponentNodeImpl) component).componentDescriptor().moduleTypes(),
+ inheritedModules(component, graph));
+ }
+
+ private Set<TypeElement> inheritedModules(ComponentNode component, BindingGraph graph) {
+ return Util.reentrantComputeIfAbsent(
+ inheritedModulesCache, component, uncachedInheritedModules(graph));
+ }
+
+ private Function<ComponentNode, Set<TypeElement>> uncachedInheritedModules(BindingGraph graph) {
+ return componentNode ->
+ componentNode.componentPath().atRoot()
+ ? ImmutableSet.of()
+ : graph
+ .componentNode(componentNode.componentPath().parent())
+ .map(parent -> union(ownedModules(parent, graph), inheritedModules(parent, graph)))
+ .get();
+ }
+
+ private void reportMissingModuleParameters(
+ ChildFactoryMethodEdge edge,
+ ImmutableSet<TypeElement> missingModules,
+ BindingGraph graph,
+ DiagnosticReporter diagnosticReporter) {
+ diagnosticReporter.reportSubcomponentFactoryMethod(
+ ERROR,
+ edge,
+ "%s requires modules which have no visible default constructors. "
+ + "Add the following modules as parameters to this method: %s",
+ graph
+ .network()
+ .incidentNodes(edge)
+ .target()
+ .componentPath()
+ .currentComponent()
+ .getQualifiedName(),
+ Joiner.on(", ").join(missingModules));
+ }
+}
diff --git a/java/dagger/internal/codegen/SubcomponentNames.java b/java/dagger/internal/codegen/SubcomponentNames.java
new file mode 100644
index 0000000..f2ffd83
--- /dev/null
+++ b/java/dagger/internal/codegen/SubcomponentNames.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.DaggerStreams.toImmutableMap;
+import static java.lang.Character.isUpperCase;
+import static java.lang.String.format;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimaps;
+import dagger.model.Key;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Holds the unique simple names for all subcomponents, keyed by their {@link ComponentDescriptor}
+ * and {@link Key} of the subcomponent builder.
+ */
+final class SubcomponentNames {
+ private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.');
+
+ private final ImmutableMap<ComponentDescriptor, String> namesByDescriptor;
+ private final ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey;
+
+ SubcomponentNames(BindingGraph graph, KeyFactory keyFactory) {
+ this.namesByDescriptor = namesByDescriptor(graph);
+ this.descriptorsByCreatorKey = descriptorsByCreatorKey(keyFactory, namesByDescriptor.keySet());
+ }
+
+ /** Returns the simple component name for the given {@link ComponentDescriptor}. */
+ String get(ComponentDescriptor componentDescriptor) {
+ return namesByDescriptor.get(componentDescriptor);
+ }
+
+ /**
+ * Returns the simple name for the subcomponent creator implementation with the given {@link Key}.
+ */
+ String getCreatorName(Key key) {
+ return getCreatorName(descriptorsByCreatorKey.get(key));
+ }
+
+ /**
+ * Returns the simple name for the subcomponent creator implementation for the given {@link
+ * ComponentDescriptor}.
+ */
+ String getCreatorName(ComponentDescriptor componentDescriptor) {
+ checkArgument(componentDescriptor.creatorDescriptor().isPresent());
+ ComponentCreatorDescriptor creatorDescriptor = componentDescriptor.creatorDescriptor().get();
+ return get(componentDescriptor) + creatorDescriptor.kind().typeName();
+ }
+
+ private static ImmutableMap<ComponentDescriptor, String> namesByDescriptor(BindingGraph graph) {
+ ImmutableListMultimap<String, ComponentDescriptor> componentDescriptorsBySimpleName =
+ Multimaps.index(
+ graph.componentDescriptors(),
+ componentDescriptor -> componentDescriptor.typeElement().getSimpleName().toString());
+ ImmutableMap<ComponentDescriptor, Namer> componentNamers =
+ qualifiedNames(graph.componentDescriptors());
+ Map<ComponentDescriptor, String> subcomponentImplSimpleNames = new LinkedHashMap<>();
+ componentDescriptorsBySimpleName
+ .asMap()
+ .values()
+ .forEach(
+ components ->
+ subcomponentImplSimpleNames.putAll(
+ disambiguateConflictingSimpleNames(components, componentNamers)));
+ subcomponentImplSimpleNames.remove(graph.componentDescriptor());
+ return ImmutableMap.copyOf(subcomponentImplSimpleNames);
+ }
+
+ private static ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey(
+ KeyFactory keyFactory, ImmutableSet<ComponentDescriptor> subcomponents) {
+ return subcomponents.stream()
+ .filter(subcomponent -> subcomponent.creatorDescriptor().isPresent())
+ .collect(
+ toImmutableMap(
+ subcomponent ->
+ keyFactory.forSubcomponentCreator(
+ subcomponent.creatorDescriptor().get().typeElement().asType()),
+ subcomponent -> subcomponent));
+ }
+
+ private static ImmutableBiMap<ComponentDescriptor, String> disambiguateConflictingSimpleNames(
+ Collection<ComponentDescriptor> components,
+ ImmutableMap<ComponentDescriptor, Namer> componentNamers) {
+ Map<String, ComponentDescriptor> generatedSimpleNames = new LinkedHashMap<>();
+
+ // Let's see if we can get away with using simpleName() everywhere.
+ for (ComponentDescriptor component : components) {
+ Namer namer = componentNamers.get(component);
+ if (generatedSimpleNames.containsKey(namer.simpleName())) {
+ break;
+ }
+ generatedSimpleNames.put(namer.simpleName(), component);
+ }
+
+ if (generatedSimpleNames.size() != components.size()) {
+ // Simple approach didn't work out, let's use more complicated names.
+ // We keep them small to fix https://github.com/google/dagger/issues/421.
+ generatedSimpleNames.clear();
+ UniqueNameSet nameSet = new UniqueNameSet();
+ for (ComponentDescriptor component : components) {
+ Namer namer = componentNamers.get(component);
+ String simpleName = namer.simpleName();
+ String basePrefix = namer.uniquingPrefix();
+ generatedSimpleNames.put(
+ format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName), component);
+ }
+ }
+ return ImmutableBiMap.copyOf(generatedSimpleNames).inverse();
+ }
+
+ private static ImmutableMap<ComponentDescriptor, Namer> qualifiedNames(
+ Iterable<ComponentDescriptor> componentDescriptors) {
+ ImmutableMap.Builder<ComponentDescriptor, Namer> builder = ImmutableMap.builder();
+ for (ComponentDescriptor component : componentDescriptors) {
+ builder.put(component, new Namer(component.typeElement()));
+ }
+ return builder.build();
+ }
+
+ private static final class Namer {
+ final TypeElement typeElement;
+
+ Namer(TypeElement typeElement) {
+ this.typeElement = typeElement;
+ }
+
+ String simpleName() {
+ return typeElement.getSimpleName().toString();
+ }
+
+ /** Returns a prefix that could make {@link #simpleName()} more unique. */
+ String uniquingPrefix() {
+ String containerName = typeElement.getEnclosingElement().getSimpleName().toString();
+
+ // If parent element looks like a class, use its initials as a prefix.
+ if (!containerName.isEmpty() && isUpperCase(containerName.charAt(0))) {
+ return CharMatcher.javaLowerCase().removeFrom(containerName);
+ }
+
+ // Not in a normally named class. Prefix with the initials of the elements leading here.
+ Name qualifiedName = typeElement.getQualifiedName();
+ Iterator<String> pieces = QUALIFIED_NAME_SPLITTER.split(qualifiedName).iterator();
+ StringBuilder b = new StringBuilder();
+
+ while (pieces.hasNext()) {
+ String next = pieces.next();
+ if (pieces.hasNext()) {
+ b.append(next.charAt(0));
+ }
+ }
+
+ // Note that a top level class in the root package will be prefixed "$_".
+ return b.length() > 0 ? b.toString() : "$";
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/SwitchingProviders.java b/java/dagger/internal/codegen/SwitchingProviders.java
new file mode 100644
index 0000000..29633d9
--- /dev/null
+++ b/java/dagger/internal/codegen/SwitchingProviders.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Keeps track of all provider expression requests for a component.
+ *
+ * <p>The provider expression request will be satisfied by a single generated {@code Provider} inner
+ * class that can provide instances for all types by switching on an id.
+ */
+// TODO(ronshapiro): either merge this with InnerSwitchingProviders, or repurpose this for
+// SwitchingProducers
+abstract class SwitchingProviders {
+ /**
+ * Defines the {@linkplain Expression expressions} for a switch case in a {@code SwitchProvider}
+ * for a particular binding.
+ */
+ // TODO(user): Consider handling SwitchingProviders with dependency arguments in this class,
+ // then we wouldn't need the getProviderExpression method.
+ // TODO(user): Consider making this an abstract class with equals/hashCode defined by the key
+ // and then using this class directly in Map types instead of Key.
+ interface SwitchCase {
+ /** Returns the {@link Key} for this switch case. */
+ Key key();
+
+ /** Returns the {@link Expression} that returns the provided instance for this case. */
+ Expression getReturnExpression(ClassName switchingProviderClass);
+
+ /**
+ * Returns the {@link Expression} that returns the {@code SwitchProvider} instance for this
+ * case.
+ */
+ Expression getProviderExpression(ClassName switchingProviderClass, int switchId);
+ }
+
+ /**
+ * Each switch size is fixed at 100 cases each and put in its own method. This is to limit the
+ * size of the methods so that we don't reach the "huge" method size limit for Android that will
+ * prevent it from being AOT compiled in some versions of Android (b/77652521). This generally
+ * starts to happen around 1500 cases, but we are choosing 100 to be safe.
+ */
+ // TODO(user): Include a proguard_spec in the Dagger library to prevent inlining these methods?
+ // TODO(ronshapiro): Consider making this configurable via a flag.
+ private static final int MAX_CASES_PER_SWITCH = 100;
+
+ private static final long MAX_CASES_PER_CLASS = MAX_CASES_PER_SWITCH * MAX_CASES_PER_SWITCH;
+ private static final TypeVariableName T = TypeVariableName.get("T");
+
+ /**
+ * Maps a {@link Key} to an instance of a {@link SwitchingProviderBuilder}. Each group of {@code
+ * MAX_CASES_PER_CLASS} keys will share the same instance.
+ */
+ private final Map<Key, SwitchingProviderBuilder> switchingProviderBuilders =
+ new LinkedHashMap<>();
+
+ private final ComponentImplementation componentImplementation;
+ private final ClassName owningComponent;
+ private final DaggerTypes types;
+ private final UniqueNameSet switchingProviderNames = new UniqueNameSet();
+
+ SwitchingProviders(ComponentImplementation componentImplementation, DaggerTypes types) {
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.types = checkNotNull(types);
+ this.owningComponent = checkNotNull(componentImplementation).name();
+ }
+
+ /** Returns the {@link TypeSpec} for a {@code SwitchingProvider} based on the given builder. */
+ protected abstract TypeSpec createSwitchingProviderType(TypeSpec.Builder builder);
+
+ /**
+ * Returns the {@link Expression} that returns the {@code SwitchProvider} instance for the case.
+ */
+ protected final Expression getProviderExpression(SwitchCase switchCase) {
+ return switchingProviderBuilders
+ .computeIfAbsent(switchCase.key(), key -> getSwitchingProviderBuilder())
+ .getProviderExpression(switchCase);
+ }
+
+ private SwitchingProviderBuilder getSwitchingProviderBuilder() {
+ if (switchingProviderBuilders.size() % MAX_CASES_PER_CLASS == 0) {
+ String name = switchingProviderNames.getUniqueName("SwitchingProvider");
+ SwitchingProviderBuilder switchingProviderBuilder =
+ new SwitchingProviderBuilder(owningComponent.nestedClass(name));
+ componentImplementation.addSwitchingProvider(switchingProviderBuilder::build);
+ return switchingProviderBuilder;
+ }
+ return getLast(switchingProviderBuilders.values());
+ }
+
+ // TODO(user): Consider just merging this class with SwitchingProviders.
+ private final class SwitchingProviderBuilder {
+ // Keep the switch cases ordered by switch id. The switch Ids are assigned in pre-order
+ // traversal, but the switch cases are assigned in post-order traversal of the binding graph.
+ private final Map<Integer, CodeBlock> switchCases = new TreeMap<>();
+ private final Map<Key, Integer> switchIds = new HashMap<>();
+ private final ClassName switchingProviderType;
+
+ SwitchingProviderBuilder(ClassName switchingProviderType) {
+ this.switchingProviderType = checkNotNull(switchingProviderType);
+ }
+
+ Expression getProviderExpression(SwitchCase switchCase) {
+ Key key = switchCase.key();
+ if (!switchIds.containsKey(key)) {
+ int switchId = switchIds.size();
+ switchIds.put(key, switchId);
+ switchCases.put(switchId, createSwitchCaseCodeBlock(switchCase));
+ }
+ return switchCase.getProviderExpression(switchingProviderType, switchIds.get(key));
+ }
+
+ private CodeBlock createSwitchCaseCodeBlock(SwitchCase switchCase) {
+ CodeBlock instanceCodeBlock =
+ switchCase.getReturnExpression(switchingProviderType).box(types).codeBlock();
+
+ return CodeBlock.builder()
+ // TODO(user): Is there something else more useful than the key?
+ .add("case $L: // $L \n", switchIds.get(switchCase.key()), switchCase.key())
+ .addStatement("return ($T) $L", T, instanceCodeBlock)
+ .build();
+ }
+
+ private TypeSpec build() {
+ return createSwitchingProviderType(
+ classBuilder(switchingProviderType)
+ .addTypeVariable(T)
+ .addSuperinterface(providerOf(T))
+ .addMethods(getMethods()));
+ }
+
+ private ImmutableList<MethodSpec> getMethods() {
+ ImmutableList<CodeBlock> switchCodeBlockPartitions = switchCodeBlockPartitions();
+ if (switchCodeBlockPartitions.size() == 1) {
+ // There are less than MAX_CASES_PER_SWITCH cases, so no need for extra get methods.
+ return ImmutableList.of(
+ methodBuilder("get")
+ .addModifiers(PUBLIC)
+ .addAnnotation(suppressWarnings(UNCHECKED))
+ .addAnnotation(Override.class)
+ .returns(T)
+ .addCode(getOnlyElement(switchCodeBlockPartitions))
+ .build());
+ }
+
+ // This is the main public "get" method that will route to private getter methods.
+ MethodSpec.Builder routerMethod =
+ methodBuilder("get")
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .returns(T)
+ .beginControlFlow("switch (id / $L)", MAX_CASES_PER_SWITCH);
+
+ ImmutableList.Builder<MethodSpec> getMethods = ImmutableList.builder();
+ for (int i = 0; i < switchCodeBlockPartitions.size(); i++) {
+ MethodSpec method =
+ methodBuilder("get" + i)
+ .addModifiers(PRIVATE)
+ .addAnnotation(suppressWarnings(UNCHECKED))
+ .returns(T)
+ .addCode(switchCodeBlockPartitions.get(i))
+ .build();
+ getMethods.add(method);
+ routerMethod.addStatement("case $L: return $N()", i, method);
+ }
+
+ routerMethod.addStatement("default: throw new $T(id)", AssertionError.class).endControlFlow();
+
+ return getMethods.add(routerMethod.build()).build();
+ }
+
+ private ImmutableList<CodeBlock> switchCodeBlockPartitions() {
+ return Lists.partition(ImmutableList.copyOf(switchCases.values()), MAX_CASES_PER_SWITCH)
+ .stream()
+ .map(
+ partitionCases ->
+ CodeBlock.builder()
+ .beginControlFlow("switch (id)")
+ .add(CodeBlocks.concat(partitionCases))
+ .addStatement("default: throw new $T(id)", AssertionError.class)
+ .endControlFlow()
+ .build())
+ .collect(toImmutableList());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/SystemComponentsModule.java b/java/dagger/internal/codegen/SystemComponentsModule.java
new file mode 100644
index 0000000..3f59b24
--- /dev/null
+++ b/java/dagger/internal/codegen/SystemComponentsModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.base.Ticker;
+import dagger.Module;
+import dagger.Provides;
+
+/** Module to provide system-level dependencies (such as time-related objects). */
+@Module
+interface SystemComponentsModule {
+
+ @Provides
+ static Ticker ticker() {
+ return Ticker.systemTicker();
+ }
+}
diff --git a/java/dagger/internal/codegen/TopLevel.java b/java/dagger/internal/codegen/TopLevel.java
new file mode 100644
index 0000000..4f456f2
--- /dev/null
+++ b/java/dagger/internal/codegen/TopLevel.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A {@link Qualifier} for bindings that are associated with the top level component implementation.
+ */
+@Retention(RUNTIME)
+@Qualifier
+@interface TopLevel {}
diff --git a/java/dagger/internal/codegen/TopLevelImplementationComponent.java b/java/dagger/internal/codegen/TopLevelImplementationComponent.java
new file mode 100644
index 0000000..306c05d
--- /dev/null
+++ b/java/dagger/internal/codegen/TopLevelImplementationComponent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Subcomponent;
+
+/**
+ * A shared subcomponent for a top-level {@link ComponentImplementation} and any nested child
+ * implementations.
+ */
+@PerGeneratedFile
+@Subcomponent
+interface TopLevelImplementationComponent {
+ CurrentImplementationSubcomponent.Builder currentImplementationSubcomponentBuilder();
+
+ @Subcomponent.Builder
+ interface Builder {
+ @BindsInstance
+ Builder topLevelComponent(@TopLevel ComponentImplementation topLevelImplementation);
+ TopLevelImplementationComponent build();
+ }
+
+ @Module(subcomponents = TopLevelImplementationComponent.class)
+ interface InstallationModule {}
+}
diff --git a/java/dagger/internal/codegen/TypeCheckingProcessingStep.java b/java/dagger/internal/codegen/TypeCheckingProcessingStep.java
new file mode 100644
index 0000000..00769b2
--- /dev/null
+++ b/java/dagger/internal/codegen/TypeCheckingProcessingStep.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import java.lang.annotation.Annotation;
+import java.util.function.Function;
+import javax.lang.model.element.Element;
+
+/**
+ * A {@link ProcessingStep} that processes one element at a time and defers any for which {@link
+ * TypeNotPresentException} is thrown.
+ */
+// TODO(dpb): Contribute to auto-common.
+abstract class TypeCheckingProcessingStep<E extends Element> implements ProcessingStep {
+ private final Function<Element, E> downcaster;
+
+ TypeCheckingProcessingStep(Function<Element, E> downcaster) {
+ this.downcaster = checkNotNull(downcaster);
+ }
+
+ @Override
+ public ImmutableSet<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
+ ImmutableSetMultimap.copyOf(elementsByAnnotation)
+ .inverse()
+ .asMap()
+ .forEach(
+ (element, annotations) -> {
+ try {
+ process(downcaster.apply(element), ImmutableSet.copyOf(annotations));
+ } catch (TypeNotPresentException e) {
+ deferredElements.add(element);
+ }
+ });
+ return deferredElements.build();
+ }
+
+ /**
+ * Processes one element. If this method throws {@link TypeNotPresentException}, the element will
+ * be deferred until the next round of processing.
+ *
+ * @param annotations the subset of {@link ProcessingStep#annotations()} that annotate {@code
+ * element}
+ */
+ protected abstract void process(E element, ImmutableSet<Class<? extends Annotation>> annotations);
+}
diff --git a/java/dagger/internal/codegen/TypeProtoConverter.java b/java/dagger/internal/codegen/TypeProtoConverter.java
new file mode 100644
index 0000000..c703bd8
--- /dev/null
+++ b/java/dagger/internal/codegen/TypeProtoConverter.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.TypeProto;
+import dagger.internal.codegen.serialization.TypeProto.PrimitiveKind;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.WildcardType;
+
+/** Converts {@link TypeMirror}s to {@link TypeProto}s and vice-versa. */
+final class TypeProtoConverter {
+ // TODO(ronshapiro): if DaggerTypes and DaggerElements become public, move this file to
+ // dagger.internal.codegen.serialization
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ @Inject
+ TypeProtoConverter(DaggerTypes types, DaggerElements elements) {
+ this.types = types;
+ this.elements = elements;
+ }
+
+ /** Translates a {@link TypeMirror} to a proto representation. */
+ static TypeProto toProto(TypeMirror type) {
+ TypeProto.Builder builder = TypeProto.newBuilder();
+ int arrayDimensions = 0;
+ while (type.getKind().equals(TypeKind.ARRAY)) {
+ type = MoreTypes.asArray(type).getComponentType();
+ arrayDimensions++;
+ }
+ builder.setArrayDimensions(arrayDimensions);
+ if (type.getKind().isPrimitive()) {
+ builder.setPrimitiveKind(PrimitiveKind.valueOf(type.getKind().name()));
+ } else if (type.getKind().equals(TypeKind.WILDCARD)) {
+ WildcardType wildcardType = MoreTypes.asWildcard(type);
+ TypeProto.Wildcard.Builder wildcardBuilder = TypeProto.Wildcard.newBuilder();
+ if (wildcardType.getExtendsBound() != null) {
+ wildcardBuilder.setExtendsBound(toProto(wildcardType.getExtendsBound()));
+ } else if (wildcardType.getSuperBound() != null) {
+ wildcardBuilder.setSuperBound(toProto(wildcardType.getSuperBound()));
+ }
+ builder.setWildcard(wildcardBuilder);
+ } else {
+ TypeElement typeElement = MoreTypes.asTypeElement(type);
+ DeclaredType declaredType = MoreTypes.asDeclared(type);
+ TypeMirror enclosingType = declaredType.getEnclosingType();
+ if (enclosingType.getKind().equals(TypeKind.NONE)) {
+ builder.setQualifiedName(typeElement.getQualifiedName().toString());
+ } else {
+ builder
+ .setEnclosingType(toProto(enclosingType))
+ .setSimpleName(typeElement.getSimpleName().toString());
+ }
+ declaredType.getTypeArguments().stream()
+ .map(TypeProtoConverter::toProto)
+ .forEachOrdered(builder::addTypeArguments);
+ }
+ return builder.build();
+ }
+
+ /** Creates an {@link TypeMirror} from its proto representation. */
+ TypeMirror fromProto(TypeProto type) {
+ if (type.hasWildcard()) {
+ return wildcardType(type.getWildcard());
+ }
+
+ TypeMirror[] typeArguments =
+ type.getTypeArgumentsList().stream().map(this::fromProto).toArray(TypeMirror[]::new);
+ TypeMirror typeMirror;
+ if (!type.getPrimitiveKind().equals(PrimitiveKind.UNKNOWN)) {
+ typeMirror = types.getPrimitiveType(TypeKind.valueOf(type.getPrimitiveKind().name()));
+ } else if (type.hasEnclosingType()) {
+ DeclaredType enclosingType = MoreTypes.asDeclared(fromProto(type.getEnclosingType()));
+ TypeElement typeElement =
+ typesIn(enclosingType.asElement().getEnclosedElements()).stream()
+ .filter(inner -> inner.getSimpleName().contentEquals(type.getSimpleName()))
+ .findFirst()
+ .get();
+ typeMirror = types.getDeclaredType(enclosingType, typeElement, typeArguments);
+ } else {
+ typeMirror =
+ types.getDeclaredType(elements.getTypeElement(type.getQualifiedName()), typeArguments);
+ }
+ for (int i = 0; i < type.getArrayDimensions(); i++) {
+ typeMirror = types.getArrayType(typeMirror);
+ }
+ return typeMirror;
+ }
+
+ private TypeMirror wildcardType(TypeProto.Wildcard wildcard) {
+ if (wildcard.hasExtendsBound()) {
+ return types.getWildcardType(fromProto(wildcard.getExtendsBound()), null);
+ } else if (wildcard.hasSuperBound()) {
+ return types.getWildcardType(null, fromProto(wildcard.getSuperBound()));
+ } else {
+ return types.getWildcardType(null, null);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/UniqueNameSet.java b/java/dagger/internal/codegen/UniqueNameSet.java
new file mode 100644
index 0000000..11c48b3
--- /dev/null
+++ b/java/dagger/internal/codegen/UniqueNameSet.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** A collector for names to be used in the same namespace that should not conflict. */
+final class UniqueNameSet {
+ private final Set<String> uniqueNames = new HashSet<>();
+
+ /**
+ * Generates a unique name using {@code base}. If {@code base} has not yet been added, it will be
+ * returned as-is. If your {@code base} is healthy, this will always return {@code base}.
+ */
+ String getUniqueName(CharSequence base) {
+ String name = base.toString();
+ for (int differentiator = 2; !uniqueNames.add(name); differentiator++) {
+ name = base.toString() + differentiator;
+ }
+ return name;
+ }
+
+ /**
+ * Adds {@code name} without any modification to the name set. Has no effect if {@code name} is
+ * already present in the set.
+ */
+ void claim(CharSequence name) {
+ uniqueNames.add(name.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java b/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java
new file mode 100644
index 0000000..2b7b02c
--- /dev/null
+++ b/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Generates classes that create annotation instances for an unwrapped {@link MapKey} annotation
+ * type whose nested value is an annotation. The generated class will have a private empty
+ * constructor and a static method that creates each annotation type that is nested in the top-level
+ * annotation type.
+ *
+ * <p>So for an example {@link MapKey} annotation:
+ *
+ * <pre>
+ * {@literal @MapKey}(unwrapValue = true)
+ * {@literal @interface} Foo {
+ * Bar bar();
+ * }
+ *
+ * {@literal @interface} Bar {
+ * {@literal Class<?> baz();}
+ * }
+ * </pre>
+ *
+ * the generated class will look like:
+ *
+ * <pre>
+ * public final class FooCreator {
+ * private FooCreator() {}
+ *
+ * public static Bar createBar({@literal Class<?> baz}) { … }
+ * }
+ * </pre>
+ */
+final class UnwrappedMapKeyGenerator extends AnnotationCreatorGenerator {
+
+ @Inject
+ UnwrappedMapKeyGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
+ Set<TypeElement> nestedAnnotationElements = super.annotationsToCreate(annotationElement);
+ nestedAnnotationElements.remove(annotationElement);
+ return nestedAnnotationElements;
+ }
+}
diff --git a/java/dagger/internal/codegen/Util.java b/java/dagger/internal/codegen/Util.java
new file mode 100644
index 0000000..1869b7c
--- /dev/null
+++ b/java/dagger/internal/codegen/Util.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import java.util.Map;
+import java.util.function.Function;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Utilities for handling types in annotation processors
+ */
+final class Util {
+ /**
+ * Returns true if and only if a component can instantiate new instances (typically of a module)
+ * rather than requiring that they be passed.
+ */
+ static boolean componentCanMakeNewInstances(TypeElement typeElement) {
+ switch (typeElement.getKind()) {
+ case CLASS:
+ break;
+ case ENUM:
+ case ANNOTATION_TYPE:
+ case INTERFACE:
+ return false;
+ default:
+ throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind());
+ }
+
+ if (typeElement.getModifiers().contains(ABSTRACT)) {
+ return false;
+ }
+
+ if (requiresEnclosingInstance(typeElement)) {
+ return false;
+ }
+
+ for (Element enclosed : typeElement.getEnclosedElements()) {
+ if (enclosed.getKind().equals(CONSTRUCTOR)
+ && ((ExecutableElement) enclosed).getParameters().isEmpty()
+ && !enclosed.getModifiers().contains(PRIVATE)) {
+ return true;
+ }
+ }
+
+ // TODO(gak): still need checks for visibility
+
+ return false;
+ }
+
+ private static boolean requiresEnclosingInstance(TypeElement typeElement) {
+ switch (typeElement.getNestingKind()) {
+ case TOP_LEVEL:
+ return false;
+ case MEMBER:
+ return !typeElement.getModifiers().contains(STATIC);
+ case ANONYMOUS:
+ case LOCAL:
+ return true;
+ }
+ throw new AssertionError(
+ "TypeElement cannot have nesting kind: " + typeElement.getNestingKind());
+ }
+
+ /**
+ * A version of {@link Map#computeIfAbsent(Object, Function)} that allows {@code mappingFunction}
+ * to update {@code map}.
+ */
+ static <K, V> V reentrantComputeIfAbsent(
+ Map<K, V> map, K key, Function<? super K, ? extends V> mappingFunction) {
+ V value = map.get(key);
+ if (value == null) {
+ value = mappingFunction.apply(key);
+ if (value != null) {
+ map.put(key, value);
+ }
+ }
+ return value;
+ }
+
+ private Util() {}
+}
diff --git a/java/dagger/internal/codegen/Validation.java b/java/dagger/internal/codegen/Validation.java
new file mode 100644
index 0000000..f6a4b3f
--- /dev/null
+++ b/java/dagger/internal/codegen/Validation.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier annotation for the {@link dagger.spi.BindingGraphPlugin}s that are used to implement
+ * core Dagger validation.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+@interface Validation {}
diff --git a/java/dagger/internal/codegen/ValidationReport.java b/java/dagger/internal/codegen/ValidationReport.java
new file mode 100644
index 0000000..d7c3252
--- /dev/null
+++ b/java/dagger/internal/codegen/ValidationReport.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
+import static javax.tools.Diagnostic.Kind.ERROR;
+import static javax.tools.Diagnostic.Kind.NOTE;
+import static javax.tools.Diagnostic.Kind.WARNING;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.Traverser;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import javax.tools.Diagnostic.Kind;
+
+/** A collection of issues to report for source code. */
+@AutoValue
+abstract class ValidationReport<T extends Element> {
+
+ /**
+ * The subject of the report. Should be an element within a compilation unit being processed by
+ * this compilation task.
+ */
+ abstract T subject();
+
+ /** The items to report for the {@linkplain #subject() subject}. */
+ abstract ImmutableSet<Item> items();
+
+ /** Returns the {@link #items()} from this report and all transitive subreports. */
+ ImmutableSet<Item> allItems() {
+ return allReports()
+ .stream()
+ .flatMap(report -> report.items().stream())
+ .collect(toImmutableSet());
+ }
+
+ /** Other reports associated with this one. */
+ abstract ImmutableSet<ValidationReport<?>> subreports();
+
+ private static final Traverser<ValidationReport<?>> SUBREPORTS =
+ Traverser.forTree(ValidationReport::subreports);
+
+ /** Returns this report and all transitive subreports. */
+ ImmutableSet<ValidationReport<?>> allReports() {
+ return ImmutableSet.copyOf(SUBREPORTS.depthFirstPreOrder(this));
+ }
+
+ /**
+ * {@code true} if {@link #isClean()} should return {@code false} even if there are no error items
+ * in this report.
+ */
+ abstract boolean markedDirty();
+
+ /**
+ * Returns {@code true} if there are no errors in this report or any subreports and {@link
+ * #markedDirty()} is {@code false}.
+ */
+ boolean isClean() {
+ if (markedDirty()) {
+ return false;
+ }
+ for (Item item : items()) {
+ switch (item.kind()) {
+ case ERROR:
+ return false;
+ default:
+ break;
+ }
+ }
+ for (ValidationReport<?> subreport : subreports()) {
+ if (!subreport.isClean()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Prints all {@linkplain #items() messages} to {@code messager} (and recurs for subreports). If a
+ * message's {@linkplain Item#element() element} is contained within the report's {@linkplain
+ * #subject() subject}, associates the message with the message's element. Otherwise, since
+ * {@link Diagnostic} reporting is expected to be associated with elements that are currently
+ * being compiled, associates the message with the subject itself and prepends a reference to the
+ * item's element.
+ */
+ void printMessagesTo(Messager messager) {
+ for (Item item : items()) {
+ if (isEnclosedIn(subject(), item.element())) {
+ if (item.annotation().isPresent()) {
+ if (item.annotationValue().isPresent()) {
+ messager.printMessage(
+ item.kind(),
+ item.message(),
+ item.element(),
+ item.annotation().get(),
+ item.annotationValue().get());
+ } else {
+ messager.printMessage(
+ item.kind(), item.message(), item.element(), item.annotation().get());
+ }
+ } else {
+ messager.printMessage(item.kind(), item.message(), item.element());
+ }
+ } else {
+ String message = String.format("[%s] %s", elementToString(item.element()), item.message());
+ messager.printMessage(item.kind(), message, subject());
+ }
+ }
+ for (ValidationReport<?> subreport : subreports()) {
+ subreport.printMessagesTo(messager);
+ }
+ }
+
+ private static boolean isEnclosedIn(Element parent, Element child) {
+ Element current = child;
+ while (current != null) {
+ if (current.equals(parent)) {
+ return true;
+ }
+ current = current.getEnclosingElement();
+ }
+ return false;
+ }
+
+ @AutoValue
+ static abstract class Item {
+ abstract String message();
+ abstract Kind kind();
+ abstract Element element();
+ abstract Optional<AnnotationMirror> annotation();
+ abstract Optional<AnnotationValue> annotationValue();
+ }
+
+ static <T extends Element> Builder<T> about(T subject) {
+ return new Builder<>(subject);
+ }
+
+ @CanIgnoreReturnValue
+ static final class Builder<T extends Element> {
+ private final T subject;
+ private final ImmutableSet.Builder<Item> items = ImmutableSet.builder();
+ private final ImmutableSet.Builder<ValidationReport<?>> subreports = ImmutableSet.builder();
+ private boolean markedDirty;
+
+ private Builder(T subject) {
+ this.subject = subject;
+ }
+
+ @CheckReturnValue
+ T getSubject() {
+ return subject;
+ }
+
+ Builder<T> addItems(Iterable<Item> newItems) {
+ items.addAll(newItems);
+ return this;
+ }
+
+ Builder<T> addError(String message) {
+ return addError(message, subject);
+ }
+
+ Builder<T> addError(String message, Element element) {
+ return addItem(message, ERROR, element);
+ }
+
+ Builder<T> addError(String message, Element element, AnnotationMirror annotation) {
+ return addItem(message, ERROR, element, annotation);
+ }
+
+ Builder<T> addError(
+ String message,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, ERROR, element, annotation, annotationValue);
+ }
+
+ Builder<T> addWarning(String message) {
+ return addWarning(message, subject);
+ }
+
+ Builder<T> addWarning(String message, Element element) {
+ return addItem(message, WARNING, element);
+ }
+
+ Builder<T> addWarning(String message, Element element, AnnotationMirror annotation) {
+ return addItem(message, WARNING, element, annotation);
+ }
+
+ Builder<T> addWarning(
+ String message,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, WARNING, element, annotation, annotationValue);
+ }
+
+ Builder<T> addNote(String message) {
+ return addNote(message, subject);
+ }
+
+ Builder<T> addNote(String message, Element element) {
+ return addItem(message, NOTE, element);
+ }
+
+ Builder<T> addNote(String message, Element element, AnnotationMirror annotation) {
+ return addItem(message, NOTE, element, annotation);
+ }
+
+ Builder<T> addNote(
+ String message,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, NOTE, element, annotation, annotationValue);
+ }
+
+ Builder<T> addItem(String message, Kind kind, Element element) {
+ return addItem(message, kind, element, Optional.empty(), Optional.empty());
+ }
+
+ Builder<T> addItem(String message, Kind kind, Element element, AnnotationMirror annotation) {
+ return addItem(message, kind, element, Optional.of(annotation), Optional.empty());
+ }
+
+ Builder<T> addItem(
+ String message,
+ Kind kind,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, kind, element, Optional.of(annotation), Optional.of(annotationValue));
+ }
+
+ private Builder<T> addItem(
+ String message,
+ Kind kind,
+ Element element,
+ Optional<AnnotationMirror> annotation,
+ Optional<AnnotationValue> annotationValue) {
+ items.add(
+ new AutoValue_ValidationReport_Item(message, kind, element, annotation, annotationValue));
+ return this;
+ }
+
+ /**
+ * If called, then {@link #isClean()} will return {@code false} even if there are no error items
+ * in the report.
+ */
+ void markDirty() {
+ this.markedDirty = true;
+ }
+
+ Builder<T> addSubreport(ValidationReport<?> subreport) {
+ subreports.add(subreport);
+ return this;
+ }
+
+ @CheckReturnValue
+ ValidationReport<T> build() {
+ return new AutoValue_ValidationReport<>(
+ subject, items.build(), subreports.build(), markedDirty);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/ValidationType.java b/java/dagger/internal/codegen/ValidationType.java
new file mode 100644
index 0000000..5d19dc1
--- /dev/null
+++ b/java/dagger/internal/codegen/ValidationType.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import java.util.Optional;
+import javax.tools.Diagnostic;
+
+/**
+ * Allows options to control how component process validates things such as scope cycles
+ * or nullability.
+ */
+enum ValidationType {
+ ERROR,
+ WARNING,
+ NONE;
+
+ Optional<Diagnostic.Kind> diagnosticKind() {
+ switch (this) {
+ case ERROR:
+ return Optional.of(Diagnostic.Kind.ERROR);
+ case WARNING:
+ return Optional.of(Diagnostic.Kind.WARNING);
+ default:
+ return Optional.empty();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/bootstrap_compiler_deploy.jar b/java/dagger/internal/codegen/bootstrap_compiler_deploy.jar
new file mode 100644
index 0000000..ad9761d
--- /dev/null
+++ b/java/dagger/internal/codegen/bootstrap_compiler_deploy.jar
Binary files differ
diff --git a/java/dagger/internal/codegen/dagger_statistics.proto b/java/dagger/internal/codegen/dagger_statistics.proto
new file mode 100644
index 0000000..273e472
--- /dev/null
+++ b/java/dagger/internal/codegen/dagger_statistics.proto
@@ -0,0 +1,25 @@
+syntax = "proto2";
+
+package dagger.internal.codegen.proto;
+option java_package = "dagger.internal.codegen.proto";
+
+import "google/protobuf/duration.proto";
+
+message DaggerBuildStatistics {
+ optional google.protobuf.Duration total_processing_time = 1;
+ repeated DaggerRound rounds = 2;
+}
+
+// Duration of each Dagger ProcessingStep for a single annotation processing
+// round.
+message DaggerRound {
+ optional google.protobuf.Duration map_key_step_time = 1;
+ optional google.protobuf.Duration inject_step_time = 2;
+ optional google.protobuf.Duration monitoring_module_step_time = 3;
+ optional google.protobuf.Duration multibinding_annotations_step_time = 4;
+ optional google.protobuf.Duration binds_instance_step_time = 5;
+ optional google.protobuf.Duration module_step_time = 6;
+ optional google.protobuf.Duration component_step_time = 7;
+ optional google.protobuf.Duration component_hjar_step_time = 8;
+ optional google.protobuf.Duration binding_method_step_time = 9;
+}
diff --git a/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
new file mode 100644
index 0000000..cc0d7de
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javapoet;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Ascii;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.squareup.javapoet.AnnotationSpec;
+import java.util.Arrays;
+
+/** Static factories to create {@link AnnotationSpec}s. */
+public final class AnnotationSpecs {
+ /** Values for an {@link SuppressWarnings} annotation. */
+ public enum Suppression {
+ RAWTYPES,
+ UNCHECKED,
+ ;
+
+ @Override
+ public String toString() {
+ return Ascii.toLowerCase(name());
+ }
+ }
+
+ /** Creates an {@link AnnotationSpec} for {@link SuppressWarnings}. */
+ public static AnnotationSpec suppressWarnings(Suppression first, Suppression... rest) {
+ checkNotNull(first);
+ Arrays.stream(rest).forEach(Preconditions::checkNotNull);
+ AnnotationSpec.Builder builder = AnnotationSpec.builder(SuppressWarnings.class);
+ Lists.asList(first, rest).forEach(suppression -> builder.addMember("value", "$S", suppression));
+ return builder.build();
+ }
+
+ private AnnotationSpecs() {}
+}
diff --git a/java/dagger/internal/codegen/javapoet/BUILD b/java/dagger/internal/codegen/javapoet/BUILD
new file mode 100644
index 0000000..f829d49
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/BUILD
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# JavaPoet extensions for use in Dagger
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "javapoet",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen:bootstrap_compiler_plugin"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/producers",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
diff --git a/java/dagger/internal/codegen/javapoet/CodeBlocks.java b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
new file mode 100644
index 0000000..3e9f75d
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javapoet;
+
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
+import static java.util.stream.StreamSupport.stream;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import java.util.stream.Collector;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** Convenience methods for creating {@link CodeBlock}s. */
+public final class CodeBlocks {
+ /**
+ * Joins {@link CodeBlock} instances in a manner suitable for use as method parameters (or
+ * arguments).
+ */
+ public static Collector<CodeBlock, ?, CodeBlock> toParametersCodeBlock() {
+ // TODO(ronshapiro,jakew): consider adding zero-width spaces to help line breaking when the
+ // formatter is off. If not, inline this
+ return CodeBlock.joining(", ");
+ }
+
+ /** Concatenates {@link CodeBlock} instances separated by newlines for readability. */
+ public static Collector<CodeBlock, ?, CodeBlock> toConcatenatedCodeBlock() {
+ return CodeBlock.joining("\n", "", "\n");
+ }
+
+ /** Returns a comma-separated version of {@code codeBlocks} as one unified {@link CodeBlock}. */
+ public static CodeBlock makeParametersCodeBlock(Iterable<CodeBlock> codeBlocks) {
+ return stream(codeBlocks.spliterator(), false).collect(toParametersCodeBlock());
+ }
+
+ /**
+ * Returns a comma-separated {@link CodeBlock} using the name of every parameter in {@code
+ * parameters}.
+ */
+ public static CodeBlock parameterNames(Iterable<ParameterSpec> parameters) {
+ // TODO(ronshapiro): Add DaggerStreams.stream(Iterable)
+ return stream(parameters.spliterator(), false)
+ .map(p -> CodeBlock.of("$N", p))
+ .collect(toParametersCodeBlock());
+ }
+
+ /**
+ * Returns one unified {@link CodeBlock} which joins each item in {@code codeBlocks} with a
+ * newline.
+ */
+ public static CodeBlock concat(Iterable<CodeBlock> codeBlocks) {
+ return stream(codeBlocks.spliterator(), false).collect(toConcatenatedCodeBlock());
+ }
+
+ /** Adds an annotation to a method. */
+ public static void addAnnotation(MethodSpec.Builder method, DeclaredType nullableType) {
+ method.addAnnotation(ClassName.get(MoreTypes.asTypeElement(nullableType)));
+ }
+
+ /**
+ * Returns an anonymous {@link javax.inject.Provider} class with the single {@link
+ * javax.inject.Provider#get()} method that returns the given {@code expression}.
+ */
+ public static CodeBlock anonymousProvider(Expression expression) {
+ // More of a precondition check that the type Provider is parameterized with is a DeclaredType
+ DeclaredType type = MoreTypes.asDeclared(expression.type());
+ return anonymousProvider(
+ TypeName.get(type), CodeBlock.of("return $L;", expression.codeBlock()));
+ }
+
+ /**
+ * Returns an anonymous {@link javax.inject.Provider} class with the single {@link
+ * javax.inject.Provider#get()} method implemented by {@code body}.
+ */
+ public static CodeBlock anonymousProvider(TypeName providedType, CodeBlock body) {
+ return CodeBlock.of(
+ "$L",
+ anonymousClassBuilder("")
+ .superclass(providerOf(providedType))
+ .addMethod(
+ methodBuilder("get")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(providedType)
+ .addCode(body)
+ .build())
+ .build());
+ }
+
+ /** Returns {@code expression} cast to a type. */
+ public static CodeBlock cast(CodeBlock expression, Class<?> castTo) {
+ return CodeBlock.of("($T) $L", castTo, expression);
+ }
+
+ public static CodeBlock type(TypeMirror type) {
+ return CodeBlock.of("$T", type);
+ }
+
+ public static CodeBlock stringLiteral(String toWrap) {
+ return CodeBlock.of("$S", toWrap);
+ }
+
+ /** Returns a javadoc {@literal @link} tag that poins to the given {@link ExecutableElement}. */
+ public static CodeBlock javadocLinkTo(ExecutableElement executableElement) {
+ CodeBlock.Builder builder =
+ CodeBlock.builder()
+ .add(
+ "{@link $T#",
+ rawTypeName(
+ ClassName.get(MoreElements.asType(executableElement.getEnclosingElement()))));
+ switch (executableElement.getKind()) {
+ case METHOD:
+ builder.add("$L", executableElement.getSimpleName());
+ break;
+ case CONSTRUCTOR:
+ builder.add("$L", executableElement.getEnclosingElement().getSimpleName());
+ break;
+ case STATIC_INIT:
+ case INSTANCE_INIT:
+ throw new IllegalArgumentException(
+ "cannot create a javadoc link to an initializer: " + executableElement);
+ default:
+ throw new AssertionError(executableElement.toString());
+ }
+ builder.add("(");
+ builder.add(
+ executableElement.getParameters().stream()
+ .map(parameter -> CodeBlock.of("$T", rawTypeName(TypeName.get(parameter.asType()))))
+ .collect(toParametersCodeBlock()));
+ return builder.add(")}").build();
+ }
+
+ private CodeBlocks() {}
+}
diff --git a/java/dagger/internal/codegen/javapoet/Expression.java b/java/dagger/internal/codegen/javapoet/Expression.java
new file mode 100644
index 0000000..b79c55c
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/Expression.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javapoet;
+
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Encapsulates a {@link CodeBlock} for an <a
+ * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html">expression</a> and the
+ * {@link TypeMirror} that it represents from the perspective of the compiler. Consider the
+ * following example:
+ *
+ * <pre><code>
+ * {@literal @SuppressWarnings("rawtypes")}
+ * private Provider fooImplProvider = DoubleCheck.provider(FooImpl_Factory.create());
+ * </code></pre>
+ *
+ * <p>An {@code Expression} for {@code fooImplProvider.get()} would have a {@link #type()} of {@code
+ * java.lang.Object} and not {@code FooImpl}.
+ */
+public final class Expression {
+ private final TypeMirror type;
+ private final CodeBlock codeBlock;
+
+ private Expression(TypeMirror type, CodeBlock codeBlock) {
+ this.type = type;
+ this.codeBlock = codeBlock;
+ }
+
+ /** Creates a new {@link Expression} with a {@link TypeMirror} and {@link CodeBlock}. */
+ public static Expression create(TypeMirror type, CodeBlock expression) {
+ return new Expression(type, expression);
+ }
+
+ /**
+ * Creates a new {@link Expression} with a {@link TypeMirror}, {@linkplain CodeBlock#of(String,
+ * Object[]) format, and arguments}.
+ */
+ public static Expression create(TypeMirror type, String format, Object... args) {
+ return create(type, CodeBlock.of(format, args));
+ }
+
+ /** Returns a new expression that casts the current expression to {@code newType}. */
+ // TODO(ronshapiro): consider overloads that take a Types and Elements and only cast if necessary,
+ // or just embedding a Types/Elements instance in an Expression.
+ public Expression castTo(TypeMirror newType) {
+ return create(newType, "($T) $L", newType, codeBlock);
+ }
+
+ /**
+ * Returns a new expression that {@link #castTo(TypeMirror)} casts the current expression to its
+ * boxed type if this expression has a primitive type.
+ */
+ public Expression box(DaggerTypes types) {
+ return type.getKind().isPrimitive()
+ ? castTo(types.boxedClass(MoreTypes.asPrimitiveType(type)).asType())
+ : this;
+ }
+
+ /** The {@link TypeMirror type} to which the expression evaluates. */
+ public TypeMirror type() {
+ return type;
+ }
+
+ /** The code of the expression. */
+ public CodeBlock codeBlock() {
+ return codeBlock;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[%s] %s", type, codeBlock);
+ }
+}
diff --git a/java/dagger/internal/codegen/javapoet/TypeNames.java b/java/dagger/internal/codegen/javapoet/TypeNames.java
new file mode 100644
index 0000000..9301dbc
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/TypeNames.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javapoet;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import dagger.Lazy;
+import dagger.MembersInjector;
+import dagger.internal.DoubleCheck;
+import dagger.internal.Factory;
+import dagger.internal.InstanceFactory;
+import dagger.internal.MapFactory;
+import dagger.internal.MapProviderFactory;
+import dagger.internal.MembersInjectors;
+import dagger.internal.ProviderOfLazy;
+import dagger.internal.SetFactory;
+import dagger.internal.SingleCheck;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.internal.AbstractProducer;
+import dagger.producers.internal.DependencyMethodProducer;
+import dagger.producers.internal.MapOfProducedProducer;
+import dagger.producers.internal.MapOfProducerProducer;
+import dagger.producers.internal.MapProducer;
+import dagger.producers.internal.Producers;
+import dagger.producers.internal.SetOfProducedProducer;
+import dagger.producers.internal.SetProducer;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.List;
+import java.util.Set;
+import javax.inject.Provider;
+
+/** Common names and convenience methods for JavaPoet {@link TypeName} usage. */
+public final class TypeNames {
+
+ public static final ClassName ABSTRACT_PRODUCER = ClassName.get(AbstractProducer.class);
+ public static final ClassName DEPENDENCY_METHOD_PRODUCER =
+ ClassName.get(DependencyMethodProducer.class);
+ public static final ClassName DOUBLE_CHECK = ClassName.get(DoubleCheck.class);
+ public static final ClassName FACTORY = ClassName.get(Factory.class);
+ public static final ClassName FUTURES = ClassName.get(Futures.class);
+ public static final ClassName INSTANCE_FACTORY = ClassName.get(InstanceFactory.class);
+ public static final ClassName LAZY = ClassName.get(Lazy.class);
+ public static final ClassName LIST = ClassName.get(List.class);
+ public static final ClassName LISTENABLE_FUTURE = ClassName.get(ListenableFuture.class);
+ public static final ClassName MAP_FACTORY = ClassName.get(MapFactory.class);
+ public static final ClassName MAP_OF_PRODUCED_PRODUCER =
+ ClassName.get(MapOfProducedProducer.class);
+ public static final ClassName MAP_OF_PRODUCER_PRODUCER =
+ ClassName.get(MapOfProducerProducer.class);
+ public static final ClassName MAP_PRODUCER = ClassName.get(MapProducer.class);
+ public static final ClassName MAP_PROVIDER_FACTORY = ClassName.get(MapProviderFactory.class);
+ public static final ClassName MEMBERS_INJECTOR = ClassName.get(MembersInjector.class);
+ public static final ClassName MEMBERS_INJECTORS = ClassName.get(MembersInjectors.class);
+ public static final ClassName PRODUCER_TOKEN = ClassName.get(ProducerToken.class);
+ public static final ClassName PRODUCED = ClassName.get(Produced.class);
+ public static final ClassName PRODUCER = ClassName.get(Producer.class);
+ public static final ClassName PRODUCERS = ClassName.get(Producers.class);
+ public static final ClassName PRODUCTION_COMPONENT_MONITOR_FACTORY =
+ ClassName.get(ProductionComponentMonitor.Factory.class);
+ public static final ClassName PROVIDER = ClassName.get(Provider.class);
+ public static final ClassName PROVIDER_OF_LAZY = ClassName.get(ProviderOfLazy.class);
+ public static final ClassName SET = ClassName.get(Set.class);
+ public static final ClassName SET_FACTORY = ClassName.get(SetFactory.class);
+ public static final ClassName SET_OF_PRODUCED_PRODUCER =
+ ClassName.get(SetOfProducedProducer.class);
+ public static final ClassName SET_PRODUCER = ClassName.get(SetProducer.class);
+ public static final ClassName SINGLE_CHECK = ClassName.get(SingleCheck.class);
+
+ /**
+ * {@link TypeName#VOID} is lowercase-v {@code void} whereas this represents the class, {@link
+ * Void}.
+ */
+ public static final ClassName VOID_CLASS = ClassName.get(Void.class);
+
+ public static ParameterizedTypeName abstractProducerOf(TypeName typeName) {
+ return ParameterizedTypeName.get(ABSTRACT_PRODUCER, typeName);
+ }
+
+ public static ParameterizedTypeName factoryOf(TypeName factoryType) {
+ return ParameterizedTypeName.get(FACTORY, factoryType);
+ }
+
+ public static ParameterizedTypeName lazyOf(TypeName typeName) {
+ return ParameterizedTypeName.get(LAZY, typeName);
+ }
+
+ public static ParameterizedTypeName listOf(TypeName typeName) {
+ return ParameterizedTypeName.get(LIST, typeName);
+ }
+
+ public static ParameterizedTypeName listenableFutureOf(TypeName typeName) {
+ return ParameterizedTypeName.get(LISTENABLE_FUTURE, typeName);
+ }
+
+ public static ParameterizedTypeName membersInjectorOf(TypeName membersInjectorType) {
+ return ParameterizedTypeName.get(MEMBERS_INJECTOR, membersInjectorType);
+ }
+
+ public static ParameterizedTypeName producedOf(TypeName typeName) {
+ return ParameterizedTypeName.get(PRODUCED, typeName);
+ }
+
+ public static ParameterizedTypeName producerOf(TypeName typeName) {
+ return ParameterizedTypeName.get(PRODUCER, typeName);
+ }
+
+ public static ParameterizedTypeName dependencyMethodProducerOf(TypeName typeName) {
+ return ParameterizedTypeName.get(DEPENDENCY_METHOD_PRODUCER, typeName);
+ }
+
+ public static ParameterizedTypeName providerOf(TypeName typeName) {
+ return ParameterizedTypeName.get(PROVIDER, typeName);
+ }
+
+ public static ParameterizedTypeName setOf(TypeName elementType) {
+ return ParameterizedTypeName.get(SET, elementType);
+ }
+
+ /**
+ * Returns the {@link TypeName} for the raw type of the given type name. If the argument isn't a
+ * parameterized type, it returns the argument unchanged.
+ */
+ public static TypeName rawTypeName(TypeName typeName) {
+ return (typeName instanceof ParameterizedTypeName)
+ ? ((ParameterizedTypeName) typeName).rawType
+ : typeName;
+ }
+
+ private TypeNames() {}
+}
diff --git a/java/dagger/internal/codegen/javapoet/TypeSpecs.java b/java/dagger/internal/codegen/javapoet/TypeSpecs.java
new file mode 100644
index 0000000..8ec8747
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/TypeSpecs.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javapoet;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeSpec;
+import javax.lang.model.element.TypeElement;
+
+/** Convenience methods for use with JavaPoet's {@link TypeSpec}. */
+public final class TypeSpecs {
+
+ /**
+ * If {@code supertype} is a class, adds it as a superclass for {@code typeBuilder}; if it is an
+ * interface, adds it as a superinterface.
+ *
+ * @return {@code typeBuilder}
+ */
+ @CanIgnoreReturnValue
+ public static TypeSpec.Builder addSupertype(TypeSpec.Builder typeBuilder, TypeElement supertype) {
+ switch (supertype.getKind()) {
+ case CLASS:
+ return typeBuilder.superclass(ClassName.get(supertype));
+ case INTERFACE:
+ return typeBuilder.addSuperinterface(ClassName.get(supertype));
+ default:
+ throw new AssertionError(supertype + " is neither a class nor an interface.");
+ }
+ }
+
+ private TypeSpecs() {}
+}
diff --git a/java/dagger/internal/codegen/kythe_plugin_deploy.jar b/java/dagger/internal/codegen/kythe_plugin_deploy.jar
new file mode 100644
index 0000000..3a1eed2
--- /dev/null
+++ b/java/dagger/internal/codegen/kythe_plugin_deploy.jar
Binary files differ
diff --git a/java/dagger/internal/codegen/langmodel/Accessibility.java b/java/dagger/internal/codegen/langmodel/Accessibility.java
new file mode 100644
index 0000000..62c1fbd
--- /dev/null
+++ b/java/dagger/internal/codegen/langmodel/Accessibility.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.langmodel;
+
+import static com.google.auto.common.MoreElements.getPackage;
+import static com.google.common.base.Preconditions.checkArgument;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.auto.common.MoreElements;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.SimpleElementVisitor6;
+import javax.lang.model.util.SimpleTypeVisitor6;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Utility methods for determining whether a {@linkplain TypeMirror type} or an {@linkplain Element
+ * element} is accessible given the rules outlined in <a
+ * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6">section 6.6 of the
+ * Java Language Specification</a>.
+ *
+ * <p>This class only provides an approximation for accessibility. It does not always yield the same
+ * result as the compiler, but will always err on the side of declaring something inaccessible. This
+ * ensures that using this class will never result in generating code that will not compile.
+ *
+ * <p>Whenever compiler independence is not a requirement, the compiler-specific implementation of
+ * this functionality should be preferred. For example, {@link
+ * com.sun.source.util.Trees#isAccessible(com.sun.source.tree.Scope, TypeElement)} would be
+ * preferable for {@code javac}.
+ */
+public final class Accessibility {
+ /** Returns true if the given type can be referenced from any package. */
+ public static boolean isTypePubliclyAccessible(TypeMirror type) {
+ return type.accept(new TypeAccessibilityVisitor(), null);
+ }
+
+ /** Returns true if the given type can be referenced from code in the given package. */
+ public static boolean isTypeAccessibleFrom(TypeMirror type, String packageName) {
+ return type.accept(new TypeAccessibilityVisitor(packageName), null);
+ }
+
+ private static boolean isTypeAccessibleFrom(TypeMirror type, Optional<String> packageName) {
+ return type.accept(new TypeAccessibilityVisitor(packageName), null);
+ }
+
+ private static final class TypeAccessibilityVisitor extends SimpleTypeVisitor6<Boolean, Void> {
+ final Optional<String> packageName;
+
+ TypeAccessibilityVisitor() {
+ this(Optional.empty());
+ }
+
+ TypeAccessibilityVisitor(String packageName) {
+ this(Optional.of(packageName));
+ }
+
+ TypeAccessibilityVisitor(Optional<String> packageName) {
+ this.packageName = packageName;
+ }
+
+ boolean isAccessible(TypeMirror type) {
+ return type.accept(this, null);
+ }
+
+ @Override
+ public Boolean visitNoType(NoType type, Void p) {
+ return true;
+ }
+
+ @Override
+ public Boolean visitDeclared(DeclaredType type, Void p) {
+ if (!isAccessible(type.getEnclosingType())) {
+ // TODO(gak): investigate this check. see comment in Binding
+ return false;
+ }
+ if (!isElementAccessibleFrom(type.asElement(), packageName)) {
+ return false;
+ }
+ for (TypeMirror typeArgument : type.getTypeArguments()) {
+ if (!isAccessible(typeArgument)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Boolean visitArray(ArrayType type, Void p) {
+ return type.getComponentType().accept(this, null);
+ }
+
+ @Override
+ public Boolean visitPrimitive(PrimitiveType type, Void p) {
+ return true;
+ }
+
+ @Override
+ public Boolean visitNull(NullType type, Void p) {
+ return true;
+ }
+
+ @Override
+ public Boolean visitTypeVariable(TypeVariable type, Void p) {
+ // a _reference_ to a type variable is always accessible
+ return true;
+ }
+
+ @Override
+ public Boolean visitWildcard(WildcardType type, Void p) {
+ if (type.getExtendsBound() != null && !isAccessible(type.getExtendsBound())) {
+ return false;
+ }
+ if (type.getSuperBound() != null && !isAccessible(type.getSuperBound())) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected Boolean defaultAction(TypeMirror type, Void p) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s of kind %s should not be checked for accessibility", type, type.getKind()));
+ }
+ }
+
+ /** Returns true if the given element can be referenced from any package. */
+ public static boolean isElementPubliclyAccessible(Element element) {
+ return element.accept(new ElementAccessibilityVisitor(), null);
+ }
+
+ /** Returns true if the given element can be referenced from code in the given package. */
+ // TODO(gak): account for protected
+ public static boolean isElementAccessibleFrom(Element element, String packageName) {
+ return element.accept(new ElementAccessibilityVisitor(packageName), null);
+ }
+
+ private static boolean isElementAccessibleFrom(Element element, Optional<String> packageName) {
+ return element.accept(new ElementAccessibilityVisitor(packageName), null);
+ }
+
+ /** Returns true if the given element can be referenced from other code in its own package. */
+ public static boolean isElementAccessibleFromOwnPackage(Element element) {
+ return isElementAccessibleFrom(
+ element, MoreElements.getPackage(element).getQualifiedName().toString());
+ }
+
+ private static final class ElementAccessibilityVisitor
+ extends SimpleElementVisitor6<Boolean, Void> {
+ final Optional<String> packageName;
+
+ ElementAccessibilityVisitor() {
+ this(Optional.empty());
+ }
+
+ ElementAccessibilityVisitor(String packageName) {
+ this(Optional.of(packageName));
+ }
+
+ ElementAccessibilityVisitor(Optional<String> packageName) {
+ this.packageName = packageName;
+ }
+
+ @Override
+ public Boolean visitPackage(PackageElement element, Void p) {
+ return true;
+ }
+
+ @Override
+ public Boolean visitType(TypeElement element, Void p) {
+ switch (element.getNestingKind()) {
+ case MEMBER:
+ return accessibleMember(element);
+ case TOP_LEVEL:
+ return accessibleModifiers(element);
+ case ANONYMOUS:
+ case LOCAL:
+ return false;
+ }
+ throw new AssertionError();
+ }
+
+ boolean accessibleMember(Element element) {
+ if (!element.getEnclosingElement().accept(this, null)) {
+ return false;
+ }
+ return accessibleModifiers(element);
+ }
+
+ boolean accessibleModifiers(Element element) {
+ if (element.getModifiers().contains(PUBLIC)) {
+ return true;
+ } else if (element.getModifiers().contains(PRIVATE)) {
+ return false;
+ } else if (packageName.isPresent()
+ && getPackage(element).getQualifiedName().contentEquals(packageName.get())) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Boolean visitTypeParameter(TypeParameterElement element, Void p) {
+ throw new IllegalArgumentException(
+ "It does not make sense to check the accessibility of a type parameter");
+ }
+
+ @Override
+ public Boolean visitExecutable(ExecutableElement element, Void p) {
+ return accessibleMember(element);
+ }
+
+ @Override
+ public Boolean visitVariable(VariableElement element, Void p) {
+ ElementKind kind = element.getKind();
+ checkArgument(kind.isField(), "checking a variable that isn't a field: %s", kind);
+ return accessibleMember(element);
+ }
+ }
+
+ private static final TypeVisitor<Boolean, Optional<String>> RAW_TYPE_ACCESSIBILITY_VISITOR =
+ new SimpleTypeVisitor8<Boolean, Optional<String>>() {
+ @Override
+ protected Boolean defaultAction(TypeMirror e, Optional<String> requestingPackage) {
+ return isTypeAccessibleFrom(e, requestingPackage);
+ }
+
+ @Override
+ public Boolean visitDeclared(DeclaredType t, Optional<String> requestingPackage) {
+ return isElementAccessibleFrom(t.asElement(), requestingPackage);
+ }
+ };
+
+ /** Returns true if the raw type of {@code type} is accessible from the given package. */
+ public static boolean isRawTypeAccessible(TypeMirror type, String requestingPackage) {
+ return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.of(requestingPackage));
+ }
+
+ /** Returns true if the raw type of {@code type} is accessible from any package. */
+ public static boolean isRawTypePubliclyAccessible(TypeMirror type) {
+ return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.empty());
+ }
+
+ private Accessibility() {}
+}
diff --git a/java/dagger/internal/codegen/langmodel/BUILD b/java/dagger/internal/codegen/langmodel/BUILD
new file mode 100644
index 0000000..16fa5d8
--- /dev/null
+++ b/java/dagger/internal/codegen/langmodel/BUILD
@@ -0,0 +1,31 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Dagger-specific extensions to the javax.lang.model APIs
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "langmodel",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen:bootstrap_compiler_plugin"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
diff --git a/java/dagger/internal/codegen/langmodel/DaggerElements.java b/java/dagger/internal/codegen/langmodel/DaggerElements.java
new file mode 100644
index 0000000..873ad3d
--- /dev/null
+++ b/java/dagger/internal/codegen/langmodel/DaggerElements.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.langmodel;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreElements.hasModifiers;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.asList;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toSet;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.graph.Traverser;
+import com.squareup.javapoet.ClassName;
+import dagger.Reusable;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.SimpleElementVisitor8;
+import javax.lang.model.util.Types;
+
+/** Extension of {@link Elements} that adds Dagger-specific methods. */
+@Reusable
+public final class DaggerElements implements Elements {
+
+ private final Elements elements;
+ private final Types types;
+
+ public DaggerElements(Elements elements, Types types) {
+ this.elements = checkNotNull(elements);
+ this.types = checkNotNull(types);
+ }
+
+ public DaggerElements(ProcessingEnvironment processingEnv) {
+ this(processingEnv.getElementUtils(), processingEnv.getTypeUtils());
+ }
+
+ /**
+ * Returns {@code true} if {@code encloser} is equal to {@code enclosed} or recursively encloses
+ * it.
+ */
+ public static boolean elementEncloses(TypeElement encloser, Element enclosed) {
+ return Iterables.contains(GET_ENCLOSED_ELEMENTS.breadthFirst(encloser), enclosed);
+ }
+
+ private static final Traverser<Element> GET_ENCLOSED_ELEMENTS =
+ Traverser.forTree(Element::getEnclosedElements);
+
+ public ImmutableSet<ExecutableElement> getUnimplementedMethods(TypeElement type) {
+ return FluentIterable.from(getLocalAndInheritedMethods(type, types, elements))
+ .filter(hasModifiers(ABSTRACT))
+ .toSet();
+ }
+
+ /** Returns the type element for a class. */
+ public TypeElement getTypeElement(Class<?> clazz) {
+ return getTypeElement(clazz.getCanonicalName());
+ }
+
+ @Override
+ public TypeElement getTypeElement(CharSequence name) {
+ return elements.getTypeElement(name);
+ }
+
+ /** Returns the type element for a class name. */
+ public TypeElement getTypeElement(ClassName className) {
+ return getTypeElement(className.withoutAnnotations().toString());
+ }
+
+ /** Returns the argument or the closest enclosing element that is a {@link TypeElement}. */
+ public static TypeElement closestEnclosingTypeElement(Element element) {
+ return element.accept(CLOSEST_ENCLOSING_TYPE_ELEMENT, null);
+ }
+
+ private static final ElementVisitor<TypeElement, Void> CLOSEST_ENCLOSING_TYPE_ELEMENT =
+ new SimpleElementVisitor8<TypeElement, Void>() {
+ @Override
+ protected TypeElement defaultAction(Element element, Void p) {
+ return element.getEnclosingElement().accept(this, null);
+ }
+
+ @Override
+ public TypeElement visitType(TypeElement type, Void p) {
+ return type;
+ }
+ };
+
+ /**
+ * Compares elements according to their declaration order among siblings. Only valid to compare
+ * elements enclosed by the same parent.
+ */
+ public static final Comparator<Element> DECLARATION_ORDER =
+ comparing(element -> siblings(element).indexOf(element));
+
+ // For parameter elements, element.getEnclosingElement().getEnclosedElements() is empty. So
+ // instead look at the parameter list of the enclosing executable.
+ private static List<? extends Element> siblings(Element element) {
+ return element.getKind().equals(ElementKind.PARAMETER)
+ ? asExecutable(element.getEnclosingElement()).getParameters()
+ : element.getEnclosingElement().getEnclosedElements();
+ }
+
+ /**
+ * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
+ * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as any of
+ * that of {@code annotationClasses}.
+ */
+ public static boolean isAnyAnnotationPresent(
+ Element element, Iterable<? extends Class<? extends Annotation>> annotationClasses) {
+ for (Class<? extends Annotation> annotation : annotationClasses) {
+ if (MoreElements.isAnnotationPresent(element, annotation)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SafeVarargs
+ public static boolean isAnyAnnotationPresent(
+ Element element,
+ Class<? extends Annotation> first,
+ Class<? extends Annotation>... otherAnnotations) {
+ return isAnyAnnotationPresent(element, asList(first, otherAnnotations));
+ }
+
+ /**
+ * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
+ * AnnotationMirror#getAnnotationType() annotation type} is equivalent to {@code annotationType}.
+ */
+ public static boolean isAnnotationPresent(Element element, TypeMirror annotationType) {
+ return element.getAnnotationMirrors().stream()
+ .map(AnnotationMirror::getAnnotationType)
+ .anyMatch(candidate -> MoreTypes.equivalence().equivalent(candidate, annotationType));
+ }
+
+ /**
+ * Returns the annotation present on {@code element} whose type is {@code first} or within {@code
+ * rest}, checking each annotation type in order.
+ */
+ @SafeVarargs
+ public static Optional<AnnotationMirror> getAnyAnnotation(
+ Element element, Class<? extends Annotation> first, Class<? extends Annotation>... rest) {
+ return getAnyAnnotation(element, asList(first, rest));
+ }
+
+ /**
+ * Returns the annotation present on {@code element} whose type is in {@code annotations},
+ * checking each annotation type in order.
+ */
+ public static Optional<AnnotationMirror> getAnyAnnotation(
+ Element element, Collection<? extends Class<? extends Annotation>> annotations) {
+ return element.getAnnotationMirrors().stream()
+ .filter(hasAnnotationTypeIn(annotations))
+ .map((AnnotationMirror a) -> a) // Avoid returning Optional<? extends AnnotationMirror>.
+ .findFirst();
+ }
+
+ /** Returns the annotations present on {@code element} of all types. */
+ @SafeVarargs
+ public static ImmutableSet<AnnotationMirror> getAllAnnotations(
+ Element element, Class<? extends Annotation> first, Class<? extends Annotation>... rest) {
+ return ImmutableSet.copyOf(
+ Iterables.filter(
+ element.getAnnotationMirrors(), hasAnnotationTypeIn(asList(first, rest))::test));
+ }
+
+ /**
+ * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on
+ * {@code element}, or {@link Optional#empty()} if no such annotation exists. This method is a
+ * safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
+ * annotation proxies.
+ */
+ public static Optional<AnnotationMirror> getAnnotationMirror(
+ Element element, Class<? extends Annotation> annotationClass) {
+ return Optional.ofNullable(MoreElements.getAnnotationMirror(element, annotationClass).orNull());
+ }
+
+ private static Predicate<AnnotationMirror> hasAnnotationTypeIn(
+ Collection<? extends Class<? extends Annotation>> annotations) {
+ Set<String> annotationClassNames =
+ annotations.stream().map(Class::getCanonicalName).collect(toSet());
+ return annotation ->
+ annotationClassNames.contains(
+ MoreTypes.asTypeElement(annotation.getAnnotationType()).getQualifiedName().toString());
+ }
+
+ public static ImmutableSet<String> suppressedWarnings(Element element) {
+ SuppressWarnings suppressedWarnings = element.getAnnotation(SuppressWarnings.class);
+ if (suppressedWarnings == null) {
+ return ImmutableSet.of();
+ }
+ return ImmutableSet.copyOf(suppressedWarnings.value());
+ }
+
+ /**
+ * Invokes {@link Elements#getTypeElement(CharSequence)}, throwing {@link TypeNotPresentException}
+ * if it is not accessible in the current compilation.
+ */
+ public TypeElement checkTypePresent(String typeName) {
+ TypeElement type = elements.getTypeElement(typeName);
+ if (type == null) {
+ throw new TypeNotPresentException(typeName, null);
+ }
+ return type;
+ }
+
+ @Override
+ public PackageElement getPackageElement(CharSequence name) {
+ return elements.getPackageElement(name);
+ }
+
+ @Override
+ public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults(
+ AnnotationMirror a) {
+ return elements.getElementValuesWithDefaults(a);
+ }
+
+ @Override
+ public String getDocComment(Element e) {
+ return elements.getDocComment(e);
+ }
+
+ @Override
+ public boolean isDeprecated(Element e) {
+ return elements.isDeprecated(e);
+ }
+
+ @Override
+ public Name getBinaryName(TypeElement type) {
+ return elements.getBinaryName(type);
+ }
+
+ @Override
+ public PackageElement getPackageOf(Element type) {
+ return elements.getPackageOf(type);
+ }
+
+ @Override
+ public List<? extends Element> getAllMembers(TypeElement type) {
+ return elements.getAllMembers(type);
+ }
+
+ @Override
+ public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {
+ return elements.getAllAnnotationMirrors(e);
+ }
+
+ @Override
+ public boolean hides(Element hider, Element hidden) {
+ return elements.hides(hider, hidden);
+ }
+
+ @Override
+ public boolean overrides(
+ ExecutableElement overrider, ExecutableElement overridden, TypeElement type) {
+ return elements.overrides(overrider, overridden, type);
+ }
+
+ @Override
+ public String getConstantExpression(Object value) {
+ return elements.getConstantExpression(value);
+ }
+
+ @Override
+ public void printElements(Writer w, Element... elements) {
+ this.elements.printElements(w, elements);
+ }
+
+ @Override
+ public Name getName(CharSequence cs) {
+ return elements.getName(cs);
+ }
+
+ @Override
+ public boolean isFunctionalInterface(TypeElement type) {
+ return elements.isFunctionalInterface(type);
+ }
+}
diff --git a/java/dagger/internal/codegen/langmodel/DaggerTypes.java b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
new file mode 100644
index 0000000..e588fbd
--- /dev/null
+++ b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.langmodel;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.Traverser;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ClassName;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.SimpleTypeVisitor8;
+import javax.lang.model.util.Types;
+
+/** Extension of {@link Types} that adds Dagger-specific methods. */
+public final class DaggerTypes implements Types {
+ private final Types types;
+ private final DaggerElements elements;
+
+ @Inject
+ public DaggerTypes(Types types, DaggerElements elements) {
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ }
+
+ /**
+ * Returns the non-{@link Object} superclass of the type with the proper type parameters. An empty
+ * {@link Optional} is returned if there is no non-{@link Object} superclass.
+ */
+ public Optional<DeclaredType> nonObjectSuperclass(DeclaredType type) {
+ return Optional.ofNullable(MoreTypes.nonObjectSuperclass(types, elements, type).orNull());
+ }
+
+ /**
+ * Returns the {@linkplain #directSupertypes(TypeMirror) supertype}s of a type in breadth-first
+ * order.
+ */
+ public Iterable<TypeMirror> supertypes(TypeMirror type) {
+ return Traverser.<TypeMirror>forGraph(this::directSupertypes).breadthFirst(type);
+ }
+
+ /**
+ * Returns {@code type}'s single type argument.
+ *
+ * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more
+ * than one type arguments.
+ */
+ public TypeMirror unwrapType(TypeMirror type) {
+ TypeMirror unwrapped = unwrapTypeOrDefault(type, null);
+ checkArgument(unwrapped != null, "%s is a raw type", type);
+ return unwrapped;
+ }
+
+ /**
+ * Returns {@code type}'s single type argument, if one exists, or {@link Object} if not.
+ *
+ * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not a declared type or has more than one
+ * type argument.
+ */
+ public TypeMirror unwrapTypeOrObject(TypeMirror type) {
+ return unwrapTypeOrDefault(type, elements.getTypeElement(Object.class).asType());
+ }
+
+ private TypeMirror unwrapTypeOrDefault(TypeMirror type, TypeMirror defaultType) {
+ DeclaredType declaredType = MoreTypes.asDeclared(type);
+ TypeElement typeElement = MoreElements.asType(declaredType.asElement());
+ checkArgument(
+ !typeElement.getTypeParameters().isEmpty(),
+ "%s does not have a type parameter",
+ typeElement.getQualifiedName());
+ return getOnlyElement(declaredType.getTypeArguments(), defaultType);
+ }
+
+ /**
+ * Returns {@code type} wrapped in {@code wrappingClass}.
+ *
+ * <p>For example, if {@code type} is {@code List<Number>} and {@code wrappingClass} is {@code
+ * Set.class}, this will return {@code Set<List<Number>>}.
+ */
+ public DeclaredType wrapType(TypeMirror type, Class<?> wrappingClass) {
+ return types.getDeclaredType(elements.getTypeElement(wrappingClass), type);
+ }
+
+ /**
+ * Returns {@code type}'s single type argument wrapped in {@code wrappingClass}.
+ *
+ * <p>For example, if {@code type} is {@code List<Number>} and {@code wrappingClass} is {@code
+ * Set.class}, this will return {@code Set<Number>}.
+ *
+ * <p>If {@code type} has no type parameters, returns a {@link TypeMirror} for {@code
+ * wrappingClass} as a raw type.
+ *
+ * @throws IllegalArgumentException if {@code} has more than one type argument.
+ */
+ public DeclaredType rewrapType(TypeMirror type, Class<?> wrappingClass) {
+ List<? extends TypeMirror> typeArguments = MoreTypes.asDeclared(type).getTypeArguments();
+ TypeElement wrappingType = elements.getTypeElement(wrappingClass);
+ switch (typeArguments.size()) {
+ case 0:
+ return getDeclaredType(wrappingType);
+ case 1:
+ return getDeclaredType(wrappingType, getOnlyElement(typeArguments));
+ default:
+ throw new IllegalArgumentException(type + " has more than 1 type argument");
+ }
+ }
+
+ /**
+ * Returns a publicly accessible type based on {@code type}:
+ *
+ * <ul>
+ * <li>If {@code type} is publicly accessible, returns it.
+ * <li>If not, but {@code type}'s raw type is publicly accessible, returns the raw type.
+ * <li>Otherwise returns {@link Object}.
+ * </ul>
+ */
+ public TypeMirror publiclyAccessibleType(TypeMirror type) {
+ return accessibleType(
+ type, Accessibility::isTypePubliclyAccessible, Accessibility::isRawTypePubliclyAccessible);
+ }
+
+ /**
+ * Returns an accessible type in {@code requestingClass}'s package based on {@code type}:
+ *
+ * <ul>
+ * <li>If {@code type} is accessible from the package, returns it.
+ * <li>If not, but {@code type}'s raw type is accessible from the package, returns the raw type.
+ * <li>Otherwise returns {@link Object}.
+ * </ul>
+ */
+ public TypeMirror accessibleType(TypeMirror type, ClassName requestingClass) {
+ return accessibleType(
+ type,
+ t -> Accessibility.isTypeAccessibleFrom(t, requestingClass.packageName()),
+ t -> Accessibility.isRawTypeAccessible(t, requestingClass.packageName()));
+ }
+
+ private TypeMirror accessibleType(
+ TypeMirror type,
+ Predicate<TypeMirror> accessibilityPredicate,
+ Predicate<TypeMirror> rawTypeAccessibilityPredicate) {
+ if (accessibilityPredicate.test(type)) {
+ return type;
+ } else if (type.getKind().equals(TypeKind.DECLARED)
+ && rawTypeAccessibilityPredicate.test(type)) {
+ return getDeclaredType(MoreTypes.asTypeElement(type));
+ } else {
+ return elements.getTypeElement(Object.class).asType();
+ }
+ }
+
+ /**
+ * Throws {@link TypeNotPresentException} if {@code type} is an {@link
+ * javax.lang.model.type.ErrorType}.
+ */
+ public static void checkTypePresent(TypeMirror type) {
+ type.accept(
+ // TODO(ronshapiro): Extract a base class that visits all components of a complex type
+ // and put it in auto.common
+ new SimpleTypeVisitor8<Void, Void>() {
+ @Override
+ public Void visitArray(ArrayType arrayType, Void p) {
+ return arrayType.getComponentType().accept(this, p);
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType declaredType, Void p) {
+ declaredType.getTypeArguments().forEach(t -> t.accept(this, p));
+ return null;
+ }
+
+ @Override
+ public Void visitError(ErrorType errorType, Void p) {
+ throw new TypeNotPresentException(type.toString(), null);
+ }
+ },
+ null);
+ }
+
+ private static final ImmutableSet<Class<?>> FUTURE_TYPES =
+ ImmutableSet.of(ListenableFuture.class, FluentFuture.class);
+
+ public static boolean isFutureType(TypeMirror type) {
+ return FUTURE_TYPES.stream().anyMatch(t -> MoreTypes.isTypeOf(t, type));
+ }
+
+ public static boolean hasTypeVariable(TypeMirror type) {
+ return type.accept(
+ new SimpleTypeVisitor8<Boolean, Void>() {
+ @Override
+ public Boolean visitArray(ArrayType arrayType, Void p) {
+ return arrayType.getComponentType().accept(this, p);
+ }
+
+ @Override
+ public Boolean visitDeclared(DeclaredType declaredType, Void p) {
+ return declaredType.getTypeArguments().stream().anyMatch(type -> type.accept(this, p));
+ }
+
+ @Override
+ public Boolean visitTypeVariable(TypeVariable t, Void aVoid) {
+ return true;
+ }
+
+ @Override
+ protected Boolean defaultAction(TypeMirror e, Void aVoid) {
+ return false;
+ }
+ },
+ null);
+ }
+
+ /**
+ * Resolves the type of the given executable element as a member of the given type. This may
+ * resolve type variables to concrete types, etc.
+ */
+ public ExecutableType resolveExecutableType(ExecutableElement element, TypeMirror containerType) {
+ return MoreTypes.asExecutable(asMemberOf(MoreTypes.asDeclared(containerType), element));
+ }
+
+ // Implementation of Types methods, delegating to types.
+
+ @Override
+ public Element asElement(TypeMirror t) {
+ return types.asElement(t);
+ }
+
+ @Override
+ public boolean isSameType(TypeMirror t1, TypeMirror t2) {
+ return types.isSameType(t1, t2);
+ }
+
+ @Override
+ public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
+ return types.isSubtype(t1, t2);
+ }
+
+ @Override
+ public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
+ return types.isAssignable(t1, t2);
+ }
+
+ @Override
+ public boolean contains(TypeMirror t1, TypeMirror t2) {
+ return types.contains(t1, t2);
+ }
+
+ @Override
+ public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
+ return types.isSubsignature(m1, m2);
+ }
+
+ @Override
+ public List<? extends TypeMirror> directSupertypes(TypeMirror t) {
+ return types.directSupertypes(t);
+ }
+
+ @Override
+ public TypeMirror erasure(TypeMirror t) {
+ return types.erasure(t);
+ }
+
+ @Override
+ public TypeElement boxedClass(PrimitiveType p) {
+ return types.boxedClass(p);
+ }
+
+ @Override
+ public PrimitiveType unboxedType(TypeMirror t) {
+ return types.unboxedType(t);
+ }
+
+ @Override
+ public TypeMirror capture(TypeMirror t) {
+ return types.capture(t);
+ }
+
+ @Override
+ public PrimitiveType getPrimitiveType(TypeKind kind) {
+ return types.getPrimitiveType(kind);
+ }
+
+ @Override
+ public NullType getNullType() {
+ return types.getNullType();
+ }
+
+ @Override
+ public NoType getNoType(TypeKind kind) {
+ return types.getNoType(kind);
+ }
+
+ @Override
+ public ArrayType getArrayType(TypeMirror componentType) {
+ return types.getArrayType(componentType);
+ }
+
+ @Override
+ public WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound) {
+ return types.getWildcardType(extendsBound, superBound);
+ }
+
+ @Override
+ public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
+ return types.getDeclaredType(typeElem, typeArgs);
+ }
+
+ @Override
+ public DeclaredType getDeclaredType(
+ DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs) {
+ return types.getDeclaredType(containing, typeElem, typeArgs);
+ }
+
+ @Override
+ public TypeMirror asMemberOf(DeclaredType containing, Element element) {
+ return types.asMemberOf(containing, element);
+ }
+}
diff --git a/java/dagger/internal/codegen/package-info.java b/java/dagger/internal/codegen/package-info.java
new file mode 100644
index 0000000..c8cc404
--- /dev/null
+++ b/java/dagger/internal/codegen/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@CheckReturnValue
+package dagger.internal.codegen;
+
+import com.google.errorprone.annotations.CheckReturnValue;
diff --git a/java/dagger/internal/codegen/serialization/BUILD b/java/dagger/internal/codegen/serialization/BUILD
new file mode 100644
index 0000000..2bc02b4
--- /dev/null
+++ b/java/dagger/internal/codegen/serialization/BUILD
@@ -0,0 +1,41 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Serialized forms of types used in the Dagger processor.
+
+package(default_visibility = ["//:src"])
+
+proto_library(
+ name = "serialization_proto",
+ srcs = ["serialization.proto"],
+ visibility = ["//visibility:private"],
+)
+
+java_proto_library(
+ name = "serialization_java_proto",
+ visibility = ["//visibility:private"],
+ deps = [":serialization_proto"],
+)
+
+java_library(
+ name = "serialization",
+ srcs = glob(["*.java"]),
+ exports = [":serialization_java_proto"],
+ deps = [
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/protobuf",
+ ],
+)
diff --git a/java/dagger/internal/codegen/serialization/ProtoSerialization.java b/java/dagger/internal/codegen/serialization/ProtoSerialization.java
new file mode 100644
index 0000000..1449e9d
--- /dev/null
+++ b/java/dagger/internal/codegen/serialization/ProtoSerialization.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.serialization;
+
+import static com.google.common.io.BaseEncoding.base64;
+
+import com.google.common.io.BaseEncoding;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
+import com.squareup.javapoet.CodeBlock;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/**
+ * Serializes and deserializes {@link Message}s using {@link BaseEncoding#base64()} for use in
+ * annotation values.
+ */
+public final class ProtoSerialization {
+ /** Returns a {@link CodeBlock} of {@code message} serialized as a String. */
+ public static CodeBlock toAnnotationValue(Message message) {
+ return CodeBlock.of("$S", base64().encode(message.toByteArray()));
+ }
+
+ /**
+ * Returns a {@link Message T} from the deserialized the String {@code value}.
+ *
+ * @throws IllegalArgumentException if {@code value} represents an {@link AnnotationValue} who's
+ * type is not {@link String}
+ */
+ public static <T extends Message> T fromAnnotationValue(
+ AnnotationValue value, T defaultInstance) {
+ byte[] bytes = base64().decode(value.accept(STRING_VALUE, null));
+ Message message;
+ try {
+ message = defaultInstance.getParserForType().parseFrom(bytes);
+ } catch (InvalidProtocolBufferException e) {
+ throw new InconsistentSerializedProtoException(e);
+ }
+ @SuppressWarnings("unchecked") // guaranteed by proto API
+ T t = (T) message;
+ return t;
+ }
+
+ private static final AnnotationValueVisitor<String, Void> STRING_VALUE =
+ new SimpleAnnotationValueVisitor8<String, Void>() {
+ @Override
+ public String visitString(String s, Void ignored) {
+ return s;
+ }
+
+ @Override
+ protected String defaultAction(Object o, Void ignored) {
+ throw new IllegalArgumentException(o + " is not a String");
+ }
+ };
+
+ /**
+ * An exception thrown when the proto that's serialized in a compiled subcomponent implementation
+ * is from a different version than the current compiler's.
+ */
+ public static final class InconsistentSerializedProtoException extends RuntimeException {
+ InconsistentSerializedProtoException(Throwable cause) {
+ super(cause);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/serialization/serialization.proto b/java/dagger/internal/codegen/serialization/serialization.proto
new file mode 100644
index 0000000..e6c9577
--- /dev/null
+++ b/java/dagger/internal/codegen/serialization/serialization.proto
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Serialized forms of types used in the Dagger processor. The wire format of
+// these types is not guaranteed to remain compatible over time; serialization
+// is only expected to function correctly within an individual version of the
+// Dagger processor.
+
+syntax = "proto3";
+
+package dagger.internal.codegen.serialization;
+option java_package = "dagger.internal.codegen.serialization";
+option java_multiple_files = true;
+
+// TODO(ronshapiro): consider exposing some of these in
+// dagger.model.serialization
+
+// Serialized form of `dagger.internal.codegen.BindingRequest`
+message BindingRequestProto {
+ KeyProto key = 1;
+ RequestKindWrapper.RequestKind request_kind = 2;
+ FrameworkTypeWrapper.FrameworkType framework_type = 3;
+}
+
+message RequestKindWrapper {
+ // Serialized form of `dagger.model.RequestKind`
+ enum RequestKind {
+ UNKNOWN = 0;
+ INSTANCE = 1;
+ PROVIDER = 2;
+ LAZY = 3;
+ PROVIDER_OF_LAZY = 4;
+ MEMBERS_INJECTION = 5;
+ PRODUCER = 6;
+ PRODUCED = 7;
+ FUTURE = 8;
+ }
+}
+
+message FrameworkTypeWrapper {
+ // Serialized form of `dagger.internal.codegen.FrameworkType`
+ enum FrameworkType {
+ UNKNOWN = 0;
+ PROVIDER = 1;
+ PRODUCER_NODE = 2;
+ }
+}
+
+// Serialized form of `dagger.model.Key`
+message KeyProto {
+ TypeProto type = 1;
+ AnnotationProto qualifier = 2;
+ MultibindingContributionIdentifier multibinding_contribution_identifier =
+ 3;
+
+ // Serialized form of `dagger.model.Key.MultibindingContributionIdentifier`
+ message MultibindingContributionIdentifier {
+ string module = 1;
+ string binding_element = 2;
+ }
+}
+
+// Serialized form of `javax.lang.model.type.TypeMirror`
+message TypeProto {
+ PrimitiveKind primitive_kind = 1;
+
+ // The qualified name of the type. Absent if this is an inner type.
+ string qualified_name = 2;
+
+ // The enclosing type if this is an inner type, otherwise absent.
+ TypeProto enclosing_type = 3;
+
+ // Simple name of the type if this is an inner type, otherwise absent.
+ string simple_name = 4;
+
+ repeated TypeProto type_arguments = 5;
+
+ message Wildcard {
+ TypeProto extends_bound = 1;
+ TypeProto super_bound = 2;
+ }
+ Wildcard wildcard = 6;
+
+ int32 array_dimensions = 7;
+
+ // Kinds of primitive types
+ enum PrimitiveKind {
+ UNKNOWN = 0;
+ BOOLEAN = 1;
+ BYTE = 2;
+ SHORT = 3;
+ CHAR = 4;
+ INT = 5;
+ FLOAT = 6;
+ LONG = 7;
+ DOUBLE = 8;
+ }
+}
+
+// Serialized form of `javax.lang.model.element.AnnotationMirror`
+message AnnotationProto {
+ TypeProto annotation_type = 1;
+ map<string, AnnotationValueProto> values = 2;
+}
+
+// Serialized form of `javax.lang.model.element.AnnotationValue`
+message AnnotationValueProto {
+ Kind kind = 1;
+ bool boolean_value = 2;
+ int32 int_value = 3;
+ int64 long_value = 4;
+ float float_value = 5;
+ double double_value = 6;
+ string string_value = 7;
+ TypeProto class_literal = 8;
+ TypeProto enum_type = 9;
+ string enum_name = 10;
+ AnnotationProto nested_annotation = 11;
+
+ repeated AnnotationValueProto array_values = 12;
+
+ // The type of annotation value
+ enum Kind {
+ UNKNOWN = 0;
+ BOOLEAN = 1;
+ BYTE = 2;
+ SHORT = 3;
+ CHAR = 4;
+ INT = 5;
+ FLOAT = 6;
+ LONG = 7;
+ DOUBLE = 8;
+ STRING = 9;
+ CLASS_LITERAL = 10;
+ ENUM = 11;
+ ANNOTATION = 12;
+ ARRAY = 13;
+ }
+}
+
+// Serialized form of `dagger.internal.codegen.ComponentRequirement`
+message ComponentRequirementProto {
+ oneof requirement {
+ TypeProto dependency = 1;
+ TypeProto module = 2;
+ BoundInstanceRequirement bound_instance = 3;
+ }
+
+ message BoundInstanceRequirement {
+ KeyProto key = 1;
+ bool nullable = 2;
+ string variable_name = 3;
+ }
+}
diff --git a/java/dagger/internal/doc-files/ReferenceReleasingProvider-statemachine.png b/java/dagger/internal/doc-files/ReferenceReleasingProvider-statemachine.png
new file mode 100644
index 0000000..8195dc0
--- /dev/null
+++ b/java/dagger/internal/doc-files/ReferenceReleasingProvider-statemachine.png
Binary files differ
diff --git a/java/dagger/model/BUILD b/java/dagger/model/BUILD
new file mode 100644
index 0000000..5fe0db3
--- /dev/null
+++ b/java/dagger/model/BUILD
@@ -0,0 +1,61 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Dagger's core APIs exposed for plugins
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+)
+
+INTERNAL_PROXIES = ["BindingGraphProxies.java"]
+
+filegroup(
+ name = "model-srcs",
+ srcs = glob(
+ ["*.java"],
+ exclude = INTERNAL_PROXIES,
+ ),
+)
+
+java_library(
+ name = "model",
+ srcs = [":model-srcs"],
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen:jdk-and-guava-extras",
+ "//java/dagger/producers",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+java_library(
+ name = "internal-proxies",
+ srcs = INTERNAL_PROXIES,
+ tags = ["maven:merged"],
+ deps = [
+ ":model",
+ "@google_bazel_common//third_party/java/guava",
+ ],
+)
diff --git a/java/dagger/model/Binding.java b/java/dagger/model/Binding.java
new file mode 100644
index 0000000..81aba00
--- /dev/null
+++ b/java/dagger/model/Binding.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.model.BindingGraph.MaybeBinding;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * The association between a {@link Key} and the way in which instances of the key are provided.
+ * Includes any {@linkplain DependencyRequest dependencies} that are needed in order to provide the
+ * instances.
+ *
+ * <p>If a binding is owned by more than one component, there is one {@code Binding} for every
+ * owning component.
+ */
+public interface Binding extends MaybeBinding {
+ @Override
+ ComponentPath componentPath();
+
+ /** @deprecated This always returns {@code Optional.of(this)}. */
+ @Override
+ @Deprecated
+ default Optional<Binding> binding() {
+ return Optional.of(this);
+ }
+ /**
+ * The dependencies of this binding. The order of the dependencies corresponds to the order in
+ * which they will be injected when the binding is requested.
+ */
+ ImmutableSet<DependencyRequest> dependencies();
+
+ /**
+ * The {@link Element} that declares this binding. Absent for {@linkplain BindingKind binding
+ * kinds} that are not always declared by exactly one element.
+ *
+ * <p>For example, consider {@link BindingKind#MULTIBOUND_SET}. A component with many
+ * {@code @IntoSet} bindings for the same key will have a synthetic binding that depends on all
+ * contributions, but with no identifiying binding element. A {@code @Multibinds} method will also
+ * contribute a synthetic binding, but since multiple {@code @Multibinds} methods can coexist in
+ * the same component (and contribute to one single binding), it has no binding element.
+ */
+ Optional<Element> bindingElement();
+
+ /**
+ * The {@link TypeElement} of the module which contributes this binding. Absent for bindings that
+ * have no {@link #bindingElement() binding element}.
+ */
+ Optional<TypeElement> contributingModule();
+
+ /**
+ * Returns {@code true} if using this binding requires an instance of the {@link
+ * #contributingModule()}.
+ */
+ boolean requiresModuleInstance();
+
+ /** The scope of this binding if it has one. */
+ Optional<Scope> scope();
+
+ /**
+ * Returns {@code true} if this binding may provide {@code null} instead of an instance of {@link
+ * #key()}. Nullable bindings cannot be requested from {@linkplain DependencyRequest#isNullable()
+ * non-nullable dependency requests}.
+ */
+ boolean isNullable();
+
+ /** Returns {@code true} if this is a production binding, e.g. an {@code @Produces} method. */
+ boolean isProduction();
+
+ /** The kind of binding this instance represents. */
+ BindingKind kind();
+
+}
diff --git a/java/dagger/model/BindingGraph.java b/java/dagger/model/BindingGraph.java
new file mode 100644
index 0000000..748bf38
--- /dev/null
+++ b/java/dagger/model/BindingGraph.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import static com.google.common.collect.Sets.intersection;
+import static com.google.common.graph.Graphs.inducedSubgraph;
+import static com.google.common.graph.Graphs.reachableNodes;
+import static com.google.common.graph.Graphs.transpose;
+import static dagger.internal.codegen.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.graph.EndpointPair;
+import com.google.common.graph.ImmutableNetwork;
+import com.google.common.graph.MutableNetwork;
+import com.google.common.graph.Network;
+import com.google.common.graph.NetworkBuilder;
+import dagger.Module;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A graph of bindings, dependency requests, and components.
+ *
+ * <p>A {@link BindingGraph} represents one of the following:
+ *
+ * <ul>
+ * <li>an entire component hierarchy rooted at a {@link dagger.Component} or {@link
+ * dagger.producers.ProductionComponent}
+ * <li>a partial component hierarchy rooted at a {@link dagger.Subcomponent} or {@link
+ * dagger.producers.ProductionSubcomponent} (only when {@code
+ * -Adagger.experimentalAheadOfTimeSubcomponents=enabled} is passed to the compiler)
+ * <li>the bindings installed by a {@link Module} or {@link dagger.producers.ProducerModule},
+ * including all subcomponents generated by {@link Module#subcomponents()} ()} and {@link
+ * dagger.producers.ProducerModule#subcomponents()} ()}
+ * </ul>
+ *
+ * In the case of a {@link BindingGraph} representing a module, the root {@link ComponentNode} will
+ * actually represent the module type. The graph will also be a {@linkplain #isFullBindingGraph()
+ * full binding graph}, which means it will contain all bindings in all modules, as well as nodes
+ * for their dependencies. Otherwise it will contain only bindings that are reachable from at least
+ * one {@linkplain #entryPointEdges() entry point}.
+ *
+ * <h3>Nodes</h3>
+ *
+ * <p>There is a <b>{@link Binding}</b> for each owned binding in the graph. If a binding is owned
+ * by more than one component, there is one binding object for that binding for every owning
+ * component.
+ *
+ * <p>There is a <b>{@linkplain ComponentNode component node}</b> (without a binding) for each
+ * component in the graph.
+ *
+ * <h3>Edges</h3>
+ *
+ * <p>There is a <b>{@linkplain DependencyEdge dependency edge}</b> for each dependency request in
+ * the graph. Its target node is the binding for the binding that satisfies the request. For entry
+ * point dependency requests, the source node is the component node for the component for which it
+ * is an entry point. For other dependency requests, the source node is the binding for the binding
+ * that contains the request.
+ *
+ * <p>There is a <b>subcomponent edge</b> for each parent-child component relationship in the graph.
+ * The target node is the component node for the child component. For subcomponents defined by a
+ * {@linkplain SubcomponentCreatorBindingEdge subcomponent creator binding} (either a method on the
+ * component or a set of {@code @Module.subcomponents} annotation values), the source node is the
+ * binding for the {@code @Subcomponent.Builder} type. For subcomponents defined by {@linkplain
+ * ChildFactoryMethodEdge subcomponent factory methods}, the source node is the component node for
+ * the parent.
+ *
+ * <p><b>Note that this API is experimental and will change.</b>
+ */
+@AutoValue
+public abstract class BindingGraph {
+
+ static BindingGraph create(Network<Node, Edge> network, boolean isFullBindingGraph) {
+ return new AutoValue_BindingGraph(ImmutableNetwork.copyOf(network), isFullBindingGraph);
+ }
+
+ BindingGraph() {}
+
+ /** Returns the graph in its {@link Network} representation. */
+ public abstract ImmutableNetwork<Node, Edge> network();
+
+ @Override
+ public final String toString() {
+ return network().toString();
+ }
+
+ /**
+ * Returns {@code true} if this graph was constructed from a module for full binding graph
+ * validation.
+ *
+ * @deprecated use {@link #isFullBindingGraph()} to tell if this is a full binding graph, or
+ * {@link ComponentNode#isRealComponent() rootComponentNode().isRealComponent()} to tell if
+ * the root component node is really a component or derived from a module. Dagger can generate
+ * full binding graphs for components and subcomponents as well as modules.
+ */
+ @Deprecated
+ public final boolean isModuleBindingGraph() {
+ return !rootComponentNode().isRealComponent();
+ }
+
+ /**
+ * Returns {@code true} if this is a full binding graph, which contains all bindings installed in
+ * the component, or {@code false} if it is a reachable binding graph, which contains only
+ * bindings that are reachable from at least one {@linkplain #entryPointEdges() entry point}.
+ *
+ * @see <a href="https://dagger.dev/compiler-options#full-binding-graph-validation">Full binding
+ * graph validation</a>
+ */
+ public abstract boolean isFullBindingGraph();
+
+ /**
+ * Returns {@code true} if the {@link #rootComponentNode()} is a subcomponent. This occurs in
+ * ahead-of-time-subcomponents mode.
+ *
+ * @deprecated use {@link ComponentNode#isSubcomponent() rootComponentNode().isSubcomponent()}
+ * instead
+ */
+ @Deprecated
+ public final boolean isPartialBindingGraph() {
+ return rootComponentNode().isSubcomponent();
+ }
+
+ /** Returns the bindings. */
+ public final ImmutableSet<Binding> bindings() {
+ return nodes(Binding.class);
+ }
+
+ /** Returns the bindings for a key. */
+ public final ImmutableSet<Binding> bindings(Key key) {
+ return nodes(Binding.class).stream()
+ .filter(binding -> binding.key().equals(key))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the nodes that represent missing bindings. */
+ public final ImmutableSet<MissingBinding> missingBindings() {
+ return nodes(MissingBinding.class);
+ }
+
+ /** Returns the component nodes. */
+ public final ImmutableSet<ComponentNode> componentNodes() {
+ return nodes(ComponentNode.class);
+ }
+
+ /** Returns the component node for a component. */
+ public final Optional<ComponentNode> componentNode(ComponentPath component) {
+ return componentNodes().stream()
+ .filter(node -> node.componentPath().equals(component))
+ .findFirst();
+ }
+
+ /** Returns the component nodes for a component. */
+ public final ImmutableSet<ComponentNode> componentNodes(TypeElement component) {
+ return componentNodes().stream()
+ .filter(node -> node.componentPath().currentComponent().equals(component))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the component node for the root component. */
+ public final ComponentNode rootComponentNode() {
+ return componentNodes().stream()
+ .filter(node -> node.componentPath().atRoot())
+ .findFirst()
+ .get();
+ }
+
+ /** Returns the dependency edges. */
+ public final ImmutableSet<DependencyEdge> dependencyEdges() {
+ return dependencyEdgeStream().collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the dependency edges for the dependencies of a binding. For valid graphs, each {@link
+ * DependencyRequest} will map to a single {@link DependencyEdge}. When conflicting bindings exist
+ * for a key, the multimap will have several edges for that {@link DependencyRequest}. Graphs that
+ * have no binding for a key will have an edge whose {@linkplain EndpointPair#target() target
+ * node} is a {@link MissingBinding}.
+ */
+ public final ImmutableSetMultimap<DependencyRequest, DependencyEdge> dependencyEdges(
+ Binding binding) {
+ return dependencyEdgeStream(binding)
+ .collect(toImmutableSetMultimap(DependencyEdge::dependencyRequest, edge -> edge));
+ }
+
+ /** Returns the dependency edges for a dependency request. */
+ public final ImmutableSet<DependencyEdge> dependencyEdges(DependencyRequest dependencyRequest) {
+ return dependencyEdgeStream()
+ .filter(edge -> edge.dependencyRequest().equals(dependencyRequest))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the dependency edges for the entry points of a given {@code component}. Each edge's
+ * source node is that component's component node.
+ */
+ public final ImmutableSet<DependencyEdge> entryPointEdges(ComponentPath component) {
+ return dependencyEdgeStream(componentNode(component).get()).collect(toImmutableSet());
+ }
+
+ private Stream<DependencyEdge> dependencyEdgeStream(Node node) {
+ return network().outEdges(node).stream().flatMap(instancesOf(DependencyEdge.class));
+ }
+
+ /**
+ * Returns the dependency edges for all entry points for all components and subcomponents. Each
+ * edge's source node is a component node.
+ */
+ public final ImmutableSet<DependencyEdge> entryPointEdges() {
+ return entryPointEdgeStream().collect(toImmutableSet());
+ }
+
+ /** Returns the binding or missing binding nodes that directly satisfy entry points. */
+ public final ImmutableSet<MaybeBinding> entryPointBindings() {
+ return entryPointEdgeStream()
+ .map(edge -> (MaybeBinding) network().incidentNodes(edge).target())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the edges for entry points that transitively depend on a binding or missing binding for
+ * a key.
+ */
+ public final ImmutableSet<DependencyEdge> entryPointEdgesDependingOnBinding(
+ MaybeBinding binding) {
+ ImmutableNetwork<Node, DependencyEdge> dependencyGraph = dependencyGraph();
+ Network<Node, DependencyEdge> subgraphDependingOnBinding =
+ inducedSubgraph(
+ dependencyGraph, reachableNodes(transpose(dependencyGraph).asGraph(), binding));
+ return intersection(entryPointEdges(), subgraphDependingOnBinding.edges()).immutableCopy();
+ }
+
+ /** Returns the bindings that directly request a given binding as a dependency. */
+ public final ImmutableSet<Binding> requestingBindings(MaybeBinding binding) {
+ return network().predecessors(binding).stream()
+ .flatMap(instancesOf(Binding.class))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the bindings that a given binding directly request as a dependency. Does not include
+ * any {@link MissingBinding}s.
+ *
+ * @see #requestedMaybeMissingBindings(Binding)
+ */
+ public final ImmutableSet<Binding> requestedBindings(Binding binding) {
+ return network().successors(binding).stream()
+ .flatMap(instancesOf(Binding.class))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the bindings or missing bindings that a given binding directly requests as a
+ * dependency.
+ *
+ * @see #requestedBindings(Binding)
+ */
+ public final ImmutableSet<MaybeBinding> requestedMaybeMissingBindings(Binding binding) {
+ return network().successors(binding).stream()
+ .flatMap(instancesOf(MaybeBinding.class))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns a subnetwork that contains all nodes but only {@link DependencyEdge}s. */
+ // TODO(dpb): Make public. Cache.
+ private ImmutableNetwork<Node, DependencyEdge> dependencyGraph() {
+ MutableNetwork<Node, DependencyEdge> dependencyGraph =
+ NetworkBuilder.from(network())
+ .expectedNodeCount(network().nodes().size())
+ .expectedEdgeCount((int) dependencyEdgeStream().count())
+ .build();
+ network().nodes().forEach(dependencyGraph::addNode); // include disconnected nodes
+ dependencyEdgeStream()
+ .forEach(
+ edge -> {
+ EndpointPair<Node> endpoints = network().incidentNodes(edge);
+ dependencyGraph.addEdge(endpoints.source(), endpoints.target(), edge);
+ });
+ return ImmutableNetwork.copyOf(dependencyGraph);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private <N extends Node> ImmutableSet<N> nodes(Class<N> clazz) {
+ return (ImmutableSet) nodesByClass().get(clazz);
+ }
+
+ private static final ImmutableSet<Class<? extends Node>> NODE_TYPES =
+ ImmutableSet.of(Binding.class, MissingBinding.class, ComponentNode.class);
+
+ @Memoized
+ ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
+ return network().nodes().stream()
+ .collect(
+ toImmutableSetMultimap(
+ node ->
+ NODE_TYPES.stream().filter(clazz -> clazz.isInstance(node)).findFirst().get(),
+ node -> node));
+ }
+
+ private Stream<DependencyEdge> dependencyEdgeStream() {
+ return network().edges().stream().flatMap(instancesOf(DependencyEdge.class));
+ }
+
+ private Stream<DependencyEdge> entryPointEdgeStream() {
+ return dependencyEdgeStream().filter(DependencyEdge::isEntryPoint);
+ }
+
+ /**
+ * An edge in the binding graph. Either a {@link DependencyEdge}, a {@link
+ * ChildFactoryMethodEdge}, or a {@link SubcomponentCreatorBindingEdge}.
+ */
+ public interface Edge {}
+
+ /**
+ * An edge that represents a dependency on a binding.
+ *
+ * <p>Because one {@link DependencyRequest} may represent a dependency from two bindings (e.g., a
+ * dependency of {@code Foo<String>} and {@code Foo<Number>} may have the same key and request
+ * element), this class does not override {@link #equals(Object)} to use value semantics.
+ *
+ * <p>For entry points, the source node is the {@link ComponentNode} that contains the entry
+ * point. Otherwise the source node is a {@link Binding}.
+ *
+ * <p>For dependencies on missing bindings, the target node is a {@link MissingBinding}. Otherwise
+ * the target node is a {@link Binding}.
+ */
+ public interface DependencyEdge extends Edge {
+ /** The dependency request. */
+ DependencyRequest dependencyRequest();
+
+ /** Returns {@code true} if this edge represents an entry point. */
+ boolean isEntryPoint();
+ }
+
+ /**
+ * An edge that represents a subcomponent factory method linking a parent component to a child
+ * subcomponent.
+ */
+ public interface ChildFactoryMethodEdge extends Edge {
+ /** The subcomponent factory method element. */
+ ExecutableElement factoryMethod();
+ }
+
+ /**
+ * An edge that represents the link between a parent component and a child subcomponent implied by
+ * a subcomponent creator ({@linkplain dagger.Subcomponent.Builder builder} or {@linkplain
+ * dagger.Subcomponent.Factory factory}) binding.
+ *
+ * <p>The {@linkplain com.google.common.graph.EndpointPair#source() source node} of this edge is a
+ * {@link Binding} for the subcomponent creator {@link Key} and the {@linkplain
+ * com.google.common.graph.EndpointPair#target() target node} is a {@link ComponentNode} for the
+ * child subcomponent.
+ */
+ public interface SubcomponentCreatorBindingEdge extends Edge {
+ /**
+ * The modules that {@linkplain Module#subcomponents() declare the subcomponent} that generated
+ * this edge. Empty if the parent component has a subcomponent creator method and there are no
+ * declaring modules.
+ */
+ ImmutableSet<TypeElement> declaringModules();
+ }
+
+ /** A node in the binding graph. Either a {@link Binding} or a {@link ComponentNode}. */
+ // TODO(dpb): Make all the node/edge types top-level.
+ public interface Node {
+ /** The component this node belongs to. */
+ ComponentPath componentPath();
+ }
+
+ /** A node in the binding graph that is either a {@link Binding} or a {@link MissingBinding}. */
+ public interface MaybeBinding extends Node {
+
+ /** The component that owns the binding, or in which the binding is missing. */
+ @Override
+ ComponentPath componentPath();
+
+ /** The key of the binding, or for which there is no binding. */
+ Key key();
+
+ /** The binding, or empty if missing. */
+ Optional<Binding> binding();
+ }
+
+ /** A node in the binding graph that represents a missing binding for a key in a component. */
+ @AutoValue
+ public abstract static class MissingBinding implements MaybeBinding {
+ static MissingBinding create(ComponentPath component, Key key) {
+ return new AutoValue_BindingGraph_MissingBinding(component, key);
+ }
+
+ /** The component in which the binding is missing. */
+ @Override
+ public abstract ComponentPath componentPath();
+
+ /** The key for which there is no binding. */
+ public abstract Key key();
+
+ /** @deprecated This always returns {@code Optional.empty()}. */
+ @Override
+ @Deprecated
+ public final Optional<Binding> binding() {
+ return Optional.empty();
+ }
+
+ @Override
+ public final String toString() {
+ return String.format("missing binding for %s in %s", key(), componentPath());
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object o);
+ }
+
+ /**
+ * A <b>component node</b> in the graph. Every entry point {@linkplain DependencyEdge dependency
+ * edge}'s source node is a component node for the component containing the entry point.
+ */
+ public interface ComponentNode extends Node {
+
+ /** The component represented by this node. */
+ @Override
+ ComponentPath componentPath();
+
+ /**
+ * Returns {@code true} if the component is a {@code @Subcomponent} or
+ * {@code @ProductionSubcomponent}.
+ */
+ boolean isSubcomponent();
+
+ /**
+ * Returns {@code true} if the component is a real component, or {@code false} if it is a
+ * fictional component based on a module.
+ */
+ boolean isRealComponent();
+
+ /** The entry points on this component. */
+ ImmutableSet<DependencyRequest> entryPoints();
+
+ /** The scopes declared on this component. */
+ ImmutableSet<Scope> scopes();
+ }
+}
diff --git a/java/dagger/model/BindingGraphProxies.java b/java/dagger/model/BindingGraphProxies.java
new file mode 100644
index 0000000..c450475
--- /dev/null
+++ b/java/dagger/model/BindingGraphProxies.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import com.google.common.graph.Network;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.MissingBinding;
+import dagger.model.BindingGraph.Node;
+
+/**
+ * Exposes package-private constructors to the {@code dagger.internal.codegen} package. <em>This
+ * class should only be used in the Dagger implementation and is not part of any documented
+ * API.</em>
+ */
+public final class BindingGraphProxies {
+ /** Creates a new {@link BindingGraph}. */
+ public static BindingGraph bindingGraph(Network<Node, Edge> network, boolean isFullBindingGraph) {
+ return BindingGraph.create(network, isFullBindingGraph);
+ }
+
+ /** Creates a new {@link MissingBinding}. */
+ public static MissingBinding missingBindingNode(ComponentPath component, Key key) {
+ return MissingBinding.create(component, key);
+ }
+
+ private BindingGraphProxies() {}
+}
diff --git a/java/dagger/model/BindingKind.java b/java/dagger/model/BindingKind.java
new file mode 100644
index 0000000..20d7b42
--- /dev/null
+++ b/java/dagger/model/BindingKind.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+/** Represents the different kinds of {@link Binding}s that can exist in a binding graph. */
+public enum BindingKind {
+ /** A binding for an {@link javax.inject.Inject}-annotated constructor. */
+ INJECTION,
+
+ /** A binding for a {@link dagger.Provides}-annotated method. */
+ PROVISION,
+
+ /**
+ * An implicit binding for a {@link dagger.Component}- or {@link
+ * dagger.producers.ProductionComponent}-annotated type.
+ */
+ COMPONENT,
+
+ /**
+ * A binding for a provision method on a component's {@linkplain dagger.Component#dependencies()
+ * dependency}.
+ */
+ COMPONENT_PROVISION,
+
+ /**
+ * A binding for an instance of a component's {@linkplain dagger.Component#dependencies()
+ * dependency}.
+ */
+ COMPONENT_DEPENDENCY,
+
+ /** A binding for a {@link dagger.MembersInjector} of a type. */
+ MEMBERS_INJECTOR,
+
+ /**
+ * A binding for a subcomponent creator (a {@linkplain dagger.Subcomponent.Builder builder} or
+ * {@linkplain dagger.Subcomponent.Factory factory}).
+ *
+ * @since 2.22 (previously named {@code SUBCOMPONENT_BUILDER})
+ */
+ SUBCOMPONENT_CREATOR,
+
+ /** A binding for a {@link dagger.BindsInstance}-annotated builder method. */
+ BOUND_INSTANCE,
+
+ /** A binding for a {@link dagger.producers.Produces}-annotated method. */
+ PRODUCTION,
+
+ /**
+ * A binding for a production method on a production component's {@linkplain
+ * dagger.producers.ProductionComponent#dependencies()} dependency} that returns a {@link
+ * com.google.common.util.concurrent.ListenableFuture} or {@link
+ * com.google.common.util.concurrent.FluentFuture}. Methods on production component dependencies
+ * that don't return a future are considered {@linkplain #COMPONENT_PROVISION component provision
+ * bindings}.
+ */
+ COMPONENT_PRODUCTION,
+
+ /**
+ * A synthetic binding for a multibound set that depends on individual multibinding {@link
+ * #PROVISION} or {@link #PRODUCTION} contributions.
+ */
+ MULTIBOUND_SET,
+
+ /**
+ * A synthetic binding for a multibound map that depends on the individual multibinding {@link
+ * #PROVISION} or {@link #PRODUCTION} contributions.
+ */
+ MULTIBOUND_MAP,
+
+ /**
+ * A synthetic binding for {@code Optional} of a type or a {@link javax.inject.Provider}, {@link
+ * dagger.Lazy}, or {@code Provider} of {@code Lazy} of a type. Generated by a {@link
+ * dagger.BindsOptionalOf} declaration.
+ */
+ OPTIONAL,
+
+ /**
+ * A binding for {@link dagger.Binds}-annotated method that that delegates from requests for one
+ * key to another.
+ */
+ // TODO(dpb,ronshapiro): This name is confusing and could use work. Not all usages of @Binds
+ // bindings are simple delegations and we should have a name that better reflects that
+ DELEGATE,
+
+ /** A binding for a members injection method on a component. */
+ MEMBERS_INJECTION,
+ ;
+
+ /**
+ * Returns {@code true} if this is a kind of multibinding (not a contribution to a multibinding,
+ * but the multibinding itself).
+ */
+ public boolean isMultibinding() {
+ switch (this) {
+ case MULTIBOUND_MAP:
+ case MULTIBOUND_SET:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+}
diff --git a/java/dagger/model/ComponentPath.java b/java/dagger/model/ComponentPath.java
new file mode 100644
index 0000000..5a74c7a
--- /dev/null
+++ b/java/dagger/model/ComponentPath.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getLast;
+import static java.util.stream.Collectors.joining;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import javax.lang.model.element.TypeElement;
+
+/** A path containing a component and all of its ancestor components. */
+@AutoValue
+public abstract class ComponentPath {
+ /** Returns a new {@link ComponentPath} from {@code components}. */
+ public static ComponentPath create(Iterable<TypeElement> components) {
+ return new AutoValue_ComponentPath(ImmutableList.copyOf(components));
+ }
+
+ /**
+ * Returns the component types, starting from the {@linkplain #rootComponent() root
+ * component} and ending with the {@linkplain #currentComponent() current component}.
+ */
+ public abstract ImmutableList<TypeElement> components();
+
+ /**
+ * Returns the root {@link dagger.Component}- or {@link
+ * dagger.producers.ProductionComponent}-annotated type
+ */
+ public final TypeElement rootComponent() {
+ return components().get(0);
+ }
+
+ /** Returns the component at the end of the path. */
+ @Memoized
+ public TypeElement currentComponent() {
+ return getLast(components());
+ }
+
+ /**
+ * Returns the parent of the {@linkplain #currentComponent()} current component}.
+ *
+ * @throws IllegalStateException if the current graph is the {@linkplain #atRoot() root component}
+ */
+ public final TypeElement parentComponent() {
+ checkState(!atRoot());
+ return components().reverse().get(1);
+ }
+
+ /**
+ * Returns this path's parent path.
+ *
+ * @throws IllegalStateException if the current graph is the {@linkplain #atRoot() root component}
+ */
+ // TODO(ronshapiro): consider memoizing this
+ public final ComponentPath parent() {
+ checkState(!atRoot());
+ return create(components().subList(0, components().size() - 1));
+ }
+
+ /** Returns the path from the root component to the {@code child} of the current component. */
+ public final ComponentPath childPath(TypeElement child) {
+ return create(ImmutableList.<TypeElement>builder().addAll(components()).add(child).build());
+ }
+
+ /**
+ * Returns {@code true} if the {@linkplain #currentComponent()} current component} is the
+ * {@linkplain #rootComponent()} root component}.
+ */
+ public final boolean atRoot() {
+ return components().size() == 1;
+ }
+
+ @Override
+ public final String toString() {
+ return components().stream().map(TypeElement::getQualifiedName).collect(joining(" → "));
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+}
diff --git a/java/dagger/model/DependencyRequest.java b/java/dagger/model/DependencyRequest.java
new file mode 100644
index 0000000..607b5ec
--- /dev/null
+++ b/java/dagger/model/DependencyRequest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import com.google.auto.value.AutoValue;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.Provides;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+
+/**
+ * Represents a request for a {@link Key} at an injection point. For example, parameters to {@link
+ * Inject} constructors, {@link Provides} methods, and component methods are all dependency
+ * requests.
+ *
+ * <p id="synthetic">A dependency request is considered to be <em>synthetic</em> if it does not have
+ * an {@link Element} in code that requests the key directly. For example, an {@link
+ * java.util.concurrent.Executor} is required for all {@code @Produces} methods to run
+ * asynchronously even though it is not directly specified as a parameter to the binding method.
+ */
+@AutoValue
+public abstract class DependencyRequest {
+ /** The kind of this request. */
+ public abstract RequestKind kind();
+
+ /** The key of this request. */
+ public abstract Key key();
+
+ /**
+ * The element that declares this dependency request. Absent for <a href="#synthetic">synthetic
+ * </a> requests.
+ */
+ public abstract Optional<Element> requestElement();
+
+ /**
+ * Returns {@code true} if this request allows null objects. A request is nullable if it is
+ * has an annotation with "Nullable" as its simple name.
+ */
+ public abstract boolean isNullable();
+
+ /** Returns a new builder of dependency requests. */
+ public static DependencyRequest.Builder builder() {
+ return new AutoValue_DependencyRequest.Builder().isNullable(false);
+ }
+
+ /** A builder of {@link DependencyRequest}s. */
+ @CanIgnoreReturnValue
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder kind(RequestKind kind);
+
+ public abstract Builder key(Key key);
+
+ public abstract Builder requestElement(Element element);
+
+ public abstract Builder isNullable(boolean isNullable);
+
+ @CheckReturnValue
+ public abstract DependencyRequest build();
+ }
+}
diff --git a/java/dagger/model/Key.java b/java/dagger/model/Key.java
new file mode 100644
index 0000000..03dd41c
--- /dev/null
+++ b/java/dagger/model/Key.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.stream.Collectors.joining;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import com.squareup.javapoet.CodeBlock;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/**
+ * A {@linkplain TypeMirror type} and an optional {@linkplain javax.inject.Qualifier qualifier} that
+ * is the lookup key for a binding.
+ */
+@AutoValue
+public abstract class Key {
+ /**
+ * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix
+ * for the type of this key.
+ */
+ public final Optional<AnnotationMirror> qualifier() {
+ return wrappedQualifier().map(Wrapper::get);
+ }
+
+ /**
+ * The type represented by this key.
+ */
+ public final TypeMirror type() {
+ return wrappedType().get();
+ }
+
+ /**
+ * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix
+ * for the type of this key.
+ *
+ * Despite documentation in {@link AnnotationMirror}, equals and hashCode aren't implemented
+ * to represent logical equality, so {@link AnnotationMirrors#equivalence()}
+ * provides this facility.
+ */
+ abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier();
+
+ /**
+ * The type represented by this key.
+ *
+ * As documented in {@link TypeMirror}, equals and hashCode aren't implemented to represent
+ * logical equality, so {@link MoreTypes#equivalence()} wraps this type.
+ */
+ abstract Equivalence.Wrapper<TypeMirror> wrappedType();
+
+ /**
+ * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link
+ * #qualifier()}.
+ *
+ * <p>Each multibound map and set has a synthetic multibinding that depends on the specific
+ * contributions to that map or set using keys that identify those multibinding contributions.
+ *
+ * <p>Absent except for multibinding contributions.
+ */
+ public abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier();
+
+ /** Returns a {@link Builder} that inherits the properties of this key. */
+ public abstract Builder toBuilder();
+
+ // The main hashCode/equality bottleneck is in MoreTypes.equivalence(). It's possible that we can
+ // avoid this by tuning that method. Perhaps we can also avoid the issue entirely by interning all
+ // Keys
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object o);
+
+ /**
+ * Returns a String rendering of an {@link AnnotationMirror} that includes attributes in the order
+ * defined in the annotation type. This will produce the same output for {@linkplain
+ * AnnotationMirrors#equivalence() equal} {@link AnnotationMirror}s even if default values are
+ * omitted or their attributes were written in different orders, e.g. {@code @A(b = "b", c = "c")}
+ * and {@code @A(c = "c", b = "b", attributeWithDefaultValue = "default value")}.
+ */
+ // TODO(ronshapiro): move this to auto-common
+ private static String stableAnnotationMirrorToString(AnnotationMirror qualifier) {
+ StringBuilder builder = new StringBuilder("@").append(qualifier.getAnnotationType());
+ ImmutableMap<ExecutableElement, AnnotationValue> elementValues =
+ AnnotationMirrors.getAnnotationValuesWithDefaults(qualifier);
+ if (!elementValues.isEmpty()) {
+ ImmutableMap.Builder<String, String> namedValuesBuilder = ImmutableMap.builder();
+ elementValues.forEach(
+ (key, value) ->
+ namedValuesBuilder.put(
+ key.getSimpleName().toString(), stableAnnotationValueToString(value)));
+ ImmutableMap<String, String> namedValues = namedValuesBuilder.build();
+ builder.append('(');
+ if (namedValues.size() == 1 && namedValues.containsKey("value")) {
+ // Omit "value ="
+ builder.append(namedValues.get("value"));
+ } else {
+ builder.append(Joiner.on(", ").withKeyValueSeparator("=").join(namedValues));
+ }
+ builder.append(')');
+ }
+ return builder.toString();
+ }
+
+ private static String stableAnnotationValueToString(AnnotationValue annotationValue) {
+ return annotationValue.accept(
+ new SimpleAnnotationValueVisitor8<String, Void>() {
+ @Override
+ protected String defaultAction(Object value, Void ignore) {
+ return value.toString();
+ }
+
+ @Override
+ public String visitString(String value, Void ignore) {
+ return CodeBlock.of("$S", value).toString();
+ }
+
+ @Override
+ public String visitAnnotation(AnnotationMirror value, Void ignore) {
+ return stableAnnotationMirrorToString(value);
+ }
+
+ @Override
+ public String visitArray(List<? extends AnnotationValue> value, Void ignore) {
+ return value.stream()
+ .map(Key::stableAnnotationValueToString)
+ .collect(joining(", ", "{", "}"));
+ }
+ },
+ null);
+ }
+
+ @Override
+ public final String toString() {
+ return Joiner.on(' ')
+ .skipNulls()
+ .join(
+ qualifier().map(Key::stableAnnotationMirrorToString).orElse(null),
+ type(),
+ multibindingContributionIdentifier().orElse(null));
+ }
+
+ /** Returns a builder for {@link Key}s. */
+ public static Builder builder(TypeMirror type) {
+ return new AutoValue_Key.Builder().type(type);
+ }
+
+ /** A builder for {@link Key}s. */
+ @CanIgnoreReturnValue
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder wrappedType(Equivalence.Wrapper<TypeMirror> wrappedType);
+
+ public final Builder type(TypeMirror type) {
+ return wrappedType(MoreTypes.equivalence().wrap(checkNotNull(type)));
+ }
+
+ abstract Builder wrappedQualifier(
+ Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier);
+
+ abstract Builder wrappedQualifier(Equivalence.Wrapper<AnnotationMirror> wrappedQualifier);
+
+ public final Builder qualifier(AnnotationMirror qualifier) {
+ return wrappedQualifier(AnnotationMirrors.equivalence().wrap(checkNotNull(qualifier)));
+ }
+
+ public final Builder qualifier(Optional<AnnotationMirror> qualifier) {
+ return wrappedQualifier(checkNotNull(qualifier).map(AnnotationMirrors.equivalence()::wrap));
+ }
+
+ public abstract Builder multibindingContributionIdentifier(
+ Optional<MultibindingContributionIdentifier> identifier);
+
+ public abstract Builder multibindingContributionIdentifier(
+ MultibindingContributionIdentifier identifier);
+
+ @CheckReturnValue
+ public abstract Key build();
+ }
+
+ /**
+ * An object that identifies a multibinding contribution method and the module class that
+ * contributes it to the graph.
+ *
+ * @see #multibindingContributionIdentifier()
+ */
+ public static final class MultibindingContributionIdentifier {
+ private final String module;
+ private final String bindingElement;
+
+ /**
+ * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}.
+ * It is not part of a specified API and may change at any point.
+ */
+ @Deprecated
+ public MultibindingContributionIdentifier(
+ // TODO(ronshapiro): reverse the order of these parameters
+ ExecutableElement bindingMethod, TypeElement contributingModule) {
+ this(
+ bindingMethod.getSimpleName().toString(),
+ contributingModule.getQualifiedName().toString());
+ }
+
+ // TODO(ronshapiro,dpb): create KeyProxies so that these constructors don't need to be public.
+ @Deprecated
+ public MultibindingContributionIdentifier(String bindingElement, String module) {
+ this.module = module;
+ this.bindingElement = bindingElement;
+ }
+
+ /**
+ * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}.
+ * It is not part of a specified API and may change at any point.
+ */
+ @Deprecated
+ public String module() {
+ return module;
+ }
+
+ /**
+ * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}.
+ * It is not part of a specified API and may change at any point.
+ */
+ @Deprecated
+ public String bindingElement() {
+ return bindingElement;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The returned string is human-readable and distinguishes the keys in the same way as the
+ * whole object.
+ */
+ @Override
+ public String toString() {
+ return String.format("%s#%s", module, bindingElement);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof MultibindingContributionIdentifier) {
+ MultibindingContributionIdentifier other = (MultibindingContributionIdentifier) obj;
+ return module.equals(other.module) && bindingElement.equals(other.bindingElement);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(module, bindingElement);
+ }
+ }
+}
diff --git a/java/dagger/model/RequestKind.java b/java/dagger/model/RequestKind.java
new file mode 100644
index 0000000..74a4346
--- /dev/null
+++ b/java/dagger/model/RequestKind.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+
+import dagger.Lazy;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+
+/**
+ * Represents the different kinds of {@link javax.lang.model.type.TypeMirror types} that may be
+ * requested as dependencies for the same key. For example, {@code String}, {@code
+ * Provider<String>}, and {@code Lazy<String>} can all be requested if a key exists for {@code
+ * String}; they have the {@link #INSTANCE}, {@link #PROVIDER}, and {@link #LAZY} request kinds,
+ * respectively.
+ */
+public enum RequestKind {
+ /** A default request for an instance. E.g.: {@code FooType} */
+ INSTANCE,
+
+ /** A request for a {@link Provider}. E.g.: {@code Provider<FooType>} */
+ PROVIDER,
+
+ /** A request for a {@link Lazy}. E.g.: {@code Lazy<FooType>} */
+ LAZY,
+
+ /** A request for a {@link Provider} of a {@link Lazy}. E.g.: {@code Provider<Lazy<FooType>>} */
+ PROVIDER_OF_LAZY,
+
+ /**
+ * A request for a members injection. E.g. {@code void injectMembers(FooType);}. Can only be
+ * requested by component interfaces.
+ */
+ MEMBERS_INJECTION,
+
+ /** A request for a {@link Producer}. E.g.: {@code Producer<FooType>} */
+ PRODUCER,
+
+ /** A request for a {@link Produced}. E.g.: {@code Produced<FooType>} */
+ PRODUCED,
+
+ /**
+ * A request for a {@link com.google.common.util.concurrent.ListenableFuture}. E.g.: {@code
+ * ListenableFuture<FooType>}. These can only be requested by component interfaces.
+ */
+ FUTURE,
+ ;
+
+ /** Returns a string that represents requests of this kind for a key. */
+ public String format(Key key) {
+ switch (this) {
+ case INSTANCE:
+ return key.toString();
+
+ case PROVIDER_OF_LAZY:
+ return String.format("Provider<Lazy<%s>>", key);
+
+ case MEMBERS_INJECTION:
+ return String.format("injectMembers(%s)", key);
+
+ case FUTURE:
+ return String.format("ListenableFuture<%s>", key);
+
+ default:
+ return String.format("%s<%s>", UPPER_UNDERSCORE.to(UPPER_CAMEL, name()), key);
+ }
+ }
+}
diff --git a/java/dagger/model/Scope.java b/java/dagger/model/Scope.java
new file mode 100644
index 0000000..f48fa16
--- /dev/null
+++ b/java/dagger/model/Scope.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import dagger.Reusable;
+import dagger.producers.ProductionScope;
+import java.lang.annotation.Annotation;
+import javax.inject.Singleton;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.TypeElement;
+
+/** A representation of a {@link javax.inject.Scope}. */
+@AutoValue
+// TODO(ronshapiro): point to SimpleAnnotationMirror
+public abstract class Scope {
+ abstract Equivalence.Wrapper<AnnotationMirror> wrappedScopeAnnotation();
+
+ /** The {@link AnnotationMirror} that represents the scope annotation. */
+ public final AnnotationMirror scopeAnnotation() {
+ return wrappedScopeAnnotation().get();
+ }
+
+ /** The scope annotation element. */
+ public final TypeElement scopeAnnotationElement() {
+ return MoreTypes.asTypeElement(scopeAnnotation().getAnnotationType());
+ }
+
+ /**
+ * Creates a {@link Scope} object from the {@link javax.inject.Scope}-annotated annotation type.
+ */
+ public static Scope scope(AnnotationMirror scopeAnnotation) {
+ checkArgument(isScope(scopeAnnotation));
+ return new AutoValue_Scope(AnnotationMirrors.equivalence().wrap(scopeAnnotation));
+ }
+
+ /**
+ * Returns {@code true} if {@link #scopeAnnotation()} is a {@link javax.inject.Scope} annotation.
+ */
+ public static boolean isScope(AnnotationMirror scopeAnnotation) {
+ return isScope(MoreElements.asType(scopeAnnotation.getAnnotationType().asElement()));
+ }
+
+ /**
+ * Returns {@code true} if {@code scopeAnnotationType} is a {@link javax.inject.Scope} annotation.
+ */
+ public static boolean isScope(TypeElement scopeAnnotationType) {
+ return isAnnotationPresent(scopeAnnotationType, javax.inject.Scope.class);
+ }
+
+ /** Returns {@code true} if this scope is the {@link Singleton @Singleton} scope. */
+ public final boolean isSingleton() {
+ return isScope(Singleton.class);
+ }
+
+ /** Returns {@code true} if this scope is the {@link Reusable @Reusable} scope. */
+ public final boolean isReusable() {
+ return isScope(Reusable.class);
+ }
+
+ /** Returns {@code true} if this scope is the {@link ProductionScope @ProductionScope} scope. */
+ public final boolean isProductionScope() {
+ return isScope(ProductionScope.class);
+ }
+
+ private boolean isScope(Class<? extends Annotation> annotation) {
+ return scopeAnnotationElement().getQualifiedName().contentEquals(annotation.getCanonicalName());
+ }
+
+ /** Returns a debug representation of the scope. */
+ @Override
+ public final String toString() {
+ return scopeAnnotation().toString();
+ }
+}
diff --git a/java/dagger/model/package-info.java b/java/dagger/model/package-info.java
new file mode 100644
index 0000000..7d09114
--- /dev/null
+++ b/java/dagger/model/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the APIs that are core to Dagger's internal model of bindings and the
+ * binding graph. The types are shared with the Dagger processor and are exposed to clients of the
+ * Dagger SPI.
+ *
+ * <p>Unless otherwise specified, the types/interfaces are only intended to be implemented in this
+ * package (i.e. via {@code @AutoValue}) or by Dagger's processor. This applies to test code as
+ * well, so if you need a fake, please file a feature request instead of implementing it yourself.
+ */
+@CheckReturnValue
+@Beta
+package dagger.model;
+
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.internal.Beta;
diff --git a/java/dagger/model/testing/BUILD b/java/dagger/model/testing/BUILD
new file mode 100644
index 0000000..a9d5f19
--- /dev/null
+++ b/java/dagger/model/testing/BUILD
@@ -0,0 +1,39 @@
+# Copyright (C) 2018 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Test utilities for the Dagger model
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+)
+
+java_library(
+ name = "testing",
+ testonly = 1,
+ srcs = glob(["*.java"]),
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ deps = [
+ "//java/dagger/internal/codegen:jdk-and-guava-extras",
+ "//java/dagger/model",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/checker_framework_annotations",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/java/dagger/model/testing/BindingGraphSubject.java b/java/dagger/model/testing/BindingGraphSubject.java
new file mode 100644
index 0000000..dc17c1d
--- /dev/null
+++ b/java/dagger/model/testing/BindingGraphSubject.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.model.testing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertAbout;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import dagger.model.Binding;
+import dagger.model.BindingGraph;
+import javax.lang.model.type.TypeMirror;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/** A Truth subject for making assertions on a {@link BindingGraph}. */
+public final class BindingGraphSubject extends Subject<BindingGraphSubject, BindingGraph> {
+
+ /** Starts a fluent assertion about a {@link BindingGraph}. */
+ public static BindingGraphSubject assertThat(BindingGraph bindingGraph) {
+ return assertAbout(BindingGraphSubject::new).that(bindingGraph);
+ }
+
+ private final BindingGraph actual;
+
+ private BindingGraphSubject(FailureMetadata metadata, @NullableDecl BindingGraph actual) {
+ super(metadata, actual);
+ this.actual = actual;
+ }
+
+ /**
+ * Asserts that the graph has at least one binding with an unqualified key.
+ *
+ * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+ */
+ public void hasBindingWithKey(String type) {
+ bindingWithKey(type);
+ }
+
+ /**
+ * Asserts that the graph has at least one binding with a qualified key.
+ *
+ * @param qualifier the canonical string form of the qualifier, as returned by {@link
+ * javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()}
+ * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+ */
+ public void hasBindingWithKey(String qualifier, String type) {
+ bindingWithKey(qualifier, type);
+ }
+
+ /**
+ * Returns a subject for testing the binding for an unqualified key.
+ *
+ * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+ */
+ public BindingSubject bindingWithKey(String type) {
+ return bindingWithKeyString(keyString(type));
+ }
+
+ /**
+ * Returns a subject for testing the binding for a qualified key.
+ *
+ * @param qualifier the canonical string form of the qualifier, as returned by {@link
+ * javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()}
+ * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+ */
+ public BindingSubject bindingWithKey(String qualifier, String type) {
+ return bindingWithKeyString(keyString(qualifier, type));
+ }
+
+ private BindingSubject bindingWithKeyString(String keyString) {
+ ImmutableSet<Binding> bindings = getBindingNodes(keyString);
+ // TODO(dpb): Handle multiple bindings for the same key.
+ check("bindingsWithKey(%s)", keyString).that(bindings).hasSize(1);
+ return check("bindingWithKey(%s)", keyString)
+ .about(BindingSubject::new)
+ .that(getOnlyElement(bindings));
+ }
+
+ private ImmutableSet<Binding> getBindingNodes(String keyString) {
+ return actual.bindings().stream()
+ .filter(binding -> binding.key().toString().equals(keyString))
+ .collect(toImmutableSet());
+ }
+
+ private static String keyString(String type) {
+ return type;
+ }
+
+ private static String keyString(String qualifier, String type) {
+ return String.format("%s %s", qualifier, type);
+ }
+
+ /** A Truth subject for a {@link Binding}. */
+ public final class BindingSubject extends Subject<BindingSubject, Binding> {
+
+ private final Binding actual;
+
+ BindingSubject(FailureMetadata metadata, @NullableDecl Binding actual) {
+ super(metadata, actual);
+ this.actual = actual;
+ }
+
+ /**
+ * Asserts that the binding depends on a binding with an unqualified key.
+ *
+ * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+ */
+ public void dependsOnBindingWithKey(String type) {
+ dependsOnBindingWithKeyString(keyString(type));
+ }
+
+ /**
+ * Asserts that the binding depends on a binding with a qualified key.
+ *
+ * @param qualifier the canonical string form of the qualifier, as returned by {@link
+ * javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()}
+ * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+ */
+ public void dependsOnBindingWithKey(String qualifier, String type) {
+ dependsOnBindingWithKeyString(keyString(qualifier, type));
+ }
+
+ private void dependsOnBindingWithKeyString(String keyString) {
+ if (actualBindingGraph().requestedBindings(actual).stream()
+ .noneMatch(binding -> binding.key().toString().equals(keyString))) {
+ failWithActual("expected to depend on binding with key", keyString);
+ }
+ }
+
+ private BindingGraph actualBindingGraph() {
+ return BindingGraphSubject.this.actual;
+ }
+ }
+}
diff --git a/java/dagger/multibindings/ClassKey.java b/java/dagger/multibindings/ClassKey.java
new file mode 100644
index 0000000..ac25545
--- /dev/null
+++ b/java/dagger/multibindings/ClassKey.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.MapKey;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A {@link MapKey} annotation for maps with {@code Class<?>} keys.
+ *
+ * <p>If your map's keys can be constrained, consider using a custom annotation instead, with a
+ * member whose type is {@code Class<? extends Something>}.
+ */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+@MapKey
+public @interface ClassKey {
+ Class<?> value();
+}
diff --git a/java/dagger/multibindings/ElementsIntoSet.java b/java/dagger/multibindings/ElementsIntoSet.java
new file mode 100644
index 0000000..5ed68c6
--- /dev/null
+++ b/java/dagger/multibindings/ElementsIntoSet.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The method's return type is {@code Set<T>} and all values are contributed to the set. The {@code
+ * Set<T>} produced from the accumulation of values will be immutable. An example use is to provide
+ * a default empty set binding, which is otherwise not possible using {@link IntoSet}.
+ *
+ * @see <a href="https://dagger.dev/multibindings#set-multibindings">Set multibinding</a>
+ */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface ElementsIntoSet {}
diff --git a/java/dagger/multibindings/IntKey.java b/java/dagger/multibindings/IntKey.java
new file mode 100644
index 0000000..55e79a1
--- /dev/null
+++ b/java/dagger/multibindings/IntKey.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.MapKey;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** A {@link MapKey} annotation for maps with {@code int} keys. */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+@MapKey
+public @interface IntKey {
+ int value();
+}
diff --git a/java/dagger/multibindings/IntoMap.java b/java/dagger/multibindings/IntoMap.java
new file mode 100644
index 0000000..d760229
--- /dev/null
+++ b/java/dagger/multibindings/IntoMap.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The method's return type forms the type argument for the value of a {@code Map<K, Provider<V>>},
+ * and the combination of the annotated key and the returned value is contributed to the map as a
+ * key/value pair. The {@code Map<K, Provider<V>>} produced from the accumulation of values will be
+ * immutable.
+ *
+ * @see <a href="https://dagger.dev/multibindings#map-multibindings">Map multibinding</a>
+ */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface IntoMap {}
diff --git a/java/dagger/multibindings/IntoSet.java b/java/dagger/multibindings/IntoSet.java
new file mode 100644
index 0000000..b4fdcc4
--- /dev/null
+++ b/java/dagger/multibindings/IntoSet.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The method's return type forms the generic type argument of a {@code Set<T>}, and the returned
+ * value is contributed to the set. The object graph will pass dependencies to the method as
+ * parameters. The {@code Set<T>} produced from the accumulation of values will be immutable.
+ *
+ * @see <a href="https://dagger.dev/multibindings#set-multibindings">Set multibinding</a>
+ */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface IntoSet {}
diff --git a/java/dagger/multibindings/LongKey.java b/java/dagger/multibindings/LongKey.java
new file mode 100644
index 0000000..71d0fe1
--- /dev/null
+++ b/java/dagger/multibindings/LongKey.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.MapKey;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** A {@link MapKey} annotation for maps with {@code long} keys. */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+@MapKey
+public @interface LongKey {
+ long value();
+}
diff --git a/java/dagger/multibindings/Multibinds.java b/java/dagger/multibindings/Multibinds.java
new file mode 100644
index 0000000..e37b39e
--- /dev/null
+++ b/java/dagger/multibindings/Multibinds.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates abstract module methods that declare multibindings.
+ *
+ * <p>You can declare that a multibound set or map is bound by annotating an abstract module method
+ * that returns the set or map you want to declare with {@code @Multibinds}.
+ *
+ * <p>You do not have to use {@code @Multibinds} for sets or maps that have at least one
+ * contribution, but you do have to declare them if they may be empty.
+ *
+ * <pre><code>
+ * {@literal @Module} abstract class MyModule {
+ * {@literal @Multibinds abstract Set<Foo> aSet();}
+ * {@literal @Multibinds abstract @MyQualifier Set<Foo> aQualifiedSet();}
+ * {@literal @Multibinds abstract Map<String, Foo> aMap();}
+ * {@literal @Multibinds abstract @MyQualifier Map<String, Foo> aQualifiedMap();}
+ *
+ * {@literal @Provides}
+ * {@literal static Object usesMultibindings(Set<Foo> set, @MyQualifier Map<String, Foo> map}) {
+ * return …
+ * }
+ * }</code></pre>
+ *
+ * <p>A given set or map multibinding can be declared any number of times without error. Dagger
+ * never implements or calls any {@code @Multibinds} methods.
+ *
+ * @see <a href="https://dagger.dev/multibindings">Multibindings</a>
+ */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface Multibinds {}
diff --git a/java/dagger/multibindings/StringKey.java b/java/dagger/multibindings/StringKey.java
new file mode 100644
index 0000000..5dad8e3
--- /dev/null
+++ b/java/dagger/multibindings/StringKey.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.MapKey;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** A {@link MapKey} annotation for maps with {@link String} keys. */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+@MapKey
+public @interface StringKey {
+ String value();
+}
diff --git a/java/dagger/multibindings/package-info.java b/java/dagger/multibindings/package-info.java
new file mode 100644
index 0000000..d6fe1e1
--- /dev/null
+++ b/java/dagger/multibindings/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the API by which Dagger allows you to bind several objects into a
+ * collection that can be injected without depending directly on each of the individual bindings.
+ *
+ * @see <a href="https://dagger.dev/multibindings">Multibindings in the Dagger User's Guide</a>
+ */
+package dagger.multibindings;
diff --git a/java/dagger/package-info.java b/java/dagger/package-info.java
new file mode 100644
index 0000000..b680a85
--- /dev/null
+++ b/java/dagger/package-info.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the public API for the <a href="https://dagger.dev/">Dagger 2</a> dependency
+ * injection framework. By building upon <a href="https://jcp.org/en/jsr/detail?id=330">JSR 330</a>,
+ * Dagger 2 provides an annotation-driven API for dependency injection whose implementation is
+ * entirely generated at compile time by <a
+ * href="http://en.wikipedia.org/wiki/Java_annotation#Processing">annotation processors</a>.
+ *
+ * <p>The entry point into the API is the {@link Component}, which annotates abstract types for
+ * Dagger 2 to implement. The dependency graph is configured using annotations such as {@link
+ * Module}, {@link Provides} and {@link javax.inject.Inject}.
+ *
+ * <p>{@code dagger.internal.codegen.ComponentProcessor} is the processor responsible for generating
+ * the implementation. Dagger uses the annotation procesor {@linkplain java.util.ServiceLoader
+ * service loader} to automatically configure the processor, so explict build configuration
+ * shouldn't be necessary.
+ */
+package dagger;
diff --git a/java/dagger/producers/BUILD b/java/dagger/producers/BUILD
new file mode 100644
index 0000000..ad065a1
--- /dev/null
+++ b/java/dagger/producers/BUILD
@@ -0,0 +1,74 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# An asynchronous dependency injection system that extends JSR-330.
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+ "SOURCE_7_TARGET_7",
+)
+load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+
+# Work around b/70476182 which prevents Kythe from connecting :producers to the .java files it
+# contains.
+SRCS = glob(["**/*.java"])
+
+filegroup(
+ name = "producers-srcs",
+ srcs = SRCS,
+)
+
+java_library(
+ name = "producers",
+ srcs = SRCS,
+ javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ tags = ["maven_coordinates=com.google.dagger:dagger-producers:" + POM_VERSION],
+ exports = [
+ # TODO(dpb): Don't export any of Guava.
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+ deps = [
+ "//java/dagger:core",
+ "@google_bazel_common//third_party/java/checker_framework_annotations",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-producers",
+ artifact_name = "Dagger Producers",
+ targets = [":producers"],
+)
+
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+javadoc_library(
+ name = "producers-javadoc",
+ srcs = SRCS,
+ exclude_packages = [
+ "dagger.producers.internal",
+ "dagger.producers.monitoring.internal",
+ ],
+ root_packages = ["dagger.producers"],
+ deps = [":producers"],
+)
diff --git a/java/dagger/producers/CancellationPolicy.java b/java/dagger/producers/CancellationPolicy.java
new file mode 100644
index 0000000..70f4a43
--- /dev/null
+++ b/java/dagger/producers/CancellationPolicy.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a production component or subcomponent to specify its policy when a child component is
+ * cancelled.
+ *
+ * <p>When a future returned from an entry point on a production component is cancelled, the
+ * component is cancelled: all producers in the component (including those for other entry points)
+ * are cancelled.
+ *
+ * <p>When a child component is cancelled, its parent component <i>is not</i> cancelled unless the
+ * parent component is annotated with {@code @CancellationPolicy(fromSubcomponents = PROPAGATE)}. If
+ * that parent component has a parent (the grandparent of the cancelled child component), it will
+ * not be cancelled unless it also has a {@code @CancellationPolicy} annotation allowing
+ * cancellation to propagate to it from subcomponents.
+ */
+@Documented
+@Target(TYPE)
+@Retention(CLASS)
+@Beta
+public @interface CancellationPolicy {
+ /**
+ * Defines whether the annotated production component is cancelled when a child component is
+ * cancelled.
+ *
+ * <p>The default, if no cancellation policy annotation is provided, is {@link
+ * Propagation#IGNORE}.
+ */
+ Propagation fromSubcomponents();
+
+ /**
+ * Enumeration of the options for what happens to a parent component when one of its child
+ * components is cancelled.
+ */
+ enum Propagation {
+ /** Cancel the annotated component when a child component is cancelled. */
+ PROPAGATE,
+
+ /** Do not cancel the annotated component when a child component is cancelled. */
+ IGNORE
+ }
+}
diff --git a/java/dagger/producers/Produced.java b/java/dagger/producers/Produced.java
new file mode 100644
index 0000000..5b7847f
--- /dev/null
+++ b/java/dagger/producers/Produced.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.internal.Beta;
+import java.util.concurrent.ExecutionException;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/**
+ * An interface that represents the result of a {@linkplain Producer production} of type {@code T},
+ * or an exception that was thrown during that production. For any type {@code T} that can be
+ * injected, you can also inject {@code Produced<T>}, which enables handling of any exceptions that
+ * were thrown during the production of {@code T}.
+ *
+ * <p>For example: <pre><code>
+ * {@literal @}Produces Html getResponse(
+ * UserInfo criticalInfo, {@literal Produced<ExtraInfo>} noncriticalInfo) {
+ * try {
+ * return new Html(criticalInfo, noncriticalInfo.get());
+ * } catch (ExecutionException e) {
+ * logger.warning(e, "Noncritical info");
+ * return new Html(criticalInfo);
+ * }
+ * }
+ * </code></pre>
+ *
+ * @since 2.0
+ */
+@Beta
+@CheckReturnValue
+public abstract class Produced<T> {
+ /**
+ * Returns the result of a production.
+ *
+ * @throws ExecutionException if the production threw an exception
+ */
+ public abstract T get() throws ExecutionException;
+
+ /**
+ * Two {@code Produced} objects compare equal if both are successful with equal values, or both
+ * are failed with equal exceptions.
+ */
+ @Override
+ public abstract boolean equals(Object o);
+
+ /** Returns an appropriate hash code to match {@link #equals(Object)}. */
+ @Override
+ public abstract int hashCode();
+
+ /** Returns a successful {@code Produced}, whose {@link #get} will return the given value. */
+ public static <T> Produced<T> successful(@NullableDecl T value) {
+ return new Successful<T>(value);
+ }
+
+ /**
+ * Returns a failed {@code Produced}, whose {@link #get} will throw an
+ * {@code ExecutionException} with the given cause.
+ */
+ public static <T> Produced<T> failed(Throwable throwable) {
+ return new Failed<T>(checkNotNull(throwable));
+ }
+
+ private static final class Successful<T> extends Produced<T> {
+ @NullableDecl private final T value;
+
+ private Successful(@NullableDecl T value) {
+ this.value = value;
+ }
+
+ @Override
+ @NullableDecl
+ public T get() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ } else if (o instanceof Successful) {
+ Successful<?> that = (Successful<?>) o;
+ return Objects.equal(this.value, that.value);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return value == null ? 0 : value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Produced[" + value + "]";
+ }
+ }
+
+ private static final class Failed<T> extends Produced<T> {
+ private final Throwable throwable;
+
+ private Failed(Throwable throwable) {
+ this.throwable = checkNotNull(throwable);
+ }
+
+ @Override
+ public T get() throws ExecutionException {
+ throw new ExecutionException(throwable);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ } else if (o instanceof Failed) {
+ Failed<?> that = (Failed<?>) o;
+ return this.throwable.equals(that.throwable);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return throwable.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Produced[failed with " + throwable.getClass().getCanonicalName() + "]";
+ }
+ }
+
+ private Produced() {}
+}
diff --git a/java/dagger/producers/Producer.java b/java/dagger/producers/Producer.java
new file mode 100644
index 0000000..8485f81
--- /dev/null
+++ b/java/dagger/producers/Producer.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.internal.Beta;
+
+/**
+ * An interface that represents the production of a type {@code T}. You can also inject
+ * {@code Producer<T>} instead of {@code T}, which will delay the execution of any code that
+ * produces the {@code T} until {@link #get} is called.
+ *
+ * <p>For example, you might inject {@code Producer} to lazily choose between several different
+ * implementations of some type: <pre><code>
+ * {@literal @Produces ListenableFuture<Heater>} getHeater(
+ * HeaterFlag flag,
+ * {@literal @Electric Producer<Heater>} electricHeater,
+ * {@literal @Gas Producer<Heater>} gasHeater) {
+ * return flag.useElectricHeater() ? electricHeater.get() : gasHeater.get();
+ * }
+ * </code></pre>
+ *
+ * <p>Here is a complete example that demonstrates how calling {@code get()} will cause each
+ * method to be executed: <pre><code>
+ *
+ * {@literal @}ProducerModule
+ * final class MyModule {
+ * {@literal @Produces ListenableFuture<A>} a() {
+ * System.out.println("a");
+ * return Futures.immediateFuture(new A());
+ * }
+ *
+ * {@literal @Produces ListenableFuture<B>} b(A a) {
+ * System.out.println("b");
+ * return Futures.immediateFuture(new B(a));
+ * }
+ *
+ * {@literal @Produces ListenableFuture<C>} c(B b) {
+ * System.out.println("c");
+ * return Futures.immediateFuture(new C(b));
+ * }
+ *
+ * {@literal @Produces @Delayed ListenableFuture<C>} delayedC(A a, {@literal Producer<C>} c) {
+ * System.out.println("delayed c");
+ * return c.get();
+ * }
+ * }
+ *
+ * {@literal @}ProductionComponent(modules = MyModule.class)
+ * interface MyComponent {
+ * {@literal @Delayed ListenableFuture<C>} delayedC();
+ * }
+ * </code></pre>
+ * Suppose we instantiate the generated implementation of this component and call
+ * {@code delayedC()}: <pre><code>
+ * MyComponent component = DaggerMyComponent
+ * .builder()
+ * .executor(MoreExecutors.directExecutor())
+ * .build();
+ * System.out.println("Constructed component");
+ * {@literal ListenableFuture<C>} cFuture = component.delayedC();
+ * System.out.println("Retrieved future");
+ * C c = cFuture.get();
+ * System.out.println("Retrieved c");
+ * </code></pre>
+ * Here, we're using {@code MoreExecutors.directExecutor} in order to illustrate how each call
+ * directly causes code to execute. The above code will print: <pre><code>
+ * Constructed component
+ * a
+ * delayed c
+ * b
+ * c
+ * Retrieved future
+ * Retrieved c
+ * </code></pre>
+ *
+ * @since 2.0
+ */
+@Beta
+public interface Producer<T> {
+ /**
+ * Returns a future representing a running task that produces a value. Calling this method will
+ * trigger the submission of this task to the executor, if it has not already been triggered. In
+ * order to trigger this task's submission, the transitive dependencies required to produce the
+ * {@code T} will be submitted to the executor, as their dependencies become available.
+ *
+ * <p>If the key is bound to a {@link Produces} method, then calling this method multiple times
+ * will return the same future.
+ */
+ @CheckReturnValue
+ ListenableFuture<T> get();
+}
diff --git a/java/dagger/producers/ProducerModule.java b/java/dagger/producers/ProducerModule.java
new file mode 100644
index 0000000..e14d450
--- /dev/null
+++ b/java/dagger/producers/ProducerModule.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.Module;
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a class that contributes {@link Produces} bindings to the production component.
+ *
+ * @since 2.0
+ */
+@Documented
+@Target(TYPE)
+@Retention(RUNTIME)
+@Beta
+public @interface ProducerModule {
+ /**
+ * Additional {@code @ProducerModule}- or {@link Module}-annotated classes from which this module
+ * is composed. The de-duplicated contributions of the modules in {@code includes}, and of their
+ * inclusions recursively, are all contributed to the object graph.
+ */
+ Class<?>[] includes() default {};
+
+ /**
+ * Any {@link dagger.Subcomponent}- or {@link ProductionSubcomponent}-annotated classes which
+ * should be children of the component in which this module is installed. A subcomponent may be
+ * listed in more than one module in a component.
+ *
+ * @since 2.7
+ */
+ Class<?>[] subcomponents() default {};
+}
diff --git a/java/dagger/producers/Producers.java b/java/dagger/producers/Producers.java
new file mode 100644
index 0000000..1c8da03
--- /dev/null
+++ b/java/dagger/producers/Producers.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.Beta;
+import dagger.producers.internal.CancellableProducer;
+import dagger.producers.internal.CancellationListener;
+
+/** Utility methods to create {@link Producer}s. */
+@Beta
+public final class Producers {
+ /** Returns a producer that succeeds with the given value. */
+ public static <T> Producer<T> immediateProducer(final T value) {
+ return new ImmediateProducer<>(Futures.immediateFuture(value));
+ }
+
+ /** Returns a producer that fails with the given exception. */
+ public static <T> Producer<T> immediateFailedProducer(final Throwable throwable) {
+ return new ImmediateProducer<>(Futures.<T>immediateFailedFuture(throwable));
+ }
+
+ /** A {@link CancellableProducer} with an immediate result. */
+ private static final class ImmediateProducer<T> implements CancellableProducer<T> {
+ private final ListenableFuture<T> future;
+
+ ImmediateProducer(ListenableFuture<T> future) {
+ this.future = future;
+ }
+
+ @Override
+ public ListenableFuture<T> get() {
+ return future;
+ }
+
+ @Override
+ public void cancel(boolean mayInterruptIfRunning) {}
+
+ @Override
+ public Producer<T> newDependencyView() {
+ return this;
+ }
+
+ @Override
+ public Producer<T> newEntryPointView(CancellationListener cancellationListener) {
+ return this;
+ }
+ }
+
+ private Producers() {}
+}
diff --git a/java/dagger/producers/Produces.java b/java/dagger/producers/Produces.java
new file mode 100644
index 0000000..df859ad
--- /dev/null
+++ b/java/dagger/producers/Produces.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates methods of a producer module to create a production binding. If the method returns a
+ * {@link com.google.common.util.concurrent.ListenableFuture} or {@link
+ * com.google.common.util.concurrent.FluentFuture}, then the parameter type of the future is bound
+ * to the value that the future produces; otherwise, the return type is bound to the returned value.
+ * The production component will pass dependencies to the method as parameters.
+ *
+ * @since 2.0
+ */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+@Beta
+public @interface Produces {}
diff --git a/java/dagger/producers/Production.java b/java/dagger/producers/Production.java
new file mode 100644
index 0000000..563fc16
--- /dev/null
+++ b/java/dagger/producers/Production.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifies a type that will be provided to the framework for use internally.
+ *
+ * <p>The only type that may be so qualified is {@link java.util.concurrent.Executor}. In this case,
+ * the resulting executor is used to schedule {@linkplain Produces producer methods} in a
+ * {@link ProductionComponent} or {@link ProductionSubcomponent}.
+ */
+@Documented
+@Retention(RUNTIME)
+@Qualifier
+@Beta
+public @interface Production {}
diff --git a/java/dagger/producers/ProductionComponent.java b/java/dagger/producers/ProductionComponent.java
new file mode 100644
index 0000000..a0337cf
--- /dev/null
+++ b/java/dagger/producers/ProductionComponent.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+
+/**
+ * Annotates an interface or abstract class for which a fully-formed, dependency-injected
+ * implementation is to be generated from a set of {@linkplain #modules modules}. The generated
+ * class will have the name of the type annotated with {@code @ProductionComponent} prepended with
+ * {@code Dagger}. For example, {@code @ProductionComponent interface MyComponent {...}} will
+ * produce an implementation named {@code DaggerMyComponent}.
+ *
+ * <p>Each {@link Produces} method that contributes to the component will be called at most once per
+ * component instance, no matter how many times that binding is used as a dependency. TODO(beder):
+ * Decide on how scope works for producers.
+ *
+ * <h2>Component methods</h2>
+ *
+ * <p>Every type annotated with {@code @ProductionComponent} must contain at least one abstract
+ * component method. Component methods must represent {@linkplain Producer production}.
+ *
+ * <p>Production methods have no arguments and return either a {@link ListenableFuture} or {@link
+ * Producer} of a type that is {@link Inject injected}, {@link Provides provided}, or {@link
+ * Produces produced}. Each may have a {@link Qualifier} annotation as well. The following are all
+ * valid production method declarations:
+ *
+ * <pre><code>
+ * {@literal ListenableFuture<SomeType>} getSomeType();
+ * {@literal Producer<Set<SomeType>>} getSomeTypes();
+ * {@literal @Response ListenableFuture<Html>} getResponse();
+ * </code></pre>
+ *
+ * <h2>Exceptions</h2>
+ *
+ * <p>When a producer throws an exception, the exception will be propagated to its downstream
+ * producers in the following way: if the downstream producer injects a type {@code T}, then that
+ * downstream producer will be skipped, and the exception propagated to its downstream producers;
+ * and if the downstream producer injects a {@code Produced<T>}, then the downstream producer will
+ * be run with the exception stored in the {@code Produced<T>}.
+ *
+ * <p>If a non-execution exception is thrown (e.g., an {@code InterruptedException} or {@code
+ * CancellationException}), then exception is handled as in {@link
+ * com.google.common.util.concurrent.Futures#transform}.
+ * <!-- TODO(beder): Explain this more thoroughly, and update the javadocs of those utilities. -->
+ *
+ * <h2>Executor</h2>
+ *
+ * <p>The component must include a binding for <code>{@literal @}{@link Production}
+ * {@link java.util.concurrent.Executor}</code>; this binding will be called exactly once, and the
+ * provided executor will be used by the framework to schedule all producer methods (for this
+ * component, and any {@link ProductionSubcomponent} it may have.
+ *
+ * @since 2.0
+ */
+@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+@Documented
+@Target(TYPE)
+@Beta
+public @interface ProductionComponent {
+ /**
+ * A list of classes annotated with {@link Module} or {@link ProducerModule} whose bindings are
+ * used to generate the component implementation.
+ */
+ Class<?>[] modules() default {};
+
+ /**
+ * A list of types that are to be used as component dependencies.
+ */
+ Class<?>[] dependencies() default {};
+
+ /**
+ * A builder for a production component.
+ *
+ * <p>This follows all the rules of {@link Component.Builder}, except it must appear in classes
+ * annotated with {@link ProductionComponent} instead of {@code Component}.
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Builder {}
+
+ /**
+ * A factory for a production component.
+ *
+ * <p>This follows all the rules of {@link Component.Factory}, except it must appear in classes
+ * annotated with {@link ProductionComponent} instead of {@code Component}.
+ *
+ * @since 2.22
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Factory {}
+}
diff --git a/java/dagger/producers/ProductionScope.java b/java/dagger/producers/ProductionScope.java
new file mode 100644
index 0000000..393c240
--- /dev/null
+++ b/java/dagger/producers/ProductionScope.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * A scope annotation for provision bindings that are tied to the lifetime of a
+ * {@link ProductionComponent} or {@link ProductionSubcomponent}.
+ */
+@Documented
+@Retention(RUNTIME)
+@Scope
+public @interface ProductionScope {}
diff --git a/java/dagger/producers/ProductionSubcomponent.java b/java/dagger/producers/ProductionSubcomponent.java
new file mode 100644
index 0000000..5774945
--- /dev/null
+++ b/java/dagger/producers/ProductionSubcomponent.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Subcomponent;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A subcomponent that inherits the bindings from a parent {@link Component}, {@link Subcomponent},
+ * {@link ProductionComponent}, or {@link ProductionSubcomponent}. The details of how to associate a
+ * subcomponent with a parent are described in the documentation for {@link Component}.
+ *
+ * <p>The executor for a production subcomponent is supplied by binding
+ * <code>{@literal @}Production Executor</code>, similar to {@link ProductionComponent}. Note that
+ * this binding may be in an ancestor component.
+ *
+ * @since 2.1
+ */
+@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+@Target(TYPE)
+@Documented
+public @interface ProductionSubcomponent {
+ /**
+ * A list of classes annotated with {@link Module} or {@link ProducerModule} whose bindings are
+ * used to generate the subcomponent implementation. Note that through the use of
+ * {@link Module#includes} or {@link ProducerModule#includes} the full set of modules used to
+ * implement the subcomponent may include more modules that just those listed here.
+ */
+ Class<?>[] modules() default {};
+
+ /**
+ * A builder for a production subcomponent.
+ *
+ * <p>This follows all the rules of {@link Component.Builder}, except it must appear in classes
+ * annotated with {@link ProductionSubcomponent} instead of {@code Component}.
+ *
+ * <p>If a subcomponent defines a builder, its parent component(s) will have a binding for that
+ * builder type, allowing an instance or {@code Provider} of that builder to be injected or
+ * returned from a method on that component like any other binding.
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Builder {}
+
+ /**
+ * A factory for a production subcomponent.
+ *
+ * <p>This follows all the rules of {@link Component.Factory}, except it must appear in classes
+ * annotated with {@link ProductionSubcomponent} instead of {@code Component}.
+ *
+ * <p>If a subcomponent defines a factory, its parent component(s) will have a binding for that
+ * factory type, allowing an instance that factory to be injected or returned from a method on
+ * that component like any other binding.
+ *
+ * @since 2.22
+ */
+ @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
+ @Target(TYPE)
+ @Documented
+ @interface Factory {}
+}
diff --git a/java/dagger/producers/internal/AbstractMapProducer.java b/java/dagger/producers/internal/AbstractMapProducer.java
new file mode 100644
index 0000000..c60f0cb
--- /dev/null
+++ b/java/dagger/producers/internal/AbstractMapProducer.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.producers.internal.Producers.producerFromProvider;
+
+import com.google.common.collect.ImmutableMap;
+import dagger.producers.Producer;
+import java.util.Map;
+import javax.inject.Provider;
+
+/**
+ * An {@code abstract} {@link Producer} implementation used to implement {@link Map} bindings.
+ *
+ * @param <K>The key type of the map that this produces
+ * @param <V>The type that each contributing producer
+ * @param <V2>The value type of the map that this produces. For {@link MapProducer}, {@code V} and
+ * {@code V2} will be equivalent.
+ */
+abstract class AbstractMapProducer<K, V, V2> extends AbstractProducer<Map<K, V2>> {
+ private final ImmutableMap<K, Producer<V>> contributingMap;
+
+ AbstractMapProducer(ImmutableMap<K, Producer<V>> contributingMap) {
+ this.contributingMap = contributingMap;
+ }
+
+ /** The map of {@link Producer}s that contribute to this map binding. */
+ final ImmutableMap<K, Producer<V>> contributingMap() {
+ return contributingMap;
+ }
+
+ /** A builder for {@link AbstractMapProducer} */
+ public abstract static class Builder<K, V, V2> {
+ final ImmutableMap.Builder<K, Producer<V>> mapBuilder;
+
+ Builder(int size) {
+ mapBuilder = ImmutableMap.builderWithExpectedSize(size);
+ }
+
+ // Unfortunately, we cannot return a self-type here because a raw Producer type passed to one of
+ // these methods affects the returned type of the method. The first put*() call erases the self
+ // type to the "raw" self type, and the second erases the type to the upper bound
+ // (AbstractMapProducer.Builder), which doesn't have a build() method.
+ //
+ // The methods are therefore not declared public so that each subtype will redeclare them and
+ // expand their accessibility
+
+ /** Associates {@code key} with {@code producerOfValue}. */
+ Builder<K, V, V2> put(K key, Producer<V> producerOfValue) {
+ checkNotNull(key, "key");
+ checkNotNull(producerOfValue, "producer of value");
+ mapBuilder.put(key, producerOfValue);
+ return this;
+ }
+
+ /** Associates {@code key} with {@code providerOfValue}. */
+ Builder<K, V, V2> put(K key, Provider<V> providerOfValue) {
+ checkNotNull(key, "key");
+ checkNotNull(providerOfValue, "provider of value");
+ mapBuilder.put(key, producerFromProvider(providerOfValue));
+ return this;
+ }
+
+ /** Adds contributions from a super-implementation of a component into this builder. */
+ Builder<K, V, V2> putAll(Producer<Map<K, V2>> mapOfProducers) {
+ if (mapOfProducers instanceof DelegateProducer) {
+ @SuppressWarnings("unchecked")
+ DelegateProducer<Map<K, V2>> asDelegateProducer = (DelegateProducer) mapOfProducers;
+ return putAll(asDelegateProducer.getDelegate());
+ }
+ @SuppressWarnings("unchecked")
+ AbstractMapProducer<K, V, ?> asAbstractMapProducer =
+ ((AbstractMapProducer<K, V, ?>) (Producer) mapOfProducers);
+ mapBuilder.putAll(asAbstractMapProducer.contributingMap);
+ return this;
+ }
+ }
+}
diff --git a/java/dagger/producers/internal/AbstractProducer.java b/java/dagger/producers/internal/AbstractProducer.java
new file mode 100644
index 0000000..3dcd906
--- /dev/null
+++ b/java/dagger/producers/internal/AbstractProducer.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import com.google.common.util.concurrent.AbstractFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** An abstract {@link Producer} implementation that memoizes the result of its compute method. */
+public abstract class AbstractProducer<T> implements CancellableProducer<T> {
+ private final AtomicBoolean requested = new AtomicBoolean();
+ private final NonExternallyCancellableFuture<T> future = new NonExternallyCancellableFuture<T>();
+
+ protected AbstractProducer() {}
+
+ /** Computes this producer's future, which is then cached in {@link #get}. */
+ protected abstract ListenableFuture<T> compute();
+
+ @Override
+ public final ListenableFuture<T> get() {
+ if (requested.compareAndSet(false, true)) {
+ future.setFuture(compute());
+ }
+ return future;
+ }
+
+ @Override
+ public final void cancel(boolean mayInterruptIfRunning) {
+ requested.set(true); // Avoid potentially starting the task later only to cancel it immediately.
+ future.doCancel(mayInterruptIfRunning);
+ }
+
+ @Override
+ public Producer<T> newDependencyView() {
+ return new NonCancellationPropagatingView();
+ }
+
+ @Override
+ public Producer<T> newEntryPointView(CancellationListener cancellationListener) {
+ NonCancellationPropagatingView result = new NonCancellationPropagatingView();
+ result.addCancellationListener(cancellationListener);
+ return result;
+ }
+
+ /**
+ * A view of this producer that returns a future that can be cancelled without cancelling the
+ * producer itself.
+ */
+ private final class NonCancellationPropagatingView implements Producer<T> {
+ /**
+ * An independently cancellable view of this node. Needs to be cancellable by normal future
+ * cancellation so that the view at an entry point can listen for its cancellation.
+ */
+ private final ListenableFuture<T> viewFuture = nonCancellationPropagating(future);
+
+ @SuppressWarnings("FutureReturnValueIgnored")
+ @Override
+ public ListenableFuture<T> get() {
+ AbstractProducer.this.get(); // force compute()
+ return viewFuture;
+ }
+
+ void addCancellationListener(final CancellationListener cancellationListener) {
+ viewFuture.addListener(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (viewFuture.isCancelled()) {
+ boolean mayInterruptIfRunning =
+ viewFuture instanceof NonCancellationPropagatingFuture
+ && ((NonCancellationPropagatingFuture) viewFuture).interrupted();
+ cancellationListener.onProducerFutureCancelled(mayInterruptIfRunning);
+ }
+ }
+ },
+ directExecutor());
+ }
+ }
+
+ /** A settable future that can't be cancelled via normal future cancellation. */
+ private static final class NonExternallyCancellableFuture<T> extends AbstractFuture<T> {
+
+ @Override
+ public boolean setFuture(ListenableFuture<? extends T> future) {
+ return super.setFuture(future);
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ /** Actually cancels this future. */
+ void doCancel(boolean mayInterruptIfRunning) {
+ super.cancel(mayInterruptIfRunning);
+ }
+ }
+
+ private static <T> ListenableFuture<T> nonCancellationPropagating(ListenableFuture<T> future) {
+ if (future.isDone()) {
+ return future;
+ }
+ NonCancellationPropagatingFuture<T> output = new NonCancellationPropagatingFuture<T>(future);
+ future.addListener(output, directExecutor());
+ return output;
+ }
+
+ /**
+ * Equivalent to {@code Futures.nonCancellationPropagating}, but allowing us to check whether or
+ * not {@code mayInterruptIfRunning} was set when cancelling it.
+ */
+ private static final class NonCancellationPropagatingFuture<T> extends AbstractFuture<T>
+ implements Runnable {
+ // TODO(cgdecker): This is copied directly from Producers.nonCancellationPropagating, but try
+ // to find out why this doesn't need to be volatile.
+ private ListenableFuture<T> delegate;
+
+ NonCancellationPropagatingFuture(final ListenableFuture<T> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void run() {
+ // This prevents cancellation from propagating because we don't call setFuture(delegate) until
+ // delegate is already done, so calling cancel() on this future won't affect it.
+ ListenableFuture<T> localDelegate = delegate;
+ if (localDelegate != null) {
+ setFuture(localDelegate);
+ }
+ }
+
+ @Override
+ protected String pendingToString() {
+ ListenableFuture<T> localDelegate = delegate;
+ if (localDelegate != null) {
+ return "delegate=[" + localDelegate + "]";
+ }
+ return null;
+ }
+
+ @Override
+ protected void afterDone() {
+ delegate = null;
+ }
+
+ public boolean interrupted() {
+ return super.wasInterrupted();
+ }
+ }
+}
diff --git a/java/dagger/producers/internal/AbstractProducesMethodProducer.java b/java/dagger/producers/internal/AbstractProducesMethodProducer.java
new file mode 100644
index 0000000..0cf36ca
--- /dev/null
+++ b/java/dagger/producers/internal/AbstractProducesMethodProducer.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.concurrent.Executor;
+import javax.inject.Provider;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/**
+ * An {@link AbstractProducer} for all {@link dagger.producers.Produces} methods.
+ *
+ * @param <D> the type of asynchronous dependencies. These will be collected in {@link
+ * #collectDependencies()} and then made available to the {@code @Produces method in} {@link
+ * #callProducesMethod(Object)}. If there is only one asynchronous dependency, {@code D} can be
+ * the key for that dependency. If there are multiple, they should be wrapped in a list and
+ * unwrapped in {@link #callProducesMethod(Object)}.
+ * @param <T> the produced type
+ */
+public abstract class AbstractProducesMethodProducer<D, T> extends AbstractProducer<T>
+ implements AsyncFunction<D, T>, Executor {
+ private final Provider<ProductionComponentMonitor> monitorProvider;
+ @NullableDecl private final ProducerToken token;
+ private final Provider<Executor> executorProvider;
+ private volatile ProducerMonitor monitor = null;
+
+ protected AbstractProducesMethodProducer(
+ Provider<ProductionComponentMonitor> monitorProvider,
+ @NullableDecl ProducerToken token,
+ Provider<Executor> executorProvider) {
+ this.monitorProvider = checkNotNull(monitorProvider);
+ this.token = token;
+ this.executorProvider = checkNotNull(executorProvider);
+ }
+
+ @Override
+ protected final ListenableFuture<T> compute() {
+ monitor = monitorProvider.get().producerMonitorFor(token);
+ monitor.requested();
+ ListenableFuture<T> result = Futures.transformAsync(collectDependencies(), this, this);
+ monitor.addCallbackTo(result);
+ return result;
+ }
+
+ /**
+ * Collects the asynchronous dependencies to be passed to {@link
+ * Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)}.
+ */
+ protected abstract ListenableFuture<D> collectDependencies();
+
+ /** @deprecated this may only be called from the internal {@link #compute()} */
+ @Deprecated
+ @Override
+ public final ListenableFuture<T> apply(D asyncDependencies) throws Exception {
+ // NOTE(beder): We don't worry about catching exceptions from the monitor methods themselves
+ // because we'll wrap all monitoring in non-throwing monitors before we pass them to the
+ // factories.
+ monitor.methodStarting();
+ try {
+ return callProducesMethod(asyncDependencies);
+ } finally {
+ monitor.methodFinished();
+ }
+ }
+
+ /**
+ * Calls the {@link dagger.producers.Produces} method. This will always be called on the {@link
+ * Executor} provided to this producer.
+ */
+ protected abstract ListenableFuture<T> callProducesMethod(D asyncDependencies) throws Exception;
+
+ /** @deprecated this may only be called from the internal {@link #compute()} */
+ @Deprecated
+ @Override
+ public final void execute(Runnable runnable) {
+ monitor.ready();
+ executorProvider.get().execute(runnable);
+ }
+}
diff --git a/java/dagger/producers/internal/CancellableProducer.java b/java/dagger/producers/internal/CancellableProducer.java
new file mode 100644
index 0000000..6a1475e
--- /dev/null
+++ b/java/dagger/producers/internal/CancellableProducer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import dagger.producers.Producer;
+
+/** A {@link Producer} that can be cancelled directly even if it hasn't been started. */
+public interface CancellableProducer<T> extends Producer<T> {
+
+ /**
+ * Cancels this producer. If {@link #get()} has already been called, the future it returns will be
+ * cancelled if possible. If not, calling {@link #get()} will return a cancelled future and will
+ * not actually start the underlying operation.
+ *
+ * @param mayInterruptIfRunning the value that should be passed to {@code Future.cancel(boolean)}
+ * for the futures for any running tasks when cancelling them
+ */
+ void cancel(boolean mayInterruptIfRunning);
+
+ /** Returns a new view of this producer for use as a dependency of another node. */
+ Producer<T> newDependencyView();
+
+ /**
+ * Returns a new view of this producer for use as an entry point.
+ *
+ * <p>When the view's future is cancelled, the given {@code cancellableListener} will be called.
+ */
+ Producer<T> newEntryPointView(CancellationListener cancellationListener);
+}
diff --git a/java/dagger/producers/internal/CancellationListener.java b/java/dagger/producers/internal/CancellationListener.java
new file mode 100644
index 0000000..182ddc6
--- /dev/null
+++ b/java/dagger/producers/internal/CancellationListener.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+/** A listener for producer future cancellation. */
+public interface CancellationListener {
+ /** Called when the future for a producer this listener has been added to is cancelled. */
+ // Note that this name is intentionally a bit verbose to make it unlikely that it will conflict
+ // with any user-defined methods on a component.
+ void onProducerFutureCancelled(boolean mayInterruptIfRunning);
+}
diff --git a/java/dagger/producers/internal/DelegateProducer.java b/java/dagger/producers/internal/DelegateProducer.java
new file mode 100644
index 0000000..6cc7547
--- /dev/null
+++ b/java/dagger/producers/internal/DelegateProducer.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.DoubleCheck;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+
+/**
+ * A DelegateProducer that is used to stitch Producer indirection during initialization across
+ * partial subcomponent implementations.
+ */
+public final class DelegateProducer<T> implements CancellableProducer<T> {
+ private CancellableProducer<T> delegate;
+
+ @Override
+ public ListenableFuture<T> get() {
+ return delegate.get();
+ }
+
+ // TODO(ronshapiro): remove this once we can reasonably expect generated code is no longer using
+ // this method
+ @Deprecated
+ public void setDelegatedProducer(Producer<T> delegate) {
+ setDelegate(this, delegate);
+ }
+
+ /**
+ * Sets {@code delegateProducer}'s delegate producer to {@code delegate}.
+ *
+ * <p>{@code delegateProducer} must be an instance of {@link DelegateProducer}, otherwise this
+ * method will throw a {@link ClassCastException}.
+ */
+ public static <T> void setDelegate(Producer<T> delegateProducer, Producer<T> delegate) {
+ checkNotNull(delegate);
+ DelegateProducer<T> asDelegateProducer = (DelegateProducer<T>) delegateProducer;
+ if (asDelegateProducer.delegate != null) {
+ throw new IllegalStateException();
+ }
+ asDelegateProducer.delegate = (CancellableProducer<T>) delegate;
+ }
+
+ /**
+ * Returns the factory's delegate.
+ *
+ * @throws NullPointerException if the delegate has not been set
+ */
+ CancellableProducer<T> getDelegate() {
+ return checkNotNull(delegate);
+ }
+
+ @Override
+ public void cancel(boolean mayInterruptIfRunning) {
+ delegate.cancel(mayInterruptIfRunning);
+ }
+
+ @Override
+ public Producer<T> newDependencyView() {
+ return new ProducerView<T>() {
+ @Override
+ Producer<T> createDelegate() {
+ return delegate.newDependencyView();
+ }
+ };
+ }
+
+ @Override
+ public Producer<T> newEntryPointView(final CancellationListener cancellationListener) {
+ return new ProducerView<T>() {
+ @Override
+ Producer<T> createDelegate() {
+ return delegate.newEntryPointView(cancellationListener);
+ }
+ };
+ }
+
+ private abstract static class ProducerView<T> implements Producer<T> {
+ private final Provider<Producer<T>> delegate =
+ DoubleCheck.provider(
+ new Provider<Producer<T>>() {
+ @Override
+ public Producer<T> get() {
+ return createDelegate();
+ }
+ });
+
+ abstract Producer<T> createDelegate();
+
+ @Override
+ public ListenableFuture<T> get() {
+ return delegate.get().get();
+ }
+ }
+}
diff --git a/java/dagger/producers/internal/DependencyMethodProducer.java b/java/dagger/producers/internal/DependencyMethodProducer.java
new file mode 100644
index 0000000..be118f2
--- /dev/null
+++ b/java/dagger/producers/internal/DependencyMethodProducer.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import com.google.common.collect.MapMaker;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Abstract class for implementing producers derived from methods on component dependencies.
+ *
+ * <p>Unlike most other {@link CancellableProducer} implementations, cancelling the future returned
+ * by a {@linkplain #newDependencyView dependency view} injected into an {@code @Produces} method
+ * will actually cancel the underlying future. This is because the future comes from outside the
+ * component's producer graph (including possibly from another object that isn't a component at
+ * all), so if we don't cancel it when the user asks to cancel it, there might just be no way to
+ * cancel it at all.
+ */
+public abstract class DependencyMethodProducer<T> implements CancellableProducer<T> {
+
+ /** Weak set of all incomplete futures this producer has returned. */
+ private final Set<ListenableFuture<T>> futures =
+ Collections.newSetFromMap(new MapMaker().weakKeys().<ListenableFuture<T>, Boolean>makeMap());
+
+ private boolean cancelled = false;
+
+ /** Calls a method on a component dependency to get a future. */
+ protected abstract ListenableFuture<T> callDependencyMethod();
+
+ @Override
+ public final ListenableFuture<T> get() {
+ synchronized (futures) {
+ if (cancelled) {
+ return Futures.immediateCancelledFuture();
+ }
+
+ final ListenableFuture<T> future = callDependencyMethod();
+ if (!future.isDone() && futures.add(future)) {
+ future.addListener(
+ new Runnable() {
+ @Override
+ public void run() {
+ synchronized (futures) {
+ futures.remove(future);
+ }
+ }
+ },
+ directExecutor());
+ }
+ return future;
+ }
+ }
+
+ @Override
+ public final void cancel(boolean mayInterruptIfRunning) {
+ synchronized (futures) {
+ cancelled = true;
+ for (ListenableFuture<T> future : futures) {
+ // futures is a concurrent set so that the concurrent removal that will happen here is not
+ // a problem
+ future.cancel(mayInterruptIfRunning);
+ }
+ }
+ }
+
+ @Override
+ public final Producer<T> newDependencyView() {
+ return this;
+ }
+
+ @Override
+ public final Producer<T> newEntryPointView(final CancellationListener cancellationListener) {
+ return new Producer<T>() {
+ private final Set<ListenableFuture<T>> entryPointFutures =
+ Collections.newSetFromMap(
+ new MapMaker().weakKeys().<ListenableFuture<T>, Boolean>makeMap());
+
+ @Override
+ public ListenableFuture<T> get() {
+ final ListenableFuture<T> future = DependencyMethodProducer.this.get();
+ if (!future.isDone() && entryPointFutures.add(future)) {
+ future.addListener(
+ new Runnable() {
+ @Override
+ public void run() {
+ entryPointFutures.remove(future);
+ if (future.isCancelled()) {
+ // TODO(cgdecker): Make this also propagate the actual value that was passed for
+ // mayInterruptIfRunning
+ cancellationListener.onProducerFutureCancelled(true);
+ }
+ }
+ },
+ directExecutor());
+ }
+ return future;
+ }
+ };
+ }
+}
diff --git a/java/dagger/producers/internal/MapOfProducedProducer.java b/java/dagger/producers/internal/MapOfProducedProducer.java
new file mode 100644
index 0000000..bd9f1bf
--- /dev/null
+++ b/java/dagger/producers/internal/MapOfProducedProducer.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.util.concurrent.Futures.transform;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.List;
+import java.util.Map;
+import javax.inject.Provider;
+
+/**
+ * A {@link Producer} implementation used to implement {@link Map} bindings. This producer returns a
+ * {@code Map<K, Produced<V>>} which is populated by calls to the delegate {@link Producer#get}
+ * methods.
+ */
+public final class MapOfProducedProducer<K, V> extends AbstractMapProducer<K, V, Produced<V>> {
+ private MapOfProducedProducer(ImmutableMap<K, Producer<V>> contributingMap) {
+ super(contributingMap);
+ }
+
+ @Override
+ public ListenableFuture<Map<K, Produced<V>>> compute() {
+ return Futures.transform(
+ Futures.allAsList(
+ Iterables.transform(
+ contributingMap().entrySet(), MapOfProducedProducer.<K, V>entryUnwrapper())),
+ new Function<List<Map.Entry<K, Produced<V>>>, Map<K, Produced<V>>>() {
+ @Override
+ public Map<K, Produced<V>> apply(List<Map.Entry<K, Produced<V>>> entries) {
+ return ImmutableMap.copyOf(entries);
+ }
+ },
+ directExecutor());
+ }
+
+ private static final Function<
+ Map.Entry<Object, Producer<Object>>,
+ ListenableFuture<Map.Entry<Object, Produced<Object>>>>
+ ENTRY_UNWRAPPER =
+ new Function<
+ Map.Entry<Object, Producer<Object>>,
+ ListenableFuture<Map.Entry<Object, Produced<Object>>>>() {
+ @Override
+ public ListenableFuture<Map.Entry<Object, Produced<Object>>> apply(
+ final Map.Entry<Object, Producer<Object>> entry) {
+ return transform(
+ Producers.createFutureProduced(entry.getValue().get()),
+ new Function<Produced<Object>, Map.Entry<Object, Produced<Object>>>() {
+ @Override
+ public Map.Entry<Object, Produced<Object>> apply(Produced<Object> value) {
+ return Maps.immutableEntry(entry.getKey(), value);
+ }
+ },
+ directExecutor());
+ }
+ };
+
+ @SuppressWarnings({"unchecked", "rawtypes"}) // bivariate implementation
+ private static <K, V>
+ Function<Map.Entry<K, Producer<V>>, ListenableFuture<Map.Entry<K, Produced<V>>>>
+ entryUnwrapper() {
+ return (Function) ENTRY_UNWRAPPER;
+ }
+
+ /** Returns a new {@link Builder}. */
+ public static <K, V> Builder<K, V> builder(int size) {
+ return new Builder<>(size);
+ }
+
+ /** A builder for {@link MapOfProducedProducer}. */
+ public static final class Builder<K, V> extends AbstractMapProducer.Builder<K, V, Produced<V>> {
+ private Builder(int size) {
+ super(size);
+ }
+
+ @Override
+ public Builder<K, V> put(K key, Producer<V> producerOfValue) {
+ super.put(key, producerOfValue);
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+ super.put(key, providerOfValue);
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> putAll(Producer<Map<K, Produced<V>>> mapOfProducedProducer) {
+ super.putAll(mapOfProducedProducer);
+ return this;
+ }
+
+ /** Returns a new {@link MapOfProducedProducer}. */
+ public MapOfProducedProducer<K, V> build() {
+ return new MapOfProducedProducer<>(mapBuilder.build());
+ }
+ }
+}
diff --git a/java/dagger/producers/internal/MapOfProducerProducer.java b/java/dagger/producers/internal/MapOfProducerProducer.java
new file mode 100644
index 0000000..064cf74
--- /dev/null
+++ b/java/dagger/producers/internal/MapOfProducerProducer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static dagger.producers.internal.Producers.entryPointViewOf;
+import static dagger.producers.internal.Producers.nonCancellationPropagatingViewOf;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import java.util.Map;
+import javax.inject.Provider;
+
+/**
+ * A {@link Producer} implementation used to implement {@link Map} bindings. This factory returns an
+ * immediate future of {@code Map<K, Producer<V>>} when calling {@link #get}.
+ */
+public final class MapOfProducerProducer<K, V> extends AbstractMapProducer<K, V, Producer<V>> {
+ /** Returns a new {@link Builder}. */
+ public static <K, V> Builder<K, V> builder(int size) {
+ return new Builder<>(size);
+ }
+
+ private MapOfProducerProducer(ImmutableMap<K, Producer<V>> contributingMap) {
+ super(contributingMap);
+ }
+
+ @Override
+ public ListenableFuture<Map<K, Producer<V>>> compute() {
+ return Futures.<Map<K, Producer<V>>>immediateFuture(contributingMap());
+ }
+
+ /** A builder for {@link MapOfProducerProducer} */
+ public static final class Builder<K, V> extends AbstractMapProducer.Builder<K, V, Producer<V>> {
+ private Builder(int size) {
+ super(size);
+ }
+
+ @Override
+ public Builder<K, V> put(K key, Producer<V> producerOfValue) {
+ super.put(key, producerOfValue);
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+ super.put(key, providerOfValue);
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> putAll(Producer<Map<K, Producer<V>>> mapOfProducerProducer) {
+ super.putAll(mapOfProducerProducer);
+ return this;
+ }
+
+ /** Returns a new {@link MapOfProducerProducer}. */
+ public MapOfProducerProducer<K, V> build() {
+ return new MapOfProducerProducer<>(mapBuilder.build());
+ }
+ }
+
+ @Override
+ public Producer<Map<K, Producer<V>>> newDependencyView() {
+ return newTransformedValuesView(MapOfProducerProducer.<V>toDependencyView());
+ }
+
+ @Override
+ public Producer<Map<K, Producer<V>>> newEntryPointView(
+ CancellationListener cancellationListener) {
+ return newTransformedValuesView(
+ MapOfProducerProducer.<V>toEntryPointView(cancellationListener));
+ }
+
+ private Producer<Map<K, Producer<V>>> newTransformedValuesView(
+ Function<Producer<V>, Producer<V>> valueTransformationFunction) {
+ return dagger.producers.Producers.<Map<K, Producer<V>>>immediateProducer(
+ ImmutableMap.copyOf(Maps.transformValues(contributingMap(), valueTransformationFunction)));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> Function<Producer<T>, Producer<T>> toDependencyView() {
+ return (Function) TO_DEPENDENCY_VIEW;
+ }
+
+ private static <T> Function<Producer<T>, Producer<T>> toEntryPointView(
+ final CancellationListener cancellationListener) {
+ return new Function<Producer<T>, Producer<T>>() {
+ @Override
+ public Producer<T> apply(Producer<T> input) {
+ return entryPointViewOf(input, cancellationListener);
+ }
+ };
+ }
+
+ private static final Function<Producer<?>, Producer<?>> TO_DEPENDENCY_VIEW =
+ new Function<Producer<?>, Producer<?>>() {
+ @Override
+ public Producer<?> apply(Producer<?> input) {
+ return nonCancellationPropagatingViewOf(input);
+ }
+ };
+}
diff --git a/java/dagger/producers/internal/MapProducer.java b/java/dagger/producers/internal/MapProducer.java
new file mode 100644
index 0000000..8caeb45
--- /dev/null
+++ b/java/dagger/producers/internal/MapProducer.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.inject.Provider;
+
+/**
+ * A {@link Producer} implementation used to implement {@link Map} bindings. This producer returns a
+ * {@code Map<K, V>} which is populated by calls to the delegate {@link Producer#get} methods.
+ */
+public final class MapProducer<K, V> extends AbstractMapProducer<K, V, V> {
+ private MapProducer(ImmutableMap<K, Producer<V>> contributingMap) {
+ super(contributingMap);
+ }
+
+ /** Returns a new {@link Builder}. */
+ public static <K, V> Builder<K, V> builder(int size) {
+ return new Builder<>(size);
+ }
+
+ /** A builder for {@link MapProducer} */
+ public static final class Builder<K, V> extends AbstractMapProducer.Builder<K, V, V> {
+ private Builder(int size) {
+ super(size);
+ }
+
+ @Override
+ public Builder<K, V> put(K key, Producer<V> producerOfValue) {
+ super.put(key, producerOfValue);
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+ super.put(key, providerOfValue);
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> putAll(Producer<Map<K, V>> mapProducer) {
+ super.putAll(mapProducer);
+ return this;
+ }
+
+ /** Returns a new {@link MapProducer}. */
+ public MapProducer<K, V> build() {
+ return new MapProducer<>(mapBuilder.build());
+ }
+ }
+
+ @Override
+ protected ListenableFuture<Map<K, V>> compute() {
+ final List<ListenableFuture<Map.Entry<K, V>>> listOfEntries = new ArrayList<>();
+ for (final Entry<K, Producer<V>> entry : contributingMap().entrySet()) {
+ listOfEntries.add(
+ Futures.transform(
+ entry.getValue().get(),
+ new Function<V, Entry<K, V>>() {
+ @Override
+ public Entry<K, V> apply(V computedValue) {
+ return Maps.immutableEntry(entry.getKey(), computedValue);
+ }
+ },
+ directExecutor()));
+ }
+
+ return Futures.transform(
+ Futures.allAsList(listOfEntries),
+ new Function<List<Map.Entry<K, V>>, Map<K, V>>() {
+ @Override
+ public Map<K, V> apply(List<Map.Entry<K, V>> entries) {
+ return ImmutableMap.copyOf(entries);
+ }
+ },
+ directExecutor());
+ }
+}
diff --git a/java/dagger/producers/internal/MissingBindingProducer.java b/java/dagger/producers/internal/MissingBindingProducer.java
new file mode 100644
index 0000000..5721569
--- /dev/null
+++ b/java/dagger/producers/internal/MissingBindingProducer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+
+/**
+ * A {@link Producer} that always throws on calls to {@link Producer#get()}. This is necessary in
+ * ahead-of-time subcomponents mode, where modifiable binding methods need to return a {@code
+ * Producer<T>} to a framework instance initialization that is pruned and no longer in the binding
+ * graph, but was present in a superclass implementation. This class fulfills that requirement but
+ * is still practically unusable.
+ */
+public final class MissingBindingProducer<T> extends AbstractProducer<T> {
+ private static final MissingBindingProducer<Object> INSTANCE = new MissingBindingProducer<>();
+
+ private MissingBindingProducer() {}
+
+ @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast
+ public static <T> Producer<T> create() {
+ return (Producer) INSTANCE;
+ }
+
+ @Override
+ protected ListenableFuture<T> compute() {
+ throw new AssertionError(
+ "This binding is not part of the final binding graph. The key was requested by a binding "
+ + "that was believed to possibly be part of the graph, but is no longer requested. "
+ + "If this exception is thrown, it is the result of a Dagger bug.");
+ }
+}
diff --git a/java/dagger/producers/internal/Producers.java b/java/dagger/producers/internal/Producers.java
new file mode 100644
index 0000000..54e4d5e
--- /dev/null
+++ b/java/dagger/producers/internal/Producers.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.catchingAsync;
+import static com.google.common.util.concurrent.Futures.transform;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Provider;
+
+/**
+ * Utility methods for use in generated producer code.
+ */
+public final class Producers {
+ /**
+ * Returns a future of {@link Produced} that represents the completion (either success or failure)
+ * of the given future. If the input future succeeds, then the resulting future also succeeds with
+ * a successful {@code Produced}; if the input future fails, then the resulting future succeeds
+ * with a failing {@code Produced}.
+ *
+ * <p>Cancelling the resulting future will propagate the cancellation to the input future; but
+ * cancelling the input future will trigger the resulting future to succeed with a failing
+ * {@code Produced}.
+ */
+ // TODO(beder): Document what happens with an InterruptedException after you figure out how to
+ // trigger one in a test.
+ public static <T> ListenableFuture<Produced<T>> createFutureProduced(ListenableFuture<T> future) {
+ return catchingAsync(
+ transform(future, Producers.<T>resultToProduced(), directExecutor()),
+ Throwable.class,
+ Producers.<T>futureFallbackForProduced(),
+ directExecutor());
+ }
+
+ private static final Function<Object, Produced<Object>> RESULT_TO_PRODUCED =
+ new Function<Object, Produced<Object>>() {
+ @Override
+ public Produced<Object> apply(Object result) {
+ return Produced.successful(result);
+ }
+ };
+
+ @SuppressWarnings({"unchecked", "rawtypes"}) // bivariant implementation
+ private static <T> Function<T, Produced<T>> resultToProduced() {
+ return (Function) RESULT_TO_PRODUCED;
+ }
+
+ private static final AsyncFunction<Throwable, Produced<Object>> FUTURE_FALLBACK_FOR_PRODUCED =
+ new AsyncFunction<Throwable, Produced<Object>>() {
+ @Override
+ public ListenableFuture<Produced<Object>> apply(Throwable t) throws Exception {
+ Produced<Object> produced = Produced.failed(t);
+ return Futures.immediateFuture(produced);
+ }
+ };
+
+ @SuppressWarnings({"unchecked", "rawtypes"}) // bivariant implementation
+ private static <T> AsyncFunction<Throwable, Produced<T>> futureFallbackForProduced() {
+ return (AsyncFunction) FUTURE_FALLBACK_FOR_PRODUCED;
+ }
+
+ /**
+ * Returns a future of a {@code Set} that contains a single element: the result of the input
+ * future.
+ */
+ public static <T> ListenableFuture<Set<T>> createFutureSingletonSet(ListenableFuture<T> future) {
+ return transform(
+ future,
+ new Function<T, Set<T>>() {
+ @Override
+ public Set<T> apply(T value) {
+ return ImmutableSet.of(value);
+ }
+ },
+ directExecutor());
+ }
+
+ /**
+ * Creates a new {@code ListenableFuture} whose value is a set containing the values of all its
+ * input futures, if all succeed. If any input fails, the returned future fails immediately.
+ *
+ * <p>This is the set equivalent of {@link Futures#allAsList}.
+ */
+ public static <T> ListenableFuture<Set<T>> allAsSet(
+ Iterable<? extends ListenableFuture<? extends T>> futures) {
+ return transform(
+ Futures.allAsList(futures),
+ new Function<List<T>, Set<T>>() {
+ @Override
+ public Set<T> apply(List<T> values) {
+ return ImmutableSet.copyOf(values);
+ }
+ },
+ directExecutor());
+ }
+
+ /**
+ * Returns a producer that immediately executes the binding logic for the given provider every
+ * time it is called.
+ */
+ public static <T> Producer<T> producerFromProvider(final Provider<T> provider) {
+ checkNotNull(provider);
+ return new CompletedProducer<T>() {
+ @Override
+ public ListenableFuture<T> get() {
+ return Futures.immediateFuture(provider.get());
+ }
+ };
+ }
+
+ /**
+ * Returns a producer that succeeds with the given value.
+ *
+ * @deprecated Prefer the non-internal version of this method: {@link
+ * dagger.producers.Producers#immediateProducer(Object)}.
+ */
+ @Deprecated
+ public static <T> Producer<T> immediateProducer(T value) {
+ return dagger.producers.Producers.immediateProducer(value);
+ }
+
+ /**
+ * Returns a producer that fails with the given exception.
+ *
+ * @deprecated Prefer the non-internal version of this method: {@link
+ * dagger.producers.Producers#immediateFailedProducer(Throwable)}.
+ */
+ @Deprecated
+ public static <T> Producer<T> immediateFailedProducer(Throwable throwable) {
+ return dagger.producers.Producers.immediateFailedProducer(throwable);
+ }
+
+ /**
+ * Returns a new view of the given {@code producer} if and only if it is a {@link
+ * CancellableProducer}. Cancelling the returned producer's future will not cancel the underlying
+ * task for the given producer.
+ *
+ * @throws IllegalArgumentException if {@code producer} is not a {@code CancellableProducer}
+ */
+ public static <T> Producer<T> nonCancellationPropagatingViewOf(Producer<T> producer) {
+ // This is a hack until we change the types of Producer fields to be CancellableProducer or
+ // some other type.
+ if (producer instanceof CancellableProducer) {
+ return ((CancellableProducer<T>) producer).newDependencyView();
+ }
+ throw new IllegalArgumentException(
+ "nonCancellationPropagatingViewOf called with non-CancellableProducer: " + producer);
+ }
+
+ /**
+ * Returns a new view of the given {@code producer} for use as an entry point in a production
+ * component, if and only if it is a {@link CancellableProducer}. When the returned producer's
+ * future is cancelled, the given {@code cancellable} will also be cancelled.
+ *
+ * @throws IllegalArgumentException if {@code producer} is not a {@code CancellableProducer}
+ */
+ public static <T> Producer<T> entryPointViewOf(
+ Producer<T> producer, CancellationListener cancellationListener) {
+ // This is a hack until we change the types of Producer fields to be CancellableProducer or
+ // some other type.
+ if (producer instanceof CancellableProducer) {
+ return ((CancellableProducer<T>) producer).newEntryPointView(cancellationListener);
+ }
+ throw new IllegalArgumentException(
+ "entryPointViewOf called with non-CancellableProducer: " + producer);
+ }
+
+ /**
+ * Calls {@code cancel} on the given {@code producer} if it is a {@link CancellableProducer}.
+ *
+ * @throws IllegalArgumentException if {@code producer} is not a {@code CancellableProducer}
+ */
+ public static void cancel(Producer<?> producer, boolean mayInterruptIfRunning) {
+ // This is a hack until we change the types of Producer fields to be CancellableProducer or
+ // some other type.
+ if (producer instanceof CancellableProducer) {
+ ((CancellableProducer<?>) producer).cancel(mayInterruptIfRunning);
+ } else {
+ throw new IllegalArgumentException("cancel called with non-CancellableProducer: " + producer);
+ }
+ }
+
+ private static final Producer<Map<Object, Object>> EMPTY_MAP_PRODUCER =
+ dagger.producers.Producers.<Map<Object, Object>>immediateProducer(ImmutableMap.of());
+
+ @SuppressWarnings("unchecked") // safe contravariant cast
+ public static <K, V> Producer<Map<K, V>> emptyMapProducer() {
+ return (Producer<Map<K, V>>) (Producer) EMPTY_MAP_PRODUCER;
+ }
+
+ /**
+ * A {@link CancellableProducer} which can't be cancelled because it represents an
+ * already-completed task.
+ */
+ private abstract static class CompletedProducer<T> implements CancellableProducer<T> {
+ @Override
+ public void cancel(boolean mayInterruptIfRunning) {}
+
+ @Override
+ public Producer<T> newDependencyView() {
+ return this;
+ }
+
+ @Override
+ public Producer<T> newEntryPointView(CancellationListener cancellationListener) {
+ return this;
+ }
+ }
+
+ private Producers() {}
+}
diff --git a/java/dagger/producers/internal/ProductionExecutorModule.java b/java/dagger/producers/internal/ProductionExecutorModule.java
new file mode 100644
index 0000000..e233ae9
--- /dev/null
+++ b/java/dagger/producers/internal/ProductionExecutorModule.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.producers.Production;
+import dagger.producers.ProductionScope;
+import java.util.concurrent.Executor;
+
+/**
+ * Binds the {@code @ProductionImplementation Executor} binding in {@link ProductionScope} so that
+ * only on instance is ever used within production components.
+ */
+@Module
+public abstract class ProductionExecutorModule {
+ @Binds
+ @ProductionScope
+ @ProductionImplementation
+ abstract Executor productionImplementationExecutor(@Production Executor executor);
+
+ private ProductionExecutorModule() {}
+}
diff --git a/java/dagger/producers/internal/ProductionImplementation.java b/java/dagger/producers/internal/ProductionImplementation.java
new file mode 100644
index 0000000..8a0149c
--- /dev/null
+++ b/java/dagger/producers/internal/ProductionImplementation.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.internal.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifies a type that will be used as an internal implementation detail in the framework.
+ *
+ * <p>This is only intended to be used by the framework. It is the internal counterpart to
+ * {@link dagger.producers.Production}.
+ */
+@Documented
+@Retention(RUNTIME)
+@Qualifier
+@Beta
+public @interface ProductionImplementation {}
diff --git a/java/dagger/producers/internal/SetOfProducedProducer.java b/java/dagger/producers/internal/SetOfProducedProducer.java
new file mode 100644
index 0000000..40833e5
--- /dev/null
+++ b/java/dagger/producers/internal/SetOfProducedProducer.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+import static dagger.internal.DaggerCollections.hasDuplicates;
+import static dagger.internal.DaggerCollections.presizedList;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns a
+ * future {@code Set<Produced<T>>} whose elements are populated by subsequent calls to the delegate
+ * {@link Producer#get} methods.
+ */
+public final class SetOfProducedProducer<T> extends AbstractProducer<Set<Produced<T>>> {
+ public static <T> Producer<Set<T>> empty() {
+ return SetProducer.empty();
+ }
+
+ /**
+ * Constructs a new {@link Builder} for a {@link SetProducer} with {@code individualProducerSize}
+ * individual {@code Producer<T>} and {@code collectionProducerSize} {@code
+ * Producer<Collection<T>>} instances.
+ */
+ public static <T> Builder<T> builder(int individualProducerSize, int collectionProducerSize) {
+ return new Builder<T>(individualProducerSize, collectionProducerSize);
+ }
+
+ /**
+ * A builder to accumulate {@code Producer<T>} and {@code Producer<Collection<T>>} instances.
+ * These are only intended to be single-use and from within generated code. Do <em>NOT</em> add
+ * producers after calling {@link #build()}.
+ */
+ public static final class Builder<T> {
+ private final List<Producer<T>> individualProducers;
+ private final List<Producer<Collection<T>>> collectionProducers;
+
+ private Builder(int individualProducerSize, int collectionProducerSize) {
+ individualProducers = presizedList(individualProducerSize);
+ collectionProducers = presizedList(collectionProducerSize);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Builder<T> addProducer(Producer<? extends T> individualProducer) {
+ assert individualProducer != null : "Codegen error? Null producer";
+ individualProducers.add((Producer<T>) individualProducer);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Builder<T> addCollectionProducer(
+ Producer<? extends Collection<? extends T>> multipleProducer) {
+ assert multipleProducer != null : "Codegen error? Null producer";
+ collectionProducers.add((Producer<Collection<T>>) multipleProducer);
+ return this;
+ }
+
+ public SetOfProducedProducer<T> build() {
+ assert !hasDuplicates(individualProducers)
+ : "Codegen error? Duplicates in the producer list";
+ assert !hasDuplicates(collectionProducers)
+ : "Codegen error? Duplicates in the producer list";
+
+ return new SetOfProducedProducer<T>(individualProducers, collectionProducers);
+ }
+ }
+
+ private final List<Producer<T>> individualProducers;
+ private final List<Producer<Collection<T>>> collectionProducers;
+
+ private SetOfProducedProducer(
+ List<Producer<T>> individualProducers, List<Producer<Collection<T>>> collectionProducers) {
+ this.individualProducers = individualProducers;
+ this.collectionProducers = collectionProducers;
+ }
+
+ /**
+ * Returns a future {@link Set} of {@link Produced} elements given by each of the producers.
+ *
+ * <p>If any of the delegate collections, or any elements therein, are null, then that
+ * corresponding {@code Produced} element will fail with a NullPointerException.
+ *
+ * <p>Canceling this future will attempt to cancel all of the component futures; but if any of the
+ * delegate futures fail or are canceled, this future succeeds, with the appropriate failed {@link
+ * Produced}.
+ *
+ * @throws NullPointerException if any of the delegate producers return null
+ */
+ @Override
+ public ListenableFuture<Set<Produced<T>>> compute() {
+ List<ListenableFuture<? extends Produced<? extends Collection<T>>>> futureProducedCollections =
+ new ArrayList<ListenableFuture<? extends Produced<? extends Collection<T>>>>(
+ individualProducers.size() + collectionProducers.size());
+ for (Producer<T> producer : individualProducers) {
+ // TODO(ronshapiro): Don't require individual productions to be added to a collection just to
+ // be materialized into futureProducedCollections.
+ futureProducedCollections.add(
+ Producers.createFutureProduced(
+ Producers.createFutureSingletonSet(checkNotNull(producer.get()))));
+ }
+ for (Producer<Collection<T>> producer : collectionProducers) {
+ futureProducedCollections.add(Producers.createFutureProduced(checkNotNull(producer.get())));
+ }
+
+ return Futures.transform(
+ Futures.allAsList(futureProducedCollections),
+ new Function<List<Produced<? extends Collection<T>>>, Set<Produced<T>>>() {
+ @Override
+ public Set<Produced<T>> apply(
+ List<Produced<? extends Collection<T>>> producedCollections) {
+ ImmutableSet.Builder<Produced<T>> builder = ImmutableSet.builder();
+ for (Produced<? extends Collection<T>> producedCollection : producedCollections) {
+ try {
+ Collection<T> collection = producedCollection.get();
+ if (collection == null) {
+ // TODO(beder): This is a vague exception. Can we somehow point to the failing
+ // producer? See the similar comment in the component writer about null
+ // provisions.
+ builder.add(
+ Produced.<T>failed(
+ new NullPointerException(
+ "Cannot contribute a null collection into a producer set binding when"
+ + " it's injected as Set<Produced<T>>.")));
+ } else {
+ for (T value : collection) {
+ if (value == null) {
+ builder.add(
+ Produced.<T>failed(
+ new NullPointerException(
+ "Cannot contribute a null element into a producer set binding"
+ + " when it's injected as Set<Produced<T>>.")));
+ } else {
+ builder.add(Produced.successful(value));
+ }
+ }
+ }
+ } catch (ExecutionException e) {
+ builder.add(Produced.<T>failed(e.getCause()));
+ }
+ }
+ return builder.build();
+ }
+ },
+ directExecutor());
+ }
+}
diff --git a/java/dagger/producers/internal/SetProducer.java b/java/dagger/producers/internal/SetProducer.java
new file mode 100644
index 0000000..c0196ae
--- /dev/null
+++ b/java/dagger/producers/internal/SetProducer.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.transform;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+import static dagger.internal.DaggerCollections.hasDuplicates;
+import static dagger.internal.DaggerCollections.presizedList;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns
+ * a future {@link Set} whose elements are populated by subsequent calls to the delegate
+ * {@link Producer#get} methods.
+ */
+public final class SetProducer<T> extends AbstractProducer<Set<T>> {
+ private static final Producer<Set<Object>> EMPTY_PRODUCER =
+ dagger.producers.Producers.<Set<Object>>immediateProducer(ImmutableSet.<Object>of());
+
+ @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast
+ public static <T> Producer<Set<T>> empty() {
+ return (Producer) EMPTY_PRODUCER;
+ }
+
+ /**
+ * Constructs a new {@link Builder} for a {@link SetProducer} with {@code individualProducerSize}
+ * individual {@code Producer<T>} and {@code collectionProducerSize} {@code
+ * Producer<Collection<T>>} instances.
+ */
+ public static <T> Builder<T> builder(int individualProducerSize, int collectionProducerSize) {
+ return new Builder<T>(individualProducerSize, collectionProducerSize);
+ }
+
+ /**
+ * A builder to accumulate {@code Producer<T>} and {@code Producer<Collection<T>>} instances.
+ * These are only intended to be single-use and from within generated code. Do <em>NOT</em> add
+ * producers after calling {@link #build()}.
+ */
+ public static final class Builder<T> {
+ private final List<Producer<T>> individualProducers;
+ private final List<Producer<Collection<T>>> collectionProducers;
+
+ private Builder(int individualProducerSize, int collectionProducerSize) {
+ individualProducers = presizedList(individualProducerSize);
+ collectionProducers = presizedList(collectionProducerSize);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Builder<T> addProducer(Producer<? extends T> individualProducer) {
+ assert individualProducer != null : "Codegen error? Null producer";
+ individualProducers.add((Producer<T>) individualProducer);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Builder<T> addCollectionProducer(
+ Producer<? extends Collection<? extends T>> multipleProducer) {
+ assert multipleProducer != null : "Codegen error? Null producer";
+ collectionProducers.add((Producer<Collection<T>>) multipleProducer);
+ return this;
+ }
+
+ public SetProducer<T> build() {
+ assert !hasDuplicates(individualProducers)
+ : "Codegen error? Duplicates in the producer list";
+ assert !hasDuplicates(collectionProducers)
+ : "Codegen error? Duplicates in the producer list";
+
+ return new SetProducer<T>(individualProducers, collectionProducers);
+ }
+ }
+
+ private final List<Producer<T>> individualProducers;
+ private final List<Producer<Collection<T>>> collectionProducers;
+
+ private SetProducer(
+ List<Producer<T>> individualProducers, List<Producer<Collection<T>>> collectionProducers) {
+ this.individualProducers = individualProducers;
+ this.collectionProducers = collectionProducers;
+ }
+
+ /**
+ * Returns a future {@link Set} that contains the elements given by each of the producers.
+ *
+ * <p>If any of the delegate collections, or any elements therein, are null, then this future will
+ * fail with a NullPointerException.
+ *
+ * <p>Canceling this future will attempt to cancel all of the component futures, and if any of the
+ * delegate futures fails or is canceled, this one is, too.
+ *
+ * @throws NullPointerException if any of the delegate producers return null
+ */
+ @Override
+ public ListenableFuture<Set<T>> compute() {
+ List<ListenableFuture<T>> individualFutures =
+ new ArrayList<ListenableFuture<T>>(individualProducers.size());
+ for (Producer<T> producer : individualProducers) {
+ individualFutures.add(checkNotNull(producer.get()));
+ }
+
+ // Presize the list of collections produced by the amount of collectionProducers, with one more
+ // for the consolidate individualFutures from Futures.allAsList.
+ List<ListenableFuture<? extends Collection<T>>> futureCollections =
+ new ArrayList<ListenableFuture<? extends Collection<T>>>(collectionProducers.size() + 1);
+ futureCollections.add(Futures.allAsList(individualFutures));
+ for (Producer<Collection<T>> producer : collectionProducers) {
+ futureCollections.add(checkNotNull(producer.get()));
+ }
+ return transform(
+ Futures.allAsList(futureCollections),
+ new Function<List<Collection<T>>, Set<T>>() {
+ @Override
+ public Set<T> apply(List<Collection<T>> sets) {
+ ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+ for (Collection<T> set : sets) {
+ builder.addAll(set);
+ }
+ return builder.build();
+ }
+ },
+ directExecutor());
+ }
+}
diff --git a/java/dagger/producers/monitoring/ProducerMonitor.java b/java/dagger/producers/monitoring/ProducerMonitor.java
new file mode 100644
index 0000000..c0379fd
--- /dev/null
+++ b/java/dagger/producers/monitoring/ProducerMonitor.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import static com.google.common.util.concurrent.Futures.addCallback;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import dagger.producers.Produces;
+
+/**
+ * A hook for monitoring the execution of individual {@linkplain Produces producer methods}. See
+ * {@link ProductionComponentMonitor} for how to install these monitors.
+ *
+ * <p>The lifecycle of the monitor, under normal conditions, is:
+ * <ul>
+ * <li>{@link #requested()}
+ * <li>{@link #methodStarting()}
+ * <li>The method is called
+ * <li>{@link #methodFinished()}
+ * <li>If the method returns a value, then:
+ * <ul>
+ * <li>{@link #succeeded(Object)} if the method returned normally; or
+ * <li>{@link #failed(Throwable)} if the method threw an exception.
+ * </ul>
+ * <li>If the method returns a future, then:
+ * <ul>
+ * <li>{@link #succeeded(Object)} if the method returned normally, and the future succeeded; or
+ * <li>{@link #failed(Throwable)} if the method threw an exception, or returned normally and the
+ * future failed.
+ * </ul>
+ * </ul>
+ *
+ * <p>If any input to the monitored producer fails, {@link #failed(Throwable)} will be called
+ * immediately with the failed input's exception. If more than one input fails, an arbitrary failed
+ * input's exception is used.
+ *
+ * <p>For example, given an entry point A that depends on B, which depends on C, when the entry
+ * point A is called, this will trigger the following sequence of events, assuming all methods and
+ * futures complete successfully:
+ * <ul>
+ * <li>A requested
+ * <li>B requested
+ * <li>C requested
+ * <li>C methodStarting
+ * <li>C methodFinished
+ * <li>C succeeded
+ * <li>B methodStarting
+ * <li>B methodFinished
+ * <li>B succeeded
+ * <li>A methodStarting
+ * <li>A methodFinished
+ * <li>A succeeded
+ * </ul>
+ *
+ * <p>If any of the monitor's methods throw, then the exception will be logged and processing will
+ * continue unaffected.
+ *
+ * @since 2.1
+ */
+public abstract class ProducerMonitor {
+ /**
+ * Called when the producer's output is requested; that is, when the first method is called that
+ * requires the production of this producer's output.
+ *
+ * <p>Note that if a method depends on {@code Producer<T>}, then this does not count as requesting
+ * {@code T}; that is only triggered by calling {@link Producer#get()}.
+ *
+ * <p>Depending on how this producer is requested, the following threading constraints are
+ * guaranteed:
+ *
+ * <ol>
+ * <li>If the producer is requested directly by a method on a component, then {@code requested}
+ * will be called on the same thread as the component method call.
+ * <li>If the producer is requested by value from another producer (i.e., injected as {@code T}
+ * or {@code Produced<T>}), then {@code requested} will be called from the same thread as
+ * the other producer's {@code requested}.
+ * <li>If the producer is requested by calling {@link Producer#get()}, then {@code requested}
+ * will be called from the same thread as that {@code get()} call.
+ * </ol>
+ *
+ * <p>When multiple monitors are installed, the order that each monitor will call this method is
+ * unspecified, but will remain consistent throughout the course of the execution of a component.
+ *
+ * <p>This implementation is a no-op.
+ */
+ public void requested() {}
+
+ /**
+ * Called when all of the producer's inputs are available. This is called regardless of whether
+ * the inputs have succeeded or not; when the inputs have succeeded, this is called prior to
+ * scheduling the method on the executor, and if an input has failed and the producer will be
+ * skipped, this method will be called before {@link #failed(Throwable)} is called.
+ *
+ * <p>When multiple monitors are installed, the order that each monitor will call this method is
+ * unspecified, but will remain consistent throughout the course of the execution of a component.
+ *
+ * <p>This implementation is a no-op.
+ */
+ public void ready() {}
+
+ /**
+ * Called when the producer method is about to start executing. This will be called from the same
+ * thread as the producer method itself.
+ *
+ * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
+ * calls to {@link #requested()}.
+ *
+ * <p>This implementation is a no-op.
+ */
+ public void methodStarting() {}
+
+ /**
+ * Called when the producer method has finished executing. This will be called from the same
+ * thread as {@link #methodStarting()} and the producer method itself.
+ *
+ * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
+ * calls to {@link #requested()}.
+ *
+ * <p>This implementation is a no-op.
+ */
+ public void methodFinished() {}
+
+ /**
+ * Called when the producer’s future has completed successfully with a value.
+ *
+ * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
+ * calls to {@link #requested()}.
+ *
+ * <p>This implementation is a no-op.
+ */
+ public void succeeded(@SuppressWarnings("unused") Object value) {}
+
+ /**
+ * Called when the producer's future has failed with an exception.
+ *
+ * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
+ * calls to {@link #requested()}.
+ *
+ * <p>This implementation is a no-op.
+ */
+ public void failed(@SuppressWarnings("unused") Throwable t) {}
+
+ /**
+ * Adds this monitor's completion methods as a callback to the future. This is only intended to be
+ * overridden in the framework!
+ */
+ public <T> void addCallbackTo(ListenableFuture<T> future) {
+ addCallback(
+ future,
+ new FutureCallback<T>() {
+ @Override
+ public void onSuccess(T value) {
+ succeeded(value);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ failed(t);
+ }
+ },
+ directExecutor());
+ }
+
+ private static final ProducerMonitor NO_OP =
+ new ProducerMonitor() {
+ @Override
+ public <T> void addCallbackTo(ListenableFuture<T> future) {
+ // overridden to avoid adding a do-nothing callback
+ }
+ };
+
+ /** Returns a monitor that does no monitoring. */
+ public static ProducerMonitor noOp() {
+ return NO_OP;
+ }
+}
diff --git a/java/dagger/producers/monitoring/ProducerTimingRecorder.java b/java/dagger/producers/monitoring/ProducerTimingRecorder.java
new file mode 100644
index 0000000..f4894c9
--- /dev/null
+++ b/java/dagger/producers/monitoring/ProducerTimingRecorder.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+
+/**
+ * A hook for recording the timing of the execution of individual
+ * {@linkplain Produces producer methods}. See {@link ProductionComponentTimingRecorder} for how to
+ * install these monitors.
+ *
+ * <p>If any of the recorder's methods throw, then the exception will be logged and processing will
+ * continue unaffected.
+ *
+ * <p>All timings are measured at nanosecond precision, but not necessarily nanosecond resolution.
+ * That is, timings will be reported in nanoseconds, but the timing source will not necessarily
+ * update at nanosecond resolution. For example, {@link System#nanoTime()} would satisfy these
+ * constraints.
+ *
+ * @since 2.1
+ */
+public abstract class ProducerTimingRecorder {
+ /**
+ * Reports that the producer method has finished executing with the given statistics.
+ *
+ * <p>If the producer was skipped due to any of its inputs failing, then this will not be called.
+ *
+ * @param startedNanos the wall-clock time, in nanoseconds, when the producer method started
+ * executing, measured from when the first method on the {@linkplain ProductionComponent
+ * production component} was called.
+ * @param durationNanos the wall-clock time, in nanoseconds, that the producer method took to
+ * execute.
+ */
+ @SuppressWarnings("GoodTime") // should accept a java.time.Duration x2 (?)
+ public void recordMethod(long startedNanos, long durationNanos) {}
+
+ /**
+ * Reports that the producer's future has succeeded with the given statistics.
+ *
+ * <p>If the producer was skipped due to any of its inputs failing, then this will not be called.
+ *
+ * @param latencyNanos the wall-clock time, in nanoseconds, of the producer's latency, measured
+ * from when the producer method started to when the future finished.
+ */
+ @SuppressWarnings("GoodTime") // should accept a java.time.Duration
+ public void recordSuccess(long latencyNanos) {}
+
+ /**
+ * Reports that the producer's future has failed with the given statistics.
+ *
+ * @param exception the exception that the future failed with.
+ * @param latencyNanos the wall-clock time, in nanoseconds, of the producer's latency, measured
+ * from when the producer method started to when the future finished.
+ */
+ @SuppressWarnings("GoodTime") // should accept a java.time.Duration
+ public void recordFailure(Throwable exception, long latencyNanos) {}
+
+ /**
+ * Reports that the producer was skipped because one of its inputs failed.
+ *
+ * @param exception the exception that its input failed with. If multiple inputs failed, this
+ * exception will be chosen arbitrarily from the input failures.
+ */
+ public void recordSkip(Throwable exception) {}
+
+ /** Returns a producer recorder that does nothing. */
+ public static ProducerTimingRecorder noOp() {
+ return NO_OP;
+ }
+
+ private static final ProducerTimingRecorder NO_OP = new ProducerTimingRecorder() {};
+}
diff --git a/java/dagger/producers/monitoring/ProducerToken.java b/java/dagger/producers/monitoring/ProducerToken.java
new file mode 100644
index 0000000..1f05146
--- /dev/null
+++ b/java/dagger/producers/monitoring/ProducerToken.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import dagger.producers.Produces;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/** A token that represents an individual {@linkplain Produces producer method}. */
+public final class ProducerToken {
+ @NullableDecl private final Class<?> classToken;
+ @NullableDecl private final String methodName;
+
+ private ProducerToken(@NullableDecl Class<?> classToken, @NullableDecl String methodName) {
+ this.classToken = classToken;
+ this.methodName = methodName;
+ }
+
+ /**
+ * Creates a token for a class token that represents the generated factory for a producer method.
+ *
+ * <p><b>Do not use this!</b> This is intended to be called by generated code only, and its
+ * signature may change at any time.
+ */
+ public static ProducerToken create(Class<?> classToken) {
+ return new ProducerToken(checkNotNull(classToken), null);
+ }
+
+ /**
+ * Creates a token for a producer method.
+ *
+ * <p><b>Do not use this!</b> This is intended to be called by generated code only, and its
+ * signature may change at any time.
+ */
+ public static ProducerToken create(String methodName) {
+ return new ProducerToken(null, checkNotNull(methodName));
+ }
+
+ /** Two tokens are equal if they represent the same method. */
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ } else if (o instanceof ProducerToken) {
+ ProducerToken that = (ProducerToken) o;
+ return Objects.equals(this.classToken, that.classToken)
+ && Objects.equals(this.methodName, that.methodName);
+ } else {
+ return false;
+ }
+ }
+
+ /** Returns an appropriate hash code to match {@link #equals(Object)}. */
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= Objects.hashCode(this.classToken);
+ h *= 1000003;
+ h ^= Objects.hashCode(this.methodName);
+ return h;
+ }
+
+ /** Returns a representation of the method. */
+ @Override
+ public String toString() {
+ if (methodName != null) {
+ return methodName;
+ } else if (classToken != null) {
+ return classToken.getCanonicalName();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/java/dagger/producers/monitoring/ProductionComponentMonitor.java b/java/dagger/producers/monitoring/ProductionComponentMonitor.java
new file mode 100644
index 0000000..ca71ece
--- /dev/null
+++ b/java/dagger/producers/monitoring/ProductionComponentMonitor.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+
+/**
+ * A hook for monitoring execution of {@linkplain ProductionComponent production components}. To
+ * install a {@code ProductionComponentMonitor}, contribute to a set binding of
+ * {@code ProductionComponentMonitor.Factory}. The factory will be asked to create one monitor for
+ * the component, and the resulting single instance will be used to create individual monitors for
+ * producers.
+ *
+ * <p>For example: <pre><code>
+ * {@literal @Module}
+ * final class MyMonitorModule {
+ * {@literal @Provides @IntoSet} ProductionComponentMonitor.Factory provideMonitorFactory(
+ * MyProductionComponentMonitor.Factory monitorFactory) {
+ * return monitorFactory;
+ * }
+ * }
+ *
+ * {@literal @ProductionComponent(modules = {MyMonitorModule.class, MyProducerModule.class})}
+ * interface MyComponent {
+ * {@literal ListenableFuture<SomeType>} someType();
+ * }
+ * </code></pre>
+ *
+ * <p>If any of these methods throw, then the exception will be logged, and the framework will act
+ * as though a no-op monitor was returned.
+ *
+ * @since 2.1
+ */
+public abstract class ProductionComponentMonitor {
+ /** Returns a monitor for an individual {@linkplain Produces producer method}. */
+ public abstract ProducerMonitor producerMonitorFor(ProducerToken token);
+
+ private static final ProductionComponentMonitor NO_OP =
+ new ProductionComponentMonitor() {
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ return ProducerMonitor.noOp();
+ }
+ };
+
+ /** Returns a monitor that does no monitoring. */
+ public static ProductionComponentMonitor noOp() {
+ return NO_OP;
+ }
+
+ public abstract static class Factory {
+ /** Creates a component-specific monitor when the component is created. */
+ public abstract ProductionComponentMonitor create(Object component);
+
+ private static final Factory NO_OP_FACTORY =
+ new Factory() {
+ @Override
+ public ProductionComponentMonitor create(Object component) {
+ return ProductionComponentMonitor.noOp();
+ }
+ };
+
+ /** Returns a factory that returns no-op monitors. */
+ public static Factory noOp() {
+ return NO_OP_FACTORY;
+ }
+ }
+}
diff --git a/java/dagger/producers/monitoring/ProductionComponentTimingRecorder.java b/java/dagger/producers/monitoring/ProductionComponentTimingRecorder.java
new file mode 100644
index 0000000..d82d8e1
--- /dev/null
+++ b/java/dagger/producers/monitoring/ProductionComponentTimingRecorder.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+
+/**
+ * A hook for recording timing of the execution of
+ * {@linkplain ProductionComponent production components}. To install a
+ * {@code ProductionComponentTimingRecorder}, contribute to a set binding of
+ * {@code ProductionComponentTimingRecorder.Factory}, and include the {@code TimingMonitorModule} to
+ * the component. The factory will be asked to create one timing recorder for the component, and the
+ * resulting instance will be used to create individual timing recorders for producers.
+ *
+ * <p>If any of these methods throw, then the exception will be logged, and the framework will act
+ * as though a no-op timing recorder was returned.
+ *
+ * @since 2.1
+ */
+public interface ProductionComponentTimingRecorder {
+ /** Returns a timing recorder for an individual {@linkplain Produces producer method}. */
+ ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token);
+
+ public interface Factory {
+ /** Creates a component-specific timing recorder when the component is created. */
+ ProductionComponentTimingRecorder create(Object component);
+ }
+}
diff --git a/java/dagger/producers/monitoring/TimingProducerMonitor.java b/java/dagger/producers/monitoring/TimingProducerMonitor.java
new file mode 100644
index 0000000..c63e108
--- /dev/null
+++ b/java/dagger/producers/monitoring/TimingProducerMonitor.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Ticker;
+
+/**
+ * A monitor that measures the timing of the execution of a producer method, and logs those timings
+ * with the given recorder.
+ */
+final class TimingProducerMonitor extends ProducerMonitor {
+ private final ProducerTimingRecorder recorder;
+ private final Stopwatch stopwatch;
+ private final Stopwatch componentStopwatch;
+ private long startNanos = -1;
+
+ TimingProducerMonitor(
+ ProducerTimingRecorder recorder, Ticker ticker, Stopwatch componentStopwatch) {
+ this.recorder = recorder;
+ this.stopwatch = Stopwatch.createUnstarted(ticker);
+ this.componentStopwatch = componentStopwatch;
+ }
+
+ @Override
+ public void methodStarting() {
+ startNanos = componentStopwatch.elapsed(NANOSECONDS);
+ stopwatch.start();
+ }
+
+ @Override
+ public void methodFinished() {
+ // TODO(beder): Is a system ticker the appropriate way to track CPU time? Should we use
+ // ThreadCpuTicker instead?
+ long durationNanos = stopwatch.elapsed(NANOSECONDS);
+ recorder.recordMethod(startNanos, durationNanos);
+ }
+
+ @Override
+ public void succeeded(Object o) {
+ long latencyNanos = stopwatch.elapsed(NANOSECONDS);
+ recorder.recordSuccess(latencyNanos);
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ if (stopwatch.isRunning()) {
+ long latencyNanos = stopwatch.elapsed(NANOSECONDS);
+ recorder.recordFailure(t, latencyNanos);
+ } else {
+ recorder.recordSkip(t);
+ }
+ }
+}
diff --git a/java/dagger/producers/monitoring/TimingProductionComponentMonitor.java b/java/dagger/producers/monitoring/TimingProductionComponentMonitor.java
new file mode 100644
index 0000000..66589d9
--- /dev/null
+++ b/java/dagger/producers/monitoring/TimingProductionComponentMonitor.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Ticker;
+import dagger.internal.Beta;
+
+/**
+ * A monitor that measures the timing of the execution of a production component, and logs those
+ * timings with the given recorder.
+ *
+ * <p>This assumes that the given recorders do not throw or return null; for example, by using
+ * {@link TimingRecorders#delegatingProductionComponentTimingRecorderFactory}.
+ */
+// TODO(beder): Reduce the visibility of this class to package-private.
+@Beta
+public final class TimingProductionComponentMonitor extends ProductionComponentMonitor {
+ private final ProductionComponentTimingRecorder recorder;
+ private final Ticker ticker;
+ private final Stopwatch stopwatch;
+
+ TimingProductionComponentMonitor(ProductionComponentTimingRecorder recorder, Ticker ticker) {
+ this.recorder = recorder;
+ this.ticker = ticker;
+ this.stopwatch = Stopwatch.createStarted(ticker);
+ }
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ return new TimingProducerMonitor(recorder.producerTimingRecorderFor(token), ticker, stopwatch);
+ }
+
+ public static final class Factory extends ProductionComponentMonitor.Factory {
+ private final ProductionComponentTimingRecorder.Factory recorderFactory;
+ private final Ticker ticker;
+
+ public Factory(ProductionComponentTimingRecorder.Factory recorderFactory) {
+ this(recorderFactory, Ticker.systemTicker());
+ }
+
+ Factory(ProductionComponentTimingRecorder.Factory recorderFactory, Ticker ticker) {
+ this.recorderFactory = recorderFactory;
+ this.ticker = ticker;
+ }
+
+ @Override
+ public ProductionComponentMonitor create(Object component) {
+ return new TimingProductionComponentMonitor(recorderFactory.create(component), ticker);
+ }
+ }
+}
diff --git a/java/dagger/producers/monitoring/TimingRecorders.java b/java/dagger/producers/monitoring/TimingRecorders.java
new file mode 100644
index 0000000..5fbe3e3
--- /dev/null
+++ b/java/dagger/producers/monitoring/TimingRecorders.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import dagger.internal.Beta;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility methods relating to timing.
+ *
+ * @since 2.1
+ */
+// TODO(beder): Reduce the visibility of this class to package-private.
+@Beta
+public final class TimingRecorders {
+ private static final Logger logger = Logger.getLogger(TimingRecorders.class.getName());
+
+ /**
+ * Returns a timing recorder factory that delegates to the given factories, and ensures that any
+ * method called on this object, even transitively, does not throw a {@link RuntimeException} or
+ * return null.
+ *
+ * <p>If the delegate recorders throw an {@link Error}, then that will escape this recorder
+ * implementation. Errors are treated as unrecoverable conditions, and may cause the entire
+ * component's execution to fail.
+ */
+ public static ProductionComponentTimingRecorder.Factory
+ delegatingProductionComponentTimingRecorderFactory(
+ Collection<ProductionComponentTimingRecorder.Factory> factories) {
+ switch (factories.size()) {
+ case 0:
+ return noOpProductionComponentTimingRecorderFactory();
+ case 1:
+ return new NonThrowingProductionComponentTimingRecorder.Factory(
+ Iterables.getOnlyElement(factories));
+ default:
+ return new DelegatingProductionComponentTimingRecorder.Factory(factories);
+ }
+ }
+
+ /**
+ * A component recorder that delegates to a single recorder, and catches and logs all exceptions
+ * that the delegate throws.
+ */
+ private static final class NonThrowingProductionComponentTimingRecorder
+ implements ProductionComponentTimingRecorder {
+ private final ProductionComponentTimingRecorder delegate;
+
+ NonThrowingProductionComponentTimingRecorder(ProductionComponentTimingRecorder delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) {
+ try {
+ ProducerTimingRecorder recorder = delegate.producerTimingRecorderFor(token);
+ return recorder == null
+ ? ProducerTimingRecorder.noOp()
+ : new NonThrowingProducerTimingRecorder(recorder);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderForException(e, delegate, token);
+ return ProducerTimingRecorder.noOp();
+ }
+ }
+
+ static final class Factory implements ProductionComponentTimingRecorder.Factory {
+ private final ProductionComponentTimingRecorder.Factory delegate;
+
+ Factory(ProductionComponentTimingRecorder.Factory delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ProductionComponentTimingRecorder create(Object component) {
+ try {
+ ProductionComponentTimingRecorder recorder = delegate.create(component);
+ return recorder == null
+ ? noOpProductionComponentTimingRecorder()
+ : new NonThrowingProductionComponentTimingRecorder(recorder);
+ } catch (RuntimeException e) {
+ logCreateException(e, delegate, component);
+ return noOpProductionComponentTimingRecorder();
+ }
+ }
+ }
+ }
+
+ /**
+ * A producer recorder that delegates to a single recorder, and catches and logs all exceptions
+ * that the delegate throws.
+ */
+ private static final class NonThrowingProducerTimingRecorder extends ProducerTimingRecorder {
+ private final ProducerTimingRecorder delegate;
+
+ NonThrowingProducerTimingRecorder(ProducerTimingRecorder delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void recordMethod(long startedNanos, long durationNanos) {
+ try {
+ delegate.recordMethod(startedNanos, durationNanos);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordMethod");
+ }
+ }
+
+ @Override
+ public void recordSuccess(long latencyNanos) {
+ try {
+ delegate.recordSuccess(latencyNanos);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordSuccess");
+ }
+ }
+
+ @Override
+ public void recordFailure(Throwable exception, long latencyNanos) {
+ try {
+ delegate.recordFailure(exception, latencyNanos);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordFailure");
+ }
+ }
+
+ @Override
+ public void recordSkip(Throwable exception) {
+ try {
+ delegate.recordSkip(exception);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordSkip");
+ }
+ }
+ }
+
+ /**
+ * A component recorder that delegates to several recorders, and catches and logs all exceptions
+ * that the delegates throw.
+ */
+ private static final class DelegatingProductionComponentTimingRecorder
+ implements ProductionComponentTimingRecorder {
+ private final ImmutableList<ProductionComponentTimingRecorder> delegates;
+
+ DelegatingProductionComponentTimingRecorder(
+ ImmutableList<ProductionComponentTimingRecorder> delegates) {
+ this.delegates = delegates;
+ }
+
+ @Override
+ public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) {
+ ImmutableList.Builder<ProducerTimingRecorder> recordersBuilder = ImmutableList.builder();
+ for (ProductionComponentTimingRecorder delegate : delegates) {
+ try {
+ ProducerTimingRecorder recorder = delegate.producerTimingRecorderFor(token);
+ if (recorder != null) {
+ recordersBuilder.add(recorder);
+ }
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderForException(e, delegate, token);
+ }
+ }
+ ImmutableList<ProducerTimingRecorder> recorders = recordersBuilder.build();
+ switch (recorders.size()) {
+ case 0:
+ return ProducerTimingRecorder.noOp();
+ case 1:
+ return new NonThrowingProducerTimingRecorder(Iterables.getOnlyElement(recorders));
+ default:
+ return new DelegatingProducerTimingRecorder(recorders);
+ }
+ }
+
+ static final class Factory implements ProductionComponentTimingRecorder.Factory {
+ private final ImmutableList<? extends ProductionComponentTimingRecorder.Factory> delegates;
+
+ Factory(Iterable<? extends ProductionComponentTimingRecorder.Factory> delegates) {
+ this.delegates = ImmutableList.copyOf(delegates);
+ }
+
+ @Override
+ public ProductionComponentTimingRecorder create(Object component) {
+ ImmutableList.Builder<ProductionComponentTimingRecorder> recordersBuilder =
+ ImmutableList.builder();
+ for (ProductionComponentTimingRecorder.Factory delegate : delegates) {
+ try {
+ ProductionComponentTimingRecorder recorder = delegate.create(component);
+ if (recorder != null) {
+ recordersBuilder.add(recorder);
+ }
+ } catch (RuntimeException e) {
+ logCreateException(e, delegate, component);
+ }
+ }
+ ImmutableList<ProductionComponentTimingRecorder> recorders = recordersBuilder.build();
+ switch (recorders.size()) {
+ case 0:
+ return noOpProductionComponentTimingRecorder();
+ case 1:
+ return new NonThrowingProductionComponentTimingRecorder(
+ Iterables.getOnlyElement(recorders));
+ default:
+ return new DelegatingProductionComponentTimingRecorder(recorders);
+ }
+ }
+ }
+ }
+
+ /**
+ * A producer recorder that delegates to several recorders, and catches and logs all exceptions
+ * that the delegates throw.
+ */
+ private static final class DelegatingProducerTimingRecorder extends ProducerTimingRecorder {
+ private final ImmutableList<ProducerTimingRecorder> delegates;
+
+ DelegatingProducerTimingRecorder(ImmutableList<ProducerTimingRecorder> delegates) {
+ this.delegates = delegates;
+ }
+
+ @Override
+ public void recordMethod(long startedNanos, long durationNanos) {
+ for (ProducerTimingRecorder delegate : delegates) {
+ try {
+ delegate.recordMethod(startedNanos, durationNanos);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordMethod");
+ }
+ }
+ }
+
+ @Override
+ public void recordSuccess(long latencyNanos) {
+ for (ProducerTimingRecorder delegate : delegates) {
+ try {
+ delegate.recordSuccess(latencyNanos);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordSuccess");
+ }
+ }
+ }
+
+ @Override
+ public void recordFailure(Throwable exception, long latencyNanos) {
+ for (ProducerTimingRecorder delegate : delegates) {
+ try {
+ delegate.recordFailure(exception, latencyNanos);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordFailure");
+ }
+ }
+ }
+
+ @Override
+ public void recordSkip(Throwable exception) {
+ for (ProducerTimingRecorder delegate : delegates) {
+ try {
+ delegate.recordSkip(exception);
+ } catch (RuntimeException e) {
+ logProducerTimingRecorderMethodException(e, delegate, "recordSkip");
+ }
+ }
+ }
+ }
+
+ /** Returns a recorder factory that returns no-op component recorders. */
+ public static ProductionComponentTimingRecorder.Factory
+ noOpProductionComponentTimingRecorderFactory() {
+ return NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER_FACTORY;
+ }
+
+ /** Returns a component recorder that returns no-op producer recorders. */
+ public static ProductionComponentTimingRecorder noOpProductionComponentTimingRecorder() {
+ return NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER;
+ }
+
+ private static final ProductionComponentTimingRecorder.Factory
+ NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER_FACTORY =
+ new ProductionComponentTimingRecorder.Factory() {
+ @Override
+ public ProductionComponentTimingRecorder create(Object component) {
+ return noOpProductionComponentTimingRecorder();
+ }
+ };
+
+ private static final ProductionComponentTimingRecorder
+ NO_OP_PRODUCTION_COMPONENT_TIMING_RECORDER =
+ new ProductionComponentTimingRecorder() {
+ @Override
+ public ProducerTimingRecorder producerTimingRecorderFor(ProducerToken token) {
+ return ProducerTimingRecorder.noOp();
+ }
+ };
+
+ private static void logCreateException(
+ RuntimeException e, ProductionComponentTimingRecorder.Factory factory, Object component) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProductionComponentTimingRecorder.Factory.create on"
+ + " factory "
+ + factory
+ + " with component "
+ + component,
+ e);
+ }
+
+ private static void logProducerTimingRecorderForException(
+ RuntimeException e, ProductionComponentTimingRecorder recorder, ProducerToken token) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProductionComponentTimingRecorder.producerTimingRecorderFor"
+ + "on recorder "
+ + recorder
+ + " with token "
+ + token,
+ e);
+ }
+
+ private static void logProducerTimingRecorderMethodException(
+ RuntimeException e, ProducerTimingRecorder recorder, String method) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProducerTimingRecorder."
+ + method
+ + " on recorder "
+ + recorder,
+ e);
+ }
+
+ private TimingRecorders() {}
+}
diff --git a/java/dagger/producers/monitoring/internal/Monitors.java b/java/dagger/producers/monitoring/internal/Monitors.java
new file mode 100644
index 0000000..13b438a
--- /dev/null
+++ b/java/dagger/producers/monitoring/internal/Monitors.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring.internal;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.Collection;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.inject.Provider;
+
+/**
+ * Utility methods relating to monitoring, for use in generated producers code.
+ */
+public final class Monitors {
+ private static final Logger logger = Logger.getLogger(Monitors.class.getName());
+
+ /**
+ * Returns a monitor factory that delegates to the given factories, and ensures that any method
+ * called on this object, even transitively, does not throw a {@link RuntimeException} or return
+ * null.
+ *
+ * <p>If the delegate monitors throw an {@link Error}, then that will escape this monitor
+ * implementation. Errors are treated as unrecoverable conditions, and may cause the entire
+ * component's execution to fail.
+ */
+ public static ProductionComponentMonitor.Factory delegatingProductionComponentMonitorFactory(
+ Collection<? extends ProductionComponentMonitor.Factory> factories) {
+ if (factories.isEmpty()) {
+ return ProductionComponentMonitor.Factory.noOp();
+ } else if (factories.size() == 1) {
+ return new NonThrowingProductionComponentMonitor.Factory(Iterables.getOnlyElement(factories));
+ } else {
+ return new DelegatingProductionComponentMonitor.Factory(factories);
+ }
+ }
+
+ /**
+ * Creates a new monitor for the given component, from a set of monitor factories. This will not
+ * throw a {@link RuntimeException} or return null.
+ */
+ public static ProductionComponentMonitor createMonitorForComponent(
+ Provider<?> componentProvider,
+ Provider<Set<ProductionComponentMonitor.Factory>> monitorFactorySetProvider) {
+ try {
+ ProductionComponentMonitor.Factory factory =
+ delegatingProductionComponentMonitorFactory(monitorFactorySetProvider.get());
+ return factory.create(componentProvider.get());
+ } catch (RuntimeException e) {
+ logger.log(Level.SEVERE, "RuntimeException while constructing monitor factories.", e);
+ return ProductionComponentMonitor.noOp();
+ }
+ }
+
+ /**
+ * A component monitor that delegates to a single monitor, and catches and logs all exceptions
+ * that the delegate throws.
+ */
+ private static final class NonThrowingProductionComponentMonitor
+ extends ProductionComponentMonitor {
+ private final ProductionComponentMonitor delegate;
+
+ NonThrowingProductionComponentMonitor(ProductionComponentMonitor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ try {
+ ProducerMonitor monitor = delegate.producerMonitorFor(token);
+ return monitor == null ? ProducerMonitor.noOp() : new NonThrowingProducerMonitor(monitor);
+ } catch (RuntimeException e) {
+ logProducerMonitorForException(e, delegate, token);
+ return ProducerMonitor.noOp();
+ }
+ }
+
+ static final class Factory extends ProductionComponentMonitor.Factory {
+ private final ProductionComponentMonitor.Factory delegate;
+
+ Factory(ProductionComponentMonitor.Factory delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ProductionComponentMonitor create(Object component) {
+ try {
+ ProductionComponentMonitor monitor = delegate.create(component);
+ return monitor == null
+ ? ProductionComponentMonitor.noOp()
+ : new NonThrowingProductionComponentMonitor(monitor);
+ } catch (RuntimeException e) {
+ logCreateException(e, delegate, component);
+ return ProductionComponentMonitor.noOp();
+ }
+ }
+ }
+ }
+
+ /**
+ * A producer monitor that delegates to a single monitor, and catches and logs all exceptions
+ * that the delegate throws.
+ */
+ private static final class NonThrowingProducerMonitor extends ProducerMonitor {
+ private final ProducerMonitor delegate;
+
+ NonThrowingProducerMonitor(ProducerMonitor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void requested() {
+ try {
+ delegate.requested();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "requested");
+ }
+ }
+
+ @Override
+ public void ready() {
+ try {
+ delegate.ready();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "ready");
+ }
+ }
+
+ @Override
+ public void methodStarting() {
+ try {
+ delegate.methodStarting();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodStarting");
+ }
+ }
+
+ @Override
+ public void methodFinished() {
+ try {
+ delegate.methodFinished();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodFinished");
+ }
+ }
+
+ @Override
+ public void succeeded(Object o) {
+ try {
+ delegate.succeeded(o);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "succeeded", o);
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ try {
+ delegate.failed(t);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "failed", t);
+ }
+ }
+ }
+
+ /**
+ * A component monitor that delegates to several monitors, and catches and logs all exceptions
+ * that the delegates throw.
+ */
+ private static final class DelegatingProductionComponentMonitor
+ extends ProductionComponentMonitor {
+ private final ImmutableList<ProductionComponentMonitor> delegates;
+
+ DelegatingProductionComponentMonitor(ImmutableList<ProductionComponentMonitor> delegates) {
+ this.delegates = delegates;
+ }
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ ImmutableList.Builder<ProducerMonitor> monitorsBuilder = ImmutableList.builder();
+ for (ProductionComponentMonitor delegate : delegates) {
+ try {
+ ProducerMonitor monitor = delegate.producerMonitorFor(token);
+ if (monitor != null) {
+ monitorsBuilder.add(monitor);
+ }
+ } catch (RuntimeException e) {
+ logProducerMonitorForException(e, delegate, token);
+ }
+ }
+ ImmutableList<ProducerMonitor> monitors = monitorsBuilder.build();
+ if (monitors.isEmpty()) {
+ return ProducerMonitor.noOp();
+ } else if (monitors.size() == 1) {
+ return new NonThrowingProducerMonitor(Iterables.getOnlyElement(monitors));
+ } else {
+ return new DelegatingProducerMonitor(monitors);
+ }
+ }
+
+ static final class Factory extends ProductionComponentMonitor.Factory {
+ private final ImmutableList<? extends ProductionComponentMonitor.Factory> delegates;
+
+ Factory(Iterable<? extends ProductionComponentMonitor.Factory> delegates) {
+ this.delegates = ImmutableList.copyOf(delegates);
+ }
+
+ @Override
+ public ProductionComponentMonitor create(Object component) {
+ ImmutableList.Builder<ProductionComponentMonitor> monitorsBuilder = ImmutableList.builder();
+ for (ProductionComponentMonitor.Factory delegate : delegates) {
+ try {
+ ProductionComponentMonitor monitor = delegate.create(component);
+ if (monitor != null) {
+ monitorsBuilder.add(monitor);
+ }
+ } catch (RuntimeException e) {
+ logCreateException(e, delegate, component);
+ }
+ }
+ ImmutableList<ProductionComponentMonitor> monitors = monitorsBuilder.build();
+ if (monitors.isEmpty()) {
+ return ProductionComponentMonitor.noOp();
+ } else if (monitors.size() == 1) {
+ return new NonThrowingProductionComponentMonitor(Iterables.getOnlyElement(monitors));
+ } else {
+ return new DelegatingProductionComponentMonitor(monitors);
+ }
+ }
+ }
+ }
+
+ /**
+ * A producer monitor that delegates to several monitors, and catches and logs all exceptions
+ * that the delegates throw.
+ */
+ private static final class DelegatingProducerMonitor extends ProducerMonitor {
+ private final ImmutableList<ProducerMonitor> delegates;
+
+ DelegatingProducerMonitor(ImmutableList<ProducerMonitor> delegates) {
+ this.delegates = delegates;
+ }
+
+ @Override
+ public void requested() {
+ for (ProducerMonitor delegate : delegates) {
+ try {
+ delegate.requested();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "requested");
+ }
+ }
+ }
+
+ @Override
+ public void ready() {
+ for (ProducerMonitor delegate : delegates) {
+ try {
+ delegate.ready();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "ready");
+ }
+ }
+ }
+
+ @Override
+ public void methodStarting() {
+ for (ProducerMonitor delegate : delegates) {
+ try {
+ delegate.methodStarting();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodStarting");
+ }
+ }
+ }
+
+ @Override
+ public void methodFinished() {
+ for (ProducerMonitor delegate : delegates.reverse()) {
+ try {
+ delegate.methodFinished();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodFinished");
+ }
+ }
+ }
+
+ @Override
+ public void succeeded(Object o) {
+ for (ProducerMonitor delegate : delegates.reverse()) {
+ try {
+ delegate.succeeded(o);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "succeeded", o);
+ }
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ for (ProducerMonitor delegate : delegates.reverse()) {
+ try {
+ delegate.failed(t);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "failed", t);
+ }
+ }
+ }
+ }
+
+ /** Returns a provider of a no-op component monitor. */
+ public static Provider<ProductionComponentMonitor> noOpProductionComponentMonitorProvider() {
+ return NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER;
+ }
+
+ private static final Provider<ProductionComponentMonitor>
+ NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER =
+ new Provider<ProductionComponentMonitor>() {
+ @Override
+ public ProductionComponentMonitor get() {
+ return ProductionComponentMonitor.noOp();
+ }
+ };
+
+ private static void logCreateException(
+ RuntimeException e, ProductionComponentMonitor.Factory factory, Object component) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProductionComponentMonitor.Factory.create on factory "
+ + factory
+ + " with component "
+ + component,
+ e);
+ }
+
+ private static void logProducerMonitorForException(
+ RuntimeException e, ProductionComponentMonitor monitor, ProducerToken token) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProductionComponentMonitor.producerMonitorFor on monitor "
+ + monitor
+ + " with token "
+ + token,
+ e);
+ }
+
+ private static void logProducerMonitorMethodException(
+ RuntimeException e, ProducerMonitor monitor, String method) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProducerMonitor." + method + " on monitor " + monitor,
+ e);
+ }
+
+ private static void logProducerMonitorArgMethodException(
+ RuntimeException e, ProducerMonitor monitor, String method, Object arg) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProducerMonitor."
+ + method
+ + " on monitor "
+ + monitor
+ + " with "
+ + arg,
+ e);
+ }
+
+ private Monitors() {}
+}
diff --git a/java/dagger/producers/monitoring/package-info.java b/java/dagger/producers/monitoring/package-info.java
new file mode 100644
index 0000000..122df5d
--- /dev/null
+++ b/java/dagger/producers/monitoring/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package provides hooks for monitoring producers.
+ *
+ * <p>The interfaces in this package are not stable. Do not use these interfaces unless you are
+ * prepared to be broken.
+ */
+
+package dagger.producers.monitoring;
diff --git a/java/dagger/producers/package-info.java b/java/dagger/producers/package-info.java
new file mode 100644
index 0000000..9693ae9
--- /dev/null
+++ b/java/dagger/producers/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the public API for Dagger 2's producer functionality.
+ *
+ * <p>Dagger Producers is an extension to Dagger that implements asynchronous dependency injection
+ * in Java.
+ *
+ * <p>Extended documentation on Dagger Producers can be found at <a
+ * href="https://dagger.dev/producers">https://dagger.dev/producers</a>.
+ */
+package dagger.producers;
diff --git a/java/dagger/spi/BUILD b/java/dagger/spi/BUILD
new file mode 100644
index 0000000..9c04582
--- /dev/null
+++ b/java/dagger/spi/BUILD
@@ -0,0 +1,56 @@
+# Copyright (C) 2018 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# The Service Provider Interface for Dagger's binding graph model
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+)
+
+filegroup(
+ name = "spi-srcs",
+ srcs = glob(["*.java"]),
+)
+
+load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+
+java_library(
+ name = "spi",
+ srcs = [":spi-srcs"],
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ tags = ["maven_coordinates=com.google.dagger:dagger-spi:" + POM_VERSION],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/model",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+pom_file(
+ name = "pom",
+ artifact_id = "dagger-spi",
+ artifact_name = "Dagger SPI",
+ targets = [
+ "//java/dagger/model",
+ ":spi",
+ ],
+)
diff --git a/java/dagger/spi/BindingGraphPlugin.java b/java/dagger/spi/BindingGraphPlugin.java
new file mode 100644
index 0000000..02a051b
--- /dev/null
+++ b/java/dagger/spi/BindingGraphPlugin.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.spi;
+
+import dagger.model.BindingGraph;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * A pluggable visitor for {@link BindingGraph}.
+ *
+ * <p>Note: This is still experimental and will change.
+ */
+public interface BindingGraphPlugin {
+ /**
+ * Called once for each valid root binding graph encountered by the Dagger processor. May report
+ * diagnostics using {@code diagnosticReporter}.
+ */
+ void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter);
+
+ /**
+ * Initializes this plugin with a {@link Filer} that it can use to write Java or other files based
+ * on the binding graph. This will be called once per instance of this plugin, before any graph is
+ * {@linkplain #visitGraph(BindingGraph, DiagnosticReporter) visited}.
+ *
+ * @see javax.annotation.processing.ProcessingEnvironment#getFiler()
+ */
+ default void initFiler(Filer filer) {}
+
+ /**
+ * Initializes this plugin with a {@link Types} instance. This will be called once per instance of
+ * this plugin, before any graph is {@linkplain #visitGraph(BindingGraph, DiagnosticReporter)
+ * visited}.
+ *
+ * @see javax.annotation.processing.ProcessingEnvironment#getTypeUtils()
+ */
+ default void initTypes(Types types) {}
+
+ /**
+ * Initializes this plugin with a {@link Elements} instance. This will be called once per instance
+ * of this plugin, before any graph is {@linkplain #visitGraph(BindingGraph, DiagnosticReporter)
+ * visited}.
+ *
+ * @see javax.annotation.processing.ProcessingEnvironment#getElementUtils()
+ */
+ default void initElements(Elements elements) {}
+
+ /**
+ * Initializes this plugin with a filtered view of the options passed on the {@code javac}
+ * command-line for all keys from {@link #supportedOptions()}. This will be called once per
+ * instance of this plugin, before any graph is {@linkplain #visitGraph(BindingGraph,
+ * DiagnosticReporter) visited}.
+ *
+ * @see javax.annotation.processing.ProcessingEnvironment#getOptions()
+ */
+ default void initOptions(Map<String, String> options) {}
+
+ /**
+ * Returns the annotation-processing options that this plugin uses to configure behavior.
+ *
+ * @see javax.annotation.processing.Processor#getSupportedOptions()
+ */
+ default Set<String> supportedOptions() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * A distinguishing name of the plugin that will be used in diagnostics printed to the {@link
+ * Messager}. By default, the {@linkplain Class#getCanonicalName() fully qualified name} of the
+ * plugin is used.
+ */
+ default String pluginName() {
+ return getClass().getCanonicalName();
+ }
+}
diff --git a/java/dagger/spi/DiagnosticReporter.java b/java/dagger/spi/DiagnosticReporter.java
new file mode 100644
index 0000000..f9ec41e
--- /dev/null
+++ b/java/dagger/spi/DiagnosticReporter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.spi;
+
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.MaybeBinding;
+import javax.tools.Diagnostic;
+
+/**
+ * An object that {@link BindingGraphPlugin}s can use to report diagnostics while visiting a {@link
+ * BindingGraph}.
+ *
+ * <p>Note: This API is still experimental and will change.
+ */
+public interface DiagnosticReporter {
+ /**
+ * Reports a diagnostic for a component. For non-root components, includes information about the
+ * path from the root component.
+ */
+ void reportComponent(Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String message);
+
+ /**
+ * Reports a diagnostic for a component. For non-root components, includes information about the
+ * path from the root component.
+ */
+ @FormatMethod
+ void reportComponent(
+ Diagnostic.Kind diagnosticKind,
+ ComponentNode componentNode,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs);
+
+ /**
+ * Reports a diagnostic for a binding or missing binding. Includes information about how the
+ * binding is reachable from entry points.
+ */
+ void reportBinding(Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message);
+
+ /**
+ * Reports a diagnostic for a binding or missing binding. Includes information about how the
+ * binding is reachable from entry points.
+ */
+ @FormatMethod
+ void reportBinding(
+ Diagnostic.Kind diagnosticKind,
+ MaybeBinding binding,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs);
+
+ /**
+ * Reports a diagnostic for a dependency. Includes information about how the dependency is
+ * reachable from entry points.
+ */
+ void reportDependency(
+ Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message);
+
+ /**
+ * Reports a diagnostic for a dependency. Includes information about how the dependency is
+ * reachable from entry points.
+ */
+ @FormatMethod
+ void reportDependency(
+ Diagnostic.Kind diagnosticKind,
+ DependencyEdge dependencyEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs);
+
+ /** Reports a diagnostic for a subcomponent factory method. */
+ void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String message);
+
+ /** Reports a diagnostic for a subcomponent factory method. */
+ @FormatMethod
+ void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs);
+}
diff --git a/java/dagger/spi/package-info.java b/java/dagger/spi/package-info.java
new file mode 100644
index 0000000..87ebb33
--- /dev/null
+++ b/java/dagger/spi/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the Service Provider interface (SPI) to the {@link dagger.model} APIs at
+ * annotation-processing-time.
+ *
+ * <p>This package is experimental, and APIs may change at over time.
+ */
+@CheckReturnValue
+@Beta
+package dagger.spi;
+
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.internal.Beta;
diff --git a/javatests/dagger/BUILD b/javatests/dagger/BUILD
new file mode 100644
index 0000000..3debec5
--- /dev/null
+++ b/javatests/dagger/BUILD
@@ -0,0 +1,35 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A JSR-330 compliant dependency injection system for android and java
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "core_tests",
+ srcs = glob(["**/*.java"]),
+ functional = 0,
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ deps = [
+ "//java/dagger:core",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/android/AndroidInjectionTest.java b/javatests/dagger/android/AndroidInjectionTest.java
new file mode 100644
index 0000000..19b6e84
--- /dev/null
+++ b/javatests/dagger/android/AndroidInjectionTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Fragment;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.FragmentTestUtil;
+
+@Config(manifest = Config.NONE)
+@RunWith(RobolectricTestRunner.class)
+public final class AndroidInjectionTest {
+
+ // Most positive tests are performed in javatests/dagger/android/support/functional, but
+ // Robolectric's support for framework fragments is lacking, so we supplement those tests here:
+ public static class InjectableFragment extends Fragment {
+ String tag;
+ }
+
+ private static AndroidInjector<Fragment> fakeFragmentInjector(String tag) {
+ return instance -> {
+ if (instance instanceof InjectableFragment) {
+ ((InjectableFragment) instance).tag = tag;
+ }
+ };
+ }
+
+ public static class ApplicationInjectsFragment extends Application
+ implements HasFragmentInjector {
+ @Override
+ public AndroidInjector<Fragment> fragmentInjector() {
+ return fakeFragmentInjector("injected by app");
+ }
+ }
+
+ @Config(manifest = Config.NONE, application = ApplicationInjectsFragment.class)
+ @Test
+ public void fragmentInjectedByApplication() {
+ Activity activity = Robolectric.setupActivity(Activity.class);
+ InjectableFragment fragment = new InjectableFragment();
+ activity.getFragmentManager().beginTransaction().add(fragment, "tag").commit();
+
+ AndroidInjection.inject(fragment);
+
+ assertThat(fragment.tag).isEqualTo("injected by app");
+ }
+
+ public static class ActivityInjectsFragment extends Activity implements HasFragmentInjector {
+ @Override
+ public AndroidInjector<Fragment> fragmentInjector() {
+ return fakeFragmentInjector("injected by activity");
+ }
+ }
+
+ @Config(manifest = Config.NONE, application = ApplicationInjectsFragment.class)
+ @Test
+ public void fragmentInjectedByActivity() {
+ ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class);
+ InjectableFragment fragment = new InjectableFragment();
+ activity.getFragmentManager().beginTransaction().add(fragment, "tag").commit();
+
+ AndroidInjection.inject(fragment);
+
+ assertThat(fragment.tag).isEqualTo("injected by activity");
+ }
+
+ public static class ParentFragmentInjectsChildFragment extends Fragment
+ implements HasFragmentInjector {
+ @Override
+ public AndroidInjector<Fragment> fragmentInjector() {
+ return fakeFragmentInjector("injected by parent fragment");
+ }
+ }
+
+ @Config(manifest = Config.NONE, application = ApplicationInjectsFragment.class)
+ @Test
+ public void fragmentInjectedByParentFragment() {
+ ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class);
+ ParentFragmentInjectsChildFragment parentFragment = new ParentFragmentInjectsChildFragment();
+ InjectableFragment childFragment = new InjectableFragment();
+
+ activity.getFragmentManager().beginTransaction().add(parentFragment, "tag").commit();
+ parentFragment
+ .getChildFragmentManager()
+ .beginTransaction()
+ .add(childFragment, "child-tag")
+ .commit();
+ AndroidInjection.inject(childFragment);
+
+ assertThat(childFragment.tag).isEqualTo("injected by parent fragment");
+ }
+
+ @Test
+ public void injectActivity_applicationDoesntImplementHasActivityInjector() {
+ Activity activity = Robolectric.setupActivity(Activity.class);
+
+ try {
+ AndroidInjection.inject(activity);
+ fail();
+ } catch (Exception e) {
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Application does not implement dagger.android.HasAndroidInjector");
+ }
+ }
+
+ @Test
+ public void injectFragment_hasFragmentInjectorNotFound() {
+ Fragment fragment = new Fragment();
+ FragmentTestUtil.startFragment(fragment);
+
+ try {
+ AndroidInjection.inject(fragment);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessageThat().contains("No injector was found");
+ }
+ }
+
+ private static class ApplicationReturnsNull extends Application
+ implements HasActivityInjector, HasFragmentInjector {
+ @Override
+ public AndroidInjector<Activity> activityInjector() {
+ return null;
+ }
+
+ @Override
+ public AndroidInjector<Fragment> fragmentInjector() {
+ return null;
+ }
+ }
+
+ @Test
+ @Config(manifest = Config.NONE, application = ApplicationReturnsNull.class)
+ public void activityInjector_returnsNull() {
+ Activity activity = Robolectric.setupActivity(Activity.class);
+
+ try {
+ AndroidInjection.inject(activity);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessageThat().contains("activityInjector() returned null");
+ }
+ }
+
+ @Test
+ @Config(manifest = Config.NONE, application = ApplicationReturnsNull.class)
+ public void fragmentInjector_returnsNull() {
+ Fragment fragment = new Fragment();
+ FragmentTestUtil.startFragment(fragment);
+
+ try {
+ AndroidInjection.inject(fragment);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessageThat().contains("fragmentInjector() returned null");
+ }
+ }
+
+ @Test
+ public void injectActivity_nullInput() {
+ try {
+ AndroidInjection.inject((Activity) null);
+ fail();
+ } catch (NullPointerException e) {
+ assertThat(e).hasMessageThat().contains("activity");
+ }
+ }
+
+ @Test
+ public void injectFragment_nullInput() {
+ try {
+ AndroidInjection.inject((Fragment) null);
+ fail();
+ } catch (NullPointerException e) {
+ assertThat(e).hasMessageThat().contains("fragment");
+ }
+ }
+}
diff --git a/javatests/dagger/android/BUILD b/javatests/dagger/android/BUILD
new file mode 100644
index 0000000..5bc3f45
--- /dev/null
+++ b/javatests/dagger/android/BUILD
@@ -0,0 +1,36 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Tests for Dagger's Android integrations
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenRobolectricTests")
+
+GenRobolectricTests(
+ name = "android_tests",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ manifest_values = {"minSdkVersion": "17"},
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/android",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/android/DispatchingAndroidInjectorTest.java b/javatests/dagger/android/DispatchingAndroidInjectorTest.java
new file mode 100644
index 0000000..d0306b8
--- /dev/null
+++ b/javatests/dagger/android/DispatchingAndroidInjectorTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import com.google.common.collect.ImmutableMap;
+import dagger.android.AndroidInjector.Factory;
+import dagger.android.DispatchingAndroidInjector.InvalidInjectorBindingException;
+import java.util.Map;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@Config(manifest = Config.NONE)
+@RunWith(RobolectricTestRunner.class)
+public final class DispatchingAndroidInjectorTest {
+ @Test
+ public void withClassKeys() {
+ DispatchingAndroidInjector<Activity> dispatchingAndroidInjector =
+ newDispatchingAndroidInjector(
+ ImmutableMap.of(FooActivity.class, FooInjector.Factory::new), ImmutableMap.of());
+
+ FooActivity activity = Robolectric.setupActivity(FooActivity.class);
+ assertThat(dispatchingAndroidInjector.maybeInject(activity)).isTrue();
+ }
+
+ @Test
+ public void withStringKeys() {
+ DispatchingAndroidInjector<Activity> dispatchingAndroidInjector =
+ newDispatchingAndroidInjector(
+ ImmutableMap.of(),
+ ImmutableMap.of(FooActivity.class.getName(), FooInjector.Factory::new));
+
+ FooActivity activity = Robolectric.setupActivity(FooActivity.class);
+ assertThat(dispatchingAndroidInjector.maybeInject(activity)).isTrue();
+ }
+
+ @Test
+ public void withMixedKeys() {
+ DispatchingAndroidInjector<Activity> dispatchingAndroidInjector =
+ newDispatchingAndroidInjector(
+ ImmutableMap.of(FooActivity.class, FooInjector.Factory::new),
+ ImmutableMap.of(BarActivity.class.getName(), BarInjector.Factory::new));
+
+ FooActivity fooActivity = Robolectric.setupActivity(FooActivity.class);
+ assertThat(dispatchingAndroidInjector.maybeInject(fooActivity)).isTrue();
+ BarActivity barActivity = Robolectric.setupActivity(BarActivity.class);
+ assertThat(dispatchingAndroidInjector.maybeInject(barActivity)).isTrue();
+ }
+
+ @Test
+ public void maybeInject_returnsFalse_ifNoMatchingInjectorExists() {
+ DispatchingAndroidInjector<Activity> dispatchingAndroidInjector =
+ newDispatchingAndroidInjector(ImmutableMap.of(), ImmutableMap.of());
+
+ BarActivity activity = Robolectric.setupActivity(BarActivity.class);
+ assertThat(dispatchingAndroidInjector.maybeInject(activity)).isFalse();
+ }
+
+ @Test
+ public void throwsIfFactoryCreateReturnsNull() {
+ DispatchingAndroidInjector<Activity> dispatchingAndroidInjector =
+ newDispatchingAndroidInjector(
+ ImmutableMap.of(FooActivity.class, () -> null), ImmutableMap.of());
+ FooActivity activity = Robolectric.setupActivity(FooActivity.class);
+
+ try {
+ dispatchingAndroidInjector.maybeInject(activity);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void throwsIfClassMismatched() {
+ DispatchingAndroidInjector<Activity> dispatchingAndroidInjector =
+ newDispatchingAndroidInjector(
+ ImmutableMap.of(FooActivity.class, BarInjector.Factory::new), ImmutableMap.of());
+ FooActivity activity = Robolectric.setupActivity(FooActivity.class);
+
+ try {
+ dispatchingAndroidInjector.maybeInject(activity);
+ fail("Expected InvalidInjectorBindingException");
+ } catch (InvalidInjectorBindingException expected) {
+ }
+ }
+
+ private static <T> DispatchingAndroidInjector<T> newDispatchingAndroidInjector(
+ Map<Class<?>, Provider<Factory<?>>> injectorFactoriesWithClassKeys,
+ Map<String, Provider<AndroidInjector.Factory<?>>>
+ injectorFactoriesWithStringKeys) {
+ return new DispatchingAndroidInjector<>(
+ injectorFactoriesWithClassKeys,
+ injectorFactoriesWithStringKeys ,
+ ImmutableMap.of(),
+ ImmutableMap.of());
+ }
+
+ static class FooActivity extends Activity {}
+
+ static class BarActivity extends Activity {}
+
+ static class FooInjector implements AndroidInjector<FooActivity> {
+ @Override
+ public void inject(FooActivity instance) {}
+
+ static class Factory implements AndroidInjector.Factory<FooActivity> {
+ @Override
+ public AndroidInjector<FooActivity> create(FooActivity activity) {
+ return new FooInjector();
+ }
+ }
+ }
+
+ static class BarInjector implements AndroidInjector<BarActivity> {
+ @Override
+ public void inject(BarActivity instance) {}
+
+ static class Factory implements AndroidInjector.Factory<BarActivity> {
+ @Override
+ public AndroidInjector<BarActivity> create(BarActivity activity) {
+ return new BarInjector();
+ }
+ }
+ }
+}
diff --git a/javatests/dagger/android/processor/AndroidMapKeyValidatorTest.java b/javatests/dagger/android/processor/AndroidMapKeyValidatorTest.java
new file mode 100644
index 0000000..cfa6e90
--- /dev/null
+++ b/javatests/dagger/android/processor/AndroidMapKeyValidatorTest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.common.base.Joiner;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.ComponentProcessor;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AndroidMapKeyValidatorTest {
+ private static final JavaFileObject FOO_ACTIVITY =
+ JavaFileObjects.forSourceLines(
+ "test.FooActivity",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "import dagger.android.AndroidInjector;",
+ "",
+ "public class FooActivity extends Activity {",
+ " interface Factory extends AndroidInjector.Factory<FooActivity> {}",
+ " abstract static class Builder extends AndroidInjector.Builder<FooActivity> {}",
+ "}");
+ private static final JavaFileObject BAR_ACTIVITY =
+ JavaFileObjects.forSourceLines(
+ "test.BarActivity",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "",
+ "public class BarActivity extends Activity {}");
+
+ private static JavaFileObject moduleWithMethod(String... lines) {
+ return JavaFileObjects.forSourceLines(
+ "test.AndroidModule",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "import android.app.Fragment;",
+ "import dagger.Module;",
+ "import dagger.*;",
+ "import dagger.android.*;",
+ "import dagger.multibindings.*;",
+ "import javax.inject.*;",
+ "",
+ "@Module",
+ "abstract class AndroidModule {",
+ " " + Joiner.on("\n ").join(lines),
+ "}");
+ }
+
+ // TODO(dpb): Change these tests to use onLineContaining() instead of onLine().
+ private static final int LINES_BEFORE_METHOD = 12;
+
+ @Test
+ public void rawFactoryType() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "abstract AndroidInjector.Factory bindRawFactory(FooActivity.Factory factory);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "should bind dagger.android.AndroidInjector.Factory<?>, "
+ + "not dagger.android.AndroidInjector.Factory");
+ }
+
+ @Test
+ public void rawBuilderType() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "abstract AndroidInjector.Builder bindRawBuilder(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "should bind dagger.android.AndroidInjector.Factory<?>, "
+ + "not dagger.android.AndroidInjector.Builder");
+ }
+
+ @Test
+ public void bindsToBuilderNotFactory() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "abstract AndroidInjector.Builder<?> bindBuilder(",
+ " FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "should bind dagger.android.AndroidInjector.Factory<?>, not "
+ + "dagger.android.AndroidInjector.Builder<?>");
+ }
+
+ @Test
+ public void providesToBuilderNotFactory() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Provides",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "static AndroidInjector.Builder<?> bindBuilder(FooActivity.Builder builder) {",
+ " return builder;",
+ "}");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "should bind dagger.android.AndroidInjector.Factory<?>, not "
+ + "dagger.android.AndroidInjector.Builder<?>");
+ }
+
+ @Test
+ public void bindsToConcreteTypeInsteadOfWildcard() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "abstract AndroidInjector.Builder<FooActivity> bindBuilder(",
+ " FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "should bind dagger.android.AndroidInjector.Factory<?>, not "
+ + "dagger.android.AndroidInjector.Builder<test.FooActivity>");
+ }
+
+ @Test
+ public void bindsToBaseTypeInsteadOfWildcard() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "abstract AndroidInjector.Builder<Activity> bindBuilder(",
+ " FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Binds methods' parameter type must be assignable to the return type");
+ }
+
+ @Test
+ public void bindsCorrectType() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "abstract AndroidInjector.Factory<?> bindCorrectType(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void bindsCorrectType_AndroidInjectionKey() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@AndroidInjectionKey(\"test.FooActivity\")",
+ "abstract AndroidInjector.Factory<?> bindCorrectType(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void bindsCorrectType_AndroidInjectionKey_unbounded() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@AndroidInjectionKey(\"test.FooActivity\")",
+ "abstract AndroidInjector.Factory<?> bindCorrectType(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void bindsWithScope() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "@Singleton",
+ "abstract AndroidInjector.Factory<?> bindWithScope(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("should not be scoped");
+ }
+
+ @Test
+ public void bindsWithScope_suppressWarnings() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@SuppressWarnings(\"dagger.android.ScopedInjectorFactory\")",
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "@Singleton",
+ "abstract AndroidInjector.Factory<?> bindWithScope(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void mismatchedMapKey_bindsFactory() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(BarActivity.class)",
+ "abstract AndroidInjector.Factory<?> mismatchedFactory(FooActivity.Factory factory);");
+ Compilation compilation = compile(module, FOO_ACTIVITY, BAR_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.FooActivity.Factory does not implement AndroidInjector<test.BarActivity>")
+ .inFile(module)
+ .onLine(LINES_BEFORE_METHOD + 3);
+ }
+
+ @Test
+ public void mismatchedMapKey_bindsBuilder() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(BarActivity.class)",
+ "abstract AndroidInjector.Factory<?> mismatchedBuilder(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY, BAR_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.FooActivity.Builder does not implement AndroidInjector<test.BarActivity>")
+ .inFile(module)
+ .onLine(LINES_BEFORE_METHOD + 3);
+ }
+
+ @Test
+ public void mismatchedMapKey_bindsBuilder_androidInjectionKey() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@AndroidInjectionKey(\"test.BarActivity\")",
+ "abstract AndroidInjector.Factory<?> mismatchedBuilder(FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY, BAR_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.FooActivity.Builder does not implement AndroidInjector<test.BarActivity>")
+ .inFile(module)
+ .onLine(LINES_BEFORE_METHOD + 3);
+ }
+
+ @Test
+ public void mismatchedMapKey_providesBuilder() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Provides",
+ "@IntoMap",
+ "@ClassKey(BarActivity.class)",
+ "static AndroidInjector.Factory<?> mismatchedBuilder(FooActivity.Builder builder) {",
+ " return builder;",
+ "}");
+ Compilation compilation = compile(module, FOO_ACTIVITY, BAR_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void bindsQualifier_ignoresChecks() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "@Named(\"unused\")",
+ // normally this should fail, since it is binding to a Builder not a Factory
+ "abstract AndroidInjector.Builder<?> bindsBuilderWithQualifier(",
+ " FooActivity.Builder builder);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void bindToPrimitive() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@AndroidInjectionKey(\"test.FooActivity\")",
+ "abstract int bindInt(@Named(\"unused\") int otherInt);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void bindToNonFrameworkClass() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@AndroidInjectionKey(\"test.FooActivity\")",
+ "abstract Number bindInt(Integer integer);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void invalidBindsMethod() {
+ JavaFileObject module =
+ moduleWithMethod(
+ "@Binds",
+ "@IntoMap",
+ "@ClassKey(FooActivity.class)",
+ "abstract AndroidInjector.Factory<?> bindCorrectType(",
+ " FooActivity.Builder builder, FooActivity.Builder builder2);");
+ Compilation compilation = compile(module, FOO_ACTIVITY);
+ assertThat(compilation).failed();
+ }
+
+ private Compilation compile(JavaFileObject... files) {
+ return javac().withProcessors(new ComponentProcessor(), new AndroidProcessor()).compile(files);
+ }
+}
diff --git a/javatests/dagger/android/processor/AndroidProcessorTest.java b/javatests/dagger/android/processor/AndroidProcessorTest.java
new file mode 100644
index 0000000..1a45bdd
--- /dev/null
+++ b/javatests/dagger/android/processor/AndroidProcessorTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AndroidProcessorTest {
+ @Test
+ public void generatedProguardFile() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.android.AndroidInjectionKey;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class TestModule {",
+ " @Provides",
+ " @IntoMap",
+ " @AndroidInjectionKey(\"test.TestActivity\")",
+ " static int i() { ",
+ " return 1;",
+ " }",
+ "}");
+ Compilation enabled =
+ javac()
+ .withProcessors(new AndroidProcessor())
+ .withOptions("-Adagger.android.experimentalUseStringKeys=true")
+ .compile(module);
+ assertThat(enabled).succeeded();
+ assertThat(enabled)
+ .generatedFile(CLASS_OUTPUT, "META-INF/proguard/dagger.android.AndroidInjectionKeys");
+
+ Compilation disabled =
+ javac()
+ .withProcessors(new AndroidProcessor())
+ .withOptions("-Adagger.android.experimentalUseStringKeys=false")
+ .compile(module);
+ assertThat(disabled).succeeded();
+ assertThat(
+ disabled.generatedFile(
+ CLASS_OUTPUT, "META-INF/proguard/dagger.android.AndroidInjectionKeys"))
+ .isEmpty();
+
+ Compilation noFlag = javac().withProcessors(new AndroidProcessor()).compile(module);
+ assertThat(noFlag).succeeded();
+ assertThat(
+ noFlag.generatedFile(
+ CLASS_OUTPUT, "META-INF/proguard/dagger.android.AndroidInjectionKeys"))
+ .isEmpty();
+ }
+}
diff --git a/javatests/dagger/android/processor/BUILD b/javatests/dagger/android/processor/BUILD
new file mode 100644
index 0000000..8a9c16e
--- /dev/null
+++ b/javatests/dagger/android/processor/BUILD
@@ -0,0 +1,42 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Tests for Dagger's Android integrations
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "android_processor_tests",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "@google_bazel_common//third_party/java/guava",
+ "@androidsdk//com.android.support:support-fragment-25.0.0",
+ # TODO(ronshapiro): create a common location to define the current Android version
+ "@androidsdk//:platforms/android-26/android.jar",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ "@google_bazel_common//third_party/java/truth:truth8",
+ "//java/dagger/android",
+ "//java/dagger/android/processor",
+ "//java/dagger/internal/codegen:processor",
+ ],
+)
diff --git a/javatests/dagger/android/processor/ContributesAndroidInjectorTest.java b/javatests/dagger/android/processor/ContributesAndroidInjectorTest.java
new file mode 100644
index 0000000..1718737
--- /dev/null
+++ b/javatests/dagger/android/processor/ContributesAndroidInjectorTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ContributesAndroidInjectorTest {
+ private static final JavaFileObject TEST_ACTIVITY =
+ JavaFileObjects.forSourceLines(
+ "test.TestActivity",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "",
+ "class TestActivity extends Activity {}");
+
+ @Test
+ public void notAbstract() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.android.ContributesAndroidInjector;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @ContributesAndroidInjector",
+ " static TestActivity test() {",
+ " return null;",
+ " }",
+ "}");
+
+ Compilation compilation = compile(module, TEST_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("must be abstract")
+ .inFile(module)
+ .onLineContaining("test()");
+ }
+
+ @Test
+ public void hasParameters() {
+ JavaFileObject otherActivity =
+ JavaFileObjects.forSourceLines(
+ "test.OtherActivity",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "",
+ "class OtherActivity extends Activity {}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.android.ContributesAndroidInjector;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @ContributesAndroidInjector",
+ " abstract TestActivity oneParam(TestActivity one);",
+ "",
+ " @ContributesAndroidInjector",
+ " abstract OtherActivity manyParams(OtherActivity two, Object o);",
+ "}");
+
+ Compilation compilation = compile(module, TEST_ACTIVITY, otherActivity);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("cannot have parameters")
+ .inFile(module)
+ .onLineContaining("oneParam(");
+ assertThat(compilation)
+ .hadErrorContaining("cannot have parameters")
+ .inFile(module)
+ .onLineContaining("manyParams(");
+ }
+
+ @Test
+ public void notInAModule() {
+ JavaFileObject randomFile =
+ JavaFileObjects.forSourceLines(
+ "test.RandomFile",
+ "package test;",
+ "",
+ "import dagger.android.ContributesAndroidInjector;",
+ "",
+ "abstract class RandomFile {",
+ " @ContributesAndroidInjector",
+ " abstract TestActivity test() {}",
+ "}");
+
+ Compilation compilation = compile(randomFile, TEST_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("must be in a @Module")
+ .inFile(randomFile)
+ .onLineContaining("test()");
+ }
+
+ @Test
+ public void parameterizedReturnType() {
+ JavaFileObject parameterizedActivity =
+ JavaFileObjects.forSourceLines(
+ "test.ParameterizedActivity",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "",
+ "class ParameterizedActivity<T> extends Activity {}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.android.ContributesAndroidInjector;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @ContributesAndroidInjector",
+ " abstract <T> ParameterizedActivity<T> test();",
+ "}");
+
+ Compilation compilation = compile(module, TEST_ACTIVITY, parameterizedActivity);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("cannot return parameterized types")
+ .inFile(module)
+ .onLineContaining("test()");
+ }
+
+ @Test
+ public void moduleIsntModule() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.android.ContributesAndroidInjector;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @ContributesAndroidInjector(modules = android.content.Intent.class)",
+ " abstract TestActivity test();",
+ "}");
+
+ Compilation compilation = compile(module, TEST_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Intent is not a @Module")
+ .inFile(module)
+ .onLineContaining("modules = android.content.Intent.class");
+ }
+
+ @Test
+ public void hasQualifier() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.android.ContributesAndroidInjector;",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Qualifier @interface AndroidQualifier {}",
+ "",
+ " @AndroidQualifier",
+ " @ContributesAndroidInjector",
+ " abstract TestActivity test();",
+ "}");
+
+ Compilation compilation = compile(module, TEST_ACTIVITY);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@ContributesAndroidInjector methods cannot have qualifiers")
+ .inFile(module)
+ .onLineContaining("@AndroidQualifier");
+ }
+
+ private static Compilation compile(JavaFileObject... javaFileObjects) {
+ return javac().withProcessors(new AndroidProcessor()).compile(javaFileObjects);
+ }
+}
diff --git a/javatests/dagger/android/processor/DuplicateAndroidInjectorsCheckerTest.java b/javatests/dagger/android/processor/DuplicateAndroidInjectorsCheckerTest.java
new file mode 100644
index 0000000..a84c7eb
--- /dev/null
+++ b/javatests/dagger/android/processor/DuplicateAndroidInjectorsCheckerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.ComponentProcessor;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class DuplicateAndroidInjectorsCheckerTest {
+ @Test
+ public void conflictingMapKeys() {
+ JavaFileObject activity =
+ JavaFileObjects.forSourceLines(
+ "test.TestActivity",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "",
+ "public class TestActivity extends Activity {}");
+ JavaFileObject injectorFactory =
+ JavaFileObjects.forSourceLines(
+ "test.TestInjectorFactory",
+ "package test;",
+ "",
+ "import dagger.android.AndroidInjector;",
+ "import javax.inject.Inject;",
+ "",
+ "class TestInjectorFactory implements AndroidInjector.Factory<TestActivity> {",
+ " @Inject TestInjectorFactory() {}",
+ "",
+ " @Override",
+ " public AndroidInjector<TestActivity> create(TestActivity instance) { return null; }",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.android.*;",
+ "import dagger.multibindings.*;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds",
+ " @IntoMap",
+ " @ClassKey(TestActivity.class)",
+ " AndroidInjector.Factory<?> classKey(TestInjectorFactory factory);",
+ "",
+ " @Binds",
+ " @IntoMap",
+ " @AndroidInjectionKey(\"test.TestActivity\")",
+ " AndroidInjector.Factory<?> stringKey(TestInjectorFactory factory);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "import dagger.Component;",
+ "import dagger.android.DispatchingAndroidInjector;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " DispatchingAndroidInjector<Activity> dispatchingInjector();",
+ "}");
+
+ Compilation compilation =
+ javac()
+ .withProcessors(ComponentProcessor.forTesting(new DuplicateAndroidInjectorsChecker()))
+ .compile(activity, injectorFactory, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Multiple injector factories bound for the same type")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ assertThat(compilation).hadErrorContaining("classKey(test.TestInjectorFactory)");
+ assertThat(compilation).hadErrorContaining("stringKey(test.TestInjectorFactory)");
+ assertThat(compilation).hadErrorCount(1);
+ }
+}
diff --git a/javatests/dagger/android/support/AndroidSupportInjectionTest.java b/javatests/dagger/android/support/AndroidSupportInjectionTest.java
new file mode 100644
index 0000000..51e9992
--- /dev/null
+++ b/javatests/dagger/android/support/AndroidSupportInjectionTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import android.app.Application;
+import android.support.v4.app.Fragment;
+import dagger.android.AndroidInjector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.support.v4.SupportFragmentTestUtil;
+
+@Config(manifest = Config.NONE)
+@RunWith(RobolectricTestRunner.class)
+public final class AndroidSupportInjectionTest {
+ @Test
+ public void injectFragment_simpleApplication() {
+ Fragment fragment = new Fragment();
+ SupportFragmentTestUtil.startFragment(fragment);
+
+ try {
+ AndroidSupportInjection.inject(fragment);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessageThat().contains("No injector was found");
+ }
+ }
+
+ private static class ApplicationReturnsNull extends Application
+ implements HasSupportFragmentInjector {
+ @Override
+ public AndroidInjector<Fragment> supportFragmentInjector() {
+ return null;
+ }
+ }
+
+ @Test
+ @Config(manifest = Config.NONE, application = ApplicationReturnsNull.class)
+ public void fragmentInjector_returnsNull() {
+ Fragment fragment = new Fragment();
+ SupportFragmentTestUtil.startFragment(fragment);
+
+ try {
+ AndroidSupportInjection.inject(fragment);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessageThat().contains("supportFragmentInjector() returned null");
+ }
+ }
+
+ @Test
+ public void injectFragment_nullInput() {
+ try {
+ AndroidSupportInjection.inject(null);
+ fail();
+ } catch (NullPointerException e) {
+ assertThat(e).hasMessageThat().contains("fragment");
+ }
+ }
+}
diff --git a/javatests/dagger/android/support/BUILD b/javatests/dagger/android/support/BUILD
new file mode 100644
index 0000000..6d8f43b
--- /dev/null
+++ b/javatests/dagger/android/support/BUILD
@@ -0,0 +1,38 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Tests for Dagger's Android and Support library integrations
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenRobolectricTests")
+
+GenRobolectricTests(
+ name = "android-support-tests",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/android",
+ "//java/dagger/android/support",
+ "@androidsdk//com.android.support:appcompat-v7-25.0.0",
+ "@androidsdk//com.android.support:support-fragment-25.0.0",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/android/support/functional/AllControllersAreDirectChildrenOfApplication.java b/javatests/dagger/android/support/functional/AllControllersAreDirectChildrenOfApplication.java
new file mode 100644
index 0000000..fa05158
--- /dev/null
+++ b/javatests/dagger/android/support/functional/AllControllersAreDirectChildrenOfApplication.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.android.AndroidInjectionModule;
+import dagger.android.AndroidInjector;
+import dagger.android.support.DaggerApplication;
+import dagger.android.support.functional.AllControllersAreDirectChildrenOfApplication.ApplicationComponent.BroadcastReceiverSubcomponent.BroadcastReceiverModule;
+import dagger.android.support.functional.AllControllersAreDirectChildrenOfApplication.ApplicationComponent.ContentProviderSubcomponent.ContentProviderModule;
+import dagger.android.support.functional.AllControllersAreDirectChildrenOfApplication.ApplicationComponent.InnerActivitySubcomponent.InnerActivityModule;
+import dagger.android.support.functional.AllControllersAreDirectChildrenOfApplication.ApplicationComponent.IntentServiceSubcomponent.IntentServiceModule;
+import dagger.android.support.functional.AllControllersAreDirectChildrenOfApplication.ApplicationComponent.ServiceSubcomponent.ServiceModule;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+
+public final class AllControllersAreDirectChildrenOfApplication extends DaggerApplication {
+
+ @Override
+ protected AndroidInjector<AllControllersAreDirectChildrenOfApplication> applicationInjector() {
+ return DaggerAllControllersAreDirectChildrenOfApplication_ApplicationComponent.create();
+ }
+
+ @Component(modules = {ApplicationComponent.ApplicationModule.class, AndroidInjectionModule.class})
+ interface ApplicationComponent
+ extends AndroidInjector<AllControllersAreDirectChildrenOfApplication> {
+ @Module(
+ subcomponents = {
+ ActivitySubcomponent.class,
+ InnerActivitySubcomponent.class,
+ ParentFragmentSubcomponent.class,
+ ChildFragmentSubcomponent.class,
+ DialogFragmentSubcomponent.class,
+ ServiceSubcomponent.class,
+ IntentServiceSubcomponent.class,
+ BroadcastReceiverSubcomponent.class,
+ ContentProviderSubcomponent.class
+ }
+ )
+ abstract class ApplicationModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ApplicationComponent.class;
+ }
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestActivity.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForTestActivity(
+ ActivitySubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(OuterClass.TestInnerClassActivity.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForInnerActivity(
+ InnerActivitySubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestParentFragment.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForParentFragment(
+ ParentFragmentSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestChildFragment.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForChildFragment(
+ ChildFragmentSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestDialogFragment.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForDialogFragment(
+ DialogFragmentSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestService.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForService(
+ ServiceSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestIntentService.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForIntentService(
+ IntentServiceSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestBroadcastReceiver.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForBroadcastReceiver(
+ BroadcastReceiverSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestContentProvider.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForContentProvider(
+ ContentProviderSubcomponent.Builder builder);
+ }
+
+ @Subcomponent(modules = ActivitySubcomponent.ActivityModule.class)
+ interface ActivitySubcomponent extends AndroidInjector<TestActivity> {
+ @Module
+ abstract class ActivityModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ActivitySubcomponent.class;
+ }
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestActivity> {}
+ }
+
+ @Subcomponent(modules = InnerActivityModule.class)
+ interface InnerActivitySubcomponent extends AndroidInjector<OuterClass.TestInnerClassActivity> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<OuterClass.TestInnerClassActivity> {}
+
+ @Module
+ abstract class InnerActivityModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return InnerActivitySubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = ParentFragmentSubcomponent.ParentFragmentModule.class)
+ interface ParentFragmentSubcomponent extends AndroidInjector<TestParentFragment> {
+ @Module
+ abstract class ParentFragmentModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ParentFragmentSubcomponent.class;
+ }
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestParentFragment> {}
+ }
+
+ @Subcomponent(modules = ChildFragmentSubcomponent.ChildFragmentModule.class)
+ interface ChildFragmentSubcomponent extends AndroidInjector<TestChildFragment> {
+ @Module
+ abstract class ChildFragmentModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ChildFragmentSubcomponent.class;
+ }
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestChildFragment> {}
+ }
+
+ @Subcomponent(modules = DialogFragmentSubcomponent.DialogFragmentModule.class)
+ interface DialogFragmentSubcomponent extends AndroidInjector<TestDialogFragment> {
+ @Module
+ abstract class DialogFragmentModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return DialogFragmentSubcomponent.class;
+ }
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestDialogFragment> {}
+ }
+
+ @Subcomponent(modules = ServiceModule.class)
+ interface ServiceSubcomponent extends AndroidInjector<TestService> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestService> {}
+
+ @Module
+ abstract class ServiceModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ServiceSubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = IntentServiceModule.class)
+ interface IntentServiceSubcomponent extends AndroidInjector<TestIntentService> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestIntentService> {}
+
+ @Module
+ abstract class IntentServiceModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return IntentServiceSubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = BroadcastReceiverModule.class)
+ interface BroadcastReceiverSubcomponent extends AndroidInjector<TestBroadcastReceiver> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestBroadcastReceiver> {}
+
+ @Module
+ abstract class BroadcastReceiverModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return BroadcastReceiverSubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = ContentProviderModule.class)
+ interface ContentProviderSubcomponent extends AndroidInjector<TestContentProvider> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestContentProvider> {}
+
+ @Module
+ abstract class ContentProviderModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ContentProviderSubcomponent.class;
+ }
+ }
+ }
+ }
+}
diff --git a/javatests/dagger/android/support/functional/AndroidManifest.xml b/javatests/dagger/android/support/functional/AndroidManifest.xml
new file mode 100644
index 0000000..2e40a35
--- /dev/null
+++ b/javatests/dagger/android/support/functional/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.android.support.functional">
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="26" />
+
+ <application android:theme="@style/Theme.AppCompat"
+ android:name=".UsesGeneratedModulesApplication">
+ <activity android:name="dagger.android.support.functional.ParentOfFragmentActivity"/>
+ <activity android:name="dagger.android.support.functional.SiblingOfFragmentActivity"/>
+ <activity android:name="dagger.android.support.functional.InjectedWithoutSubcomponentActivity"/>
+ </application>
+</manifest>
diff --git a/javatests/dagger/android/support/functional/BUILD b/javatests/dagger/android/support/functional/BUILD
new file mode 100644
index 0000000..130b971
--- /dev/null
+++ b/javatests/dagger/android/support/functional/BUILD
@@ -0,0 +1,56 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Functional test code for Dagger-Android
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "functional",
+ srcs = glob(
+ ["*.java"],
+ exclude = ["*Test.java"],
+ ),
+ exports_manifest = 1,
+ manifest = "AndroidManifest.xml",
+ resource_files = glob(["res/**"]),
+ deps = [
+ "@androidsdk//com.android.support:support-fragment-25.0.0",
+ "@androidsdk//com.android.support:appcompat-v7-25.0.0",
+ "@google_bazel_common//third_party/java/guava",
+ "//:dagger_with_compiler",
+ "//:android",
+ "//:android-support",
+ # TODO(ronshapiro): figure out why strict deps is failing without this
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ ],
+)
+
+load("//:test_defs.bzl", "GenRobolectricTests")
+
+GenRobolectricTests(
+ name = "functional_tests",
+ srcs = glob(["*Test.java"]),
+ deps = [
+ ":functional",
+ "//:android",
+ "//:android-support",
+ "//:dagger_with_compiler",
+ "@androidsdk//com.android.support:support-fragment-25.0.0",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/robolectric",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/android/support/functional/ComponentStructureFollowsControllerStructureApplication.java b/javatests/dagger/android/support/functional/ComponentStructureFollowsControllerStructureApplication.java
new file mode 100644
index 0000000..48e9a59
--- /dev/null
+++ b/javatests/dagger/android/support/functional/ComponentStructureFollowsControllerStructureApplication.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.android.AndroidInjectionModule;
+import dagger.android.AndroidInjector;
+import dagger.android.support.DaggerApplication;
+import dagger.android.support.functional.ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.BroadcastReceiverSubcomponent.BroadcastReceiverModule;
+import dagger.android.support.functional.ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.ContentProviderSubcomponent.ContentProviderModule;
+import dagger.android.support.functional.ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.InnerActivitySubcomponent.InnerActivityModule;
+import dagger.android.support.functional.ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.IntentServiceSubcomponent.IntentServiceModule;
+import dagger.android.support.functional.ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.ServiceSubcomponent.ServiceModule;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+
+public final class ComponentStructureFollowsControllerStructureApplication
+ extends DaggerApplication {
+
+ @Override
+ protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
+ return DaggerComponentStructureFollowsControllerStructureApplication_ApplicationComponent
+ .create();
+ }
+
+ @Component(modules = {ApplicationComponent.ApplicationModule.class, AndroidInjectionModule.class})
+ interface ApplicationComponent
+ extends AndroidInjector<ComponentStructureFollowsControllerStructureApplication> {
+ @Module(
+ subcomponents = {
+ ActivitySubcomponent.class,
+ InnerActivitySubcomponent.class,
+ ServiceSubcomponent.class,
+ IntentServiceSubcomponent.class,
+ BroadcastReceiverSubcomponent.class,
+ ContentProviderSubcomponent.class,
+ }
+ )
+ abstract class ApplicationModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ApplicationComponent.class;
+ }
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestActivity.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForTestActivity(
+ ActivitySubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(OuterClass.TestInnerClassActivity.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForInnerActivity(
+ InnerActivitySubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestService.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForService(
+ ServiceSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestIntentService.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForIntentService(
+ IntentServiceSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestBroadcastReceiver.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForBroadcastReceiver(
+ BroadcastReceiverSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestContentProvider.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForContentProvider(
+ ContentProviderSubcomponent.Builder builder);
+ }
+
+ @Subcomponent(modules = ActivitySubcomponent.ActivityModule.class)
+ interface ActivitySubcomponent extends AndroidInjector<TestActivity> {
+ @Module(subcomponents = {ParentFragmentSubcomponent.class, DialogFragmentSubcomponent.class})
+ abstract class ActivityModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ActivitySubcomponent.class;
+ }
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestParentFragment.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForParentFragment(
+ ParentFragmentSubcomponent.Builder builder);
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestDialogFragment.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForDialogFragment(
+ DialogFragmentSubcomponent.Builder builder);
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestActivity> {}
+
+ @Subcomponent(modules = ParentFragmentSubcomponent.ParentFragmentModule.class)
+ interface ParentFragmentSubcomponent extends AndroidInjector<TestParentFragment> {
+ @Module(subcomponents = ChildFragmentSubcomponent.class)
+ abstract class ParentFragmentModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ParentFragmentSubcomponent.class;
+ }
+
+ @Binds
+ @IntoMap
+ @ClassKey(TestChildFragment.class)
+ abstract AndroidInjector.Factory<?> bindFactoryForChildFragment(
+ ChildFragmentSubcomponent.Builder builder);
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestParentFragment> {}
+
+ @Subcomponent(modules = ChildFragmentSubcomponent.ChildFragmentModule.class)
+ interface ChildFragmentSubcomponent extends AndroidInjector<TestChildFragment> {
+ @Module
+ abstract class ChildFragmentModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ChildFragmentSubcomponent.class;
+ }
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestChildFragment> {}
+ }
+ }
+
+ @Subcomponent(modules = DialogFragmentSubcomponent.DialogFragmentModule.class)
+ interface DialogFragmentSubcomponent extends AndroidInjector<TestDialogFragment> {
+ @Module
+ abstract class DialogFragmentModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return DialogFragmentSubcomponent.class;
+ }
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestDialogFragment> {}
+ }
+ }
+
+ @Subcomponent(modules = InnerActivityModule.class)
+ interface InnerActivitySubcomponent extends AndroidInjector<OuterClass.TestInnerClassActivity> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<OuterClass.TestInnerClassActivity> {}
+
+ @Module
+ abstract class InnerActivityModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return InnerActivitySubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = ServiceModule.class)
+ interface ServiceSubcomponent extends AndroidInjector<TestService> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestService> {}
+
+ @Module
+ abstract class ServiceModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ServiceSubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = IntentServiceModule.class)
+ interface IntentServiceSubcomponent extends AndroidInjector<TestIntentService> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestIntentService> {}
+
+ @Module
+ abstract class IntentServiceModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return IntentServiceSubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = BroadcastReceiverModule.class)
+ interface BroadcastReceiverSubcomponent extends AndroidInjector<TestBroadcastReceiver> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestBroadcastReceiver> {}
+
+ @Module
+ abstract class BroadcastReceiverModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return BroadcastReceiverSubcomponent.class;
+ }
+ }
+ }
+
+ @Subcomponent(modules = ContentProviderModule.class)
+ interface ContentProviderSubcomponent extends AndroidInjector<TestContentProvider> {
+ @Subcomponent.Builder
+ abstract class Builder extends AndroidInjector.Builder<TestContentProvider> {}
+
+ @Module
+ abstract class ContentProviderModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ContentProviderSubcomponent.class;
+ }
+ }
+ }
+ }
+}
diff --git a/javatests/dagger/android/support/functional/InjectorsTest.java b/javatests/dagger/android/support/functional/InjectorsTest.java
new file mode 100644
index 0000000..c5cb150
--- /dev/null
+++ b/javatests/dagger/android/support/functional/InjectorsTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+public class InjectorsTest {
+ private ActivityController<TestActivity> activityController;
+ private TestActivity activity;
+ private TestParentFragment parentFragment;
+ private TestChildFragment childFragment;
+ private TestDialogFragment dialogFragment;
+ private TestService service;
+ private TestIntentService intentService;
+ private TestBroadcastReceiver broadcastReceiver;
+ private TestContentProvider contentProvider;
+
+ @Before
+ public void setUp() {
+ activityController = Robolectric.buildActivity(TestActivity.class);
+ activity = activityController.setup().get();
+ parentFragment =
+ (TestParentFragment)
+ activity.getSupportFragmentManager().findFragmentByTag("parent-fragment");
+ childFragment =
+ (TestChildFragment)
+ parentFragment.getChildFragmentManager().findFragmentByTag("child-fragment");
+ dialogFragment =
+ (TestDialogFragment)
+ activity.getSupportFragmentManager().findFragmentByTag("dialog-fragment");
+
+ service = Robolectric.buildService(TestService.class).create().get();
+ intentService = Robolectric.buildIntentService(TestIntentService.class).create().get();
+
+ broadcastReceiver = new TestBroadcastReceiver();
+ broadcastReceiver.onReceive(RuntimeEnvironment.application, new Intent());
+
+ contentProvider = Robolectric.setupContentProvider(TestContentProvider.class);
+ }
+
+ @Test
+ @Config(application = ComponentStructureFollowsControllerStructureApplication.class)
+ public void componentStructureFollowsControllerStructure() {
+ assertThat(activity.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.class);
+ assertThat(parentFragment.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.ParentFragmentSubcomponent.class);
+ assertThat(childFragment.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.ParentFragmentSubcomponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.ParentFragmentSubcomponent.ChildFragmentSubcomponent.class);
+ assertThat(dialogFragment.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ActivitySubcomponent.DialogFragmentSubcomponent.class);
+
+ assertThat(service.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ServiceSubcomponent.class);
+ assertThat(intentService.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .IntentServiceSubcomponent.class);
+
+ assertThat(broadcastReceiver.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .BroadcastReceiverSubcomponent.class);
+
+ assertThat(contentProvider.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .ContentProviderSubcomponent.class);
+
+ changeConfiguration();
+
+ OuterClass.TestInnerClassActivity innerClassActivity =
+ Robolectric.setupActivity(OuterClass.TestInnerClassActivity.class);
+ assertThat(innerClassActivity.componentHierarchy)
+ .containsExactly(
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent.class,
+ ComponentStructureFollowsControllerStructureApplication.ApplicationComponent
+ .InnerActivitySubcomponent.class);
+ }
+
+ @Test
+ @Config(application = AllControllersAreDirectChildrenOfApplication.class)
+ public void allControllersAreDirectChildrenOfApplication() {
+ assertThat(activity.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.ActivitySubcomponent
+ .class);
+ assertThat(parentFragment.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent
+ .ParentFragmentSubcomponent.class);
+ assertThat(childFragment.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent
+ .ChildFragmentSubcomponent.class);
+ assertThat(dialogFragment.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent
+ .DialogFragmentSubcomponent.class);
+
+ assertThat(service.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.ServiceSubcomponent
+ .class);
+ assertThat(intentService.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent
+ .IntentServiceSubcomponent.class);
+
+ assertThat(broadcastReceiver.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent
+ .BroadcastReceiverSubcomponent.class);
+
+ assertThat(contentProvider.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent
+ .ContentProviderSubcomponent.class);
+
+ changeConfiguration();
+
+ OuterClass.TestInnerClassActivity innerClassActivity =
+ Robolectric.setupActivity(OuterClass.TestInnerClassActivity.class);
+ assertThat(innerClassActivity.componentHierarchy)
+ .containsExactly(
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent.class,
+ AllControllersAreDirectChildrenOfApplication.ApplicationComponent
+ .InnerActivitySubcomponent.class);
+ }
+
+ @Test
+ @Config(application = UsesGeneratedModulesApplication.class)
+ public void usesGeneratedModules() {
+ assertThat(activity.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyActivitySubcomponent.class);
+ assertThat(parentFragment.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyParentFragmentSubcomponent.class);
+ assertThat(childFragment.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyChildFragmentSubcomponent.class);
+ assertThat(dialogFragment.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyDialogFragmentSubcomponent.class);
+
+ assertThat(service.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyServiceSubcomponent.class);
+ assertThat(intentService.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyIntentServiceSubcomponent.class);
+
+ assertThat(broadcastReceiver.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyBroadcastReceiverSubcomponent.class);
+
+ assertThat(contentProvider.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyContentProviderSubcomponent.class);
+
+ changeConfiguration();
+
+ TestActivityWithScope activityWithScope =
+ Robolectric.setupActivity(TestActivityWithScope.class);
+ assertThat(activityWithScope.scopedStringProvider.get())
+ .isSameInstanceAs(activityWithScope.scopedStringProvider.get());
+
+ OuterClass.TestInnerClassActivity innerClassActivity =
+ Robolectric.setupActivity(OuterClass.TestInnerClassActivity.class);
+ assertThat(innerClassActivity.componentHierarchy)
+ .containsExactly(
+ UsesGeneratedModulesApplication.ApplicationComponent.class,
+ UsesGeneratedModulesApplication.DummyInnerActivitySubcomponent.class);
+ }
+
+ // https://github.com/google/dagger/issues/598
+ private void changeConfiguration() {
+ Configuration oldConfiguration = activity.getResources().getConfiguration();
+ Configuration newConfiguration = new Configuration(oldConfiguration);
+ newConfiguration.orientation =
+ oldConfiguration.orientation == Configuration.ORIENTATION_LANDSCAPE
+ ? Configuration.ORIENTATION_PORTRAIT
+ : Configuration.ORIENTATION_LANDSCAPE;
+ activityController.configurationChange(newConfiguration);
+ }
+}
diff --git a/javatests/dagger/android/support/functional/OuterClass.java b/javatests/dagger/android/support/functional/OuterClass.java
new file mode 100644
index 0000000..e5d6ed5
--- /dev/null
+++ b/javatests/dagger/android/support/functional/OuterClass.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import dagger.android.support.DaggerAppCompatActivity;
+import java.util.Set;
+import javax.inject.Inject;
+
+final class OuterClass {
+ public static class TestInnerClassActivity extends DaggerAppCompatActivity {
+ @Inject
+ Set<Class<?>> componentHierarchy;
+ }
+}
diff --git a/javatests/dagger/android/support/functional/TestActivity.java b/javatests/dagger/android/support/functional/TestActivity.java
new file mode 100644
index 0000000..84c44fd
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import android.os.Bundle;
+import dagger.android.support.DaggerAppCompatActivity;
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class TestActivity extends DaggerAppCompatActivity {
+ @Inject Set<Class<?>> componentHierarchy;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_layout);
+
+ getSupportFragmentManager()
+ .beginTransaction()
+ .add(new TestParentFragment(), "parent-fragment")
+ .add(new TestDialogFragment(), "dialog-fragment")
+ .commit();
+ }
+}
diff --git a/javatests/dagger/android/support/functional/TestActivityWithScope.java b/javatests/dagger/android/support/functional/TestActivityWithScope.java
new file mode 100644
index 0000000..d7cb891
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestActivityWithScope.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import dagger.android.support.DaggerAppCompatActivity;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+public final class TestActivityWithScope extends DaggerAppCompatActivity {
+ @Inject Provider<String> scopedStringProvider;
+}
diff --git a/javatests/dagger/android/support/functional/TestBroadcastReceiver.java b/javatests/dagger/android/support/functional/TestBroadcastReceiver.java
new file mode 100644
index 0000000..edf93fe
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestBroadcastReceiver.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import dagger.android.DaggerBroadcastReceiver;
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class TestBroadcastReceiver extends DaggerBroadcastReceiver {
+ @Inject Set<Class<?>> componentHierarchy;
+}
diff --git a/javatests/dagger/android/support/functional/TestChildFragment.java b/javatests/dagger/android/support/functional/TestChildFragment.java
new file mode 100644
index 0000000..781c578
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestChildFragment.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import dagger.android.support.DaggerFragment;
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class TestChildFragment extends DaggerFragment {
+ @Inject Set<Class<?>> componentHierarchy;
+}
diff --git a/javatests/dagger/android/support/functional/TestContentProvider.java b/javatests/dagger/android/support/functional/TestContentProvider.java
new file mode 100644
index 0000000..1668ce6
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestContentProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import dagger.android.DaggerContentProvider;
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class TestContentProvider extends DaggerContentProvider {
+ @Inject
+ Set<Class<?>> componentHierarchy;
+
+ @Nullable
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/javatests/dagger/android/support/functional/TestDialogFragment.java b/javatests/dagger/android/support/functional/TestDialogFragment.java
new file mode 100644
index 0000000..d499ee5
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestDialogFragment.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import dagger.android.support.DaggerAppCompatDialogFragment;
+import java.util.Set;
+import javax.inject.Inject;
+
+public class TestDialogFragment extends DaggerAppCompatDialogFragment {
+ @Inject Set<Class<?>> componentHierarchy;
+}
diff --git a/javatests/dagger/android/support/functional/TestIntentService.java b/javatests/dagger/android/support/functional/TestIntentService.java
new file mode 100644
index 0000000..7f36e93
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestIntentService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import android.content.Intent;
+import dagger.android.DaggerIntentService;
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class TestIntentService extends DaggerIntentService {
+ @Inject Set<Class<?>> componentHierarchy;
+
+ public TestIntentService() {
+ super("TestIntentService");
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {}
+}
diff --git a/javatests/dagger/android/support/functional/TestParentFragment.java b/javatests/dagger/android/support/functional/TestParentFragment.java
new file mode 100644
index 0000000..6d2d6f4
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestParentFragment.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import android.content.Context;
+import dagger.android.support.DaggerFragment;
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class TestParentFragment extends DaggerFragment {
+ @Inject Set<Class<?>> componentHierarchy;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ getChildFragmentManager()
+ .beginTransaction()
+ .add(new TestChildFragment(), "child-fragment")
+ .commit();
+ }
+}
diff --git a/javatests/dagger/android/support/functional/TestService.java b/javatests/dagger/android/support/functional/TestService.java
new file mode 100644
index 0000000..d3c6dc1
--- /dev/null
+++ b/javatests/dagger/android/support/functional/TestService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+import dagger.android.DaggerService;
+import java.io.FileDescriptor;
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class TestService extends DaggerService {
+ @Inject Set<Class<?>> componentHierarchy;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MockBinder();
+ }
+
+ private static class MockBinder implements IBinder {
+ @Override
+ public String getInterfaceDescriptor() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean pingBinder() {
+ return false;
+ }
+
+ @Override
+ public boolean isBinderAlive() {
+ return false;
+ }
+
+ @Override
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {}
+
+ @Override
+ public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {}
+
+ @Override
+ public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {}
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ return false;
+ }
+ }
+}
diff --git a/javatests/dagger/android/support/functional/UsesGeneratedModulesApplication.java b/javatests/dagger/android/support/functional/UsesGeneratedModulesApplication.java
new file mode 100644
index 0000000..002da89
--- /dev/null
+++ b/javatests/dagger/android/support/functional/UsesGeneratedModulesApplication.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.support.functional;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.android.AndroidInjectionModule;
+import dagger.android.AndroidInjector;
+import dagger.android.ContributesAndroidInjector;
+import dagger.android.support.DaggerApplication;
+import dagger.multibindings.IntoSet;
+import java.lang.annotation.Retention;
+import java.util.UUID;
+import javax.inject.Scope;
+
+public final class UsesGeneratedModulesApplication extends DaggerApplication {
+
+ @Override
+ protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
+ return DaggerUsesGeneratedModulesApplication_ApplicationComponent.create();
+ }
+
+ @Component(modules = {ApplicationModule.class, AndroidInjectionModule.class})
+ interface ApplicationComponent extends AndroidInjector<UsesGeneratedModulesApplication> {}
+
+ @Module
+ abstract static class ApplicationModule {
+ @Provides
+ @IntoSet
+ static Class<?> addToComponentHierarchy() {
+ return ApplicationComponent.class;
+ }
+
+ @ActivityScope
+ @ContributesAndroidInjector(modules = ActivityScopedModule.class)
+ abstract TestActivityWithScope contributeTestActivityWithScopeInjector();
+
+ @ContributesAndroidInjector(modules = DummyActivitySubcomponent.AddToHierarchy.class)
+ abstract TestActivity contributeTestActivityInjector();
+
+ @ContributesAndroidInjector(modules = DummyInnerActivitySubcomponent.AddToHierarchy.class)
+ abstract OuterClass.TestInnerClassActivity contributeInnerActivityInjector();
+
+ @ContributesAndroidInjector(modules = DummyParentFragmentSubcomponent.AddToHierarchy.class)
+ abstract TestParentFragment contributeTestParentFragmentInjector();
+
+ @ContributesAndroidInjector(modules = DummyChildFragmentSubcomponent.AddToHierarchy.class)
+ abstract TestChildFragment contributeTestChildFragmentInjector();
+
+ @ContributesAndroidInjector(modules = DummyDialogFragmentSubcomponent.AddToHierarchy.class)
+ abstract TestDialogFragment contributeTestDialogFragmentInjector();
+
+ @ContributesAndroidInjector(modules = DummyServiceSubcomponent.AddToHierarchy.class)
+ abstract TestService contributeTestServiceInjector();
+
+ @ContributesAndroidInjector(modules = DummyIntentServiceSubcomponent.AddToHierarchy.class)
+ abstract TestIntentService contributeTestIntentServiceInjector();
+
+ @ContributesAndroidInjector(modules = DummyBroadcastReceiverSubcomponent.AddToHierarchy.class)
+ abstract TestBroadcastReceiver contributeTestBroadcastReceiverInjector();
+
+ @ContributesAndroidInjector(modules = DummyContentProviderSubcomponent.AddToHierarchy.class)
+ abstract TestContentProvider contributeTestContentProviderInjector();
+ }
+
+ @Retention(RUNTIME)
+ @Scope
+ @interface ActivityScope {}
+
+ @Module
+ static class ActivityScopedModule {
+ @Provides
+ @ActivityScope
+ static String provideScopedString() {
+ return UUID.randomUUID().toString();
+ }
+ }
+
+ interface DummyActivitySubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyActivitySubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyInnerActivitySubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyInnerActivitySubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyParentFragmentSubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyParentFragmentSubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyChildFragmentSubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyChildFragmentSubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyDialogFragmentSubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyDialogFragmentSubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyServiceSubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyServiceSubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyIntentServiceSubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyIntentServiceSubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyBroadcastReceiverSubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyBroadcastReceiverSubcomponent.class;
+ }
+ }
+ }
+
+ interface DummyContentProviderSubcomponent {
+ @Module
+ abstract class AddToHierarchy {
+ @Provides
+ @IntoSet
+ static Class<?> addDummyValueToComponentHierarchy() {
+ return DummyContentProviderSubcomponent.class;
+ }
+ }
+ }
+}
diff --git a/javatests/dagger/android/support/functional/res/layout/activity_layout.xml b/javatests/dagger/android/support/functional/res/layout/activity_layout.xml
new file mode 100644
index 0000000..d886d97
--- /dev/null
+++ b/javatests/dagger/android/support/functional/res/layout/activity_layout.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2016 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/javatests/dagger/functional/A.java b/javatests/dagger/functional/A.java
new file mode 100644
index 0000000..c295dc1
--- /dev/null
+++ b/javatests/dagger/functional/A.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+class A {
+ @Inject A() {}
+}
diff --git a/javatests/dagger/functional/AbstractMembersInjectingBaseClass.java b/javatests/dagger/functional/AbstractMembersInjectingBaseClass.java
new file mode 100644
index 0000000..fcab318
--- /dev/null
+++ b/javatests/dagger/functional/AbstractMembersInjectingBaseClass.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+abstract class AbstractMembersInjectingBaseClass {
+ @Inject Thing thing;
+}
+
diff --git a/javatests/dagger/functional/AbstractMiddleClassWithoutMembers.java b/javatests/dagger/functional/AbstractMiddleClassWithoutMembers.java
new file mode 100644
index 0000000..4ff55eb
--- /dev/null
+++ b/javatests/dagger/functional/AbstractMiddleClassWithoutMembers.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+abstract class AbstractMiddleClassWithoutMembers extends AbstractMembersInjectingBaseClass {
+}
+
diff --git a/javatests/dagger/functional/B.java b/javatests/dagger/functional/B.java
new file mode 100644
index 0000000..55d05af
--- /dev/null
+++ b/javatests/dagger/functional/B.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+class B {
+ @Inject B() {}
+}
diff --git a/javatests/dagger/functional/BUILD b/javatests/dagger/functional/BUILD
new file mode 100644
index 0000000..f417bec
--- /dev/null
+++ b/javatests/dagger/functional/BUILD
@@ -0,0 +1,48 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Functional tests for Dagger
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "SOURCE_7_TARGET_7",
+)
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "functional_tests",
+ srcs = glob(
+ ["**/*.java"],
+ ),
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ lib_javacopts = SOURCE_7_TARGET_7,
+ # NOTE: This should not depend on Guava or jsr305 to ensure that Dagger can be
+ # used without Guava and jsr305 deps.
+ test_only_deps = [
+ "@google_bazel_common//third_party/java/guava:testlib",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/truth",
+ "@google_bazel_common//third_party/java/junit",
+ ],
+ deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/auto:factory",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
diff --git a/javatests/dagger/functional/BasicAbstractClassComponent.java b/javatests/dagger/functional/BasicAbstractClassComponent.java
new file mode 100644
index 0000000..2bd849d
--- /dev/null
+++ b/javatests/dagger/functional/BasicAbstractClassComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+
+/**
+ * This component tests behavior equivalent to {@link BasicComponent}, but as an abstract class
+ * rather than an interface.
+ */
+@Component(modules = {PrimitivesModule.class, NullableModule.class})
+abstract class BasicAbstractClassComponent implements BasicComponent {
+ void throwAParty() {
+ throw new RuntimeException("Paaarrrrrtaaaaaaaay!");
+ }
+}
diff --git a/javatests/dagger/functional/BasicComponent.java b/javatests/dagger/functional/BasicComponent.java
new file mode 100644
index 0000000..295c1e1
--- /dev/null
+++ b/javatests/dagger/functional/BasicComponent.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Lazy;
+import dagger.MembersInjector;
+import dagger.functional.NullableModule.Nullable;
+import javax.inject.Provider;
+
+@Component(modules = {PrimitivesModule.class, NullableModule.class})
+interface BasicComponent
+ extends Injector<Thing>,
+ // Implements two types that define the same method, not overridden here, to test that the
+ // method is implemented only once.
+ ComponentSupertypeOne,
+ ComponentSupertypeTwo {
+ byte getByte();
+ char getChar();
+ short getShort();
+ int getInt();
+ long getLong();
+ boolean getBoolean();
+ float getFloat();
+ double getDouble();
+
+ Byte getBoxedByte();
+ Character getBoxedChar();
+ Short getBoxedShort();
+ Integer getBoxedInt();
+ Long getBoxedLong();
+ Boolean getBoxedBoolean();
+ Float getBoxedFloat();
+ Double getBoxedDouble();
+
+ Provider<Byte> getByteProvider();
+ Provider<Character> getCharProvider();
+ Provider<Short> getShortProvider();
+ Provider<Integer> getIntProvider();
+ Provider<Long> getLongProvider();
+ Provider<Boolean> getBooleanProvider();
+ Provider<Float> getFloatProvider();
+ Provider<Double> getDoubleProvider();
+
+ byte[] getByteArray();
+ char[] getCharArray();
+ short[] getShortArray();
+ int[] getIntArray();
+ long[] getLongArray();
+ boolean[] getBooleanArray();
+ float[] getFloatArray();
+ double[] getDoubleArray();
+
+ Provider<byte[]> getByteArrayProvider();
+ Provider<char[]> getCharArrayProvider();
+ Provider<short[]> getShortArrayProvider();
+ Provider<int[]> getIntArrayProvider();
+ Provider<long[]> getLongArrayProvider();
+ Provider<boolean[]> getBooleanArrayProvider();
+ Provider<float[]> getFloatArrayProvider();
+ Provider<double[]> getDoubleArrayProvider();
+
+ Object noOpMembersInjection(Object obviouslyDoesNotHaveMembersToInject);
+
+ Thing thing();
+ InjectedThing injectedThing();
+ Provider<InjectedThing> injectedThingProvider();
+ Lazy<InjectedThing> lazyInjectedThing();
+ Provider<Lazy<InjectedThing>> lazyInjectedThingProvider();
+ MembersInjector<InjectedThing> injectedThingMembersInjector();
+
+ @Nullable Object nullObject();
+ Provider<Object> nullObjectProvider();
+ Lazy<Object> lazyNullObject();
+
+ TypeWithInheritedMembersInjection typeWithInheritedMembersInjection();
+ MembersInjector<TypeWithInheritedMembersInjection>
+ typeWithInheritedMembersInjectionMembersInjector();
+}
diff --git a/javatests/dagger/functional/BasicTest.java b/javatests/dagger/functional/BasicTest.java
new file mode 100644
index 0000000..80fc5e6
--- /dev/null
+++ b/javatests/dagger/functional/BasicTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.functional.PrimitivesModule.BOUND_BOOLEAN;
+import static dagger.functional.PrimitivesModule.BOUND_BOOLEAN_ARRAY;
+import static dagger.functional.PrimitivesModule.BOUND_BYTE;
+import static dagger.functional.PrimitivesModule.BOUND_BYTE_ARRAY;
+import static dagger.functional.PrimitivesModule.BOUND_CHAR;
+import static dagger.functional.PrimitivesModule.BOUND_CHAR_ARRAY;
+import static dagger.functional.PrimitivesModule.BOUND_DOUBLE;
+import static dagger.functional.PrimitivesModule.BOUND_DOUBLE_ARRAY;
+import static dagger.functional.PrimitivesModule.BOUND_FLOAT;
+import static dagger.functional.PrimitivesModule.BOUND_FLOAT_ARRAY;
+import static dagger.functional.PrimitivesModule.BOUND_INT;
+import static dagger.functional.PrimitivesModule.BOUND_INT_ARRAY;
+import static dagger.functional.PrimitivesModule.BOUND_LONG;
+import static dagger.functional.PrimitivesModule.BOUND_LONG_ARRAY;
+import static dagger.functional.PrimitivesModule.BOUND_SHORT;
+import static dagger.functional.PrimitivesModule.BOUND_SHORT_ARRAY;
+
+import dagger.Lazy;
+import javax.inject.Provider;
+import org.junit.experimental.theories.DataPoint;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class BasicTest {
+ @DataPoint
+ public static final BasicComponent basicComponent = DaggerBasicComponent.create();
+ @DataPoint
+ public static final BasicComponent abstractClassBasicComponent =
+ DaggerBasicAbstractClassComponent.create();
+
+ @Theory public void primitives(BasicComponent basicComponent) {
+ assertThat(basicComponent.getByte()).isEqualTo(BOUND_BYTE);
+ assertThat(basicComponent.getChar()).isEqualTo(BOUND_CHAR);
+ assertThat(basicComponent.getShort()).isEqualTo(BOUND_SHORT);
+ assertThat(basicComponent.getInt()).isEqualTo(BOUND_INT);
+ assertThat(basicComponent.getLong()).isEqualTo(BOUND_LONG);
+ assertThat(basicComponent.getBoolean()).isEqualTo(BOUND_BOOLEAN);
+ assertThat(basicComponent.getFloat()).isEqualTo(BOUND_FLOAT);
+ assertThat(basicComponent.getDouble()).isEqualTo(BOUND_DOUBLE);
+ }
+
+ @Theory public void boxedPrimitives(BasicComponent basicComponent) {
+ assertThat(basicComponent.getBoxedByte()).isEqualTo(new Byte(BOUND_BYTE));
+ assertThat(basicComponent.getBoxedChar()).isEqualTo(new Character(BOUND_CHAR));
+ assertThat(basicComponent.getBoxedShort()).isEqualTo(new Short(BOUND_SHORT));
+ assertThat(basicComponent.getBoxedInt()).isEqualTo(new Integer(BOUND_INT));
+ assertThat(basicComponent.getBoxedLong()).isEqualTo(new Long(BOUND_LONG));
+ assertThat(basicComponent.getBoxedBoolean()).isEqualTo(new Boolean(BOUND_BOOLEAN));
+ assertThat(basicComponent.getBoxedFloat()).isEqualTo(BOUND_FLOAT);
+ assertThat(basicComponent.getBoxedDouble()).isEqualTo(BOUND_DOUBLE);
+ }
+
+ @Theory public void boxedPrimitiveProviders(BasicComponent basicComponent) {
+ assertThat(basicComponent.getByteProvider().get()).isEqualTo(new Byte(BOUND_BYTE));
+ assertThat(basicComponent.getCharProvider().get()).isEqualTo(new Character(BOUND_CHAR));
+ assertThat(basicComponent.getShortProvider().get()).isEqualTo(new Short(BOUND_SHORT));
+ assertThat(basicComponent.getIntProvider().get()).isEqualTo(new Integer(BOUND_INT));
+ assertThat(basicComponent.getLongProvider().get()).isEqualTo(new Long(BOUND_LONG));
+ assertThat(basicComponent.getBooleanProvider().get()).isEqualTo(new Boolean(BOUND_BOOLEAN));
+ assertThat(basicComponent.getFloatProvider().get()).isEqualTo(BOUND_FLOAT);
+ assertThat(basicComponent.getDoubleProvider().get()).isEqualTo(BOUND_DOUBLE);
+ }
+
+ @Theory public void primitiveArrays(BasicComponent basicComponent) {
+ assertThat(basicComponent.getByteArray()).isSameInstanceAs(BOUND_BYTE_ARRAY);
+ assertThat(basicComponent.getCharArray()).isSameInstanceAs(BOUND_CHAR_ARRAY);
+ assertThat(basicComponent.getShortArray()).isSameInstanceAs(BOUND_SHORT_ARRAY);
+ assertThat(basicComponent.getIntArray()).isSameInstanceAs(BOUND_INT_ARRAY);
+ assertThat(basicComponent.getLongArray()).isSameInstanceAs(BOUND_LONG_ARRAY);
+ assertThat(basicComponent.getBooleanArray()).isSameInstanceAs(BOUND_BOOLEAN_ARRAY);
+ assertThat(basicComponent.getFloatArray()).isSameInstanceAs(BOUND_FLOAT_ARRAY);
+ assertThat(basicComponent.getDoubleArray()).isSameInstanceAs(BOUND_DOUBLE_ARRAY);
+ }
+
+ @Theory public void primitiveArrayProviders(BasicComponent basicComponent) {
+ assertThat(basicComponent.getByteArrayProvider().get()).isSameInstanceAs(BOUND_BYTE_ARRAY);
+ assertThat(basicComponent.getCharArrayProvider().get()).isSameInstanceAs(BOUND_CHAR_ARRAY);
+ assertThat(basicComponent.getShortArrayProvider().get()).isSameInstanceAs(BOUND_SHORT_ARRAY);
+ assertThat(basicComponent.getIntArrayProvider().get()).isSameInstanceAs(BOUND_INT_ARRAY);
+ assertThat(basicComponent.getLongArrayProvider().get()).isSameInstanceAs(BOUND_LONG_ARRAY);
+ assertThat(basicComponent.getBooleanArrayProvider().get())
+ .isSameInstanceAs(BOUND_BOOLEAN_ARRAY);
+ assertThat(basicComponent.getFloatArrayProvider().get()).isSameInstanceAs(BOUND_FLOAT_ARRAY);
+ assertThat(basicComponent.getDoubleArrayProvider().get()).isSameInstanceAs(BOUND_DOUBLE_ARRAY);
+ }
+
+ @Theory public void noOpMembersInjection(BasicComponent basicComponent) {
+ Object object = new Object();
+ assertThat(basicComponent.noOpMembersInjection(object)).isSameInstanceAs(object);
+ }
+
+ @Theory public void basicObject_noDeps(BasicComponent basicComponent) {
+ assertThat(basicComponent.thing()).isNotNull();
+ }
+
+ @Theory public void inheritedMembersInjection(BasicComponent basicComponent) {
+ assertThat(basicComponent.typeWithInheritedMembersInjection().thing).isNotNull();
+ }
+
+ @Theory
+ public void nullableInjection(BasicComponent basicComponent) {
+ assertThat(basicComponent.nullObject()).isNull();
+ assertThat(basicComponent.nullObjectProvider().get()).isNull();
+ assertThat(basicComponent.lazyNullObject().get()).isNull();
+ }
+
+ @Theory
+ public void providerOfLazy(BasicComponent basicComponent) {
+ Provider<Lazy<InjectedThing>> lazyInjectedThingProvider =
+ basicComponent.lazyInjectedThingProvider();
+ Lazy<InjectedThing> lazyInjectedThing1 = lazyInjectedThingProvider.get();
+ Lazy<InjectedThing> lazyInjectedThing2 = lazyInjectedThingProvider.get();
+ assertThat(lazyInjectedThing2).isNotSameInstanceAs(lazyInjectedThing1);
+ assertThat(lazyInjectedThing1.get()).isSameInstanceAs(lazyInjectedThing1.get());
+ assertThat(lazyInjectedThing2.get()).isSameInstanceAs(lazyInjectedThing2.get());
+ assertThat(lazyInjectedThing2.get()).isNotSameInstanceAs(lazyInjectedThing1.get());
+ }
+}
diff --git a/javatests/dagger/functional/BooleanKey.java b/javatests/dagger/functional/BooleanKey.java
new file mode 100644
index 0000000..28e1c08
--- /dev/null
+++ b/javatests/dagger/functional/BooleanKey.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+
+@MapKey(unwrapValue = true)
+@interface BooleanKey {
+ boolean value();
+}
diff --git a/javatests/dagger/functional/BoundedGenericComponent.java b/javatests/dagger/functional/BoundedGenericComponent.java
new file mode 100644
index 0000000..270316e
--- /dev/null
+++ b/javatests/dagger/functional/BoundedGenericComponent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+@Component(modules = BoundedGenericModule.class)
+interface BoundedGenericComponent {
+ BoundedGenerics<Integer, ArrayList<String>, LinkedList<CharSequence>, Integer, List<Integer>>
+ bounds1();
+ BoundedGenerics<Double, LinkedList<String>, LinkedList<Comparable<String>>, Double, Set<Double>>
+ bounds2();
+}
diff --git a/javatests/dagger/functional/BoundedGenericModule.java b/javatests/dagger/functional/BoundedGenericModule.java
new file mode 100644
index 0000000..b630cd6
--- /dev/null
+++ b/javatests/dagger/functional/BoundedGenericModule.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+@Module
+class BoundedGenericModule {
+
+ @Provides
+ Integer provideInteger() {
+ return 1;
+ }
+
+ @Provides
+ Double provideDouble() {
+ return 2d;
+ }
+
+ @Provides
+ ArrayList<String> provideArrayListString() {
+ ArrayList<String> list = new ArrayList<>();
+ list.add("arrayListOfString");
+ return list;
+ }
+
+ @Provides
+ LinkedList<String> provideLinkedListString() {
+ LinkedList<String> list = new LinkedList<>();
+ list.add("linkedListOfString");
+ return list;
+ }
+
+ @Provides
+ LinkedList<CharSequence> provideLinkedListCharSeq() {
+ LinkedList<CharSequence> list = new LinkedList<>();
+ list.add("linkedListOfCharSeq");
+ return list;
+ }
+
+ @Provides
+ @SuppressWarnings("unchecked")
+ LinkedList<Comparable<String>> provideArrayListOfComparableString() {
+ LinkedList<Comparable<String>> list = new LinkedList<>();
+ list.add("arrayListOfComparableOfString");
+ return list;
+ }
+
+ @Provides
+ List<Integer> provideListOfInteger() {
+ LinkedList<Integer> list = new LinkedList<>();
+ list.add(3);
+ return list;
+ }
+
+ @Provides
+ Set<Double> provideSetOfDouble() {
+ Set<Double> set = new HashSet<>();
+ set.add(4d);
+ return set;
+ }
+}
diff --git a/javatests/dagger/functional/BoundedGenerics.java b/javatests/dagger/functional/BoundedGenerics.java
new file mode 100644
index 0000000..812cd04
--- /dev/null
+++ b/javatests/dagger/functional/BoundedGenerics.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import java.util.List;
+import javax.inject.Inject;
+
+class BoundedGenerics<A extends Number & Comparable<? super A>,
+ B extends List<? extends CharSequence>,
+ C extends List<? super String>,
+ D extends A,
+ E extends Iterable<D>> {
+
+ final A a;
+ final B b;
+ final C c;
+ final D d;
+ final E e;
+
+ @Inject BoundedGenerics(A a, B b, C c, D d, E e) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ this.e = e;
+ }
+
+}
diff --git a/javatests/dagger/functional/BoxedPrimitives.java b/javatests/dagger/functional/BoxedPrimitives.java
new file mode 100644
index 0000000..adb61f2
--- /dev/null
+++ b/javatests/dagger/functional/BoxedPrimitives.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Provider;
+
+interface BoxedPrimitives {
+ interface Dependency {
+ int primitive();
+ }
+
+ @Component(dependencies = Dependency.class)
+ interface ConsumesPrimitiveThroughDependency {
+ Provider<Integer> providerOfBoxedPrimitive();
+ }
+
+ @Module
+ class PrimitiveModule {
+ @Provides
+ static int providePrimitive() {
+ return 1;
+ }
+ }
+
+ @Component(modules = PrimitiveModule.class)
+ interface ConsumesPrimitiveFromProvidesMethod {
+ Provider<Integer> providerOfBoxedPrimitive();
+ }
+
+}
diff --git a/javatests/dagger/functional/ByteKey.java b/javatests/dagger/functional/ByteKey.java
new file mode 100644
index 0000000..f0ee7ec
--- /dev/null
+++ b/javatests/dagger/functional/ByteKey.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+
+@MapKey(unwrapValue = true)
+@interface ByteKey {
+ byte value();
+}
diff --git a/javatests/dagger/functional/CharKey.java b/javatests/dagger/functional/CharKey.java
new file mode 100644
index 0000000..3e12785
--- /dev/null
+++ b/javatests/dagger/functional/CharKey.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+
+@MapKey(unwrapValue = true)
+@interface CharKey {
+ char value();
+}
diff --git a/javatests/dagger/functional/ChildDoubleModule.java b/javatests/dagger/functional/ChildDoubleModule.java
new file mode 100644
index 0000000..cff8f0b
--- /dev/null
+++ b/javatests/dagger/functional/ChildDoubleModule.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+import java.util.ArrayList;
+import java.util.List;
+
+@Module
+class ChildDoubleModule extends ParentModule<Double, String, List<Double>> {
+
+ @Provides Double provideDouble() {
+ return 3d;
+ }
+
+ @Provides List<Double> provideListOfDouble() {
+ List<Double> list = new ArrayList<>();
+ list.add(4d);
+ return list;
+ }
+
+}
diff --git a/javatests/dagger/functional/ChildIntegerModule.java b/javatests/dagger/functional/ChildIntegerModule.java
new file mode 100644
index 0000000..45c7c1a
--- /dev/null
+++ b/javatests/dagger/functional/ChildIntegerModule.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+import java.util.ArrayList;
+import java.util.List;
+
+@Module
+class ChildIntegerModule extends ParentModule<Integer, String, List<Integer>> {
+
+ @Provides Integer provideInteger() {
+ return 1;
+ }
+
+ @Provides List<Integer> provideListOfInteger() {
+ List<Integer> list = new ArrayList<>();
+ list.add(2);
+ return list;
+ }
+
+}
diff --git a/javatests/dagger/functional/ComplexGenerics.java b/javatests/dagger/functional/ComplexGenerics.java
new file mode 100644
index 0000000..9309583
--- /dev/null
+++ b/javatests/dagger/functional/ComplexGenerics.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Lazy;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+class ComplexGenerics {
+
+ final Generic2<Generic<A>> g2ga;
+ final Lazy<Generic2<Generic<A>>> g2gaLazy;
+ final Provider<Generic2<Generic<A>>> g2gaProvider;
+ final Generic2<Generic<B>> g2gb;
+ final Lazy<Generic2<Generic<B>>> g2gbLazy;
+ final Provider<Generic2<Generic<B>>> g2gbProvider;
+ final Generic2<A> g2a;
+ final Generic<Generic2<A>> gg2a;
+ final Generic<Generic2<B>> gg2b;
+
+ @Inject ComplexGenerics(
+ Generic2<Generic<A>> g2ga,
+ Lazy<Generic2<Generic<A>>> g2gaLazy,
+ Provider<Generic2<Generic<A>>> g2gaProvider,
+ Generic2<Generic<B>> g2gb,
+ Lazy<Generic2<Generic<B>>> g2gbLazy,
+ Provider<Generic2<Generic<B>>> g2gbProvider,
+ Generic2<A> g2a,
+ Generic<Generic2<A>> gg2a,
+ Generic<Generic2<B>> gg2b) {
+ this.g2ga = g2ga;
+ this.g2gaLazy = g2gaLazy;
+ this.g2gaProvider = g2gaProvider;
+ this.g2gb = g2gb;
+ this.g2gbLazy = g2gbLazy;
+ this.g2gbProvider = g2gbProvider;
+ this.g2a = g2a;
+ this.gg2a = gg2a;
+ this.gg2b = gg2b;
+ }
+}
diff --git a/javatests/dagger/functional/ComponentDependsOnGeneratedCode.java b/javatests/dagger/functional/ComponentDependsOnGeneratedCode.java
new file mode 100644
index 0000000..9c44c5c
--- /dev/null
+++ b/javatests/dagger/functional/ComponentDependsOnGeneratedCode.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+
+/**
+ * A component that indirectly depends on code generated by another processor, in this case
+ * {@link com.google.auto.factory.AutoFactory}. Neither this type nor its immediately referenced
+ * types are generated, but {@link NeedsFactory} depends on the generated
+ * {@link NeedsFactory_SomethingFactory}.
+ *
+ */
+@Component
+interface ComponentDependsOnGeneratedCode {
+ NeedsFactory needsFactory();
+}
diff --git a/javatests/dagger/functional/ComponentMethodTest.java b/javatests/dagger/functional/ComponentMethodTest.java
new file mode 100644
index 0000000..5e53ea4
--- /dev/null
+++ b/javatests/dagger/functional/ComponentMethodTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * This is a regression test that makes sure component method order does not affect initialization
+ * order.
+ */
+@RunWith(JUnit4.class)
+public final class ComponentMethodTest {
+
+ static final class Dep1 {
+
+ @Inject
+ Dep1(Dep2 dep2) {}
+ }
+
+ static final class Dep2 {
+
+ @Inject
+ Dep2(Dep3 dep3) {}
+ }
+
+ static final class Dep3 {
+
+ @Inject
+ Dep3() {}
+ }
+
+ @Component
+ interface NonTopologicalOrderComponent {
+
+ Provider<Dep1> dep1Provider();
+
+ Provider<Dep2> dep2Provider();
+ }
+
+ @Component
+ interface TopologicalOrderComponent {
+
+ Provider<Dep2> dep2Provider();
+
+ Provider<Dep1> dep1Provider();
+ }
+
+ @Test
+ public void testNonTopologicalOrderComponent() throws Exception {
+ NonTopologicalOrderComponent component =
+ DaggerComponentMethodTest_NonTopologicalOrderComponent.create();
+ component.dep1Provider().get();
+ component.dep2Provider().get();
+ }
+
+ @Test
+ public void testTopologicalOrderComponent() throws Exception {
+ TopologicalOrderComponent component =
+ DaggerComponentMethodTest_TopologicalOrderComponent.create();
+ component.dep1Provider().get();
+ component.dep2Provider().get();
+ }
+}
diff --git a/javatests/dagger/functional/ComponentSupertypeDependsOnGeneratedCode.java b/javatests/dagger/functional/ComponentSupertypeDependsOnGeneratedCode.java
new file mode 100644
index 0000000..a26292a
--- /dev/null
+++ b/javatests/dagger/functional/ComponentSupertypeDependsOnGeneratedCode.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+
+/**
+ * A component whose supertype depends on code generated by another processor, in this case
+ * {@link com.google.auto.factory.AutoFactory}.
+ *
+ */
+@Component
+interface ComponentSupertypeDependsOnGeneratedCode
+ extends ComponentSupertypeDependsOnGeneratedCodeInterface {}
diff --git a/javatests/dagger/functional/ComponentSupertypeDependsOnGeneratedCodeInterface.java b/javatests/dagger/functional/ComponentSupertypeDependsOnGeneratedCodeInterface.java
new file mode 100644
index 0000000..7744d19
--- /dev/null
+++ b/javatests/dagger/functional/ComponentSupertypeDependsOnGeneratedCodeInterface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+interface ComponentSupertypeDependsOnGeneratedCodeInterface {
+ NeedsFactory_SomethingFactory somethingFactory();
+}
diff --git a/javatests/dagger/functional/ComponentSupertypeOne.java b/javatests/dagger/functional/ComponentSupertypeOne.java
new file mode 100644
index 0000000..c63d1da
--- /dev/null
+++ b/javatests/dagger/functional/ComponentSupertypeOne.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+public interface ComponentSupertypeOne {
+ Thing inheritedThing();
+}
diff --git a/javatests/dagger/functional/ComponentSupertypeTwo.java b/javatests/dagger/functional/ComponentSupertypeTwo.java
new file mode 100644
index 0000000..3c8312b
--- /dev/null
+++ b/javatests/dagger/functional/ComponentSupertypeTwo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+public interface ComponentSupertypeTwo {
+ Thing inheritedThing();
+}
diff --git a/javatests/dagger/functional/ComponentWithReusableBindings.java b/javatests/dagger/functional/ComponentWithReusableBindings.java
new file mode 100644
index 0000000..afad0d7
--- /dev/null
+++ b/javatests/dagger/functional/ComponentWithReusableBindings.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Reusable;
+import dagger.Subcomponent;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+
+@Component(modules = ComponentWithReusableBindings.ReusableBindingsModule.class)
+interface ComponentWithReusableBindings {
+
+ @Qualifier
+ @interface InParent {}
+
+ @Qualifier
+ @interface InChildren {}
+
+ @InParent
+ Object reusableInParent();
+
+ ChildOne childOne();
+
+ ChildTwo childTwo();
+
+ // b/77150738
+ int primitive();
+
+ // b/77150738: This is used as a regression test for fastInit mode's switching providers. In
+ // particular, it occurs when a @Provides method returns the boxed type but the component method
+ // returns the unboxed type, and the instance is requested from a SwitchingProvider.
+ boolean unboxedPrimitive();
+
+ // b/77150738
+ Provider<Boolean> booleanProvider();
+
+ @Subcomponent
+ interface ChildOne {
+ @InParent
+ Object reusableInParent();
+
+ @InChildren
+ Object reusableInChild();
+ }
+
+ @Subcomponent
+ interface ChildTwo {
+ @InParent
+ Object reusableInParent();
+
+ @InChildren
+ Object reusableInChild();
+ }
+
+ @Module
+ static class ReusableBindingsModule {
+ @Provides
+ @Reusable
+ @InParent
+ static Object inParent() {
+ return new Object();
+ }
+
+ @Provides
+ @Reusable
+ @InChildren
+ static Object inChildren() {
+ return new Object();
+ }
+
+ // b/77150738
+ @Provides
+ @Reusable
+ static int primitive() {
+ return 0;
+ }
+
+ // b/77150738
+ @Provides
+ @Reusable
+ static Boolean boxedPrimitive() {
+ return false;
+ }
+ }
+}
diff --git a/javatests/dagger/functional/ComponentsWithNestedModules.java b/javatests/dagger/functional/ComponentsWithNestedModules.java
new file mode 100644
index 0000000..a42dd73
--- /dev/null
+++ b/javatests/dagger/functional/ComponentsWithNestedModules.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Module;
+
+// https://github.com/google/dagger/issues/279
+public class ComponentsWithNestedModules {
+ @Component(modules = RegularComponent.SharedModule.class)
+ public interface RegularComponent {
+ @Module class SharedModule {}
+ }
+
+ @Component(modules = ExtendsRegularComponent.SharedModule.class)
+ public interface ExtendsRegularComponent extends RegularComponent {
+ @Module(includes = RegularComponent.SharedModule.class)
+ class SharedModule {}
+ }
+}
diff --git a/javatests/dagger/functional/DependsOnGeneratedCodeTest.java b/javatests/dagger/functional/DependsOnGeneratedCodeTest.java
new file mode 100644
index 0000000..ca7631f
--- /dev/null
+++ b/javatests/dagger/functional/DependsOnGeneratedCodeTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @see <a href="http://b/19435358">Bug 19435358</a>
+ */
+@RunWith(JUnit4.class)
+public class DependsOnGeneratedCodeTest {
+ @Test public void testComponentDependsOnGeneratedCode() {
+ assertThat(DaggerComponentDependsOnGeneratedCode.create().needsFactory()).isNotNull();
+ }
+}
diff --git a/javatests/dagger/functional/Generic.java b/javatests/dagger/functional/Generic.java
new file mode 100644
index 0000000..9e77efe
--- /dev/null
+++ b/javatests/dagger/functional/Generic.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+public class Generic<T> {
+ final T t;
+
+ @Inject public Generic(T t) {
+ this.t = t;
+ }
+}
diff --git a/javatests/dagger/functional/Generic2.java b/javatests/dagger/functional/Generic2.java
new file mode 100644
index 0000000..f53c0f8
--- /dev/null
+++ b/javatests/dagger/functional/Generic2.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+public class Generic2<T> {
+ final T t;
+
+ @Inject Generic2(T t) {
+ this.t = t;
+ }
+}
diff --git a/javatests/dagger/functional/GenericChild.java b/javatests/dagger/functional/GenericChild.java
new file mode 100644
index 0000000..bb7330e
--- /dev/null
+++ b/javatests/dagger/functional/GenericChild.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+class GenericChild<T> extends GenericParent<T, B> {
+
+ A registeredA;
+ T registeredT;
+
+ @Inject GenericChild() {}
+
+ @Inject A a;
+ @Inject T t;
+
+ @Inject void registerA(A a) { this.registeredA = a; }
+ @Inject void registerT(T t) { this.registeredT = t; }
+
+}
diff --git a/javatests/dagger/functional/GenericComponent.java b/javatests/dagger/functional/GenericComponent.java
new file mode 100644
index 0000000..e27df3b
--- /dev/null
+++ b/javatests/dagger/functional/GenericComponent.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.functional.GenericComponent.NongenericModule;
+import dagger.functional.sub.Exposed;
+import dagger.functional.sub.PublicSubclass;
+import java.util.Arrays;
+import java.util.List;
+import javax.inject.Provider;
+
+@Component(modules = {ChildDoubleModule.class, ChildIntegerModule.class, NongenericModule.class})
+interface GenericComponent {
+ ReferencesGeneric referencesGeneric();
+ GenericDoubleReferences<A> doubleGenericA();
+ GenericDoubleReferences<B> doubleGenericB();
+ ComplexGenerics complexGenerics();
+ GenericNoDeps<A> noDepsA();
+ GenericNoDeps<B> noDepsB();
+
+ void injectA(GenericChild<A> childA);
+ void injectB(GenericChild<B> childB);
+
+ Exposed exposed();
+ PublicSubclass publicSubclass();
+
+ Iterable<Integer> iterableInt();
+ Iterable<Double> iterableDouble();
+
+ Provider<List<String>> stringsProvider(); // b/71595104
+
+ // b/71595104
+ @Module
+ abstract class GenericModule<T> {
+ // Note that for subclasses that use String for T, this factory will still need two
+ // Provider<String> framework dependencies.
+ @Provides
+ List<T> list(T t, String string) {
+ return Arrays.asList(t);
+ }
+ }
+
+ // b/71595104
+ @Module
+ class NongenericModule extends GenericModule<String> {
+ @Provides
+ static String string() {
+ return "string";
+ }
+ }
+}
diff --git a/javatests/dagger/functional/GenericDoubleReferences.java b/javatests/dagger/functional/GenericDoubleReferences.java
new file mode 100644
index 0000000..ff79a98
--- /dev/null
+++ b/javatests/dagger/functional/GenericDoubleReferences.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+class GenericDoubleReferences<T> {
+ final T t;
+ final T t2;
+ final Thing a;
+ final Thing a2;
+
+ @Inject GenericDoubleReferences(T t, Thing a, T t2, Thing a2) {
+ this.t = t;
+ this.a = a;
+ this.t2 = t2;
+ this.a2 = a2;
+ }
+}
diff --git a/javatests/dagger/functional/GenericNoDeps.java b/javatests/dagger/functional/GenericNoDeps.java
new file mode 100644
index 0000000..c3f38b4
--- /dev/null
+++ b/javatests/dagger/functional/GenericNoDeps.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+class GenericNoDeps<T> {
+
+ @Inject GenericNoDeps() {}
+
+}
diff --git a/javatests/dagger/functional/GenericParent.java b/javatests/dagger/functional/GenericParent.java
new file mode 100644
index 0000000..13f9e2f
--- /dev/null
+++ b/javatests/dagger/functional/GenericParent.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+class GenericParent<X, Y> {
+
+ Provider<X> registeredX;
+ Y registeredY;
+ B registeredB;
+ Parameterized<Y> registerParameterizedOfY;
+
+ @Inject GenericParent() {}
+
+ @Inject Provider<X> x;
+ @Inject Y y;
+ @Inject B b;
+ @Inject Parameterized<X> parameterizedOfX;
+
+ @Inject
+ void registerX(Provider<X> x) {
+ this.registeredX = x;
+ }
+ @Inject void registerY(Y y) { this.registeredY = y; }
+ @Inject void registerB(B b) { this.registeredB = b; }
+ @Inject void registerParameterizedOfY(Parameterized<Y> parameterizedOfY) {
+ this.registerParameterizedOfY = parameterizedOfY;
+ }
+
+ static class Parameterized<P> {
+ @Inject P p;
+
+ @Inject Parameterized() {}
+ }
+}
diff --git a/javatests/dagger/functional/GenericTest.java b/javatests/dagger/functional/GenericTest.java
new file mode 100644
index 0000000..3e4a271
--- /dev/null
+++ b/javatests/dagger/functional/GenericTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import dagger.functional.sub.Exposed;
+import dagger.functional.sub.PublicSubclass;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class GenericTest {
+
+ @Test public void testGenericComponentCreate() {
+ GenericComponent component = DaggerGenericComponent.create();
+ assertThat(component).isNotNull();
+ }
+
+ @Test public void testGenericSimpleReferences() {
+ GenericComponent component = DaggerGenericComponent.create();
+ assertThat(component.referencesGeneric().genericA.t).isNotNull();
+ }
+
+ @Test public void testGenericDoubleReferences() {
+ GenericComponent component = DaggerGenericComponent.create();
+ GenericDoubleReferences<A> doubleA = component.doubleGenericA();
+ assertThat(doubleA.a).isNotNull();
+ assertThat(doubleA.a2).isNotNull();
+ assertThat(doubleA.t).isNotNull();
+ assertThat(doubleA.t2).isNotNull();
+
+ GenericDoubleReferences<B> doubleB = component.doubleGenericB();
+ assertThat(doubleB.a).isNotNull();
+ assertThat(doubleB.a2).isNotNull();
+ assertThat(doubleB.t).isNotNull();
+ assertThat(doubleB.t2).isNotNull();
+ }
+
+ @Test public void complexGenerics() {
+ GenericComponent component = DaggerGenericComponent.create();
+ // validate these can be called w/o exceptions.
+ component.complexGenerics();
+ }
+
+ @Test public void noDepsGenerics() {
+ GenericComponent component = DaggerGenericComponent.create();
+ // validate these can be called w/o exceptions.
+ component.noDepsA();
+ component.noDepsB();
+ }
+
+ @Test public void boundedGenerics() {
+ BoundedGenericModule expected = new BoundedGenericModule();
+ BoundedGenericComponent component = DaggerBoundedGenericComponent.create();
+ BoundedGenerics<Integer, ArrayList<String>, LinkedList<CharSequence>, Integer, List<Integer>>
+ b1 = component.bounds1();
+ assertEquals(expected.provideInteger(), b1.a);
+ assertEquals(expected.provideArrayListString(), b1.b);
+ assertEquals(expected.provideLinkedListCharSeq(), b1.c);
+ assertEquals(expected.provideInteger(), b1.d);
+ assertEquals(expected.provideListOfInteger(), b1.e);
+
+ BoundedGenerics<Double, LinkedList<String>, LinkedList<Comparable<String>>, Double, Set<Double>>
+ b2 = component.bounds2();
+ assertEquals(expected.provideDouble(), b2.a);
+ assertEquals(expected.provideLinkedListString(), b2.b);
+ assertEquals(expected.provideArrayListOfComparableString(), b2.c);
+ assertEquals(expected.provideDouble(), b2.d);
+ assertEquals(expected.provideSetOfDouble(), b2.e);
+ }
+
+ @Test public void membersInjections() {
+ GenericComponent component = DaggerGenericComponent.create();
+ GenericChild<A> childA = new GenericChild<A>();
+ component.injectA(childA);
+ assertThat(childA.a).isNotNull();
+ assertThat(childA.b).isNotNull();
+ assertThat(childA.registeredA).isNotNull();
+ assertThat(childA.registeredB).isNotNull();
+ assertThat(childA.registeredT).isNotNull();
+ assertThat(childA.registeredX).isNotNull();
+ assertThat(childA.registeredY).isNotNull();
+
+ GenericChild<B> childB = new GenericChild<B>();
+ component.injectB(childB);
+ assertThat(childB.a).isNotNull();
+ assertThat(childB.b).isNotNull();
+ assertThat(childB.registeredA).isNotNull();
+ assertThat(childB.registeredB).isNotNull();
+ assertThat(childB.registeredT).isNotNull();
+ assertThat(childB.registeredX).isNotNull();
+ assertThat(childB.registeredY).isNotNull();
+ }
+
+ @Test public void packagePrivateTypeParameterDependencies() {
+ GenericComponent component = DaggerGenericComponent.create();
+ Exposed exposed = component.exposed();
+ assertThat(exposed.gpp.t).isNotNull();
+ assertThat(exposed.gpp2).isNotNull();
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Test public void publicSubclassWithPackagePrivateTypeParameterOfSuperclass() {
+ GenericComponent component = DaggerGenericComponent.create();
+ PublicSubclass publicSubclass = component.publicSubclass();
+ assertThat(((Generic)publicSubclass).t).isNotNull();
+ }
+
+ @Test public void singletonScopesAppliesToEachResolvedType() {
+ SingletonGenericComponent component = DaggerSingletonGenericComponent.create();
+ ScopedGeneric<A> a = component.scopedGenericA();
+ assertThat(a).isSameInstanceAs(component.scopedGenericA());
+ assertThat(a.t).isNotNull();
+
+ ScopedGeneric<B> b = component.scopedGenericB();
+ assertThat(b).isSameInstanceAs(component.scopedGenericB());
+ assertThat(b.t).isNotNull();
+
+ assertThat(a).isNotSameInstanceAs(b);
+ }
+
+ @Test // See https://github.com/google/dagger/issues/671
+ public void scopedSimpleGenerics() {
+ SingletonGenericComponent component = DaggerSingletonGenericComponent.create();
+ ScopedSimpleGeneric<A> a = component.scopedSimpleGenericA();
+ assertThat(a).isSameInstanceAs(component.scopedSimpleGenericA());
+
+ ScopedSimpleGeneric<B> b = component.scopedSimpleGenericB();
+ assertThat(b).isSameInstanceAs(component.scopedSimpleGenericB());
+
+ assertThat(a).isNotSameInstanceAs(b);
+ }
+
+ @Test public void genericModules() {
+ GenericComponent component = DaggerGenericComponent.create();
+ assertThat(component.iterableInt()).containsExactly(1, 2).inOrder();
+ assertThat(component.iterableDouble()).containsExactly(3d, 4d).inOrder();
+ }
+}
diff --git a/javatests/dagger/functional/InjectedThing.java b/javatests/dagger/functional/InjectedThing.java
new file mode 100644
index 0000000..3041fb0
--- /dev/null
+++ b/javatests/dagger/functional/InjectedThing.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Lazy;
+import dagger.MembersInjector;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+@SuppressWarnings("unused")
+final class InjectedThing {
+ @Inject byte primitiveByte;
+ @Inject char primitiveChar;
+ @Inject short primitiveShort;
+ @Inject int primitiveInt;
+ @Inject long primitiveLong;
+ @Inject boolean primitiveBoolean;
+ @Inject float primitiveFloat;
+ @Inject double primitiveDouble;
+
+ @Inject Provider<Byte> byteProvider;
+ @Inject Provider<Character> charProvider;
+ @Inject Provider<Short> shortProvider;
+ @Inject Provider<Integer> intProvider;
+ @Inject Provider<Long> longProvider;
+ @Inject Provider<Boolean> booleanProvider;
+ @Inject Provider<Float> floatProvider;
+ @Inject Provider<Double> doubleProvider;
+
+ @Inject Lazy<Byte> lazyByte;
+ @Inject Lazy<Character> lazyChar;
+ @Inject Lazy<Short> lazyShort;
+ @Inject Lazy<Integer> lazyInt;
+ @Inject Lazy<Long> lazyLong;
+ @Inject Lazy<Boolean> lazyBoolean;
+ @Inject Lazy<Float> lazyFloat;
+ @Inject Lazy<Double> lazyDouble;
+
+ @Inject Byte boxedBype;
+ @Inject Character boxedChar;
+ @Inject Short boxedShort;
+ @Inject Integer boxedInt;
+ @Inject Long boxedLong;
+ @Inject Boolean boxedBoolean;
+ @Inject Float boxedFloat;
+ @Inject Double boxedDouble;
+
+ @Inject byte[] byteArray;
+ @Inject char[] charArray;
+ @Inject short[] shortArray;
+ @Inject int[] intArray;
+ @Inject long[] longArray;
+ @Inject boolean[] booleanArray;
+ @Inject float[] floatArray;
+ @Inject double[] doubleArray;
+
+ @Inject Provider<byte[]> byteArrayProvider;
+ @Inject Provider<char[]> charArrayProvider;
+ @Inject Provider<short[]> shortArrayProvider;
+ @Inject Provider<int[]> intArrayProvider;
+ @Inject Provider<long[]> longArrayProvider;
+ @Inject Provider<boolean[]> booleanArrayProvider;
+ @Inject Provider<float[]> floatArrayProvider;
+ @Inject Provider<double[]> doubleArrayProvider;
+
+ @Inject Lazy<byte[]> lazyByteArray;
+ @Inject Lazy<char[]> lazyCharArray;
+ @Inject Lazy<short[]> lazyShortArray;
+ @Inject Lazy<int[]> lazyIntArray;
+ @Inject Lazy<long[]> lazyLongArray;
+ @Inject Lazy<boolean[]> lazyBooleanArray;
+ @Inject Lazy<float[]> lazy;
+ @Inject Lazy<double[]> lazyDoubleArray;
+
+ @Inject Thing thing;
+ @Inject Provider<Thing> thingProvider;
+ @Inject Lazy<Thing> lazyThing;
+ @Inject Provider<Lazy<Thing>> lazyThingProvider;
+ @Inject MembersInjector<Thing> thingMembersInjector;
+
+ @Inject InjectedThing(
+ byte primitiveByte,
+ char primitiveChar,
+ short primitiveShort,
+ int primitiveInt,
+ long primitiveLong,
+ boolean primitiveBoolean,
+ float primitiveFloat,
+ double primitiveDouble,
+
+ Provider<Byte> byteProvider,
+ Provider<Character> charProvider,
+ Provider<Short> shortProvider,
+ Provider<Integer> intProvider,
+ Provider<Long> longProvider,
+ Provider<Boolean> booleanProvider,
+ Provider<Float> floatProvider,
+ Provider<Double> doubleProvider,
+
+ Lazy<Byte> lazyByte,
+ Lazy<Character> lazyChar,
+ Lazy<Short> lazyShort,
+ Lazy<Integer> lazyInt,
+ Lazy<Long> lazyLong,
+ Lazy<Boolean> lazyBoolean,
+ Lazy<Float> lazyFloat,
+ Lazy<Double> lazyDouble,
+
+ Byte boxedBype,
+ Character boxedChar,
+ Short boxedShort,
+ Integer boxedInt,
+ Long boxedLong,
+ Boolean boxedBoolean,
+ Float boxedFloat,
+ Double boxedDouble,
+
+ byte[] byteArray,
+ char[] charArray,
+ short[] shortArray,
+ int[] intArray,
+ long[] longArray,
+ boolean[] booleanArray,
+ float[] floatArray,
+ double[] doubleArray,
+
+ Provider<byte[]> byteArrayProvider,
+ Provider<char[]> charArrayProvider,
+ Provider<short[]> shortArrayProvider,
+ Provider<int[]> intArrayProvider,
+ Provider<long[]> longArrayProvider,
+ Provider<boolean[]> booleanArrayProvider,
+ Provider<float[]> floatArrayProvider,
+ Provider<double[]> doubleArrayProvider,
+
+ Lazy<byte[]> lazyByteArray,
+ Lazy<char[]> lazyCharArray,
+ Lazy<short[]> lazyShortArray,
+ Lazy<int[]> lazyIntArray,
+ Lazy<long[]> lazyLongArray,
+ Lazy<boolean[]> lazyBooleanArray,
+ Lazy<float[]> lazy,
+ Lazy<double[]> lazyDoubleArray,
+
+ Thing thing,
+ Provider<Thing> thingProvider,
+ Lazy<Thing> lazyThing,
+ Provider<Lazy<Thing>> lazyThingProvider,
+ MembersInjector<Thing> thingMembersInjector) {}
+
+ @Inject void primitiveByte(byte primitiveByte) {}
+ @Inject void primitiveChar(char primitiveChar) {}
+ @Inject void primitiveShort(short primitiveShort) {}
+ @Inject void primitiveInt(int primitiveInt) {}
+ @Inject void primitiveLong(long primitiveLong) {}
+ @Inject void primitiveBoolean(boolean primitiveBoolean) {}
+ @Inject void primitiveFloat(float primitiveFloat) {}
+ @Inject void primitiveDouble(double primitiveDouble) {}
+
+ @Inject void byteProvider(Provider<Byte> byteProvider) {}
+ @Inject void charProvider(Provider<Character> charProvider) {}
+ @Inject void shortProvider(Provider<Short> shortProvider) {}
+ @Inject void intProvider(Provider<Integer> intProvider) {}
+ @Inject void longProvider(Provider<Long> longProvider) {}
+ @Inject void booleanProvider(Provider<Boolean> booleanProvider) {}
+ @Inject void floatProvider(Provider<Float> floatProvider) {}
+ @Inject void doubleProvider(Provider<Double> doubleProvider) {}
+
+ @Inject void lazyByte(Lazy<Byte> lazyByte) {}
+ @Inject void lazyChar(Lazy<Character> lazyChar) {}
+ @Inject void lazyShort(Lazy<Short> lazyShort) {}
+ @Inject void lazyInt(Lazy<Integer> lazyInt) {}
+ @Inject void lazyLong(Lazy<Long> lazyLong) {}
+ @Inject void lazyBoolean(Lazy<Boolean> lazyBoolean) {}
+ @Inject void lazyFloat(Lazy<Float> lazyFloat) {}
+ @Inject void lazyDouble(Lazy<Double> lazyDouble) {}
+
+ @Inject void boxedBype(Byte boxedBype) {}
+ @Inject void boxedChar(Character boxedChar) {}
+ @Inject void boxedShort(Short boxedShort) {}
+ @Inject void boxedInt(Integer boxedInt) {}
+ @Inject void boxedLong(Long boxedLong) {}
+ @Inject void boxedBoolean(Boolean boxedBoolean) {}
+ @Inject void boxedFloat(Float boxedFloat) {}
+ @Inject void boxedDouble(Double boxedDouble) {}
+
+ @Inject void byteArray(byte[] byteArray) {}
+ @Inject void charArray(char[] charArray) {}
+ @Inject void shortArray(short[] shortArray) {}
+ @Inject void intArray(int[] intArray) {}
+ @Inject void longArray(long[] longArray) {}
+ @Inject void booleanArray(boolean[] booleanArray) {}
+ @Inject void floatArray(float[] floatArray) {}
+ @Inject void doubleArray(double[] doubleArray) {}
+
+ @Inject void byteArrayProvider(Provider<byte[]> byteArrayProvider) {}
+ @Inject void charArrayProvider(Provider<char[]> charArrayProvider) {}
+ @Inject void shortArrayProvider(Provider<short[]> shortArrayProvider) {}
+ @Inject void intArrayProvider(Provider<int[]> intArrayProvider) {}
+ @Inject void longArrayProvider(Provider<long[]> longArrayProvider) {}
+ @Inject void booleanArrayProvider(Provider<boolean[]> booleanArrayProvider) {}
+ @Inject void floatArrayProvider(Provider<float[]> floatArrayProvider) {}
+ @Inject void doubleArrayProvider(Provider<double[]> doubleArrayProvider) {}
+
+ @Inject void lazyByteArray(Lazy<byte[]> lazyByteArray) {}
+ @Inject void lazyCharArray(Lazy<char[]> lazyCharArray) {}
+ @Inject void lazyShortArray(Lazy<short[]> lazyShortArray) {}
+ @Inject void lazyIntArray(Lazy<int[]> lazyIntArray) {}
+ @Inject void lazyLongArray(Lazy<long[]> lazyLongArray) {}
+ @Inject void lazyBooleanArray(Lazy<boolean[]> lazyBooleanArray) {}
+ @Inject void lazy(Lazy<float[]> lazy) {}
+ @Inject void lazyThingProvider(Provider<Lazy<Thing>> lazyThingProvider) {}
+ @Inject void lazyDoubleArray(Lazy<double[]> lazyDoubleArray) {}
+
+ @Inject void thing(Thing thing) {}
+ @Inject void thingProvider(Provider<Thing> thingProvider) {}
+ @Inject void lazyThing(Lazy<Thing> lazyThing) {}
+ @Inject void thingMembersInjector(MembersInjector<Thing> thingMembersInjector) {}
+}
diff --git a/javatests/dagger/functional/Injector.java b/javatests/dagger/functional/Injector.java
new file mode 100644
index 0000000..d001f93
--- /dev/null
+++ b/javatests/dagger/functional/Injector.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Lazy;
+import dagger.MembersInjector;
+import javax.inject.Provider;
+
+/**
+ * A simple interface that exercises all forms of injection for a given type.
+ */
+interface Injector<T> {
+ T instance();
+ Provider<T> provider();
+ Lazy<T> lazy();
+ MembersInjector<T> membersInjector();
+ void injectMembers(T t);
+ T injectMembersAndReturn(T t);
+}
diff --git a/javatests/dagger/functional/LazyMaps.java b/javatests/dagger/functional/LazyMaps.java
new file mode 100644
index 0000000..eeb1368
--- /dev/null
+++ b/javatests/dagger/functional/LazyMaps.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.StringKey;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+/**
+ * Bindings that use {@code Lazy<T>} as the value in a multibound map. A regression was uncovered
+ * when using {@code MapType.valuesAreFrameworkType()}, which treats {@link Lazy} as a framework
+ * type and incorrectly suggested {@link dagger.internal.MapProviderFactory} for a {@code Map<K,
+ * Lazy<V>>} instead of a plain {@link dagger.internal.MapFactory}. See b/65084589.
+ */
+class LazyMaps {
+ @Module
+ abstract static class TestModule {
+ @Provides
+ @Singleton
+ static AtomicInteger provideAtomicInteger() {
+ return new AtomicInteger();
+ }
+
+ @Provides
+ static String provideString(AtomicInteger atomicInteger) {
+ return "value-" + atomicInteger.incrementAndGet();
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("key")
+ static Lazy<String> mapContribution(Lazy<String> lazy) {
+ return lazy;
+ }
+
+ /* TODO(b/65118638) Replace once @Binds @IntoMap Lazy<T> methods work properly.
+ @Binds
+ @IntoMap
+ @StringKey("binds-key")
+ abstract Lazy<String> mapContributionAsBinds(Lazy<String> lazy);
+ */
+ }
+
+ @Singleton
+ @Component(modules = TestModule.class)
+ interface TestComponent {
+ Map<String, Lazy<String>> mapOfLazy();
+
+ Map<String, Provider<Lazy<String>>> mapOfProviderOfLazy();
+
+ Provider<Map<String, Lazy<String>>> providerForMapOfLazy();
+
+ Provider<Map<String, Provider<Lazy<String>>>> providerForMapOfProviderOfLazy();
+ }
+}
diff --git a/javatests/dagger/functional/LazyMapsTest.java b/javatests/dagger/functional/LazyMapsTest.java
new file mode 100644
index 0000000..a441653
--- /dev/null
+++ b/javatests/dagger/functional/LazyMapsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Lazy;
+import dagger.functional.LazyMaps.TestComponent;
+import java.util.Map;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link LazyMaps}. */
+@RunWith(JUnit4.class)
+public class LazyMapsTest {
+ @Test
+ public void mapOfLazies() {
+ TestComponent component = DaggerLazyMaps_TestComponent.create();
+ Map<String, Lazy<String>> laziesMap = component.mapOfLazy();
+
+ String firstGet = laziesMap.get("key").get();
+ assertThat(firstGet).isEqualTo("value-1");
+ assertThat(firstGet).isSameInstanceAs(laziesMap.get("key").get());
+
+ assertThat(component.mapOfLazy().get("key").get()).isEqualTo("value-2");
+ }
+
+ @Test
+ public void mapOfProviderOfLaziesReturnsDifferentLazy() {
+ TestComponent component = DaggerLazyMaps_TestComponent.create();
+ Map<String, Provider<Lazy<String>>> providersOfLaziesMap = component.mapOfProviderOfLazy();
+
+ assertThat(providersOfLaziesMap.get("key").get().get())
+ .isNotEqualTo(providersOfLaziesMap.get("key").get().get());
+ }
+}
diff --git a/javatests/dagger/functional/ModuleCycles.java b/javatests/dagger/functional/ModuleCycles.java
new file mode 100644
index 0000000..9df6685
--- /dev/null
+++ b/javatests/dagger/functional/ModuleCycles.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Module;
+
+final class ModuleCycles {
+ @Module(includes = ModuleB.class)
+ interface ModuleA {}
+
+ @Module(includes = ModuleA.class)
+ interface ModuleB {}
+
+ @Component(modules = ModuleA.class)
+ interface CycleComponent {}
+}
diff --git a/javatests/dagger/functional/ModuleIncludesCollectedFromModuleSuperclasses.java b/javatests/dagger/functional/ModuleIncludesCollectedFromModuleSuperclasses.java
new file mode 100644
index 0000000..fb18b6d
--- /dev/null
+++ b/javatests/dagger/functional/ModuleIncludesCollectedFromModuleSuperclasses.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * This tests that @Module.includes are traversed for supertypes of a module.
+ */
+final class ModuleIncludesCollectedFromModuleSuperclasses {
+ @Component(modules = TopLevelModule.class)
+ interface C {
+ Foo<String> foo();
+ int includedInTopLevelModule();
+ String includedFromModuleInheritance();
+ }
+
+ @Module(includes = IncludedTopLevel.class)
+ static class TopLevelModule extends FooModule<String> {}
+
+ static class Foo<T> {}
+
+ @Module(includes = IncludedFromModuleInheritance.class)
+ abstract static class FooModule<T> extends FooCreator {
+ @Provides Foo<T> fooOfT() {
+ return createFoo();
+ }
+ }
+
+ static class FooCreator {
+ <T> Foo<T> createFoo() {
+ return new Foo<T>();
+ }
+ }
+
+ @Module
+ static class IncludedTopLevel {
+ @Provides int i() {
+ return 123;
+ }
+ }
+
+ @Module
+ static class IncludedFromModuleInheritance {
+ @Provides String inheritedProvision() {
+ return "inherited";
+ }
+ }
+}
diff --git a/javatests/dagger/functional/ModuleWithConflictingNames.java b/javatests/dagger/functional/ModuleWithConflictingNames.java
new file mode 100644
index 0000000..43dd58e
--- /dev/null
+++ b/javatests/dagger/functional/ModuleWithConflictingNames.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * Module with bindings that might result in generated factories with conflicting field and
+ * parameter names.
+ */
+@Module
+final class ModuleWithConflictingNames {
+ @Provides
+ static Object object(int foo, Provider<String> fooProvider) {
+ return foo + fooProvider.get();
+ }
+
+ /**
+ * A class that might result in a generated factory with conflicting field and parameter names.
+ */
+ static class InjectedClassWithConflictingNames {
+ final int foo;
+ final Provider<String> fooProvider;
+
+ @Inject
+ InjectedClassWithConflictingNames(int foo, Provider<String> fooProvider) {
+ this.foo = foo;
+ this.fooProvider = fooProvider;
+ }
+ }
+}
diff --git a/javatests/dagger/functional/MultibindingComponent.java b/javatests/dagger/functional/MultibindingComponent.java
new file mode 100644
index 0000000..60ed5cf
--- /dev/null
+++ b/javatests/dagger/functional/MultibindingComponent.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.functional.sub.ContributionsModule;
+import dagger.multibindings.StringKey;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+@Component(
+ modules = {MultibindingModule.class, MultibindsModule.class, ContributionsModule.class},
+ dependencies = MultibindingDependency.class
+)
+interface MultibindingComponent {
+ Map<String, String> map();
+ Map<String, String[]> mapOfArrays();
+ Map<String, Provider<String>> mapOfProviders();
+ Set<String> mapKeys();
+ Collection<String> mapValues();
+ Set<Integer> set();
+ Map<NestedAnnotationContainer.NestedWrappedKey, String> nestedKeyMap();
+ Map<Class<? extends Number>, String> numberClassKeyMap();
+ Map<Class<?>, String> classKeyMap();
+ Map<Long, String> longKeyMap();
+ Map<Integer, String> integerKeyMap();
+ Map<Short, String> shortKeyMap();
+ Map<Byte, String> byteKeyMap();
+ Map<Boolean, String> booleanKeyMap();
+ Map<Character, String> characterKeyMap();
+ Map<StringKey, String> unwrappedAnnotationKeyMap();
+ Map<WrappedAnnotationKey, String> wrappedAnnotationKeyMap();
+ @Named("complexQualifier") Set<String> complexQualifierStringSet();
+ Set<Object> emptySet();
+
+ @Named("complexQualifier")
+ Set<Object> emptyQualifiedSet();
+
+ Map<String, Object> emptyMap();
+
+ @Named("complexQualifier")
+ Map<String, Object> emptyQualifiedMap();
+
+ Set<CharSequence> maybeEmptySet();
+
+ @Named("complexQualifier")
+ Set<CharSequence> maybeEmptyQualifiedSet();
+
+ Map<String, CharSequence> maybeEmptyMap();
+
+ @Named("complexQualifier")
+ Map<String, CharSequence> maybeEmptyQualifiedMap();
+}
diff --git a/javatests/dagger/functional/MultibindingDependency.java b/javatests/dagger/functional/MultibindingDependency.java
new file mode 100644
index 0000000..4240051
--- /dev/null
+++ b/javatests/dagger/functional/MultibindingDependency.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+interface MultibindingDependency {
+ double doubleDependency();
+}
diff --git a/javatests/dagger/functional/MultibindingModule.java b/javatests/dagger/functional/MultibindingModule.java
new file mode 100644
index 0000000..e167628
--- /dev/null
+++ b/javatests/dagger/functional/MultibindingModule.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import dagger.multibindings.LongKey;
+import dagger.multibindings.StringKey;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+@Module
+class MultibindingModule {
+ @Provides
+ @IntoMap
+ @StringKey("foo")
+ static String provideFooKey(@SuppressWarnings("unused") double doubleDependency) {
+ return "foo value";
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("bar")
+ static String provideBarKey() {
+ return "bar value";
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("foo")
+ static String[] provideFooArrayValue(@SuppressWarnings("unused") double doubleDependency) {
+ return new String[] {"foo1", "foo2"};
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("bar")
+ static String[] provideBarArrayValue() {
+ return new String[] {"bar1", "bar2"};
+ }
+
+ @Provides
+ @IntoSet
+ static int provideFiveToSet() {
+ return 5;
+ }
+
+ @Provides
+ @IntoSet
+ static int provideSixToSet() {
+ return 6;
+ }
+
+ @Provides
+ @ElementsIntoSet
+ static Set<Integer> provideElementsIntoSet() {
+ Set<Integer> set = new HashSet<>();
+ set.add(-101);
+ set.add(-102);
+ return set;
+ }
+
+ @Provides
+ static Set<String> provideMapKeys(Map<String, Provider<String>> map) {
+ return map.keySet();
+ }
+
+ @Provides
+ static Collection<String> provideMapValues(Map<String, String> map) {
+ return map.values();
+ }
+
+ @Provides
+ @IntoMap
+ @NestedAnnotationContainer.NestedWrappedKey(Integer.class)
+ static String valueForInteger() {
+ return "integer";
+ }
+
+ @Provides
+ @IntoMap
+ @NestedAnnotationContainer.NestedWrappedKey(Long.class)
+ static String valueForLong() {
+ return "long";
+ }
+
+ @Provides
+ @IntoMap
+ @ClassKey(Integer.class)
+ static String valueForClassInteger() {
+ return "integer";
+ }
+
+ @Provides
+ @IntoMap
+ @ClassKey(Long.class)
+ static String valueForClassLong() {
+ return "long";
+ }
+
+ @Provides
+ @IntoMap
+ @NumberClassKey(BigDecimal.class)
+ static String valueForNumberClassBigDecimal() {
+ return "bigdecimal";
+ }
+
+ @Provides
+ @IntoMap
+ @NumberClassKey(BigInteger.class)
+ static String valueForNumberClassBigInteger() {
+ return "biginteger";
+ }
+
+ @Provides
+ @IntoMap
+ @LongKey(100)
+ static String valueFor100Long() {
+ return "100 long";
+ }
+
+ @Provides
+ @IntoMap
+ @IntKey(100)
+ static String valueFor100Int() {
+ return "100 int";
+ }
+
+ @Provides
+ @IntoMap
+ @ShortKey(100)
+ static String valueFor100Short() {
+ return "100 short";
+ }
+
+ @Provides
+ @IntoMap
+ @ByteKey(100)
+ static String valueFor100Byte() {
+ return "100 byte";
+ }
+
+ @Provides
+ @IntoMap
+ @BooleanKey(true)
+ static String valueForTrue() {
+ return "true";
+ }
+
+ @Provides
+ @IntoMap
+ @CharKey('a')
+ static String valueForA() {
+ return "a char";
+ }
+
+ @Provides
+ @IntoMap
+ @CharKey('\n')
+ static String valueForNewline() {
+ return "newline char";
+ }
+
+ @Provides
+ @IntoMap
+ @UnwrappedAnnotationKey(@StringKey("foo\n"))
+ static String valueForUnwrappedAnnotationKeyFoo() {
+ return "foo annotation";
+ }
+
+ @Provides
+ @IntoMap
+ @WrappedAnnotationKey(
+ value = @StringKey("foo"),
+ integers = {1, 2, 3},
+ annotations = {},
+ classes = {Long.class, Integer.class}
+ )
+ static String valueForWrappedAnnotationKeyFoo() {
+ return "wrapped foo annotation";
+ }
+
+ @Provides
+ @IntoSet
+ @Named("complexQualifier")
+ static String valueForComplexQualifierSet() {
+ return "foo";
+ }
+
+ @Provides
+ @IntoSet
+ static CharSequence setContribution() {
+ return "foo";
+ }
+
+ @Provides
+ @IntoSet
+ @Named("complexQualifier")
+ static CharSequence qualifiedSetContribution() {
+ return "qualified foo";
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("key")
+ static CharSequence mapContribution() {
+ return "foo value";
+ }
+
+ @Provides
+ @IntoMap
+ @Named("complexQualifier")
+ @StringKey("key")
+ static CharSequence qualifiedMapContribution() {
+ return "qualified foo value";
+ }
+}
diff --git a/javatests/dagger/functional/MultibindingTest.java b/javatests/dagger/functional/MultibindingTest.java
new file mode 100644
index 0000000..d99e46d
--- /dev/null
+++ b/javatests/dagger/functional/MultibindingTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.value.AutoAnnotation;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.StringKey;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Map;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link MultibindingComponent}. */
+@RunWith(JUnit4.class)
+public class MultibindingTest {
+
+ private final MultibindingComponent multibindingComponent =
+ DaggerMultibindingComponent.builder().multibindingDependency(() -> 0.0).build();
+
+ @Test public void map() {
+ Map<String, String> map = multibindingComponent.map();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsEntry("foo", "foo value");
+ assertThat(map).containsEntry("bar", "bar value");
+ }
+
+ @Test public void mapOfArrays() {
+ Map<String, String[]> map = multibindingComponent.mapOfArrays();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsKey("foo");
+ assertThat(map.get("foo")).asList().containsExactly("foo1", "foo2").inOrder();
+ assertThat(map).containsKey("bar");
+ assertThat(map.get("bar")).asList().containsExactly("bar1", "bar2").inOrder();
+ }
+
+ @Test public void mapOfProviders() {
+ Map<String, Provider<String>> mapOfProviders = multibindingComponent.mapOfProviders();
+ assertThat(mapOfProviders).hasSize(2);
+ assertThat(mapOfProviders.get("foo").get()).isEqualTo("foo value");
+ assertThat(mapOfProviders.get("bar").get()).isEqualTo("bar value");
+ }
+
+ @Test public void mapKeysAndValues() {
+ assertThat(multibindingComponent.mapKeys())
+ .containsExactly("foo", "bar");
+ assertThat(multibindingComponent.mapValues())
+ .containsExactly("foo value", "bar value");
+ }
+
+ @Test public void nestedKeyMap() {
+ assertThat(multibindingComponent.nestedKeyMap())
+ .containsExactly(
+ nestedWrappedKey(Integer.class), "integer", nestedWrappedKey(Long.class), "long");
+ }
+
+ @Test
+ public void unwrappedAnnotationKeyMap() {
+ assertThat(multibindingComponent.unwrappedAnnotationKeyMap())
+ .containsExactly(testStringKey("foo\n"), "foo annotation");
+ }
+
+ @Test
+ public void wrappedAnnotationKeyMap() {
+ @SuppressWarnings("unchecked")
+ Class<? extends Number>[] classes = new Class[] {Long.class, Integer.class};
+ assertThat(multibindingComponent.wrappedAnnotationKeyMap())
+ .containsExactly(
+ testWrappedAnnotationKey(
+ testStringKey("foo"), new int[] {1, 2, 3}, new ClassKey[] {}, classes),
+ "wrapped foo annotation");
+ }
+
+ @Test
+ public void booleanKeyMap() {
+ assertThat(multibindingComponent.booleanKeyMap()).containsExactly(true, "true");
+ }
+
+ @Test
+ public void byteKeyMap() {
+ assertThat(multibindingComponent.byteKeyMap()).containsExactly((byte) 100, "100 byte");
+ }
+
+ @Test
+ public void charKeyMap() {
+ assertThat(multibindingComponent.characterKeyMap())
+ .containsExactly('a', "a char", '\n', "newline char");
+ }
+
+ @Test
+ public void classKeyMap() {
+ assertThat(multibindingComponent.classKeyMap())
+ .containsExactly(Integer.class, "integer", Long.class, "long");
+ }
+
+ @Test
+ public void numberClassKeyMap() {
+ assertThat(multibindingComponent.numberClassKeyMap())
+ .containsExactly(BigDecimal.class, "bigdecimal", BigInteger.class, "biginteger");
+ }
+
+ @Test
+ public void intKeyMap() {
+ assertThat(multibindingComponent.integerKeyMap()).containsExactly(100, "100 int");
+ }
+
+ @Test
+ public void longKeyMap() {
+ assertThat(multibindingComponent.longKeyMap()).containsExactly((long) 100, "100 long");
+ }
+
+ @Test
+ public void shortKeyMap() {
+ assertThat(multibindingComponent.shortKeyMap()).containsExactly((short) 100, "100 short");
+ }
+
+ @Test public void setBindings() {
+ assertThat(multibindingComponent.set())
+ .containsExactly(-90, -17, -1, 5, 6, 832, 1742, -101, -102);
+ }
+
+ @Test
+ public void complexQualifierSet() {
+ assertThat(multibindingComponent.complexQualifierStringSet()).containsExactly("foo");
+ }
+
+ @Test
+ public void emptySet() {
+ assertThat(multibindingComponent.emptySet()).isEmpty();
+ }
+
+ @Test
+ public void emptyQualifiedSet() {
+ assertThat(multibindingComponent.emptyQualifiedSet()).isEmpty();
+ }
+
+ @Test
+ public void emptyMap() {
+ assertThat(multibindingComponent.emptyMap()).isEmpty();
+ }
+
+ @Test
+ public void emptyQualifiedMap() {
+ assertThat(multibindingComponent.emptyQualifiedMap()).isEmpty();
+ }
+
+ @Test
+ public void maybeEmptySet() {
+ assertThat(multibindingComponent.maybeEmptySet()).containsExactly("foo");
+ }
+
+ @Test
+ public void maybeEmptyQualifiedSet() {
+ assertThat(multibindingComponent.maybeEmptyQualifiedSet()).containsExactly("qualified foo");
+ }
+
+ @Test
+ public void maybeEmptyMap() {
+ assertThat(multibindingComponent.maybeEmptyMap()).containsEntry("key", "foo value");
+ }
+
+ @Test
+ public void maybeEmptyQualifiedMap() {
+ assertThat(multibindingComponent.maybeEmptyQualifiedMap())
+ .containsEntry("key", "qualified foo value");
+ }
+
+ @AutoAnnotation
+ static StringKey testStringKey(String value) {
+ return new AutoAnnotation_MultibindingTest_testStringKey(value);
+ }
+
+ @AutoAnnotation
+ static NestedAnnotationContainer.NestedWrappedKey nestedWrappedKey(Class<?> value) {
+ return new AutoAnnotation_MultibindingTest_nestedWrappedKey(value);
+ }
+
+ @AutoAnnotation
+ static WrappedAnnotationKey testWrappedAnnotationKey(
+ StringKey value, int[] integers, ClassKey[] annotations, Class<? extends Number>[] classes) {
+ return new AutoAnnotation_MultibindingTest_testWrappedAnnotationKey(
+ value, integers, annotations, classes);
+ }
+}
diff --git a/javatests/dagger/functional/MultibindsModule.java b/javatests/dagger/functional/MultibindsModule.java
new file mode 100644
index 0000000..65ba7c8
--- /dev/null
+++ b/javatests/dagger/functional/MultibindsModule.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.multibindings.Multibinds;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Named;
+
+/**
+ * A module that uses {@link Multibinds @Multibinds}-annotated abstract methods to declare
+ * multibindings.
+ */
+@Module
+abstract class MultibindsModule {
+
+ @Multibinds
+ abstract Set<Object> emptySet();
+
+ @Multibinds
+ abstract Map<String, Object> emptyMap();
+
+ @Multibinds
+ abstract Set<CharSequence> set();
+
+ @Multibinds
+ abstract Map<String, CharSequence> map();
+
+ @Multibinds
+ @Named("complexQualifier")
+ abstract Set<Object> emptyQualifiedSet();
+
+ @Multibinds
+ @Named("complexQualifier")
+ abstract Map<String, Object> emptyQualifiedMap();
+
+ @Multibinds
+ @Named("complexQualifier")
+ abstract Set<CharSequence> qualifiedSet();
+
+ @Multibinds
+ @Named("complexQualifier")
+ abstract Map<String, CharSequence> qualifiedMap();
+}
diff --git a/javatests/dagger/functional/NeedsFactory.java b/javatests/dagger/functional/NeedsFactory.java
new file mode 100644
index 0000000..2ea01ec
--- /dev/null
+++ b/javatests/dagger/functional/NeedsFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import com.google.auto.factory.AutoFactory;
+import javax.inject.Inject;
+
+class NeedsFactory {
+ @Inject
+ NeedsFactory(@SuppressWarnings("unused") NeedsFactory_SomethingFactory somethingFactory) {}
+
+ @AutoFactory
+ static class Something {}
+}
+
diff --git a/javatests/dagger/functional/NeedsProviderOfFactory.java b/javatests/dagger/functional/NeedsProviderOfFactory.java
new file mode 100644
index 0000000..efbd73d
--- /dev/null
+++ b/javatests/dagger/functional/NeedsProviderOfFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import com.google.auto.factory.AutoFactory;
+import dagger.Component;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+// b/73455487
+class NeedsProviderOfFactory {
+ static class InjectsProviderOfFactory {
+ @Inject
+ InjectsProviderOfFactory(
+ Provider<NeedsProviderOfFactory_SomethingFactory> provider) {}
+ }
+
+ @Component
+ interface ExposesFactoryAsProvider {
+ InjectsProviderOfFactory injectsProviderOfFactory();
+ }
+
+ @AutoFactory
+ static class Something {}
+}
diff --git a/javatests/dagger/functional/NestedAnnotationContainer.java b/javatests/dagger/functional/NestedAnnotationContainer.java
new file mode 100644
index 0000000..a2b61fa
--- /dev/null
+++ b/javatests/dagger/functional/NestedAnnotationContainer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+
+public final class NestedAnnotationContainer {
+
+ @MapKey(unwrapValue = false)
+ @interface NestedWrappedKey {
+ Class<?> value();
+ }
+}
diff --git a/javatests/dagger/functional/NestedTest.java b/javatests/dagger/functional/NestedTest.java
new file mode 100644
index 0000000..c1e21b2
--- /dev/null
+++ b/javatests/dagger/functional/NestedTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NestedTest {
+ @Test public void nestedFoo() {
+ OuterClassFoo.NestedComponent nestedFoo = DaggerOuterClassFoo_NestedComponent.create();
+ assertThat(nestedFoo.thing()).isNotNull();
+ }
+
+ @Test public void nestedBar() {
+ OuterClassBar.NestedComponent nestedBar = DaggerOuterClassBar_NestedComponent.create();
+ assertThat(nestedBar.injectedThing()).isNotNull();
+ }
+}
diff --git a/javatests/dagger/functional/NonComponentDependencyComponent.java b/javatests/dagger/functional/NonComponentDependencyComponent.java
new file mode 100644
index 0000000..ee679fa
--- /dev/null
+++ b/javatests/dagger/functional/NonComponentDependencyComponent.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import dagger.functional.sub.OtherThing;
+import javax.inject.Inject;
+
+@Component(dependencies = {NonComponentDependencyComponent.ThingComponent.class})
+interface NonComponentDependencyComponent {
+ ThingTwo thingTwo();
+
+ static class ThingTwo {
+ @SuppressWarnings("unused")
+ @Inject
+ ThingTwo(
+ Thing thing,
+ NonComponentDependencyComponent nonComponentDependencyComponent,
+ NonComponentDependencyComponent.ThingComponent thingComponent) {}
+ }
+
+ // A non-component interface which this interface depends upon.
+ interface ThingComponent {
+ Thing thing();
+ }
+
+ // The implementation for that interface.
+ static class ThingComponentImpl implements ThingComponent {
+ @Override
+ public Thing thing() {
+ return new Thing(new OtherThing(1));
+ }
+ }
+}
diff --git a/javatests/dagger/functional/NonComponentDependencyTest.java b/javatests/dagger/functional/NonComponentDependencyTest.java
new file mode 100644
index 0000000..436183f
--- /dev/null
+++ b/javatests/dagger/functional/NonComponentDependencyTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NonComponentDependencyTest {
+ @Test public void testThing() {
+ NonComponentDependencyComponent component =
+ DaggerNonComponentDependencyComponent.builder()
+ .thingComponent(new NonComponentDependencyComponent.ThingComponentImpl())
+ .build();
+ assertThat(component).isNotNull();
+ assertThat(component.thingTwo()).isNotNull();
+ }
+}
diff --git a/javatests/dagger/functional/NullableModule.java b/javatests/dagger/functional/NullableModule.java
new file mode 100644
index 0000000..406024f
--- /dev/null
+++ b/javatests/dagger/functional/NullableModule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class NullableModule {
+ /**
+ * A {@code Nullable} that isn't {@link javax.annotation.Nullable}, to ensure that Dagger can be
+ * built without depending on JSR-305.
+ */
+ @interface Nullable {}
+
+ @Provides
+ @Nullable
+ static Object nullObject() {
+ return null;
+ }
+}
diff --git a/javatests/dagger/functional/NumberClassKey.java b/javatests/dagger/functional/NumberClassKey.java
new file mode 100644
index 0000000..5909a9f
--- /dev/null
+++ b/javatests/dagger/functional/NumberClassKey.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+
+@MapKey(unwrapValue = true)
+@interface NumberClassKey {
+ Class<? extends Number> value();
+}
diff --git a/javatests/dagger/functional/OuterClassBar.java b/javatests/dagger/functional/OuterClassBar.java
new file mode 100644
index 0000000..b455595
--- /dev/null
+++ b/javatests/dagger/functional/OuterClassBar.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+
+final class OuterClassBar {
+ @Component(modules = PrimitivesModule.class)
+ interface NestedComponent {
+ InjectedThing injectedThing();
+ }
+}
diff --git a/javatests/dagger/functional/OuterClassFoo.java b/javatests/dagger/functional/OuterClassFoo.java
new file mode 100644
index 0000000..213bd80
--- /dev/null
+++ b/javatests/dagger/functional/OuterClassFoo.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+
+final class OuterClassFoo {
+ @Component(modules = PrimitivesModule.class)
+ interface NestedComponent {
+ Thing thing();
+ }
+}
diff --git a/javatests/dagger/functional/ParentModule.java b/javatests/dagger/functional/ParentModule.java
new file mode 100644
index 0000000..8bd4fea
--- /dev/null
+++ b/javatests/dagger/functional/ParentModule.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@Module
+abstract class ParentModule<A extends Number & Comparable<A>, B, C extends Iterable<A>> {
+ @Provides Iterable<A> provideIterableOfAWithC(A a, C c) {
+ List<A> list = new ArrayList<>();
+ list.add(a);
+ for (A elt : c) {
+ list.add(elt);
+ }
+ return list;
+ }
+
+ @Provides static char provideNonGenericBindingInParameterizedModule() {
+ return 'c';
+ }
+
+ @Provides
+ static List<Set<String>> provideStaticGenericTypeWithNoTypeParametersInParameterizedModule() {
+ return new ArrayList<>();
+ }
+}
diff --git a/javatests/dagger/functional/PrimitivesModule.java b/javatests/dagger/functional/PrimitivesModule.java
new file mode 100644
index 0000000..7b7203b
--- /dev/null
+++ b/javatests/dagger/functional/PrimitivesModule.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class PrimitivesModule {
+ static final byte BOUND_BYTE = -41;
+ static final char BOUND_CHAR = 'g';
+ static final short BOUND_SHORT = 21840;
+ static final int BOUND_INT = 1894833693;
+ static final long BOUND_LONG = -4369839828653523584L;
+ static final boolean BOUND_BOOLEAN = true;
+ static final float BOUND_FLOAT = (float) 0.9964542;
+ static final double BOUND_DOUBLE = 0.12681322049667765;
+
+ /*
+ * While we can't ensure that these constants stay constant, this is a test so we're just going to
+ * keep our fingers crossed that we're not going to be jerks.
+ */
+ static final byte[] BOUND_BYTE_ARRAY = {1, 2, 3};
+ static final char[] BOUND_CHAR_ARRAY = {'g', 'a', 'k'};
+ static final short[] BOUND_SHORT_ARRAY = {2, 4};
+ static final int[] BOUND_INT_ARRAY = {3, 1, 2};
+ static final long[] BOUND_LONG_ARRAY = {1, 1, 2, 3, 5};
+ static final boolean[] BOUND_BOOLEAN_ARRAY = {false, true, false, false};
+ static final float[] BOUND_FLOAT_ARRAY = {(float) 0.1, (float) 0.01, (float) 0.001};
+ static final double[] BOUND_DOUBLE_ARRAY = {0.2, 0.02, 0.002};
+
+ @Provides static byte provideByte() {
+ return BOUND_BYTE;
+ }
+
+ @Provides static char provideChar() {
+ return BOUND_CHAR;
+ }
+
+ @Provides static short provideShort() {
+ return BOUND_SHORT;
+ }
+
+ @Provides static int provideInt() {
+ return BOUND_INT;
+ }
+
+ @Provides static long provideLong() {
+ return BOUND_LONG;
+ }
+
+ @Provides static boolean provideBoolean() {
+ return BOUND_BOOLEAN;
+ }
+
+ @Provides static float provideFloat() {
+ return BOUND_FLOAT;
+ }
+
+ @Provides static double boundDouble() {
+ return BOUND_DOUBLE;
+ }
+
+ @Provides static byte[] provideByteArray() {
+ return BOUND_BYTE_ARRAY;
+ }
+
+ @Provides static char[] provideCharArray() {
+ return BOUND_CHAR_ARRAY;
+ }
+
+ @Provides static short[] provideShortArray() {
+ return BOUND_SHORT_ARRAY;
+ }
+
+ @Provides static int[] provideIntArray() {
+ return BOUND_INT_ARRAY;
+ }
+
+ @Provides static long[] provideLongArray() {
+ return BOUND_LONG_ARRAY;
+ }
+
+ @Provides static boolean[] provideBooleanArray() {
+ return BOUND_BOOLEAN_ARRAY;
+ }
+
+ @Provides static float[] provideFloatArray() {
+ return BOUND_FLOAT_ARRAY;
+ }
+
+ @Provides static double[] boundDoubleArray() {
+ return BOUND_DOUBLE_ARRAY;
+ }
+}
diff --git a/javatests/dagger/functional/ReferencesGeneric.java b/javatests/dagger/functional/ReferencesGeneric.java
new file mode 100644
index 0000000..c83dbcc
--- /dev/null
+++ b/javatests/dagger/functional/ReferencesGeneric.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+class ReferencesGeneric {
+ final Generic<A> genericA;
+
+ @Inject ReferencesGeneric(Generic<A> genericA) {
+ this.genericA = genericA;
+ }
+}
diff --git a/javatests/dagger/functional/ReusableTest.java b/javatests/dagger/functional/ReusableTest.java
new file mode 100644
index 0000000..221b288
--- /dev/null
+++ b/javatests/dagger/functional/ReusableTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.ComponentWithReusableBindings.ChildOne;
+import dagger.functional.ComponentWithReusableBindings.ChildTwo;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ReusableTest {
+ @Test
+ public void testReusable() {
+ ComponentWithReusableBindings parent = DaggerComponentWithReusableBindings.create();
+ ChildOne childOne = parent.childOne();
+ ChildTwo childTwo = parent.childTwo();
+
+ Object reusableInParent = parent.reusableInParent();
+ assertThat(parent.reusableInParent()).isSameInstanceAs(reusableInParent);
+ assertThat(childOne.reusableInParent()).isSameInstanceAs(reusableInParent);
+ assertThat(childTwo.reusableInParent()).isSameInstanceAs(reusableInParent);
+
+ Object reusableFromChildOne = childOne.reusableInChild();
+ assertThat(childOne.reusableInChild()).isSameInstanceAs(reusableFromChildOne);
+
+ Object reusableFromChildTwo = childTwo.reusableInChild();
+ assertThat(childTwo.reusableInChild()).isSameInstanceAs(reusableFromChildTwo);
+
+ assertThat(reusableFromChildTwo).isNotSameInstanceAs(reusableFromChildOne);
+ }
+}
diff --git a/javatests/dagger/functional/ScopedGeneric.java b/javatests/dagger/functional/ScopedGeneric.java
new file mode 100644
index 0000000..da7e157
--- /dev/null
+++ b/javatests/dagger/functional/ScopedGeneric.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
+class ScopedGeneric<T> {
+ final T t;
+ @Inject ScopedGeneric(T t) {
+ this.t = t;
+ }
+}
diff --git a/javatests/dagger/functional/ScopedSimpleGeneric.java b/javatests/dagger/functional/ScopedSimpleGeneric.java
new file mode 100644
index 0000000..8e48eba
--- /dev/null
+++ b/javatests/dagger/functional/ScopedSimpleGeneric.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * An {@link Inject Inject}ed generic class with no dependencies. Its factory class will have a
+ * generic {@code create()} method returning an object whose type parameters cannot be inferred from
+ * its arguments. Since it's scoped, the initialization of its field in a generated component must
+ * use a raw {@link javax.inject.Provider} in order to allow casting from {@code
+ * Provider<ScopedSimpleGeneric<Object>>} to {@code Provider<ScopedSimpleGeneric<Foo>>}.
+ */
+@Singleton
+class ScopedSimpleGeneric<T> {
+ @Inject
+ ScopedSimpleGeneric() {}
+}
diff --git a/javatests/dagger/functional/ShortKey.java b/javatests/dagger/functional/ShortKey.java
new file mode 100644
index 0000000..3d81d8f
--- /dev/null
+++ b/javatests/dagger/functional/ShortKey.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+
+@MapKey(unwrapValue = true)
+@interface ShortKey {
+ short value();
+}
diff --git a/javatests/dagger/functional/SingletonGenericComponent.java b/javatests/dagger/functional/SingletonGenericComponent.java
new file mode 100644
index 0000000..d9823cd
--- /dev/null
+++ b/javatests/dagger/functional/SingletonGenericComponent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.Component;
+import javax.inject.Singleton;
+
+@Singleton
+@Component
+interface SingletonGenericComponent {
+
+ ScopedGeneric<A> scopedGenericA();
+
+ ScopedGeneric<B> scopedGenericB();
+
+ ScopedSimpleGeneric<A> scopedSimpleGenericA();
+
+ ScopedSimpleGeneric<B> scopedSimpleGenericB();
+
+}
diff --git a/javatests/dagger/functional/SomeQualifier.java b/javatests/dagger/functional/SomeQualifier.java
new file mode 100644
index 0000000..126398c
--- /dev/null
+++ b/javatests/dagger/functional/SomeQualifier.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+@Documented
+@Retention(RUNTIME)
+@Qualifier
+public @interface SomeQualifier {}
diff --git a/javatests/dagger/functional/Thing.java b/javatests/dagger/functional/Thing.java
new file mode 100644
index 0000000..b51b96a
--- /dev/null
+++ b/javatests/dagger/functional/Thing.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.functional.sub.OtherThing;
+import javax.inject.Inject;
+
+final class Thing {
+ @Inject Thing(@SuppressWarnings("unused") OtherThing unused) {}
+}
diff --git a/javatests/dagger/functional/TypeWithInheritedMembersInjection.java b/javatests/dagger/functional/TypeWithInheritedMembersInjection.java
new file mode 100644
index 0000000..b74f348
--- /dev/null
+++ b/javatests/dagger/functional/TypeWithInheritedMembersInjection.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import javax.inject.Inject;
+
+final class TypeWithInheritedMembersInjection extends AbstractMiddleClassWithoutMembers {
+ @Inject TypeWithInheritedMembersInjection() {}
+}
+
diff --git a/javatests/dagger/functional/UnwrappedAnnotationKey.java b/javatests/dagger/functional/UnwrappedAnnotationKey.java
new file mode 100644
index 0000000..e9298f2
--- /dev/null
+++ b/javatests/dagger/functional/UnwrappedAnnotationKey.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+import dagger.multibindings.StringKey;
+
+@MapKey(unwrapValue = true)
+@interface UnwrappedAnnotationKey {
+ StringKey value();
+}
diff --git a/javatests/dagger/functional/WrappedAnnotationKey.java b/javatests/dagger/functional/WrappedAnnotationKey.java
new file mode 100644
index 0000000..0256111
--- /dev/null
+++ b/javatests/dagger/functional/WrappedAnnotationKey.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import dagger.MapKey;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.StringKey;
+
+@MapKey(unwrapValue = false)
+@interface WrappedAnnotationKey {
+ StringKey value();
+ int[] integers();
+ ClassKey[] annotations();
+ Class<? extends Number>[] classes();
+}
diff --git a/javatests/dagger/functional/aot/DependsOnMissingArrayKey.java b/javatests/dagger/functional/aot/DependsOnMissingArrayKey.java
new file mode 100644
index 0000000..20a89d4
--- /dev/null
+++ b/javatests/dagger/functional/aot/DependsOnMissingArrayKey.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Regression test for an ahead-of-time subcomponents bug where generating the name for a missing
+ * binding method for a key of an array type threw an exception.
+ */
+final class DependsOnMissingArrayKey {
+ @Module
+ abstract static class ModuleArrayDependencies {
+ @Provides
+ static int dependsOnMissingArrayType(int[] primitive, Object[] object, String[][] doubleArray) {
+ return 0;
+ }
+ }
+
+ @Subcomponent(modules = ModuleArrayDependencies.class)
+ interface HasMissingArrayBindings {
+ int dependsOnMissingArrayType();
+ }
+}
diff --git a/javatests/dagger/functional/aot/MapFrameworkInstanceWithContributionsInMultipleImplementationsTest.java b/javatests/dagger/functional/aot/MapFrameworkInstanceWithContributionsInMultipleImplementationsTest.java
new file mode 100644
index 0000000..ae6b3a4
--- /dev/null
+++ b/javatests/dagger/functional/aot/MapFrameworkInstanceWithContributionsInMultipleImplementationsTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.StringKey;
+import java.util.Map;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests that framework instances of map bindings are properly instantiated in ahead-of-time mode
+ * when contributions are made in 3 or more implementations.
+ */
+@RunWith(JUnit4.class)
+public final class MapFrameworkInstanceWithContributionsInMultipleImplementationsTest {
+ @Subcomponent(modules = LeafModule.class)
+ interface Leaf {
+ Provider<Map<String, String>> providerOfMapOfValues();
+ Provider<Map<String, Provider<String>>> providerOfMapOfProviders();
+ }
+
+ @Module
+ interface LeafModule {
+ @Provides
+ @IntoMap
+ @StringKey("a")
+ static String fromLeaf() {
+ return "a";
+ }
+ }
+
+ @Subcomponent(modules = AncestorModule.class)
+ interface Ancestor {
+ Leaf leaf();
+ }
+
+ @Module
+ interface AncestorModule {
+ @Provides
+ @IntoMap
+ @StringKey("b")
+ static String fromAncestor() {
+ return "b";
+ }
+ }
+
+ @Component(modules = RootModule.class)
+ interface Root {
+ Ancestor ancestor();
+ }
+
+ @Module
+ interface RootModule {
+ @Provides
+ @IntoMap
+ @StringKey("c")
+ static String fromRoot() {
+ return "c";
+ }
+ }
+
+ @Test
+ public void mapFactoryCanBeInstantiatedAcrossComponentImplementations() {
+ Leaf leaf =
+ DaggerMapFrameworkInstanceWithContributionsInMultipleImplementationsTest_Root.create()
+ .ancestor()
+ .leaf();
+ assertThat(leaf.providerOfMapOfValues().get()).hasSize(3);
+ assertThat(leaf.providerOfMapOfProviders().get()).hasSize(3);
+ }
+}
diff --git a/javatests/dagger/functional/aot/MissingBindingReplacedWithGeneratedInstance.java b/javatests/dagger/functional/aot/MissingBindingReplacedWithGeneratedInstance.java
new file mode 100644
index 0000000..1813ad2
--- /dev/null
+++ b/javatests/dagger/functional/aot/MissingBindingReplacedWithGeneratedInstance.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Subcomponent;
+import javax.inject.Inject;
+
+/**
+ * This class demonstrates a regression where a missing binding method was generated in a leaf
+ * component and then satisfied in an ancestor with a generated instance binding. If the ancestor's
+ * generated instance method had the same name as the formerly-missing binding method, Dagger would
+ * generate code without a proper {@code DaggerOuter.this} reference:
+ *
+ * <pre>{@code
+ * public class DaggerAncestor implements Ancestor {
+ * protected abstract Ancestor getAncestor();
+ *
+ * protected abstract class LeafImpl extends DaggerLeaf {
+ * {@literal @Override}
+ * protected final Ancestor getAncestor() {
+ * return getAncestor();
+ * // ^ should be DaggerAncestor.this.getAncestor()
+ * }
+ * }
+ * }
+ * }</pre>
+ */
+final class MissingBindingReplacedWithGeneratedInstance {
+ @Subcomponent
+ interface Leaf {
+ DependsOnGeneratedInstance dependsOnGeneratedInstance();
+ }
+
+ static class DependsOnGeneratedInstance {
+ @Inject DependsOnGeneratedInstance(Ancestor generatedInstance) {}
+ }
+
+ @Subcomponent
+ interface Ancestor {
+ Leaf child();
+ }
+}
diff --git a/javatests/dagger/functional/aot/ModifiedFrameworkInstancesTest.java b/javatests/dagger/functional/aot/ModifiedFrameworkInstancesTest.java
new file mode 100644
index 0000000..7084f83
--- /dev/null
+++ b/javatests/dagger/functional/aot/ModifiedFrameworkInstancesTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.multibindings.IntoSet;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ModifiedFrameworkInstancesTest {
+ static class DependsOnModifiableBinding {
+ @Inject
+ DependsOnModifiableBinding(Set<Integer> modifiableDependency) {}
+ }
+
+ @Module
+ interface ChildModule {
+ @Provides
+ @IntoSet
+ static int contribution() {
+ return 1;
+ }
+ }
+
+ @Subcomponent(modules = ChildModule.class)
+ interface Child {
+ Provider<DependsOnModifiableBinding> frameworkInstanceWithModifiedDependency();
+ }
+
+ @Module
+ interface ParentModule {
+ @Provides
+ @IntoSet
+ static int contribution() {
+ return 2;
+ }
+ }
+
+ @Component(modules = ParentModule.class)
+ interface Parent {
+ Child child();
+ }
+
+ @Test
+ public void dependsOnModifiedFrameworkInstance() {
+ DaggerModifiedFrameworkInstancesTest_Parent.create()
+ .child()
+ .frameworkInstanceWithModifiedDependency()
+ // Ensure that modified framework instances that are dependencies to other framework
+ // instances from superclass implementations are initialized correctly. This fixes a
+ // regression where a null instance would be passed to the superclass initialization, and
+ // then a NullPointerException would be thrown when the factory attempted to satisfy the
+ // dependency in get(). If get() succeeds, this test should pass.
+ .get();
+ }
+}
diff --git a/javatests/dagger/functional/aot/PrunedBindingDependedOnInSuperInitializationTest.java b/javatests/dagger/functional/aot/PrunedBindingDependedOnInSuperInitializationTest.java
new file mode 100644
index 0000000..853e22b
--- /dev/null
+++ b/javatests/dagger/functional/aot/PrunedBindingDependedOnInSuperInitializationTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PrunedBindingDependedOnInSuperInitializationTest {
+ interface PrunedDependency {}
+
+ static class WillHavePrunedDependency {
+ @Inject WillHavePrunedDependency(PrunedDependency pruned) {}
+ }
+
+ @Subcomponent
+ interface Child {
+ Provider<WillHavePrunedDependency> frameworkInstance();
+ }
+
+ @Module
+ static class ParentModule {
+ @Provides
+ static WillHavePrunedDependency pruneDependency() {
+ return new WillHavePrunedDependency(new PrunedDependency() {});
+ }
+ }
+
+ @Component(modules = ParentModule.class)
+ interface Parent {
+ Child child();
+ }
+
+ @Test
+ public void prunedFrameworkInstanceBindingUsedInInitializationDoesntThrow() {
+ Parent parent = DaggerPrunedBindingDependedOnInSuperInitializationTest_Parent.create();
+ // This test ensures that pruned bindings that are used during unpruned initialization
+ // statements do not throw exceptions. If the subcomponent initialization succeeds, the test
+ // should pass
+ parent.child();
+ }
+}
diff --git a/javatests/dagger/functional/aot/PrunedFrameworkInstanceWithModuleInstanceTest.java b/javatests/dagger/functional/aot/PrunedFrameworkInstanceWithModuleInstanceTest.java
new file mode 100644
index 0000000..f995789
--- /dev/null
+++ b/javatests/dagger/functional/aot/PrunedFrameworkInstanceWithModuleInstanceTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PrunedFrameworkInstanceWithModuleInstanceTest {
+ static class Pruned {}
+
+ static class InjectsPruned {
+ @Inject
+ InjectsPruned(Provider<Pruned> pruned) {}
+ }
+
+ @Module
+ static class InstanceStateModule {
+ @Provides
+ /* intentionally not static */ Pruned pruned() {
+ return new Pruned();
+ }
+ }
+
+ @Subcomponent(modules = InstanceStateModule.class)
+ interface LeafWithoutCreator {
+ InjectsPruned injectsPruned();
+ }
+
+ @Subcomponent(modules = InstanceStateModule.class)
+ interface LeafWithCreator {
+ InjectsPruned injectsPruned();
+
+ @Subcomponent.Builder
+ interface Builder {
+ Builder module(InstanceStateModule module);
+ LeafWithCreator build();
+ }
+ }
+
+ @Module
+ interface RootModule {
+ @Provides
+ static InjectsPruned pruneBindingWithInstanceState() {
+ return new InjectsPruned(null);
+ }
+ }
+
+ @Component(modules = RootModule.class)
+ interface Root {
+ LeafWithoutCreator leafWithoutCreator(InstanceStateModule pruned);
+ LeafWithCreator.Builder leafWithCreator();
+ }
+
+ @Test
+ public void prunedBindingWithModuleInstance_doesntThrowDuringInitialization() {
+ Root root = DaggerPrunedFrameworkInstanceWithModuleInstanceTest_Root.create();
+
+ Object unused = root.leafWithoutCreator(new InstanceStateModule()).injectsPruned();
+ unused = root.leafWithCreator().module(new InstanceStateModule()).build().injectsPruned();
+ }
+}
diff --git a/javatests/dagger/functional/aot/ScopedBindsWithMissingDependency.java b/javatests/dagger/functional/aot/ScopedBindsWithMissingDependency.java
new file mode 100644
index 0000000..2723ac0
--- /dev/null
+++ b/javatests/dagger/functional/aot/ScopedBindsWithMissingDependency.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Reusable;
+import dagger.Subcomponent;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Scope;
+
+/**
+ * A regression test for ahead-of-time subcomponents mode where a scoped {@link Binds} method whose
+ * dependency was missing in a partial subcomponent implementation threw an exception in the
+ * processor.
+ */
+final class ScopedBindsWithMissingDependency {
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Scope
+ @interface CustomScope {}
+
+ @Module
+ interface ScopedBindsWithMissingDependencyModule {
+ @Binds
+ @CustomScope
+ Object bindsCustomScopeToMissingDep(String missingDependency);
+
+ @Binds
+ @Reusable
+ CharSequence bindsReusableScopeToMissingDep(String missingDependency);
+ }
+
+ @CustomScope
+ @Subcomponent(modules = ScopedBindsWithMissingDependencyModule.class)
+ interface HasScopedBindsWithMissingDependency {
+ Object customScopedBindsWithMissingDependency();
+ CharSequence reusableScopedBindsWithMissingDependency();
+ }
+}
diff --git a/javatests/dagger/functional/aot/SubcomponentWithInaccessibleMissingBindingMethod.java b/javatests/dagger/functional/aot/SubcomponentWithInaccessibleMissingBindingMethod.java
new file mode 100644
index 0000000..689689c
--- /dev/null
+++ b/javatests/dagger/functional/aot/SubcomponentWithInaccessibleMissingBindingMethod.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Subcomponent;
+import dagger.functional.aot.sub.PublicTypeWithPackagePrivateMissingDep;
+import javax.inject.Provider;
+
+@Subcomponent
+interface SubcomponentWithInaccessibleMissingBindingMethod {
+ PublicTypeWithPackagePrivateMissingDep instance();
+ Provider<PublicTypeWithPackagePrivateMissingDep> frameworkInstance();
+}
diff --git a/javatests/dagger/functional/aot/SubcomponentWithModifiedInaccessibleDependency.java b/javatests/dagger/functional/aot/SubcomponentWithModifiedInaccessibleDependency.java
new file mode 100644
index 0000000..06cebb9
--- /dev/null
+++ b/javatests/dagger/functional/aot/SubcomponentWithModifiedInaccessibleDependency.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot;
+
+import dagger.Subcomponent;
+import dagger.functional.aot.sub.BindsPackagePrivateModule;
+import dagger.functional.aot.sub.SubcomponentWithInaccessibleOptionalBindingMethod;
+
+/**
+ * See {@link dagger.functional.aot.sub.SubcomponentWithInaccessibleOptionalBindingMethod}. This
+ * subcomponent will induce a modified binding method for its single child for the key {@code
+ * Optional<dagger.functional.aot.sub.PackagePrivate>}. When it tries to reimplement it, it must use
+ * the publicly accessible type.
+ */
+@Subcomponent(modules = BindsPackagePrivateModule.class)
+interface SubcomponentWithModifiedInaccessibleDependency {
+ SubcomponentWithInaccessibleOptionalBindingMethod child();
+}
diff --git a/javatests/dagger/functional/aot/sub/BindsPackagePrivateModule.java b/javatests/dagger/functional/aot/sub/BindsPackagePrivateModule.java
new file mode 100644
index 0000000..2eee3c9
--- /dev/null
+++ b/javatests/dagger/functional/aot/sub/BindsPackagePrivateModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot.sub;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public final class BindsPackagePrivateModule {
+ @Provides
+ static PackagePrivate packagePrivate() {
+ return new PackagePrivate();
+ }
+}
diff --git a/javatests/dagger/functional/aot/sub/PackagePrivate.java b/javatests/dagger/functional/aot/sub/PackagePrivate.java
new file mode 100644
index 0000000..c629f6e
--- /dev/null
+++ b/javatests/dagger/functional/aot/sub/PackagePrivate.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot.sub;
+
+final class PackagePrivate {}
diff --git a/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateMissingDep.java b/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateMissingDep.java
new file mode 100644
index 0000000..b5ced6f
--- /dev/null
+++ b/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateMissingDep.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot.sub;
+
+import javax.inject.Inject;
+
+public class PublicTypeWithPackagePrivateMissingDep {
+ @Inject
+ PublicTypeWithPackagePrivateMissingDep(PackagePrivate packagePrivate) {}
+}
diff --git a/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateOptionalDep.java b/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateOptionalDep.java
new file mode 100644
index 0000000..1d69723
--- /dev/null
+++ b/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateOptionalDep.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot.sub;
+
+import java.util.Optional;
+import javax.inject.Inject;
+
+public class PublicTypeWithPackagePrivateOptionalDep {
+ @Inject
+ PublicTypeWithPackagePrivateOptionalDep(Optional<PackagePrivate> packagePrivateOptional) {}
+}
diff --git a/javatests/dagger/functional/aot/sub/SubcomponentWithInaccessibleOptionalBindingMethod.java b/javatests/dagger/functional/aot/sub/SubcomponentWithInaccessibleOptionalBindingMethod.java
new file mode 100644
index 0000000..d908b1c
--- /dev/null
+++ b/javatests/dagger/functional/aot/sub/SubcomponentWithInaccessibleOptionalBindingMethod.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.aot.sub;
+
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.functional.aot.sub.SubcomponentWithInaccessibleOptionalBindingMethod.ExposesModifiablePackagePrivateBindingModule;
+import javax.inject.Provider;
+
+/**
+ * This component will generate a modifiable binding method for the key {@code
+ * Optional<PackagePrivate>} as a dependency of {@link PublicTypeWithPackagePrivateOptionalDep}.
+ * Even though this subcomponent implementation can refer to the parameterized type, a subclass
+ * implementation in another package will not be able to, and thus the return type must be reduced
+ * to the publicly accessible type. This is exhibited in {@link
+ * dagger.functional.aot.SubcomponentWithModifiedInaccessibleDependency}.
+ */
+@Subcomponent(modules = ExposesModifiablePackagePrivateBindingModule.class)
+public interface SubcomponentWithInaccessibleOptionalBindingMethod {
+ PublicTypeWithPackagePrivateOptionalDep instance();
+ Provider<PublicTypeWithPackagePrivateOptionalDep> frameworkInstance();
+
+ @Module
+ interface ExposesModifiablePackagePrivateBindingModule {
+ @BindsOptionalOf
+ PackagePrivate optional();
+ }
+}
diff --git a/javatests/dagger/functional/binds/AccessesExposedComponent.java b/javatests/dagger/functional/binds/AccessesExposedComponent.java
new file mode 100644
index 0000000..61952b6
--- /dev/null
+++ b/javatests/dagger/functional/binds/AccessesExposedComponent.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import dagger.Component;
+import dagger.functional.binds.subpackage.Exposed;
+import dagger.functional.binds.subpackage.ExposedModule;
+import dagger.functional.binds.subpackage.UsesExposedInjectsMembers;
+import java.util.List;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+/**
+ * This component tests cases where the right-hand-side of a {@link dagger.Binds} method is not
+ * accessible from the component, but the left-hand-side is. If the right-hand-side is represented
+ * as a Provider (e.g. because it is scoped), then the raw {@code Provider.get()} will return {@link
+ * Object}, which must be downcasted to the type accessible from the component. See {@code
+ * instanceRequiresCast()} in {@link dagger.internal.codegen.DelegateBindingExpression}.
+ */
+@Singleton
+@Component(modules = ExposedModule.class)
+interface AccessesExposedComponent {
+ Exposed exposed();
+ Provider<Exposed> exposedProvider();
+
+ List<? extends Exposed> listOfExposed();
+ Provider<List<? extends Exposed>> providerOfListOfExposed();
+
+ UsesExposedInjectsMembers usesExposedInjectsMembers();
+
+ /**
+ * This provider needs a {@code Provider<ExposedInjectsMembers>}, which is bound to a {@code
+ * Provider<NotExposedInjectsMembers>}. This method is here to make sure that the cast happens
+ * appropriately.
+ */
+ Provider<UsesExposedInjectsMembers> usesExposedInjectsMembersProvider();
+}
diff --git a/javatests/dagger/functional/binds/BindsCollectionsWithoutMultibindingsTest.java b/javatests/dagger/functional/binds/BindsCollectionsWithoutMultibindingsTest.java
new file mode 100644
index 0000000..90b1bbc
--- /dev/null
+++ b/javatests/dagger/functional/binds/BindsCollectionsWithoutMultibindingsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BindsCollectionsWithoutMultibindingsTest {
+ @Module
+ abstract static class M {
+ @Provides
+ static HashSet<String> provideHashSet() {
+ HashSet<String> set = new HashSet<>();
+ set.add("binds");
+ set.add("set");
+ return set;
+ }
+
+ @Binds
+ abstract Set<String> bindStringSet(HashSet<String> set);
+
+ @Provides
+ static HashMap<String, String> provideHashMap() {
+ HashMap<String, String> map = new HashMap<>();
+ map.put("binds", "map");
+ map.put("without", "multibindings");
+ return map;
+ }
+
+ @Binds
+ abstract Map<String, String> bindStringMap(HashMap<String, String> map);
+ }
+
+ @Component(modules = M.class)
+ interface C {
+ Set<String> set();
+
+ Map<String, String> map();
+ }
+
+ @Test
+ public void works() {
+ C component = DaggerBindsCollectionsWithoutMultibindingsTest_C.create();
+
+ assertThat(component.set()).containsExactly("binds", "set");
+ assertThat(component.map())
+ .containsExactly(
+ "binds", "map",
+ "without", "multibindings");
+ }
+}
diff --git a/javatests/dagger/functional/binds/BindsTest.java b/javatests/dagger/functional/binds/BindsTest.java
new file mode 100644
index 0000000..999c303
--- /dev/null
+++ b/javatests/dagger/functional/binds/BindsTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BindsTest {
+
+ private TestComponent component;
+
+ @Before
+ public void setUp() {
+ component = DaggerTestComponent.create();
+ }
+
+ @Test
+ public void bindDelegates() {
+ assertThat(component.object()).isInstanceOf(FooOfStrings.class);
+ assertThat(component.fooOfStrings()).isInstanceOf(FooOfStrings.class);
+ assertThat(component.fooOfObjects()).isInstanceOf(FooOfObjects.class);
+ assertThat(component.fooOfIntegers()).isNotNull();
+ }
+
+ @Test
+ public void bindWithScope() {
+ assertThat(component.qualifiedFooOfStrings())
+ .isSameInstanceAs(component.qualifiedFooOfStrings());
+ }
+
+ @Test
+ public void multibindings() {
+ assertThat(component.foosOfNumbers()).hasSize(2);
+ assertThat(component.objects()).hasSize(3);
+ assertThat(component.charSequences()).hasSize(5);
+
+ assertThat(component.integerObjectMap())
+ .containsExactly(123, "123-string", 456, "456-string", 789, "789-string");
+ assertThat(component.integerProviderOfObjectMap()).hasSize(3);
+ assertThat(component.integerProviderOfObjectMap().get(123).get()).isEqualTo("123-string");
+ assertThat(component.integerProviderOfObjectMap().get(456).get()).isEqualTo("456-string");
+ assertThat(component.integerProviderOfObjectMap().get(789).get()).isEqualTo("789-string");
+
+ assertThat(component.qualifiedIntegerObjectMap()).hasSize(1);
+
+ assertThat(component.primitiveSet()).containsExactly(100);
+ assertThat(component.primitiveValueMap()).containsExactly(10, 100);
+ }
+}
diff --git a/javatests/dagger/functional/binds/Foo.java b/javatests/dagger/functional/binds/Foo.java
new file mode 100644
index 0000000..12b0e4e
--- /dev/null
+++ b/javatests/dagger/functional/binds/Foo.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+/**
+ * This is the type that will be bound. We throw in generics just to complicate the test.
+ */
+interface Foo<T> {}
diff --git a/javatests/dagger/functional/binds/FooOfObjects.java b/javatests/dagger/functional/binds/FooOfObjects.java
new file mode 100644
index 0000000..c50f2fa
--- /dev/null
+++ b/javatests/dagger/functional/binds/FooOfObjects.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import javax.inject.Inject;
+
+final class FooOfObjects implements Foo<Object> {
+ @Inject FooOfObjects() {}
+}
diff --git a/javatests/dagger/functional/binds/FooOfStrings.java b/javatests/dagger/functional/binds/FooOfStrings.java
new file mode 100644
index 0000000..42fc704
--- /dev/null
+++ b/javatests/dagger/functional/binds/FooOfStrings.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import javax.inject.Inject;
+
+final class FooOfStrings implements Foo<String> {
+ @Inject
+ FooOfStrings() {}
+}
diff --git a/javatests/dagger/functional/binds/InterfaceModule.java b/javatests/dagger/functional/binds/InterfaceModule.java
new file mode 100644
index 0000000..e9d36c0
--- /dev/null
+++ b/javatests/dagger/functional/binds/InterfaceModule.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+interface InterfaceModule {
+ @Binds Foo<Object> bindFooOfObjects(FooOfObjects impl);
+}
diff --git a/javatests/dagger/functional/binds/SimpleBindingModule.java b/javatests/dagger/functional/binds/SimpleBindingModule.java
new file mode 100644
index 0000000..e1d5227
--- /dev/null
+++ b/javatests/dagger/functional/binds/SimpleBindingModule.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Reusable;
+import dagger.functional.SomeQualifier;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Module(includes = InterfaceModule.class)
+abstract class SimpleBindingModule {
+ @Binds
+ abstract Object bindObject(FooOfStrings impl);
+
+ @Binds
+ @Reusable
+ @SomeQualifier
+ abstract Object bindReusableObject(FooOfStrings impl);
+
+ @Binds
+ abstract Foo<String> bindFooOfStrings(FooOfStrings impl);
+
+ @Binds
+ abstract Foo<? extends Number> bindFooOfNumbers(Foo<Integer> fooOfIntegers);
+
+ @Binds
+ @Singleton
+ @SomeQualifier
+ abstract Foo<String> bindQualifiedFooOfStrings(FooOfStrings impl);
+
+ @Provides
+ static Foo<Integer> provideFooOfIntegers() {
+ return new Foo<Integer>() {};
+ }
+
+ @Provides
+ static Foo<Double> provideFooOfDoubles() {
+ return new Foo<Double>() {};
+ }
+
+ @Binds
+ @IntoSet
+ abstract Foo<? extends Number> bindFooOfIntegersIntoSet(Foo<Integer> fooOfIntegers);
+
+ @Binds
+ @IntoSet
+ abstract Foo<? extends Number> bindFooExtendsNumberIntoSet(Foo<Double> fooOfDoubles);
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<Object> bindSetOfFooNumbersToObjects(Set<Foo<? extends Number>> setOfFooNumbers);
+
+ @Binds
+ @IntoSet
+ abstract Object bindFooOfStringsIntoSetOfObjects(FooOfStrings impl);
+
+ @Provides
+ static HashSet<String> provideStringHashSet() {
+ return new HashSet<>(Arrays.asList("hash-string1", "hash-string2"));
+ }
+
+ @Provides
+ static TreeSet<CharSequence> provideCharSequenceTreeSet() {
+ return new TreeSet<CharSequence>(Arrays.asList("tree-charSequence1", "tree-charSequence2"));
+ }
+
+ @Provides
+ static Collection<CharSequence> provideCharSequenceCollection() {
+ return Arrays.<CharSequence>asList("list-charSequence");
+ }
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<CharSequence> bindHashSetOfStrings(HashSet<String> set);
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<CharSequence> bindTreeSetOfCharSequences(TreeSet<CharSequence> set);
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<CharSequence> bindCollectionOfCharSequences(Collection<CharSequence> collection);
+
+ @Binds
+ @IntoMap
+ @IntKey(123)
+ abstract Object bind123ForMap(@Named("For-123") String string);
+
+ @Binds
+ @IntoMap
+ @IntKey(456)
+ abstract Object bind456ForMap(@Named("For-456") String string);
+
+ @Provides
+ @IntoMap
+ @IntKey(789)
+ static Object provide789ForMap() {
+ return "789-string";
+ }
+
+ @Binds
+ @SomeQualifier
+ abstract int primitiveToPrimitive(int intValue);
+
+ @Binds
+ @IntoSet
+ abstract int intValueIntoSet(int intValue);
+
+ @Binds
+ @IntoMap
+ @IntKey(10)
+ abstract int intValueIntoMap(int intValue);
+
+ @Provides
+ static int intValue() {
+ return 100;
+ }
+
+ @Binds
+ @IntoMap
+ @IntKey(123)
+ @SomeQualifier
+ abstract Object bindFooOfStringsIntoQualifiedMap(FooOfStrings fooOfStrings);
+
+ @Provides
+ @Named("For-123")
+ static String provide123String() {
+ return "123-string";
+ }
+
+ @Provides
+ @Named("For-456")
+ static String provide456String() {
+ return "456-string";
+ }
+}
diff --git a/javatests/dagger/functional/binds/TestComponent.java b/javatests/dagger/functional/binds/TestComponent.java
new file mode 100644
index 0000000..3299dc8
--- /dev/null
+++ b/javatests/dagger/functional/binds/TestComponent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds;
+
+import dagger.Component;
+import dagger.functional.SomeQualifier;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+@Singleton
+@Component(modules = SimpleBindingModule.class)
+public interface TestComponent {
+ Object object();
+
+ @SomeQualifier
+ Object reusableObject();
+
+ Foo<String> fooOfStrings();
+
+ Foo<Object> fooOfObjects();
+
+ @SomeQualifier
+ Foo<String> qualifiedFooOfStrings();
+
+ Foo<Integer> fooOfIntegers();
+
+ Set<Foo<? extends Number>> foosOfNumbers();
+
+ Set<Object> objects();
+
+ Set<CharSequence> charSequences();
+
+ Map<Integer, Object> integerObjectMap();
+
+ Map<Integer, Provider<Object>> integerProviderOfObjectMap();
+
+ @SomeQualifier Map<Integer, Object> qualifiedIntegerObjectMap();
+
+ @SomeQualifier int uniquePrimitive();
+
+ Set<Integer> primitiveSet();
+
+ Map<Integer, Integer> primitiveValueMap();
+}
diff --git a/javatests/dagger/functional/binds/subpackage/Exposed.java b/javatests/dagger/functional/binds/subpackage/Exposed.java
new file mode 100644
index 0000000..885aee4
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/Exposed.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds.subpackage;
+
+public interface Exposed {}
\ No newline at end of file
diff --git a/javatests/dagger/functional/binds/subpackage/ExposedInjectsMembers.java b/javatests/dagger/functional/binds/subpackage/ExposedInjectsMembers.java
new file mode 100644
index 0000000..074289c
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/ExposedInjectsMembers.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds.subpackage;
+
+public interface ExposedInjectsMembers {}
diff --git a/javatests/dagger/functional/binds/subpackage/ExposedModule.java b/javatests/dagger/functional/binds/subpackage/ExposedModule.java
new file mode 100644
index 0000000..a2660d9
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/ExposedModule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds.subpackage;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import java.util.ArrayList;
+import java.util.List;
+import javax.inject.Singleton;
+
+@Module
+public abstract class ExposedModule {
+ @Binds
+ abstract Exposed notExposed(NotExposed notExposed);
+
+ @Provides
+ @Singleton // force a rawtypes Provider
+ static List<NotExposed> notExposedList() {
+ return new ArrayList<>();
+ }
+
+ @Binds
+ abstract List<? extends Exposed> bindList(List<NotExposed> notExposedList);
+
+ @Binds
+ abstract ExposedInjectsMembers bindExposedInjectsMembers(
+ NotExposedInjectsMembers notExposedInjectsMembers);
+}
diff --git a/javatests/dagger/functional/binds/subpackage/NotExposed.java b/javatests/dagger/functional/binds/subpackage/NotExposed.java
new file mode 100644
index 0000000..a8774ee
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/NotExposed.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds.subpackage;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton // force a Provider, which will not have a type parameter since this is not public
+class NotExposed implements Exposed {
+ @Inject
+ NotExposed() {}
+}
\ No newline at end of file
diff --git a/javatests/dagger/functional/binds/subpackage/NotExposedInjectsMembers.java b/javatests/dagger/functional/binds/subpackage/NotExposedInjectsMembers.java
new file mode 100644
index 0000000..1f6a1b7
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/NotExposedInjectsMembers.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds.subpackage;
+
+import javax.inject.Inject;
+
+final class NotExposedInjectsMembers implements ExposedInjectsMembers {
+ @Inject Exposed exposed;
+
+ @Inject NotExposedInjectsMembers() {}
+}
diff --git a/javatests/dagger/functional/binds/subpackage/UsesExposedInjectsMembers.java b/javatests/dagger/functional/binds/subpackage/UsesExposedInjectsMembers.java
new file mode 100644
index 0000000..3ee2e8f
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/UsesExposedInjectsMembers.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.binds.subpackage;
+
+import javax.inject.Inject;
+
+public class UsesExposedInjectsMembers {
+ @Inject ExposedInjectsMembers exposedInjectsMembers;
+
+ @Inject UsesExposedInjectsMembers(ExposedInjectsMembers exposedInjectsMembers) {}
+}
diff --git a/javatests/dagger/functional/builder/BuildMethodCovariantReturn.java b/javatests/dagger/functional/builder/BuildMethodCovariantReturn.java
new file mode 100644
index 0000000..b55981f
--- /dev/null
+++ b/javatests/dagger/functional/builder/BuildMethodCovariantReturn.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+
+@Component
+interface BuildMethodCovariantReturn {
+
+ @Component.Builder
+ interface Builder {
+ Object build();
+ }
+}
diff --git a/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java b/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java
new file mode 100644
index 0000000..07ebbc8
--- /dev/null
+++ b/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+
+interface BuildMethodCovariantReturnInherited {
+ @Component
+ interface Simple {
+ interface BuilderSupertype {
+ Object build();
+ }
+
+ @Component.Builder
+ interface Builder extends BuilderSupertype {}
+ }
+
+ interface ComponentSupertype {}
+
+ @Component
+ interface GenericBuilderType extends ComponentSupertype {
+ interface GenericBuilderSupertype<T> {
+ T build();
+ }
+
+ @Component.Builder
+ interface Builder extends GenericBuilderSupertype<ComponentSupertype> {}
+ }
+
+ interface ParameterizedComponentSupertype<T> {}
+
+ @Component
+ interface GenericComponentSupertypeAndBuilderSupertype
+ extends ParameterizedComponentSupertype<Object> {
+
+ interface GenericBuilderSupertype<T> {
+ ParameterizedComponentSupertype<T> build();
+ }
+
+ @Component.Builder
+ interface Builder extends GenericBuilderSupertype<Object> {}
+ }
+}
diff --git a/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java b/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java
new file mode 100644
index 0000000..33d3816
--- /dev/null
+++ b/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.BindsInstance;
+import dagger.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests that {@code @BindsInstance} works when applied to the parameter of a builder's setter
+ * method.
+ */
+@RunWith(JUnit4.class)
+public final class BuilderBindsInstanceParameterTest {
+
+ @Component
+ interface TestComponent {
+ String s();
+
+ int i();
+
+ @Component.Builder
+ interface Builder {
+ // https://github.com/google/dagger/issues/1464
+ Builder s(@BindsInstance String notTheSameNameAsMethod);
+
+ Builder i(@BindsInstance int i);
+
+ TestComponent build();
+ }
+ }
+
+ @Test
+ public void builder_bindsInstanceOnParameter_allowed() {
+ TestComponent component = DaggerBuilderBindsInstanceParameterTest_TestComponent.builder()
+ .s("hello")
+ .i(42)
+ .build();
+ assertThat(component.s()).isEqualTo("hello");
+ assertThat(component.i()).isEqualTo(42);
+ }
+}
diff --git a/javatests/dagger/functional/builder/BuilderTest.java b/javatests/dagger/functional/builder/BuilderTest.java
new file mode 100644
index 0000000..75b15c4
--- /dev/null
+++ b/javatests/dagger/functional/builder/BuilderTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BuilderTest {
+
+ @Test public void interfaceBuilder() {
+ TestComponentWithBuilderInterface.Builder builder =
+ DaggerTestComponentWithBuilderInterface.builder();
+
+ // Make sure things fail if we don't set our required modules.
+ try {
+ builder.build();
+ fail();
+ } catch(IllegalStateException expected) {}
+
+ builder.intModule(new IntModuleIncludingDoubleAndFloat(1))
+ .stringModule(new StringModule("sam"))
+ .depComponent(new DepComponent() {});
+ builder.doubleModule(new DoubleModule());
+ // Don't set other modules -- make sure it works.
+
+ TestComponentWithBuilderInterface component = builder.build();
+ assertThat(component.s()).isEqualTo("sam");
+ assertThat(component.i()).isEqualTo(1);
+ assertThat(component.d()).isEqualTo(4.2d);
+ assertThat(component.f()).isEqualTo(5.5f);
+ assertThat(component.l()).isEqualTo(6L);
+ }
+
+ @Test public void abstractClassBuilder() {
+ TestComponentWithBuilderAbstractClass.Builder builder =
+ TestComponentWithBuilderAbstractClass.builder();
+
+ // Make sure things fail if we don't set our required modules.
+ try {
+ builder.build();
+ fail();
+ } catch(IllegalStateException expected) {}
+
+ builder.intModule(new IntModuleIncludingDoubleAndFloat(1))
+ .stringModule(new StringModule("sam"))
+ .depComponent(new DepComponent() {});
+ builder.doubleModule(new DoubleModule());
+ // Don't set other modules -- make sure it works.
+
+ TestComponentWithBuilderAbstractClass component = builder.build();
+ assertThat(component.s()).isEqualTo("sam");
+ assertThat(component.i()).isEqualTo(1);
+ assertThat(component.d()).isEqualTo(4.2d);
+ assertThat(component.f()).isEqualTo(5.5f);
+ assertThat(component.l()).isEqualTo(6L);
+ }
+
+ @Test public void interfaceGenericBuilder() {
+ TestComponentWithGenericBuilderInterface.Builder builder =
+ DaggerTestComponentWithGenericBuilderInterface.builder();
+
+ // Make sure things fail if we don't set our required modules.
+ try {
+ builder.build();
+ fail();
+ } catch(IllegalStateException expected) {}
+
+ builder.setM2(new IntModuleIncludingDoubleAndFloat(1))
+ .setM1(new StringModule("sam"))
+ .depComponent(new DepComponent() {});
+ builder.doubleModule(new DoubleModule());
+ // Don't set other modules -- make sure it works.
+
+ TestComponentWithGenericBuilderInterface component = builder.build();
+ assertThat(component.s()).isEqualTo("sam");
+ assertThat(component.i()).isEqualTo(1);
+ assertThat(component.d()).isEqualTo(4.2d);
+ assertThat(component.f()).isEqualTo(5.5f);
+ assertThat(component.l()).isEqualTo(6L);
+ }
+
+ @Test public void abstractClassGenericBuilder() {
+ TestComponentWithGenericBuilderAbstractClass.Builder builder =
+ DaggerTestComponentWithGenericBuilderAbstractClass.builder();
+
+ // Make sure things fail if we don't set our required modules.
+ try {
+ builder.build();
+ fail();
+ } catch(IllegalStateException expected) {}
+
+ builder.setM2(new IntModuleIncludingDoubleAndFloat(1))
+ .setM1(new StringModule("sam"))
+ .depComponent(new DepComponent() {});
+ builder.doubleModule(new DoubleModule());
+ // Don't set other modules -- make sure it works.
+
+ TestComponentWithGenericBuilderAbstractClass component = builder.build();
+ assertThat(component.s()).isEqualTo("sam");
+ assertThat(component.i()).isEqualTo(1);
+ assertThat(component.d()).isEqualTo(4.2d);
+ assertThat(component.f()).isEqualTo(5.5f);
+ assertThat(component.l()).isEqualTo(6L);
+ }
+
+ @Test public void subcomponents_interface() {
+ ParentComponent parent = DaggerParentComponent.create();
+ TestChildComponentWithBuilderInterface.Builder builder1 = parent.childInterfaceBuilder();
+ try {
+ builder1.build();
+ fail();
+ } catch(IllegalStateException expected) {}
+
+ builder1.setM2(new IntModuleIncludingDoubleAndFloat(1))
+ .setM1(new StringModule("sam"))
+ .set(new ByteModule((byte)7));
+ builder1.set(new FloatModule());
+ TestChildComponentWithBuilderInterface child1 = builder1.build();
+ assertThat(child1.s()).isEqualTo("sam");
+ assertThat(child1.i()).isEqualTo(1);
+ assertThat(child1.d()).isEqualTo(4.2d);
+ assertThat(child1.f()).isEqualTo(5.5f);
+ assertThat(child1.l()).isEqualTo(6L);
+ assertThat(child1.b()).isEqualTo((byte)7);
+ }
+
+ @Test public void subcomponents_abstractclass() {
+ ParentComponent parent = DaggerParentComponent.create();
+ TestChildComponentWithBuilderAbstractClass.Builder builder2 =
+ parent.childAbstractClassBuilder();
+ try {
+ builder2.build();
+ fail();
+ } catch(IllegalStateException expected) {}
+
+ builder2.setM2(new IntModuleIncludingDoubleAndFloat(10))
+ .setM1(new StringModule("tara"))
+ .set(new ByteModule((byte)70));
+ builder2.set(new FloatModule());
+ TestChildComponentWithBuilderAbstractClass child2 = builder2.build();
+ assertThat(child2.s()).isEqualTo("tara");
+ assertThat(child2.i()).isEqualTo(10);
+ assertThat(child2.d()).isEqualTo(4.2d);
+ assertThat(child2.f()).isEqualTo(5.5f);
+ assertThat(child2.l()).isEqualTo(6L);
+ assertThat(child2.b()).isEqualTo((byte)70);
+ }
+
+ @Test
+ public void grandchildren() {
+ ParentComponent parent = DaggerParentComponent.create();
+ MiddleChild middle1 = parent.middleBuilder().set(new StringModule("sam")).build();
+ Grandchild grandchild1 =
+ middle1.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(21)).build();
+ Grandchild grandchild2 =
+ middle1.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(22)).build();
+
+ assertThat(middle1.s()).isEqualTo("sam");
+ assertThat(grandchild1.i()).isEqualTo(21);
+ assertThat(grandchild1.s()).isEqualTo("sam");
+ assertThat(grandchild2.i()).isEqualTo(22);
+ assertThat(grandchild2.s()).isEqualTo("sam");
+
+ // Make sure grandchildren from newer children have no relation to the older ones.
+ MiddleChild middle2 = parent.middleBuilder().set(new StringModule("tara")).build();
+ Grandchild grandchild3 =
+ middle2.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(23)).build();
+ Grandchild grandchild4 =
+ middle2.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(24)).build();
+
+ assertThat(middle2.s()).isEqualTo("tara");
+ assertThat(grandchild3.i()).isEqualTo(23);
+ assertThat(grandchild3.s()).isEqualTo("tara");
+ assertThat(grandchild4.i()).isEqualTo(24);
+ assertThat(grandchild4.s()).isEqualTo("tara");
+ }
+
+ @Test
+ public void diamondGrandchildren() {
+ ParentComponent parent = DaggerParentComponent.create();
+ MiddleChild middle = parent.middleBuilder().set(new StringModule("sam")).build();
+ OtherMiddleChild other = parent.otherBuilder().set(new StringModule("tara")).build();
+
+ Grandchild middlegrand =
+ middle.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(21)).build();
+ Grandchild othergrand =
+ other.grandchildBuilder().set(new IntModuleIncludingDoubleAndFloat(22)).build();
+
+ assertThat(middle.s()).isEqualTo("sam");
+ assertThat(other.s()).isEqualTo("tara");
+ assertThat(middlegrand.s()).isEqualTo("sam");
+ assertThat(othergrand.s()).isEqualTo("tara");
+ assertThat(middlegrand.i()).isEqualTo(21);
+ assertThat(othergrand.i()).isEqualTo(22);
+ }
+
+ @Test
+ public void genericSubcomponentMethod() {
+ ParentOfGenericComponent parent =
+ DaggerParentOfGenericComponent.builder().stringModule(new StringModule("sam")).build();
+ Grandchild.Builder builder = parent.subcomponentBuilder();
+ Grandchild child = builder.set(new IntModuleIncludingDoubleAndFloat(21)).build();
+ assertThat(child.s()).isEqualTo("sam");
+ assertThat(child.i()).isEqualTo(21);
+ }
+
+ @Test
+ public void requireSubcomponentBuilderProviders() {
+ ParentComponent parent = DaggerParentComponent.create();
+ MiddleChild middle =
+ parent
+ .requiresMiddleChildBuilder()
+ .subcomponentBuilderProvider()
+ .get()
+ .set(new StringModule("sam"))
+ .build();
+ Grandchild grandchild =
+ middle
+ .requiresGrandchildBuilder()
+ .subcomponentBuilderProvider()
+ .get()
+ .set(new IntModuleIncludingDoubleAndFloat(12))
+ .build();
+ assertThat(middle.s()).isEqualTo("sam");
+ assertThat(grandchild.i()).isEqualTo(12);
+ assertThat(grandchild.s()).isEqualTo("sam");
+ }
+
+ @Test
+ public void requireSubcomponentBuilders() {
+ ParentComponent parent = DaggerParentComponent.create();
+ MiddleChild middle =
+ parent
+ .requiresMiddleChildBuilder()
+ .subcomponentBuilder()
+ .set(new StringModule("sam"))
+ .build();
+ Grandchild grandchild =
+ middle
+ .requiresGrandchildBuilder()
+ .subcomponentBuilder()
+ .set(new IntModuleIncludingDoubleAndFloat(12))
+ .build();
+ assertThat(middle.s()).isEqualTo("sam");
+ assertThat(grandchild.i()).isEqualTo(12);
+ assertThat(grandchild.s()).isEqualTo("sam");
+ }
+}
diff --git a/javatests/dagger/functional/builder/ByteModule.java b/javatests/dagger/functional/builder/ByteModule.java
new file mode 100644
index 0000000..4b6602f
--- /dev/null
+++ b/javatests/dagger/functional/builder/ByteModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+class ByteModule {
+ final byte b;
+
+ ByteModule(byte b) {
+ this.b = b;
+ }
+
+ @Provides byte b() { return b; }
+}
diff --git a/javatests/dagger/functional/builder/DepComponent.java b/javatests/dagger/functional/builder/DepComponent.java
new file mode 100644
index 0000000..6e85c7a
--- /dev/null
+++ b/javatests/dagger/functional/builder/DepComponent.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+
+@Component
+interface DepComponent {
+}
diff --git a/javatests/dagger/functional/builder/DoubleModule.java b/javatests/dagger/functional/builder/DoubleModule.java
new file mode 100644
index 0000000..d1be37c
--- /dev/null
+++ b/javatests/dagger/functional/builder/DoubleModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+class DoubleModule {
+ @Provides
+ double d() {
+ return 4.2d;
+ }
+}
diff --git a/javatests/dagger/functional/builder/FloatModule.java b/javatests/dagger/functional/builder/FloatModule.java
new file mode 100644
index 0000000..d9990c0
--- /dev/null
+++ b/javatests/dagger/functional/builder/FloatModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+class FloatModule {
+ @Provides
+ float f() {
+ return 5.5f;
+ }
+}
diff --git a/javatests/dagger/functional/builder/GenericParent.java b/javatests/dagger/functional/builder/GenericParent.java
new file mode 100644
index 0000000..9563103
--- /dev/null
+++ b/javatests/dagger/functional/builder/GenericParent.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+interface GenericParent<B> {
+ B subcomponentBuilder();
+}
diff --git a/javatests/dagger/functional/builder/Grandchild.java b/javatests/dagger/functional/builder/Grandchild.java
new file mode 100644
index 0000000..45fe213
--- /dev/null
+++ b/javatests/dagger/functional/builder/Grandchild.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Subcomponent;
+
+@Subcomponent(modules = IntModuleIncludingDoubleAndFloat.class)
+interface Grandchild {
+ int i();
+ String s();
+
+ @Subcomponent.Builder
+ interface Builder {
+ Grandchild build();
+ Builder set(IntModuleIncludingDoubleAndFloat intModule);
+ }
+}
diff --git a/javatests/dagger/functional/builder/IntModuleIncludingDoubleAndFloat.java b/javatests/dagger/functional/builder/IntModuleIncludingDoubleAndFloat.java
new file mode 100644
index 0000000..37f5ad7
--- /dev/null
+++ b/javatests/dagger/functional/builder/IntModuleIncludingDoubleAndFloat.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module(includes = { DoubleModule.class, FloatModule.class })
+class IntModuleIncludingDoubleAndFloat {
+ final int integer;
+
+ IntModuleIncludingDoubleAndFloat(int integer) {
+ this.integer = integer;
+ }
+
+ @Provides
+ int integer() {
+ return integer;
+ }
+}
diff --git a/javatests/dagger/functional/builder/LongModule.java b/javatests/dagger/functional/builder/LongModule.java
new file mode 100644
index 0000000..7b777ec
--- /dev/null
+++ b/javatests/dagger/functional/builder/LongModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+class LongModule {
+ @Provides
+ long l() {
+ return 6L;
+ }
+}
diff --git a/javatests/dagger/functional/builder/MiddleChild.java b/javatests/dagger/functional/builder/MiddleChild.java
new file mode 100644
index 0000000..762e2a0
--- /dev/null
+++ b/javatests/dagger/functional/builder/MiddleChild.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Subcomponent;
+
+@MiddleScope
+@Subcomponent(modules = StringModule.class)
+interface MiddleChild {
+ String s();
+
+ Grandchild.Builder grandchildBuilder();
+
+ RequiresSubcomponentBuilder<Grandchild.Builder> requiresGrandchildBuilder();
+
+ @Subcomponent.Builder
+ interface Builder {
+ MiddleChild build();
+ Builder set(StringModule stringModule);
+ }
+}
diff --git a/javatests/dagger/functional/builder/MiddleScope.java b/javatests/dagger/functional/builder/MiddleScope.java
new file mode 100644
index 0000000..ac63626
--- /dev/null
+++ b/javatests/dagger/functional/builder/MiddleScope.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+@Scope
+@Retention(RUNTIME)
+@interface MiddleScope {
+
+}
diff --git a/javatests/dagger/functional/builder/OtherMiddleChild.java b/javatests/dagger/functional/builder/OtherMiddleChild.java
new file mode 100644
index 0000000..7ecc014
--- /dev/null
+++ b/javatests/dagger/functional/builder/OtherMiddleChild.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Subcomponent;
+
+@MiddleScope
+@Subcomponent(modules = {StringModule.class, LongModule.class})
+interface OtherMiddleChild {
+ long l();
+ String s();
+
+ Grandchild.Builder grandchildBuilder();
+
+ @Subcomponent.Builder
+ interface Builder {
+ OtherMiddleChild build();
+ Builder set(StringModule stringModule);
+ }
+}
diff --git a/javatests/dagger/functional/builder/ParentComponent.java b/javatests/dagger/functional/builder/ParentComponent.java
new file mode 100644
index 0000000..425ba9a
--- /dev/null
+++ b/javatests/dagger/functional/builder/ParentComponent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+import javax.inject.Singleton;
+
+@Singleton
+@Component
+interface ParentComponent {
+ TestChildComponentWithBuilderAbstractClass.Builder childAbstractClassBuilder();
+ TestChildComponentWithBuilderInterface.Builder childInterfaceBuilder();
+
+ MiddleChild.Builder middleBuilder();
+ OtherMiddleChild.Builder otherBuilder();
+
+ RequiresSubcomponentBuilder<MiddleChild.Builder> requiresMiddleChildBuilder();
+}
diff --git a/javatests/dagger/functional/builder/ParentOfGenericComponent.java b/javatests/dagger/functional/builder/ParentOfGenericComponent.java
new file mode 100644
index 0000000..daeb7d9
--- /dev/null
+++ b/javatests/dagger/functional/builder/ParentOfGenericComponent.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+import javax.inject.Singleton;
+
+@Component(modules = StringModule.class)
+@Singleton
+interface ParentOfGenericComponent extends GenericParent<Grandchild.Builder> {}
diff --git a/javatests/dagger/functional/builder/PrivateConstructors.java b/javatests/dagger/functional/builder/PrivateConstructors.java
new file mode 100644
index 0000000..e5e9aa6
--- /dev/null
+++ b/javatests/dagger/functional/builder/PrivateConstructors.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
+final class PrivateConstructors {
+ @Module
+ static final class M {
+ @Provides
+ static String provideString() {
+ return "str";
+ }
+
+ private M() {}
+ }
+
+ @Component(modules = M.class)
+ interface C {
+ String string();
+
+ @Component.Builder
+ interface Builder {
+ // M should not be required, even though the constructor is inaccessible
+ C build();
+ }
+ }
+}
diff --git a/javatests/dagger/functional/builder/RequiresSubcomponentBuilder.java b/javatests/dagger/functional/builder/RequiresSubcomponentBuilder.java
new file mode 100644
index 0000000..a8dfa21
--- /dev/null
+++ b/javatests/dagger/functional/builder/RequiresSubcomponentBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+class RequiresSubcomponentBuilder<B> {
+ private final Provider<B> subcomponentBuilderProvider;
+ private final B subcomponentBuilder;
+
+ @Inject
+ RequiresSubcomponentBuilder(Provider<B> subcomponentBuilderProvider, B subcomponentBuilder) {
+ this.subcomponentBuilderProvider = subcomponentBuilderProvider;
+ this.subcomponentBuilder = subcomponentBuilder;
+ }
+
+ Provider<B> subcomponentBuilderProvider() {
+ return subcomponentBuilderProvider;
+ }
+
+ B subcomponentBuilder() {
+ return subcomponentBuilder;
+ }
+}
diff --git a/javatests/dagger/functional/builder/StringModule.java b/javatests/dagger/functional/builder/StringModule.java
new file mode 100644
index 0000000..9fbaa5b
--- /dev/null
+++ b/javatests/dagger/functional/builder/StringModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+class StringModule {
+ final String string;
+
+ StringModule(String string) {
+ this.string = string;
+ }
+
+ @Provides
+ String string() {
+ return string;
+ }
+}
diff --git a/javatests/dagger/functional/builder/TestChildComponentWithBuilderAbstractClass.java b/javatests/dagger/functional/builder/TestChildComponentWithBuilderAbstractClass.java
new file mode 100644
index 0000000..1527a44
--- /dev/null
+++ b/javatests/dagger/functional/builder/TestChildComponentWithBuilderAbstractClass.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Subcomponent;
+
+@Subcomponent(modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class,
+ LongModule.class, ByteModule.class})
+interface TestChildComponentWithBuilderAbstractClass {
+ String s();
+ int i();
+ long l();
+ float f();
+ double d();
+ byte b();
+
+ abstract class SharedBuilder<B, C, M1, M2> {
+ abstract C build(); // Test resolving return type of build()
+ abstract B setM1(M1 m1); // Test resolving return type & param of setter
+ abstract SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
+ abstract void setM3(DoubleModule doubleModule); // Test being overridden
+ abstract SharedBuilder<B, C, M1, M2> set(FloatModule floatModule); // Test returning supertype.
+ }
+
+ @Subcomponent.Builder
+ abstract class Builder extends SharedBuilder<Builder, TestChildComponentWithBuilderAbstractClass,
+ StringModule, IntModuleIncludingDoubleAndFloat> {
+ @Override abstract Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariance
+ @Override abstract void setM3(DoubleModule doubleModule); // Test simple overrides allowed
+ abstract void set(ByteModule byteModule);
+
+ // Note we're missing LongModule -- it's implicit
+ }
+}
diff --git a/javatests/dagger/functional/builder/TestChildComponentWithBuilderInterface.java b/javatests/dagger/functional/builder/TestChildComponentWithBuilderInterface.java
new file mode 100644
index 0000000..a02b1a1
--- /dev/null
+++ b/javatests/dagger/functional/builder/TestChildComponentWithBuilderInterface.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Subcomponent;
+
+@Subcomponent(modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class,
+ LongModule.class, ByteModule.class})
+interface TestChildComponentWithBuilderInterface {
+ String s();
+ int i();
+ long l();
+ float f();
+ double d();
+ byte b();
+
+ interface SharedBuilder<B, C, M1, M2> {
+ C build(); // Test resolving return type of build()
+ B setM1(M1 m1); // Test resolving return type & param of setter
+ SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
+ void setM3(DoubleModule doubleModule); // Test being overridden
+ SharedBuilder<B, C, M1, M2> set(FloatModule floatModule); // Test return type is supertype.
+ }
+
+ @Subcomponent.Builder
+ interface Builder extends SharedBuilder<Builder, TestChildComponentWithBuilderInterface,
+ StringModule, IntModuleIncludingDoubleAndFloat> {
+ @Override Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariant overrides
+ @Override void setM3(DoubleModule doubleModule); // Test simple overrides allowed
+ void set(ByteModule byteModule);
+
+ // Note we're missing LongModule -- it's implicit
+ }
+}
diff --git a/javatests/dagger/functional/builder/TestComponentWithBuilderAbstractClass.java b/javatests/dagger/functional/builder/TestComponentWithBuilderAbstractClass.java
new file mode 100644
index 0000000..636c8f4
--- /dev/null
+++ b/javatests/dagger/functional/builder/TestComponentWithBuilderAbstractClass.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+
+@Component(
+ modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
+ dependencies = DepComponent.class)
+abstract class TestComponentWithBuilderAbstractClass {
+
+ static Builder builder() {
+ return DaggerTestComponentWithBuilderAbstractClass.builder();
+ }
+
+ abstract String s();
+ abstract int i();
+ abstract long l();
+ abstract float f();
+ abstract double d();
+
+
+ static abstract class SharedBuilder {
+ // Make sure we use the overriding signature.
+ abstract Object build();
+
+ Object stringModule(@SuppressWarnings("unused") StringModule stringModule) {
+ return null;
+ }
+
+ SharedBuilder ignoredLongModule(@SuppressWarnings("unused") LongModule longModule) {
+ return null;
+ }
+
+ }
+
+ @Component.Builder
+ static abstract class Builder extends SharedBuilder {
+ @Override abstract TestComponentWithBuilderAbstractClass build(); // Narrowing return type
+ @Override abstract Builder stringModule(StringModule stringModule); // Make abstract & narrow
+ abstract Builder intModule(IntModuleIncludingDoubleAndFloat intModule);
+ abstract void doubleModule(DoubleModule doubleModule); // Module w/o args
+ abstract void depComponent(DepComponent depComponent);
+
+ Builder ignoredIntModule(
+ @SuppressWarnings("unused") IntModuleIncludingDoubleAndFloat intModule) {
+ return null;
+ }
+
+ // Note we're missing LongModule & FloatModule -- they/re implicit
+ }
+}
diff --git a/javatests/dagger/functional/builder/TestComponentWithBuilderInterface.java b/javatests/dagger/functional/builder/TestComponentWithBuilderInterface.java
new file mode 100644
index 0000000..ba55090
--- /dev/null
+++ b/javatests/dagger/functional/builder/TestComponentWithBuilderInterface.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+
+@Component(
+ modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
+ dependencies = DepComponent.class)
+interface TestComponentWithBuilderInterface {
+ String s();
+ int i();
+ long l();
+ float f();
+ double d();
+
+ interface SharedBuilder {
+ // Make sure we use the overriding signature.
+ Object build();
+ Object stringModule(StringModule m1);
+ }
+
+ @Component.Builder
+ interface Builder extends SharedBuilder {
+ @Override TestComponentWithBuilderInterface build(); // Narrowing return type
+ @Override Builder stringModule(StringModule stringModule); // Narrowing return type
+ Builder intModule(IntModuleIncludingDoubleAndFloat intModule);
+ void doubleModule(DoubleModule doubleModule); // Module w/o args
+ void depComponent(DepComponent depComponent);
+
+ // Note we're missing LongModule & FloatModule -- they/re implicit
+ }
+}
diff --git a/javatests/dagger/functional/builder/TestComponentWithGenericBuilderAbstractClass.java b/javatests/dagger/functional/builder/TestComponentWithGenericBuilderAbstractClass.java
new file mode 100644
index 0000000..5324957
--- /dev/null
+++ b/javatests/dagger/functional/builder/TestComponentWithGenericBuilderAbstractClass.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+
+@Component(
+ modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
+ dependencies = DepComponent.class)
+interface TestComponentWithGenericBuilderAbstractClass {
+ String s();
+ int i();
+ long l();
+ float f();
+ double d();
+
+ static abstract class SharedBuilder<B, C, M1, M2> {
+ abstract C build(); // Test resolving return type of build()
+ abstract B setM1(M1 m1); // Test resolving return type & param of setter
+ abstract SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
+ abstract void doubleModule(DoubleModule doubleModule); // Test being overridden
+ abstract SharedBuilder<B, C, M1, M2> depComponent(FloatModule floatModule); // Test return type
+ }
+
+ @Component.Builder
+ static abstract class Builder extends SharedBuilder<Builder,
+ TestComponentWithGenericBuilderAbstractClass, StringModule,
+ IntModuleIncludingDoubleAndFloat> {
+ @Override abstract Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariant overrides
+ @Override abstract void doubleModule(DoubleModule module3); // Test simple overrides allowed
+ abstract void depComponent(DepComponent depComponent);
+
+ // Note we're missing LongModule & FloatModule -- they're implicit
+ }
+}
diff --git a/javatests/dagger/functional/builder/TestComponentWithGenericBuilderInterface.java b/javatests/dagger/functional/builder/TestComponentWithGenericBuilderInterface.java
new file mode 100644
index 0000000..8e30c78
--- /dev/null
+++ b/javatests/dagger/functional/builder/TestComponentWithGenericBuilderInterface.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builder;
+
+import dagger.Component;
+
+@Component(
+ modules = {StringModule.class, IntModuleIncludingDoubleAndFloat.class, LongModule.class},
+ dependencies = DepComponent.class)
+interface TestComponentWithGenericBuilderInterface {
+ String s();
+ int i();
+ long l();
+ float f();
+ double d();
+
+ interface SharedBuilder<B, C, M1, M2> {
+ C build(); // Test resolving return type of build()
+ B setM1(M1 m1); // Test resolving return type & param of setter
+ SharedBuilder<B, C, M1, M2> setM2(M2 m2); // Test being overridden
+ void doubleModule(DoubleModule doubleModule); // Test being overridden
+ SharedBuilder<B, C, M1, M2> set(FloatModule floatModule); // Test return type is supertype.
+ }
+
+ @Component.Builder
+ interface Builder extends SharedBuilder<Builder, TestComponentWithGenericBuilderInterface,
+ StringModule, IntModuleIncludingDoubleAndFloat> {
+ @Override Builder setM2(IntModuleIncludingDoubleAndFloat m2); // Test covariant overrides allowed
+ @Override void doubleModule(DoubleModule module3); // Test simple overrides allowed
+ void depComponent(DepComponent depComponent);
+
+ // Note we're missing M5 -- that's implicit.
+ }
+}
diff --git a/javatests/dagger/functional/builderbinds/BuilderBindsTest.java b/javatests/dagger/functional/builderbinds/BuilderBindsTest.java
new file mode 100644
index 0000000..b5e5225
--- /dev/null
+++ b/javatests/dagger/functional/builderbinds/BuilderBindsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builderbinds;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import dagger.functional.builderbinds.TestComponent.Builder;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class BuilderBindsTest {
+
+ @Test
+ public void builderBinds() {
+ TestComponent.Builder builder =
+ DaggerTestComponent.builder()
+ .count(5)
+ .l(10L)
+ .input("foo")
+ .nullableInput("bar")
+ .listOfString(Arrays.asList("x", "y", "z"));
+ builder.boundInSubtype(20);
+ TestComponent component = builder.build();
+ assertThat(component.count()).isEqualTo(5);
+ assertThat(component.input()).isEqualTo("foo");
+ assertThat(component.nullableInput()).isEqualTo("bar");
+ assertThat(component.listOfString()).containsExactly("x", "y", "z").inOrder();
+ }
+
+ @Test
+ public void builderBindsNullableWithNull() {
+ Builder builder =
+ DaggerTestComponent.builder()
+ .count(5)
+ .l(10L)
+ .input("foo")
+ .nullableInput(null)
+ .listOfString(ImmutableList.of());
+ builder.boundInSubtype(20);
+ TestComponent component = builder.build();
+
+ assertThat(component.count()).isEqualTo(5);
+ assertThat(component.input()).isEqualTo("foo");
+ assertThat(component.nullableInput()).isNull();
+ assertThat(component.listOfString()).isEmpty();
+ }
+
+ @Test
+ public void builderBindsNonNullableWithNull() {
+ try {
+ DaggerTestComponent.builder().count(5).l(10L).input(null);
+ fail("expected NullPointerException");
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void builderBindsPrimitiveNotSet() {
+ try {
+ TestComponent.Builder builder =
+ DaggerTestComponent.builder()
+ .l(10L)
+ .input("foo")
+ .nullableInput("bar")
+ .listOfString(ImmutableList.of());
+ builder.boundInSubtype(20);
+ builder.build();
+ fail("expected IllegalStateException");
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ @Test
+ public void builderBindsNonNullableNotSet() {
+ try {
+ TestComponent.Builder builder =
+ DaggerTestComponent.builder()
+ .count(5)
+ .l(10L)
+ .nullableInput("foo")
+ .listOfString(ImmutableList.of());
+ builder.boundInSubtype(20);
+ builder.build();
+ fail("expected IllegalStateException");
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ @Test
+ public void builderBindsNullableNotSet() {
+ Builder builder =
+ DaggerTestComponent.builder().count(5).l(10L).input("foo").listOfString(ImmutableList.of());
+ builder.boundInSubtype(20);
+ TestComponent component = builder.build();
+ assertThat(component.count()).isEqualTo(5);
+ assertThat(component.input()).isEqualTo("foo");
+ assertThat(component.nullableInput()).isNull();
+ assertThat(component.listOfString()).isEmpty();
+ }
+}
diff --git a/javatests/dagger/functional/builderbinds/BuilderSupertype.java b/javatests/dagger/functional/builderbinds/BuilderSupertype.java
new file mode 100644
index 0000000..5828f9d
--- /dev/null
+++ b/javatests/dagger/functional/builderbinds/BuilderSupertype.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builderbinds;
+
+import dagger.BindsInstance;
+import javax.inject.Named;
+
+interface BuilderSupertype {
+ @BindsInstance
+ void boundInSubtype(@Named("subtype") int subtype);
+}
diff --git a/javatests/dagger/functional/builderbinds/Nullable.java b/javatests/dagger/functional/builderbinds/Nullable.java
new file mode 100644
index 0000000..7924484
--- /dev/null
+++ b/javatests/dagger/functional/builderbinds/Nullable.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builderbinds;
+
+@interface Nullable {}
diff --git a/javatests/dagger/functional/builderbinds/TestComponent.java b/javatests/dagger/functional/builderbinds/TestComponent.java
new file mode 100644
index 0000000..23a32b1
--- /dev/null
+++ b/javatests/dagger/functional/builderbinds/TestComponent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.builderbinds;
+
+import dagger.BindsInstance;
+import dagger.Component;
+import java.util.List;
+import javax.inject.Named;
+
+@Component
+interface TestComponent {
+ int count();
+
+ long l();
+
+ @Named("input")
+ String input();
+
+ @Nullable
+ @Named("nullable input")
+ String nullableInput();
+
+ List<String> listOfString();
+
+ @Named("subtype")
+ int boundInSubtype();
+
+ @Component.Builder
+ interface Builder extends BuilderSupertype {
+ @BindsInstance
+ Builder count(int count);
+
+ @BindsInstance
+ Builder l(long l);
+
+ @BindsInstance
+ Builder input(@Named("input") String input);
+
+ @BindsInstance
+ Builder nullableInput(@Nullable @Named("nullable input") String nullableInput);
+
+ @BindsInstance
+ Builder listOfString(List<String> listOfString);
+
+ TestComponent build();
+ }
+}
diff --git a/javatests/dagger/functional/cycle/CycleTest.java b/javatests/dagger/functional/cycle/CycleTest.java
new file mode 100644
index 0000000..d489bc6
--- /dev/null
+++ b/javatests/dagger/functional/cycle/CycleTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.cycle;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.cycle.Cycles.A;
+import dagger.functional.cycle.Cycles.BindsCycleComponent;
+import dagger.functional.cycle.Cycles.C;
+import dagger.functional.cycle.Cycles.ChildCycleComponent;
+import dagger.functional.cycle.Cycles.CycleComponent;
+import dagger.functional.cycle.Cycles.CycleMapComponent;
+import dagger.functional.cycle.Cycles.S;
+import dagger.functional.cycle.Cycles.SelfCycleComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CycleTest {
+ @Test
+ public void providerIndirectionSelfCycle() {
+ SelfCycleComponent selfCycleComponent = DaggerCycles_SelfCycleComponent.create();
+ S s = selfCycleComponent.s();
+ assertThat(s.sProvider.get()).isNotNull();
+ }
+
+ @Test
+ public void providerIndirectionCycle() {
+ CycleComponent cycleComponent = DaggerCycles_CycleComponent.create();
+ A a = cycleComponent.a();
+ C c = cycleComponent.c();
+ assertThat(c.aProvider.get()).isNotNull();
+ assertThat(a.b.c.aProvider.get()).isNotNull();
+ assertThat(a.e.d.b.c.aProvider.get()).isNotNull();
+ }
+
+ @Test
+ public void lazyIndirectionSelfCycle() {
+ SelfCycleComponent selfCycleComponent = DaggerCycles_SelfCycleComponent.create();
+ S s = selfCycleComponent.s();
+ assertThat(s.sLazy.get()).isNotNull();
+ }
+
+ @Test
+ public void lazyIndirectionCycle() {
+ CycleComponent cycleComponent = DaggerCycles_CycleComponent.create();
+ A a = cycleComponent.a();
+ C c = cycleComponent.c();
+ assertThat(c.aLazy.get()).isNotNull();
+ assertThat(a.b.c.aLazy.get()).isNotNull();
+ assertThat(a.e.d.b.c.aLazy.get()).isNotNull();
+ }
+
+ @Test
+ public void subcomponentIndirectionCycle() {
+ ChildCycleComponent childCycleComponent = DaggerCycles_CycleComponent.create().child();
+ A a = childCycleComponent.a();
+ assertThat(a.b.c.aProvider.get()).isNotNull();
+ assertThat(a.e.d.b.c.aProvider.get()).isNotNull();
+ }
+
+ @Test
+ public void providerMapIndirectionCycle() {
+ CycleMapComponent cycleMapComponent = DaggerCycles_CycleMapComponent.create();
+ assertThat(cycleMapComponent.y()).isNotNull();
+ assertThat(cycleMapComponent.y().mapOfProvidersOfX).containsKey("X");
+ assertThat(cycleMapComponent.y().mapOfProvidersOfX.get("X")).isNotNull();
+ assertThat(cycleMapComponent.y().mapOfProvidersOfX.get("X").get()).isNotNull();
+ assertThat(cycleMapComponent.y().mapOfProvidersOfX.get("X").get().y).isNotNull();
+ assertThat(cycleMapComponent.y().mapOfProvidersOfX).hasSize(1);
+ assertThat(cycleMapComponent.y().mapOfProvidersOfY).containsKey("Y");
+ assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y")).isNotNull();
+ assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y").get()).isNotNull();
+ assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y").get().mapOfProvidersOfX).hasSize(1);
+ assertThat(cycleMapComponent.y().mapOfProvidersOfY.get("Y").get().mapOfProvidersOfY).hasSize(1);
+ assertThat(cycleMapComponent.y().mapOfProvidersOfY).hasSize(1);
+ }
+
+ /**
+ * Tests that a cycle where a {@code @Binds} binding depends on a binding that has to be deferred
+ * works.
+ */
+ @Test
+ public void cycleWithDeferredBinds() {
+ BindsCycleComponent bindsCycleComponent = DaggerCycles_BindsCycleComponent.create();
+ assertThat(bindsCycleComponent.bar()).isNotNull();
+ }
+}
diff --git a/javatests/dagger/functional/cycle/Cycles.java b/javatests/dagger/functional/cycle/Cycles.java
new file mode 100644
index 0000000..f4faeab
--- /dev/null
+++ b/javatests/dagger/functional/cycle/Cycles.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.cycle;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.StringKey;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * Cycle classes used for testing cyclic dependencies.
+ *
+ * <pre>
+ * {@literal A ← (E ← D ← B ← C ← Provider<A>, Lazy<A>), (B ← C ← Provider<A>, Lazy<A>)}
+ * {@literal S ← Provider<S>, Lazy<S>}
+ * </pre>
+ */
+final class Cycles {
+ private Cycles() {}
+
+ static class A {
+ public final B b;
+ public final E e;
+
+ @Inject
+ A(E e, B b) {
+ this.e = e;
+ this.b = b;
+ }
+ }
+
+ static class B {
+ public final C c;
+
+ @Inject
+ B(C c) {
+ this.c = c;
+ }
+ }
+
+ static class C {
+ public final Provider<A> aProvider;
+ @Inject public Lazy<A> aLazy;
+ @Inject public Provider<Lazy<A>> aLazyProvider;
+
+ @Inject
+ C(Provider<A> aProvider) {
+ this.aProvider = aProvider;
+ }
+ }
+
+ static class D {
+ public final B b;
+
+ @Inject
+ D(B b) {
+ this.b = b;
+ }
+ }
+
+ static class E {
+ public final D d;
+
+ @Inject
+ E(D d) {
+ this.d = d;
+ }
+ }
+
+ static class S {
+ public final Provider<S> sProvider;
+ @Inject public Lazy<S> sLazy;
+
+ @Inject
+ S(Provider<S> sProvider) {
+ this.sProvider = sProvider;
+ }
+ }
+
+ static class X {
+ public final Y y;
+
+ @Inject
+ X(Y y) {
+ this.y = y;
+ }
+ }
+
+ static class Y {
+ public final Map<String, Provider<X>> mapOfProvidersOfX;
+ public final Map<String, Provider<Y>> mapOfProvidersOfY;
+
+ @Inject
+ Y(Map<String, Provider<X>> mapOfProvidersOfX, Map<String, Provider<Y>> mapOfProvidersOfY) {
+ this.mapOfProvidersOfX = mapOfProvidersOfX;
+ this.mapOfProvidersOfY = mapOfProvidersOfY;
+ }
+ }
+
+ @Module
+ abstract static class CycleMapModule {
+ @Binds
+ @IntoMap
+ @StringKey("X")
+ abstract X x(X x);
+
+ @Binds
+ @IntoMap
+ @StringKey("Y")
+ abstract Y y(Y y);
+ }
+
+ @SuppressWarnings("dependency-cycle")
+ @Component(modules = CycleMapModule.class)
+ interface CycleMapComponent {
+ Y y();
+ }
+
+ @SuppressWarnings("dependency-cycle")
+ @Component(modules = CycleModule.class)
+ interface CycleComponent {
+ A a();
+
+ C c();
+
+ ChildCycleComponent child();
+ }
+
+ @Module
+ static class CycleModule {
+ @Provides
+ static Object provideObjectWithCycle(@SuppressWarnings("unused") Provider<Object> object) {
+ return "object";
+ }
+ }
+
+ @SuppressWarnings("dependency-cycle")
+ @Component
+ interface SelfCycleComponent {
+ S s();
+ }
+
+ @Subcomponent
+ interface ChildCycleComponent {
+ @SuppressWarnings("dependency-cycle")
+ A a();
+
+ @SuppressWarnings("dependency-cycle")
+ Object object();
+ }
+
+ interface Foo {}
+
+ static class Bar implements Foo {
+ @Inject
+ Bar(Provider<Foo> fooProvider) {}
+ }
+
+ /**
+ * A component with a cycle in which a {@code @Binds} binding depends on the binding that has to
+ * be deferred.
+ */
+ @Component(modules = BindsCycleModule.class)
+ interface BindsCycleComponent {
+ Bar bar();
+ }
+
+ @Module
+ abstract static class BindsCycleModule {
+ @Binds
+ abstract Foo foo(Bar bar);
+ }
+}
diff --git a/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java b/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
new file mode 100644
index 0000000..b77ee3e
--- /dev/null
+++ b/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.cycle;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.lang.Thread.State.BLOCKED;
+import static java.lang.Thread.State.WAITING;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.junit.Assert.fail;
+
+import com.google.common.util.concurrent.SettableFuture;
+import com.google.common.util.concurrent.Uninterruptibles;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DoubleCheckCycleTest {
+ // TODO(b/77916397): Migrate remaining tests in DoubleCheckTest to functional tests in this class.
+
+ /** A qualifier for a reentrant scoped binding. */
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface Reentrant {}
+
+ /** A module to be overridden in each test. */
+ @Module
+ static class OverrideModule {
+ @Provides
+ @Singleton
+ Object provideObject() {
+ throw new IllegalStateException("This method should be overridden in tests");
+ }
+
+ @Provides
+ @Singleton
+ @Reentrant
+ Object provideReentrantObject(@Reentrant Provider<Object> provider) {
+ throw new IllegalStateException("This method should be overridden in tests");
+ }
+ }
+
+ @Singleton
+ @Component(modules = OverrideModule.class)
+ interface TestComponent {
+ Object getObject();
+ @Reentrant Object getReentrantObject();
+ }
+
+ @Test
+ public void testNonReentrant() {
+ AtomicInteger callCount = new AtomicInteger(0);
+
+ // Provides a non-reentrant binding. The provides method should only be called once.
+ DoubleCheckCycleTest.TestComponent component =
+ DaggerDoubleCheckCycleTest_TestComponent.builder()
+ .overrideModule(
+ new OverrideModule() {
+ @Override Object provideObject() {
+ callCount.getAndIncrement();
+ return new Object();
+ }
+ })
+ .build();
+
+ assertThat(callCount.get()).isEqualTo(0);
+ Object first = component.getObject();
+ assertThat(callCount.get()).isEqualTo(1);
+ Object second = component.getObject();
+ assertThat(callCount.get()).isEqualTo(1);
+ assertThat(first).isSameInstanceAs(second);
+ }
+
+ @Test
+ public void testReentrant() {
+ AtomicInteger callCount = new AtomicInteger(0);
+
+ // Provides a reentrant binding. Even though it's scoped, the provides method is called twice.
+ // In this case, we allow it since the same instance is returned on the second call.
+ DoubleCheckCycleTest.TestComponent component =
+ DaggerDoubleCheckCycleTest_TestComponent.builder()
+ .overrideModule(
+ new OverrideModule() {
+ @Override Object provideReentrantObject(Provider<Object> provider) {
+ if (callCount.incrementAndGet() == 1) {
+ return provider.get();
+ }
+ return new Object();
+ }
+ })
+ .build();
+
+ assertThat(callCount.get()).isEqualTo(0);
+ Object first = component.getReentrantObject();
+ assertThat(callCount.get()).isEqualTo(2);
+ Object second = component.getReentrantObject();
+ assertThat(callCount.get()).isEqualTo(2);
+ assertThat(first).isSameInstanceAs(second);
+ }
+
+ @Test
+ public void testFailingReentrant() {
+ AtomicInteger callCount = new AtomicInteger(0);
+
+ // Provides a failing reentrant binding. Even though it's scoped, the provides method is called
+ // twice. In this case we throw an exception since a different instance is provided on the
+ // second call.
+ DoubleCheckCycleTest.TestComponent component =
+ DaggerDoubleCheckCycleTest_TestComponent.builder()
+ .overrideModule(
+ new OverrideModule() {
+ @Override Object provideReentrantObject(Provider<Object> provider) {
+ if (callCount.incrementAndGet() == 1) {
+ provider.get();
+ return new Object();
+ }
+ return new Object();
+ }
+ })
+ .build();
+
+ assertThat(callCount.get()).isEqualTo(0);
+ try {
+ component.getReentrantObject();
+ fail("Expected IllegalStateException");
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessageThat().contains("Scoped provider was invoked recursively");
+ }
+ assertThat(callCount.get()).isEqualTo(2);
+ }
+
+ @Test(timeout = 5000)
+
+ public void testGetFromMultipleThreads() throws Exception {
+ AtomicInteger callCount = new AtomicInteger(0);
+ AtomicInteger requestCount = new AtomicInteger(0);
+ SettableFuture<Object> future = SettableFuture.create();
+
+ // Provides a non-reentrant binding. In this case, we return a SettableFuture so that we can
+ // control when the provides method returns.
+ DoubleCheckCycleTest.TestComponent component =
+ DaggerDoubleCheckCycleTest_TestComponent.builder()
+ .overrideModule(
+ new OverrideModule() {
+ @Override
+ Object provideObject() {
+ callCount.incrementAndGet();
+ try {
+ return Uninterruptibles.getUninterruptibly(future);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ })
+ .build();
+
+ int numThreads = 10;
+ CountDownLatch remainingTasks = new CountDownLatch(numThreads);
+ List<Thread> tasks = new ArrayList<>(numThreads);
+ List<Object> values = Collections.synchronizedList(new ArrayList<>(numThreads));
+
+ // Set up multiple threads that call component.getObject().
+ for (int i = 0; i < numThreads; i++) {
+ tasks.add(
+ new Thread(
+ () -> {
+ requestCount.incrementAndGet();
+ values.add(component.getObject());
+ remainingTasks.countDown();
+ }));
+ }
+
+ // Check initial conditions
+ assertThat(remainingTasks.getCount()).isEqualTo(10);
+ assertThat(requestCount.get()).isEqualTo(0);
+ assertThat(callCount.get()).isEqualTo(0);
+ assertThat(values).isEmpty();
+
+ // Start all threads
+ tasks.forEach(Thread::start);
+
+ // Wait for all threads to wait/block.
+ long waiting = 0;
+ while (waiting != numThreads) {
+ waiting =
+ tasks.stream()
+ .map(Thread::getState)
+ .filter(state -> state == WAITING || state == BLOCKED)
+ .count();
+ }
+
+ // Check the intermediate state conditions.
+ // * All 10 threads should have requested the binding, but none should have finished.
+ // * Only 1 thread should have reached the provides method.
+ // * None of the threads should have set a value (since they are waiting for future to be set).
+ assertThat(remainingTasks.getCount()).isEqualTo(10);
+ assertThat(requestCount.get()).isEqualTo(10);
+ assertThat(callCount.get()).isEqualTo(1);
+ assertThat(values).isEmpty();
+
+ // Set the future and wait on all remaining threads to finish.
+ Object futureValue = new Object();
+ future.set(futureValue);
+ remainingTasks.await();
+
+ // Check the final state conditions.
+ // All values should be set now, and they should all be equal to the same instance.
+ assertThat(remainingTasks.getCount()).isEqualTo(0);
+ assertThat(requestCount.get()).isEqualTo(10);
+ assertThat(callCount.get()).isEqualTo(1);
+ assertThat(values).isEqualTo(Collections.nCopies(numThreads, futureValue));
+ }
+}
diff --git a/javatests/dagger/functional/cycle/LongCycle.java b/javatests/dagger/functional/cycle/LongCycle.java
new file mode 100644
index 0000000..1b3fb24
--- /dev/null
+++ b/javatests/dagger/functional/cycle/LongCycle.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.cycle;
+
+import dagger.Component;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * Component with a long enough cycle such that the initialization of a provider happens in a
+ * separate {@code initialize} method from the one where it is used as a delegated factory.
+ *
+ */
+// Each nested class's constructor has an intentionally unused parameter.
+@SuppressWarnings("unused")
+final class LongCycle {
+ static class Class1 { @Inject Class1(Class2 class2) {} }
+ static class Class2 { @Inject Class2(Class3 class3) {} }
+ static class Class3 { @Inject Class3(Class4 class4) {} }
+ static class Class4 { @Inject Class4(Class5 class5) {} }
+ static class Class5 { @Inject Class5(Class6 class6) {} }
+ static class Class6 { @Inject Class6(Class7 class7) {} }
+ static class Class7 { @Inject Class7(Class8 class8) {} }
+ static class Class8 { @Inject Class8(Class9 class9) {} }
+ static class Class9 { @Inject Class9(Class10 class10) {} }
+ static class Class10 { @Inject Class10(Class11 class11) {} }
+ static class Class11 { @Inject Class11(Class12 class12) {} }
+ static class Class12 { @Inject Class12(Class13 class13) {} }
+ static class Class13 { @Inject Class13(Class14 class14) {} }
+ static class Class14 { @Inject Class14(Class15 class15) {} }
+ static class Class15 { @Inject Class15(Class16 class16) {} }
+ static class Class16 { @Inject Class16(Class17 class17) {} }
+ static class Class17 { @Inject Class17(Class18 class18) {} }
+ static class Class18 { @Inject Class18(Class19 class19) {} }
+ static class Class19 { @Inject Class19(Class20 class20) {} }
+ static class Class20 { @Inject Class20(Class21 class21) {} }
+ static class Class21 { @Inject Class21(Class22 class22) {} }
+ static class Class22 { @Inject Class22(Class23 class23) {} }
+ static class Class23 { @Inject Class23(Class24 class24) {} }
+ static class Class24 { @Inject Class24(Class25 class25) {} }
+ static class Class25 { @Inject Class25(Class26 class26) {} }
+ static class Class26 { @Inject Class26(Class27 class27) {} }
+ static class Class27 { @Inject Class27(Class28 class28) {} }
+ static class Class28 { @Inject Class28(Class29 class29) {} }
+ static class Class29 { @Inject Class29(Class30 class30) {} }
+ static class Class30 { @Inject Class30(Class31 class31) {} }
+ static class Class31 { @Inject Class31(Class32 class32) {} }
+ static class Class32 { @Inject Class32(Class33 class33) {} }
+ static class Class33 { @Inject Class33(Class34 class34) {} }
+ static class Class34 { @Inject Class34(Class35 class35) {} }
+ static class Class35 { @Inject Class35(Class36 class36) {} }
+ static class Class36 { @Inject Class36(Class37 class37) {} }
+ static class Class37 { @Inject Class37(Class38 class38) {} }
+ static class Class38 { @Inject Class38(Class39 class39) {} }
+ static class Class39 { @Inject Class39(Class40 class40) {} }
+ static class Class40 { @Inject Class40(Class41 class41) {} }
+ static class Class41 { @Inject Class41(Class42 class42) {} }
+ static class Class42 { @Inject Class42(Class43 class43) {} }
+ static class Class43 { @Inject Class43(Class44 class44) {} }
+ static class Class44 { @Inject Class44(Class45 class45) {} }
+ static class Class45 { @Inject Class45(Class46 class46) {} }
+ static class Class46 { @Inject Class46(Class47 class47) {} }
+ static class Class47 { @Inject Class47(Class48 class48) {} }
+ static class Class48 { @Inject Class48(Class49 class49) {} }
+ static class Class49 { @Inject Class49(Class50 class50) {} }
+ static class Class50 { @Inject Class50(Class51 class51) {} }
+ static class Class51 { @Inject Class51(Class52 class52) {} }
+ static class Class52 { @Inject Class52(Class53 class53) {} }
+ static class Class53 { @Inject Class53(Class54 class54) {} }
+ static class Class54 { @Inject Class54(Class55 class55) {} }
+ static class Class55 { @Inject Class55(Class56 class56) {} }
+ static class Class56 { @Inject Class56(Class57 class57) {} }
+ static class Class57 { @Inject Class57(Class58 class58) {} }
+ static class Class58 { @Inject Class58(Class59 class59) {} }
+ static class Class59 { @Inject Class59(Class60 class60) {} }
+ static class Class60 { @Inject Class60(Class61 class61) {} }
+ static class Class61 { @Inject Class61(Class62 class62) {} }
+ static class Class62 { @Inject Class62(Class63 class63) {} }
+ static class Class63 { @Inject Class63(Class64 class64) {} }
+ static class Class64 { @Inject Class64(Class65 class65) {} }
+ static class Class65 { @Inject Class65(Class66 class66) {} }
+ static class Class66 { @Inject Class66(Class67 class67) {} }
+ static class Class67 { @Inject Class67(Class68 class68) {} }
+ static class Class68 { @Inject Class68(Class69 class69) {} }
+ static class Class69 { @Inject Class69(Class70 class70) {} }
+ static class Class70 { @Inject Class70(Class71 class71) {} }
+ static class Class71 { @Inject Class71(Class72 class72) {} }
+ static class Class72 { @Inject Class72(Class73 class73) {} }
+ static class Class73 { @Inject Class73(Class74 class74) {} }
+ static class Class74 { @Inject Class74(Class75 class75) {} }
+ static class Class75 { @Inject Class75(Class76 class76) {} }
+ static class Class76 { @Inject Class76(Class77 class77) {} }
+ static class Class77 { @Inject Class77(Class78 class78) {} }
+ static class Class78 { @Inject Class78(Class79 class79) {} }
+ static class Class79 { @Inject Class79(Class80 class80) {} }
+ static class Class80 { @Inject Class80(Class81 class81) {} }
+ static class Class81 { @Inject Class81(Class82 class82) {} }
+ static class Class82 { @Inject Class82(Class83 class83) {} }
+ static class Class83 { @Inject Class83(Class84 class84) {} }
+ static class Class84 { @Inject Class84(Class85 class85) {} }
+ static class Class85 { @Inject Class85(Class86 class86) {} }
+ static class Class86 { @Inject Class86(Class87 class87) {} }
+ static class Class87 { @Inject Class87(Class88 class88) {} }
+ static class Class88 { @Inject Class88(Class89 class89) {} }
+ static class Class89 { @Inject Class89(Class90 class90) {} }
+ static class Class90 { @Inject Class90(Class91 class91) {} }
+ static class Class91 { @Inject Class91(Class92 class92) {} }
+ static class Class92 { @Inject Class92(Class93 class93) {} }
+ static class Class93 { @Inject Class93(Class94 class94) {} }
+ static class Class94 { @Inject Class94(Class95 class95) {} }
+ static class Class95 { @Inject Class95(Class96 class96) {} }
+ static class Class96 { @Inject Class96(Class97 class97) {} }
+ static class Class97 { @Inject Class97(Class98 class98) {} }
+ static class Class98 { @Inject Class98(Class99 class99) {} }
+ static class Class99 { @Inject Class99(Class100 class100) {} }
+ static class Class100 { @Inject Class100(Class101 class101) {} }
+ static class Class101 { @Inject Class101(Provider<Class1> class1Provider) {} }
+
+ @SuppressWarnings("dependency-cycle")
+ @Component
+ interface LongCycleComponent {
+ Class1 class1();
+ }
+
+ private LongCycle() {}
+}
diff --git a/javatests/dagger/functional/cycle/LongCycleTest.java b/javatests/dagger/functional/cycle/LongCycleTest.java
new file mode 100644
index 0000000..a6244b1
--- /dev/null
+++ b/javatests/dagger/functional/cycle/LongCycleTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.cycle;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.TruthJUnit.assume;
+import static java.util.Arrays.stream;
+
+import dagger.functional.cycle.LongCycle.LongCycleComponent;
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class LongCycleTest {
+
+ /**
+ * Tests a cycle long enough that the real factory is created in a separate initialize method from
+ * the delegate factory.
+ */
+ @Test
+ public void longCycle() {
+ LongCycleComponent longCycleComponent = DaggerLongCycle_LongCycleComponent.create();
+ assertThat(longCycleComponent.class1()).isNotNull();
+ }
+
+ /**
+ * Fails if {@link LongCycleComponent} doesn't have a long enough cycle to make sure the real
+ * factory is created in a separate method from the delegate factory.
+ */
+ @Test
+ public void longCycleHasMoreThanOneInitializeMethod() {
+ assume().that(System.getProperty("dagger.mode")).doesNotContain("FastInit");
+ boolean hasInitialize2 =
+ stream(DaggerLongCycle_LongCycleComponent.class.getDeclaredMethods())
+ .map(Method::getName)
+ .anyMatch(name -> name.equals("initialize2"));
+ assertWithMessage("LongCycleComponent impl has an initialize2 method")
+ .that(hasInitialize2)
+ .isTrue();
+ }
+}
diff --git a/javatests/dagger/functional/factory/AbstractModule.java b/javatests/dagger/functional/factory/AbstractModule.java
new file mode 100644
index 0000000..83a13d6
--- /dev/null
+++ b/javatests/dagger/functional/factory/AbstractModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+abstract class AbstractModule {
+ @Provides
+ static String provideString() {
+ return "foo";
+ }
+}
diff --git a/javatests/dagger/functional/factory/ConcreteModuleThatCouldBeAbstract.java b/javatests/dagger/functional/factory/ConcreteModuleThatCouldBeAbstract.java
new file mode 100644
index 0000000..32591a2
--- /dev/null
+++ b/javatests/dagger/functional/factory/ConcreteModuleThatCouldBeAbstract.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class ConcreteModuleThatCouldBeAbstract {
+ @Provides
+ static double provideDouble() {
+ return 42.0;
+ }
+}
diff --git a/javatests/dagger/functional/factory/Dependency.java b/javatests/dagger/functional/factory/Dependency.java
new file mode 100644
index 0000000..90dc294
--- /dev/null
+++ b/javatests/dagger/functional/factory/Dependency.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+final class Dependency {
+ Object object() {
+ return "bar";
+ }
+}
diff --git a/javatests/dagger/functional/factory/FactoryBindsInstanceTest.java b/javatests/dagger/functional/factory/FactoryBindsInstanceTest.java
new file mode 100644
index 0000000..83505e9
--- /dev/null
+++ b/javatests/dagger/functional/factory/FactoryBindsInstanceTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.junit.Assert.fail;
+
+import dagger.BindsInstance;
+import dagger.Component;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for component factories with {@link BindsInstance} parameters. */
+@RunWith(JUnit4.class)
+public final class FactoryBindsInstanceTest {
+
+ @Component
+ interface BindsInstanceComponent {
+ String string();
+
+ @Component.Factory
+ interface Factory {
+ BindsInstanceComponent create(@BindsInstance String string);
+ }
+ }
+
+ @Test
+ public void bindsInstance() {
+ BindsInstanceComponent component =
+ DaggerFactoryBindsInstanceTest_BindsInstanceComponent.factory().create("baz");
+ assertThat(component.string()).isEqualTo("baz");
+ }
+
+ @Test
+ public void nonNullableBindsInstance_failsOnNull() {
+ try {
+ DaggerFactoryBindsInstanceTest_BindsInstanceComponent.factory().create(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Target({METHOD, PARAMETER})
+ @Retention(RUNTIME)
+ @interface Nullable {}
+
+ @Component
+ interface NullableBindsInstanceComponent {
+ @Nullable
+ String string();
+
+ @Component.Factory
+ interface Factory {
+ NullableBindsInstanceComponent create(@BindsInstance @Nullable String string);
+ }
+ }
+
+ @Test
+ public void nullableBindsInstance_doesNotFailOnNull() {
+ NullableBindsInstanceComponent component =
+ DaggerFactoryBindsInstanceTest_NullableBindsInstanceComponent.factory().create(null);
+ assertThat(component.string()).isEqualTo(null);
+ }
+
+ @Component
+ interface PrimitiveBindsInstanceComponent {
+ int getInt();
+
+ @Component.Factory
+ interface Factory {
+ PrimitiveBindsInstanceComponent create(@BindsInstance int i);
+ }
+ }
+
+ @Test
+ public void primitiveBindsInstance() {
+ PrimitiveBindsInstanceComponent component =
+ DaggerFactoryBindsInstanceTest_PrimitiveBindsInstanceComponent.factory().create(1);
+ assertThat(component.getInt()).isEqualTo(1);
+ }
+}
diff --git a/javatests/dagger/functional/factory/FactoryDependenciesTest.java b/javatests/dagger/functional/factory/FactoryDependenciesTest.java
new file mode 100644
index 0000000..35a0b85
--- /dev/null
+++ b/javatests/dagger/functional/factory/FactoryDependenciesTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import dagger.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for factories for components with a dependency. */
+@RunWith(JUnit4.class)
+public final class FactoryDependenciesTest {
+
+ @Component(dependencies = Dependency.class)
+ interface DependencyComponent {
+ Object object();
+
+ @Component.Factory
+ interface Factory {
+ DependencyComponent create(Dependency dependency);
+ }
+ }
+
+ @Test
+ public void dependency() {
+ DependencyComponent component =
+ DaggerFactoryDependenciesTest_DependencyComponent.factory().create(new Dependency());
+ assertThat(component.object()).isEqualTo("bar");
+ }
+
+ @Test
+ public void dependency_failsOnNull() {
+ try {
+ DaggerFactoryDependenciesTest_DependencyComponent.factory().create(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+}
diff --git a/javatests/dagger/functional/factory/FactoryImplicitModulesTest.java b/javatests/dagger/functional/factory/FactoryImplicitModulesTest.java
new file mode 100644
index 0000000..143f322
--- /dev/null
+++ b/javatests/dagger/functional/factory/FactoryImplicitModulesTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import dagger.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for factories for components with modules that do not require an instance to be passed to
+ * the factory. Includes both tests where the module does not have a corresponding parameter in the
+ * factory method and where it does have a parameter, for cases where that's allowed.
+ */
+@RunWith(JUnit4.class)
+public final class FactoryImplicitModulesTest {
+
+ @Component(modules = AbstractModule.class)
+ interface AbstractModuleComponent {
+ String string();
+
+ @Component.Factory
+ interface Factory {
+ AbstractModuleComponent create();
+ }
+ }
+
+ @Test
+ public void abstractModule() {
+ AbstractModuleComponent component =
+ DaggerFactoryImplicitModulesTest_AbstractModuleComponent.factory().create();
+ assertThat(component.string()).isEqualTo("foo");
+ }
+
+ @Component(modules = InstantiableConcreteModule.class)
+ interface InstantiableConcreteModuleComponent {
+ int getInt();
+
+ @Component.Factory
+ interface Factory {
+ InstantiableConcreteModuleComponent create();
+ }
+ }
+
+ @Test
+ public void instantiableConcreteModule() {
+ InstantiableConcreteModuleComponent component =
+ DaggerFactoryImplicitModulesTest_InstantiableConcreteModuleComponent.factory().create();
+ assertThat(component.getInt()).isEqualTo(42);
+ }
+
+ @Component(modules = InstantiableConcreteModule.class)
+ interface InstantiableConcreteModuleWithFactoryParameterComponent {
+ int getInt();
+
+ @Component.Factory
+ interface Factory {
+ InstantiableConcreteModuleWithFactoryParameterComponent create(
+ InstantiableConcreteModule module);
+ }
+ }
+
+ @Test
+ public void instantiableConcreteModule_withFactoryParameter() {
+ InstantiableConcreteModuleWithFactoryParameterComponent component =
+ DaggerFactoryImplicitModulesTest_InstantiableConcreteModuleWithFactoryParameterComponent
+ .factory()
+ .create(new InstantiableConcreteModule());
+ assertThat(component.getInt()).isEqualTo(42);
+ }
+
+ @Test
+ public void instantiableConcreteModule_withFactoryParameter_failsOnNull() {
+ try {
+ DaggerFactoryImplicitModulesTest_InstantiableConcreteModuleWithFactoryParameterComponent
+ .factory()
+ .create(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Component(modules = ConcreteModuleThatCouldBeAbstract.class)
+ interface ConcreteModuleThatCouldBeAbstractComponent {
+ double getDouble();
+
+ @Component.Factory
+ interface Factory {
+ ConcreteModuleThatCouldBeAbstractComponent create();
+ }
+ }
+
+ @Test
+ public void concreteModuleThatCouldBeAbstract() {
+ ConcreteModuleThatCouldBeAbstractComponent component =
+ DaggerFactoryImplicitModulesTest_ConcreteModuleThatCouldBeAbstractComponent.factory()
+ .create();
+ assertThat(component.getDouble()).isEqualTo(42.0);
+ }
+
+ @Component(modules = ConcreteModuleThatCouldBeAbstract.class)
+ interface ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent {
+ double getDouble();
+
+ @Component.Factory
+ interface Factory {
+ ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent create(
+ ConcreteModuleThatCouldBeAbstract module);
+ }
+ }
+
+ @Test
+ public void concreteModuleThatCouldBeAbstract_withFactoryParameter() {
+ ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent component =
+ DaggerFactoryImplicitModulesTest_ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent
+ .factory()
+ .create(new ConcreteModuleThatCouldBeAbstract());
+ assertThat(component.getDouble()).isEqualTo(42.0);
+ }
+
+ @Test
+ public void concreteModuleThatCouldBeAbstract_withFactoryParameter_failsOnNull() {
+ // This matches what builders do when there's a setter for such a module; the setter checks that
+ // the argument is not null but otherwise ignores it.
+ // It's possible that we shouldn't even allow such a parameter for a factory, since unlike a
+ // builder, where the setter can just not be called, a factory doesn't give the option of not
+ // passing *something* for the unused parameter.
+ try {
+ ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent component =
+ DaggerFactoryImplicitModulesTest_ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent
+ .factory()
+ .create(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+}
diff --git a/javatests/dagger/functional/factory/FactoryMixedParametersTest.java b/javatests/dagger/functional/factory/FactoryMixedParametersTest.java
new file mode 100644
index 0000000..733c673
--- /dev/null
+++ b/javatests/dagger/functional/factory/FactoryMixedParametersTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.BindsInstance;
+import dagger.Component;
+import java.util.Random;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for component factories with multiple parameters. */
+@RunWith(JUnit4.class)
+public final class FactoryMixedParametersTest {
+
+ @Component(
+ modules = {
+ AbstractModule.class,
+ UninstantiableConcreteModule.class,
+ InstantiableConcreteModule.class
+ },
+ dependencies = Dependency.class)
+ interface MixedArgComponent {
+ String string();
+ int getInt();
+ long getLong();
+ Object object();
+ double getDouble();
+ Provider<Random> randomProvider();
+
+ @Component.Factory
+ interface Factory {
+ MixedArgComponent create(
+ @BindsInstance double d,
+ Dependency dependency,
+ UninstantiableConcreteModule module,
+ @BindsInstance Random random);
+ }
+ }
+
+ @Test
+ public void mixedArgComponent() {
+ Random random = new Random();
+ MixedArgComponent component =
+ DaggerFactoryMixedParametersTest_MixedArgComponent.factory()
+ .create(3.0, new Dependency(), new UninstantiableConcreteModule(2L), random);
+ assertThat(component.string()).isEqualTo("foo");
+ assertThat(component.getInt()).isEqualTo(42);
+ assertThat(component.getDouble()).isEqualTo(3.0);
+ assertThat(component.object()).isEqualTo("bar");
+ assertThat(component.getLong()).isEqualTo(2L);
+ assertThat(component.randomProvider().get()).isSameInstanceAs(random);
+ assertThat(component.randomProvider().get()).isSameInstanceAs(random);
+ }
+}
diff --git a/javatests/dagger/functional/factory/FactoryRequiredModulesTest.java b/javatests/dagger/functional/factory/FactoryRequiredModulesTest.java
new file mode 100644
index 0000000..5b81b51
--- /dev/null
+++ b/javatests/dagger/functional/factory/FactoryRequiredModulesTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import dagger.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for factories for components that have a module that must have an instance provided by the
+ * user.
+ */
+@RunWith(JUnit4.class)
+public final class FactoryRequiredModulesTest {
+
+ @Component(modules = UninstantiableConcreteModule.class)
+ interface UninstantiableConcreteModuleComponent {
+ long getLong();
+
+ @Component.Factory
+ interface Factory {
+ UninstantiableConcreteModuleComponent create(UninstantiableConcreteModule module);
+ }
+ }
+
+ @Test
+ public void uninstantiableConcreteModule() {
+ UninstantiableConcreteModuleComponent component =
+ DaggerFactoryRequiredModulesTest_UninstantiableConcreteModuleComponent.factory()
+ .create(new UninstantiableConcreteModule(42L));
+ assertThat(component.getLong()).isEqualTo(42L);
+ }
+
+ @Test
+ public void uninstantiableConcreteModule_failsOnNull() {
+ try {
+ DaggerFactoryRequiredModulesTest_UninstantiableConcreteModuleComponent.factory().create(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+}
diff --git a/javatests/dagger/functional/factory/InstantiableConcreteModule.java b/javatests/dagger/functional/factory/InstantiableConcreteModule.java
new file mode 100644
index 0000000..37acbe0
--- /dev/null
+++ b/javatests/dagger/functional/factory/InstantiableConcreteModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import dagger.Module;
+import dagger.Provides;
+
+@SuppressWarnings("StaticModuleMethods") // intentionally non-static
+@Module
+final class InstantiableConcreteModule {
+ @Provides
+ int provideInt() {
+ return 42;
+ }
+}
diff --git a/javatests/dagger/functional/factory/SubcomponentFactoryTest.java b/javatests/dagger/functional/factory/SubcomponentFactoryTest.java
new file mode 100644
index 0000000..cbff8a1
--- /dev/null
+++ b/javatests/dagger/functional/factory/SubcomponentFactoryTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import javax.inject.Inject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@linkplain Subcomponent.Factory subcomponent factories}.
+ *
+ * <p>Most things are tested in {@code FactoryTest}; this is just intended to test some things like
+ * injecting subcomponent factories and returning them from component methods.
+ */
+@RunWith(JUnit4.class)
+public final class SubcomponentFactoryTest {
+
+ @Component
+ interface ParentWithSubcomponentFactory {
+ Sub.Factory subcomponentFactory();
+
+ @Component.Factory
+ interface Factory {
+ ParentWithSubcomponentFactory create(@BindsInstance int i);
+ }
+ }
+
+ @Subcomponent
+ interface Sub {
+ int i();
+ String s();
+
+ @Subcomponent.Factory
+ interface Factory {
+ Sub create(@BindsInstance String s);
+ }
+ }
+
+ @Test
+ public void parentComponentWithSubcomponentFactoryEntryPoint() {
+ ParentWithSubcomponentFactory parent =
+ DaggerSubcomponentFactoryTest_ParentWithSubcomponentFactory.factory().create(3);
+ Sub subcomponent = parent.subcomponentFactory().create("foo");
+ assertThat(subcomponent.i()).isEqualTo(3);
+ assertThat(subcomponent.s()).isEqualTo("foo");
+ }
+
+ @Module(subcomponents = Sub.class)
+ abstract static class ModuleWithSubcomponent {
+ @Provides
+ static int provideInt() {
+ return 42;
+ }
+ }
+
+ static class UsesSubcomponentFactory {
+ private final Sub.Factory subFactory;
+
+ @Inject
+ UsesSubcomponentFactory(Sub.Factory subFactory) {
+ this.subFactory = subFactory;
+ }
+
+ Sub getSubcomponent(String s) {
+ return subFactory.create(s);
+ }
+ }
+
+ @Component(modules = ModuleWithSubcomponent.class)
+ interface ParentWithModuleWithSubcomponent {
+ UsesSubcomponentFactory usesSubcomponentFactory();
+ }
+
+ @Test
+ public void parentComponentWithModuleWithSubcomponent() {
+ ParentWithModuleWithSubcomponent parent =
+ DaggerSubcomponentFactoryTest_ParentWithModuleWithSubcomponent.create();
+ UsesSubcomponentFactory usesSubcomponentFactory = parent.usesSubcomponentFactory();
+
+ Sub subcomponent1 = usesSubcomponentFactory.getSubcomponent("foo");
+ assertThat(subcomponent1.i()).isEqualTo(42);
+ assertThat(subcomponent1.s()).isEqualTo("foo");
+
+ Sub subcomponent2 = usesSubcomponentFactory.getSubcomponent("bar");
+ assertThat(subcomponent2.i()).isEqualTo(42);
+ assertThat(subcomponent2.s()).isEqualTo("bar");
+ }
+}
diff --git a/javatests/dagger/functional/factory/UninstantiableConcreteModule.java b/javatests/dagger/functional/factory/UninstantiableConcreteModule.java
new file mode 100644
index 0000000..13f50fd
--- /dev/null
+++ b/javatests/dagger/functional/factory/UninstantiableConcreteModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.factory;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class UninstantiableConcreteModule {
+ private final long l;
+
+ UninstantiableConcreteModule(long l) {
+ this.l = l;
+ }
+
+ @Provides
+ long provideLong() {
+ return l;
+ }
+}
diff --git a/javatests/dagger/functional/guava/BUILD b/javatests/dagger/functional/guava/BUILD
new file mode 100644
index 0000000..66492eb
--- /dev/null
+++ b/javatests/dagger/functional/guava/BUILD
@@ -0,0 +1,36 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Functional tests for Dagger that depend on Guava
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "guava_tests",
+ srcs = glob(["**/*.java"]),
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/functional/guava/OptionalBindingComponents.java b/javatests/dagger/functional/guava/OptionalBindingComponents.java
new file mode 100644
index 0000000..ee58865
--- /dev/null
+++ b/javatests/dagger/functional/guava/OptionalBindingComponents.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.guava;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+import dagger.BindsOptionalOf;
+import dagger.Component;
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import java.lang.annotation.Retention;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+
+/** Classes to support testing {@code BindsOptionalOf} functionality. */
+public final class OptionalBindingComponents {
+
+ /** A qualifier. */
+ @Qualifier
+ @Retention(RUNTIME)
+ public @interface SomeQualifier {}
+
+ /** A value object that contains various optionally-bound objects. */
+ @AutoValue
+ public abstract static class Values {
+ abstract Optional<Value> optionalInstance();
+
+ abstract Optional<Provider<Value>> optionalProvider();
+
+ abstract Optional<Lazy<Value>> optionalLazy();
+
+ abstract Optional<Provider<Lazy<Value>>> optionalLazyProvider();
+ }
+
+ // Default access so that it's inaccessible to OptionalBindingComponentsWithInaccessibleTypes.
+ enum Value {
+ VALUE,
+ QUALIFIED_VALUE
+ }
+
+ static final class InjectedThing {
+ @Inject
+ InjectedThing() {}
+ }
+
+ /** Binds optionals and {@link Values}. */
+ @Module
+ public abstract static class OptionalBindingModule {
+ @BindsOptionalOf
+ abstract Value value();
+
+ @BindsOptionalOf
+ @SomeQualifier abstract Value qualifiedValue();
+
+ // Valid because it's qualified.
+ @BindsOptionalOf
+ @SomeQualifier abstract InjectedThing qualifiedInjectedThing();
+
+ @BindsOptionalOf
+ abstract Object nullableObject();
+
+ @Provides
+ static Values values(
+ Optional<Value> optionalInstance,
+ Optional<Provider<Value>> optionalProvider,
+ Optional<Lazy<Value>> optionalLazy,
+ Optional<Provider<Lazy<Value>>> optionalLazyProvider) {
+ return new AutoValue_OptionalBindingComponents_Values(
+ optionalInstance, optionalProvider, optionalLazy, optionalLazyProvider);
+ }
+
+ @Provides
+ @SomeQualifier
+ static Values qualifiedValues(
+ @SomeQualifier Optional<Value> optionalInstance,
+ @SomeQualifier Optional<Provider<Value>> optionalProvider,
+ @SomeQualifier Optional<Lazy<Value>> optionalLazy,
+ @SomeQualifier Optional<Provider<Lazy<Value>>> optionalLazyProvider) {
+ return new AutoValue_OptionalBindingComponents_Values(
+ optionalInstance, optionalProvider, optionalLazy, optionalLazyProvider);
+ }
+ }
+
+ /** Binds {@link Value}. */
+ @Module
+ public abstract static class ConcreteBindingModule {
+ /** @param cycle to demonstrate that optional {@link Provider} injection can break cycles */
+ @Provides
+ static Value value(Optional<Provider<Value>> cycle) {
+ return Value.VALUE;
+ }
+
+ @Provides
+ @SomeQualifier static Value qualifiedValue() {
+ return Value.QUALIFIED_VALUE;
+ }
+
+ @Provides
+ @Nullable
+ static Object nullableObject() {
+ return null;
+ }
+ }
+
+ /** Interface for components used to test optional bindings. */
+ public interface OptionalBindingComponent {
+ Values values();
+
+ @SomeQualifier
+ Values qualifiedValues();
+
+ // Nullable bindings can satisfy optional bindings except for Optional<Foo>.
+
+ Optional<Provider<Object>> optionalNullableProvider();
+
+ Optional<Lazy<Object>> optionalNullableLazy();
+
+ Optional<Provider<Lazy<Object>>> optionalNullableLazyProvider();
+ }
+
+ @Component(modules = OptionalBindingModule.class)
+ interface AbsentOptionalBindingComponent extends OptionalBindingComponent {
+ PresentOptionalBindingSubcomponent presentChild();
+ }
+
+ @Component(modules = {OptionalBindingModule.class, ConcreteBindingModule.class})
+ interface PresentOptionalBindingComponent extends OptionalBindingComponent {}
+
+ @Subcomponent(modules = ConcreteBindingModule.class)
+ interface PresentOptionalBindingSubcomponent extends OptionalBindingComponent {}
+}
diff --git a/javatests/dagger/functional/guava/OptionalBindingComponentsAbsentTest.java b/javatests/dagger/functional/guava/OptionalBindingComponentsAbsentTest.java
new file mode 100644
index 0000000..d563ace
--- /dev/null
+++ b/javatests/dagger/functional/guava/OptionalBindingComponentsAbsentTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.guava;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.guava.OptionalBindingComponents.AbsentOptionalBindingComponent;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for absent optional bindings. */
+@RunWith(JUnit4.class)
+public final class OptionalBindingComponentsAbsentTest {
+ private AbsentOptionalBindingComponent absent;
+
+ @Before
+ public void setUp() {
+ absent = DaggerOptionalBindingComponents_AbsentOptionalBindingComponent.create();
+ }
+
+ @Test
+ public void optional() {
+ assertThat(absent.values().optionalInstance()).isAbsent();
+ }
+
+ @Test
+ public void optionalProvider() {
+ assertThat(absent.values().optionalProvider()).isAbsent();
+ }
+
+ @Test
+ public void optionalLazy() {
+ assertThat(absent.values().optionalLazy()).isAbsent();
+ }
+
+ @Test
+ public void optionalLazyProvider() {
+ assertThat(absent.values().optionalLazyProvider()).isAbsent();
+ }
+
+ @Test
+ public void qualifiedOptional() {
+ assertThat(absent.qualifiedValues().optionalInstance()).isAbsent();
+ }
+
+ @Test
+ public void qualifiedOptionalProvider() {
+ assertThat(absent.qualifiedValues().optionalProvider()).isAbsent();
+ }
+
+ @Test
+ public void qualifiedOptionalLazy() {
+ assertThat(absent.qualifiedValues().optionalLazy()).isAbsent();
+ }
+
+ @Test
+ public void qualifiedOptionalLazyProvider() {
+ assertThat(absent.qualifiedValues().optionalLazyProvider()).isAbsent();
+ }
+}
diff --git a/javatests/dagger/functional/guava/OptionalBindingComponentsPresentTest.java b/javatests/dagger/functional/guava/OptionalBindingComponentsPresentTest.java
new file mode 100644
index 0000000..1243207
--- /dev/null
+++ b/javatests/dagger/functional/guava/OptionalBindingComponentsPresentTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.guava;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.functional.guava.OptionalBindingComponents.Value.QUALIFIED_VALUE;
+import static dagger.functional.guava.OptionalBindingComponents.Value.VALUE;
+
+import com.google.common.collect.ImmutableList;
+import dagger.functional.guava.OptionalBindingComponents.OptionalBindingComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for present optional bindings. */
+@RunWith(Parameterized.class)
+public final class OptionalBindingComponentsPresentTest {
+
+ @Parameters(name = "{0}")
+ public static Iterable<Object[]> parameters() {
+ return ImmutableList.copyOf(
+ new Object[][] {
+ {DaggerOptionalBindingComponents_PresentOptionalBindingComponent.create()},
+ {DaggerOptionalBindingComponents_AbsentOptionalBindingComponent.create().presentChild()},
+ });
+ }
+
+ private final OptionalBindingComponent component;
+
+ public OptionalBindingComponentsPresentTest(OptionalBindingComponent component) {
+ this.component = component;
+ }
+
+ @Test
+ public void optionalProvider() {
+ assertThat(component.values().optionalProvider().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void optionalLazy() {
+ assertThat(component.values().optionalLazy().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void optionalLazyProvider() {
+ assertThat(component.values().optionalLazyProvider().get().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void qualifiedOptional() {
+ assertThat(component.qualifiedValues().optionalInstance()).hasValue(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalProvider() {
+ assertThat(component.qualifiedValues().optionalProvider().get().get())
+ .isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalLazy() {
+ assertThat(component.qualifiedValues().optionalLazy().get().get()).isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalLazyProvider() {
+ assertThat(component.qualifiedValues().optionalLazyProvider().get().get().get())
+ .isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void optionalNullableProvider() {
+ assertThat(component.optionalNullableProvider().get().get()).isNull();
+ }
+
+ @Test
+ public void optionalNullableLazy() {
+ assertThat(component.optionalNullableLazy().get().get()).isNull();
+ }
+
+ @Test
+ public void optionalNullableLazyProvider() {
+ assertThat(component.optionalNullableLazyProvider().get().get().get()).isNull();
+ }
+}
diff --git a/javatests/dagger/functional/guava/a/OptionalBindingComponentsWithInaccessibleTypes.java b/javatests/dagger/functional/guava/a/OptionalBindingComponentsWithInaccessibleTypes.java
new file mode 100644
index 0000000..28a6734
--- /dev/null
+++ b/javatests/dagger/functional/guava/a/OptionalBindingComponentsWithInaccessibleTypes.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.guava.a;
+
+import dagger.Component;
+import dagger.functional.guava.OptionalBindingComponents.ConcreteBindingModule;
+import dagger.functional.guava.OptionalBindingComponents.OptionalBindingComponent;
+import dagger.functional.guava.OptionalBindingComponents.OptionalBindingModule;
+
+final class OptionalBindingComponentsWithInaccessibleTypes {
+
+ @Component(modules = OptionalBindingModule.class)
+ interface AbsentOptionalBindingComponent extends OptionalBindingComponent {}
+
+ @Component(modules = {OptionalBindingModule.class, ConcreteBindingModule.class})
+ interface PresentOptionalBindingComponent extends OptionalBindingComponent {}
+}
diff --git a/javatests/dagger/functional/guava/a/OptionalBindingComponentsWithInaccessibleTypesTest.java b/javatests/dagger/functional/guava/a/OptionalBindingComponentsWithInaccessibleTypesTest.java
new file mode 100644
index 0000000..6be593c
--- /dev/null
+++ b/javatests/dagger/functional/guava/a/OptionalBindingComponentsWithInaccessibleTypesTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.guava.a;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for optional bindings that include types inaccessible to the component. */
+@RunWith(JUnit4.class)
+public class OptionalBindingComponentsWithInaccessibleTypesTest {
+ @Test
+ public void components() {
+ DaggerOptionalBindingComponentsWithInaccessibleTypes_AbsentOptionalBindingComponent.create();
+ DaggerOptionalBindingComponentsWithInaccessibleTypes_PresentOptionalBindingComponent.create();
+ }
+}
diff --git a/javatests/dagger/functional/gwt/GwtIncompatibles.java b/javatests/dagger/functional/gwt/GwtIncompatibles.java
new file mode 100644
index 0000000..d65f116
--- /dev/null
+++ b/javatests/dagger/functional/gwt/GwtIncompatibles.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.gwt;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.Module;
+import dagger.Provides;
+import java.lang.annotation.Retention;
+import javax.inject.Inject;
+
+interface GwtIncompatibles {
+ @Retention(RUNTIME)
+ @interface GwtIncompatible {}
+
+ @GwtIncompatible
+ class OnClass {
+ @Inject
+ OnClass() {}
+ }
+
+ class OnConstructor {
+ @Inject
+ @GwtIncompatible
+ OnConstructor() {}
+ }
+
+ @GwtIncompatible
+ class OuterClass {
+ static class OnOuterClass {
+ @Inject
+ OnOuterClass() {}
+ }
+ }
+
+ @GwtIncompatible
+ class MembersInjectedType {
+ @Inject String string;
+ }
+
+ @GwtIncompatible
+ @Module
+ class OnModule {
+ @Provides
+ static String onModule() {
+ return "on module";
+ }
+ }
+
+ @Module
+ class OnMethod {
+ @GwtIncompatible
+ @Provides
+ static String onMethod() {
+ return "on method";
+ }
+ }
+}
diff --git a/javatests/dagger/functional/gwt/GwtIncompatiblesTest.java b/javatests/dagger/functional/gwt/GwtIncompatiblesTest.java
new file mode 100644
index 0000000..f6f6c41
--- /dev/null
+++ b/javatests/dagger/functional/gwt/GwtIncompatiblesTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.gwt;
+
+import dagger.functional.gwt.GwtIncompatibles.GwtIncompatible;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@code @GwtIncompatible} bindings. */
+@RunWith(JUnit4.class)
+public class GwtIncompatiblesTest {
+ @Test
+ public void testIncompatible() {
+ assertGwtIncompatible(GwtIncompatibles_OnClass_Factory.class);
+ assertGwtIncompatible(GwtIncompatibles_OnConstructor_Factory.class);
+ assertGwtIncompatible(GwtIncompatibles_OuterClass_OnOuterClass_Factory.class);
+
+ assertGwtIncompatible(GwtIncompatibles_MembersInjectedType_MembersInjector.class);
+
+ assertGwtIncompatible(GwtIncompatibles_OnModule_OnModuleFactory.class);
+ assertGwtIncompatible(GwtIncompatibles_OnMethod_OnMethodFactory.class);
+ }
+
+ private void assertGwtIncompatible(Class<?> clazz) {
+ boolean gwtIncompatible = clazz.isAnnotationPresent(GwtIncompatible.class);
+ if (!gwtIncompatible) {
+ throw new AssertionError(clazz.getCanonicalName() + " is not @GwtIncompatible");
+ }
+ }
+}
diff --git a/javatests/dagger/functional/jdk8/BUILD b/javatests/dagger/functional/jdk8/BUILD
new file mode 100644
index 0000000..6b76ba3
--- /dev/null
+++ b/javatests/dagger/functional/jdk8/BUILD
@@ -0,0 +1,37 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Functional tests for Dagger that depend on Guava
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "jdk8_tests",
+ srcs = glob(["**/*.java"]),
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ test_only_deps = [
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth:truth8",
+ ],
+ deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
diff --git a/javatests/dagger/functional/jdk8/OptionalBindingComponents.java b/javatests/dagger/functional/jdk8/OptionalBindingComponents.java
new file mode 100644
index 0000000..0048f8b
--- /dev/null
+++ b/javatests/dagger/functional/jdk8/OptionalBindingComponents.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.jdk8;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.auto.value.AutoValue;
+import dagger.BindsOptionalOf;
+import dagger.Component;
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import java.lang.annotation.Retention;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+
+/** Classes to support testing {@code BindsOptionalOf} functionality. */
+public final class OptionalBindingComponents {
+
+ /** A qualifier. */
+ @Qualifier
+ @Retention(RUNTIME)
+ public @interface SomeQualifier {}
+
+ /** A value object that contains various optionally-bound objects. */
+ @AutoValue
+ public abstract static class Values {
+ abstract Optional<Value> optionalInstance();
+
+ abstract Optional<Provider<Value>> optionalProvider();
+
+ abstract Optional<Lazy<Value>> optionalLazy();
+
+ abstract Optional<Provider<Lazy<Value>>> optionalLazyProvider();
+ }
+
+ // Default access so that it's inaccessible to OptionalBindingComponentsWithInaccessibleTypes.
+ enum Value {
+ VALUE,
+ QUALIFIED_VALUE
+ }
+
+ static final class InjectedThing {
+ @Inject
+ InjectedThing() {}
+ }
+
+ /** Binds optionals and {@link Values}. */
+ @Module
+ public abstract static class OptionalBindingModule {
+ @BindsOptionalOf
+ abstract Value value();
+
+ @BindsOptionalOf
+ @SomeQualifier abstract Value qualifiedValue();
+
+ // Valid because it's qualified.
+ @BindsOptionalOf
+ @SomeQualifier abstract InjectedThing qualifiedInjectedThing();
+
+ @BindsOptionalOf
+ abstract Object nullableObject();
+
+ @Provides
+ static Values values(
+ Optional<Value> optionalInstance,
+ Optional<Provider<Value>> optionalProvider,
+ Optional<Lazy<Value>> optionalLazy,
+ Optional<Provider<Lazy<Value>>> optionalLazyProvider) {
+ return new AutoValue_OptionalBindingComponents_Values(
+ optionalInstance, optionalProvider, optionalLazy, optionalLazyProvider);
+ }
+
+ @Provides
+ @SomeQualifier
+ static Values qualifiedValues(
+ @SomeQualifier Optional<Value> optionalInstance,
+ @SomeQualifier Optional<Provider<Value>> optionalProvider,
+ @SomeQualifier Optional<Lazy<Value>> optionalLazy,
+ @SomeQualifier Optional<Provider<Lazy<Value>>> optionalLazyProvider) {
+ return new AutoValue_OptionalBindingComponents_Values(
+ optionalInstance, optionalProvider, optionalLazy, optionalLazyProvider);
+ }
+ }
+
+ /** Binds {@link Value}. */
+ @Module
+ public abstract static class ConcreteBindingModule {
+ /** @param cycle to demonstrate that optional {@link Provider} injection can break cycles */
+ @Provides
+ static Value value(Optional<Provider<Value>> cycle) {
+ return Value.VALUE;
+ }
+
+ @Provides
+ @SomeQualifier static Value qualifiedValue() {
+ return Value.QUALIFIED_VALUE;
+ }
+
+ @Provides
+ @Nullable
+ static Object nullableObject() {
+ return null;
+ }
+ }
+
+ /** Interface for components used to test optional bindings. */
+ public interface OptionalBindingComponent {
+ Values values();
+
+ @SomeQualifier
+ Values qualifiedValues();
+
+ // Nullable bindings can satisfy optional bindings except for Optional<Foo>.
+
+ Optional<Provider<Object>> optionalNullableProvider();
+
+ Optional<Lazy<Object>> optionalNullableLazy();
+
+ Optional<Provider<Lazy<Object>>> optionalNullableLazyProvider();
+ }
+
+ @Component(modules = OptionalBindingModule.class)
+ interface EmptyOptionalBindingComponent extends OptionalBindingComponent {
+ PresentOptionalBindingSubcomponent presentChild();
+ }
+
+ @Component(modules = {OptionalBindingModule.class, ConcreteBindingModule.class})
+ interface PresentOptionalBindingComponent extends OptionalBindingComponent {}
+
+ @Subcomponent(modules = ConcreteBindingModule.class)
+ interface PresentOptionalBindingSubcomponent extends OptionalBindingComponent {}
+}
diff --git a/javatests/dagger/functional/jdk8/OptionalBindingComponentsEmptyTest.java b/javatests/dagger/functional/jdk8/OptionalBindingComponentsEmptyTest.java
new file mode 100644
index 0000000..21bdd3c
--- /dev/null
+++ b/javatests/dagger/functional/jdk8/OptionalBindingComponentsEmptyTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.jdk8;
+
+import static com.google.common.truth.Truth8.assertThat;
+
+import dagger.functional.jdk8.OptionalBindingComponents.EmptyOptionalBindingComponent;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for absent optional bindings. */
+@RunWith(JUnit4.class)
+public final class OptionalBindingComponentsEmptyTest {
+ private EmptyOptionalBindingComponent component;
+
+ @Before
+ public void setUp() {
+ component = DaggerOptionalBindingComponents_EmptyOptionalBindingComponent.create();
+ }
+
+ @Test
+ public void optional() {
+ assertThat(component.values().optionalInstance()).isEmpty();
+ }
+
+ @Test
+ public void optionalProvider() {
+ assertThat(component.values().optionalProvider()).isEmpty();
+ }
+
+ @Test
+ public void optionalLazy() {
+ assertThat(component.values().optionalLazy()).isEmpty();
+ }
+
+ @Test
+ public void optionalLazyProvider() {
+ assertThat(component.values().optionalLazyProvider()).isEmpty();
+ }
+
+ @Test
+ public void qualifiedOptional() {
+ assertThat(component.qualifiedValues().optionalInstance()).isEmpty();
+ }
+
+ @Test
+ public void qualifiedOptionalProvider() {
+ assertThat(component.qualifiedValues().optionalProvider()).isEmpty();
+ }
+
+ @Test
+ public void qualifiedOptionalLazy() {
+ assertThat(component.qualifiedValues().optionalLazy()).isEmpty();
+ }
+
+ @Test
+ public void qualifiedOptionalLazyProvider() {
+ assertThat(component.qualifiedValues().optionalLazyProvider()).isEmpty();
+ }
+}
diff --git a/javatests/dagger/functional/jdk8/OptionalBindingComponentsPresentTest.java b/javatests/dagger/functional/jdk8/OptionalBindingComponentsPresentTest.java
new file mode 100644
index 0000000..50fbefe
--- /dev/null
+++ b/javatests/dagger/functional/jdk8/OptionalBindingComponentsPresentTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.jdk8;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+import static dagger.functional.jdk8.OptionalBindingComponents.Value.QUALIFIED_VALUE;
+import static dagger.functional.jdk8.OptionalBindingComponents.Value.VALUE;
+
+import com.google.common.collect.ImmutableList;
+import dagger.functional.jdk8.OptionalBindingComponents.OptionalBindingComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for present optional bindings. */
+@RunWith(Parameterized.class)
+public final class OptionalBindingComponentsPresentTest {
+
+ @Parameters(name = "{0}")
+ public static Iterable<Object[]> parameters() {
+ return ImmutableList.copyOf(
+ new Object[][] {
+ {DaggerOptionalBindingComponents_PresentOptionalBindingComponent.create()},
+ {DaggerOptionalBindingComponents_EmptyOptionalBindingComponent.create().presentChild()},
+ });
+ }
+
+ private final OptionalBindingComponent component;
+
+ public OptionalBindingComponentsPresentTest(OptionalBindingComponent component) {
+ this.component = component;
+ }
+
+ @Test
+ public void optionalProvider() {
+ assertThat(component.values().optionalProvider().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void optionalLazy() {
+ assertThat(component.values().optionalLazy().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void optionalLazyProvider() {
+ assertThat(component.values().optionalLazyProvider().get().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void qualifiedOptional() {
+ assertThat(component.qualifiedValues().optionalInstance()).hasValue(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalProvider() {
+ assertThat(component.qualifiedValues().optionalProvider().get().get())
+ .isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalLazy() {
+ assertThat(component.qualifiedValues().optionalLazy().get().get()).isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalLazyProvider() {
+ assertThat(component.qualifiedValues().optionalLazyProvider().get().get().get())
+ .isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void optionalNullableProvider() {
+ assertThat(component.optionalNullableProvider().get().get()).isNull();
+ }
+
+ @Test
+ public void optionalNullableLazy() {
+ assertThat(component.optionalNullableLazy().get().get()).isNull();
+ }
+
+ @Test
+ public void optionalNullableLazyProvider() {
+ assertThat(component.optionalNullableLazyProvider().get().get().get()).isNull();
+ }
+}
diff --git a/javatests/dagger/functional/jdk8/a/OptionalBindingComponentsWithInaccessibleTypes.java b/javatests/dagger/functional/jdk8/a/OptionalBindingComponentsWithInaccessibleTypes.java
new file mode 100644
index 0000000..e4abbb6
--- /dev/null
+++ b/javatests/dagger/functional/jdk8/a/OptionalBindingComponentsWithInaccessibleTypes.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.jdk8.a;
+
+import dagger.Component;
+import dagger.functional.jdk8.OptionalBindingComponents.ConcreteBindingModule;
+import dagger.functional.jdk8.OptionalBindingComponents.OptionalBindingComponent;
+import dagger.functional.jdk8.OptionalBindingComponents.OptionalBindingModule;
+
+final class OptionalBindingComponentsWithInaccessibleTypes {
+
+ @Component(modules = OptionalBindingModule.class)
+ interface EmptyOptionalBindingComponent extends OptionalBindingComponent {}
+
+ @Component(modules = {OptionalBindingModule.class, ConcreteBindingModule.class})
+ interface PresentOptionalBindingComponent extends OptionalBindingComponent {}
+}
diff --git a/javatests/dagger/functional/jdk8/a/OptionalBindingComponentsWithInaccessibleTypesTest.java b/javatests/dagger/functional/jdk8/a/OptionalBindingComponentsWithInaccessibleTypesTest.java
new file mode 100644
index 0000000..8e2687a
--- /dev/null
+++ b/javatests/dagger/functional/jdk8/a/OptionalBindingComponentsWithInaccessibleTypesTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.jdk8.a;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for optional bindings that include types inaccessible to the component. */
+@RunWith(JUnit4.class)
+public class OptionalBindingComponentsWithInaccessibleTypesTest {
+ @Test
+ public void components() {
+ DaggerOptionalBindingComponentsWithInaccessibleTypes_EmptyOptionalBindingComponent.create();
+ DaggerOptionalBindingComponentsWithInaccessibleTypes_PresentOptionalBindingComponent.create();
+ }
+}
diff --git a/javatests/dagger/functional/membersinject/ChildOfArrayOfParentOfStringArray.java b/javatests/dagger/functional/membersinject/ChildOfArrayOfParentOfStringArray.java
new file mode 100644
index 0000000..61bef71
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/ChildOfArrayOfParentOfStringArray.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+class ChildOfArrayOfParentOfStringArray extends
+ MembersInjectGenericParent<MembersInjectGenericParent<String[]>[]> {
+}
diff --git a/javatests/dagger/functional/membersinject/ChildOfPrimitiveIntArray.java b/javatests/dagger/functional/membersinject/ChildOfPrimitiveIntArray.java
new file mode 100644
index 0000000..154bcff
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/ChildOfPrimitiveIntArray.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+class ChildOfPrimitiveIntArray extends MembersInjectGenericParent<int[]> {
+
+}
diff --git a/javatests/dagger/functional/membersinject/ChildOfStringArray.java b/javatests/dagger/functional/membersinject/ChildOfStringArray.java
new file mode 100644
index 0000000..d8bab7c
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/ChildOfStringArray.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+class ChildOfStringArray extends MembersInjectGenericParent<String[]> {
+
+}
diff --git a/javatests/dagger/functional/membersinject/MembersInjectComponent.java b/javatests/dagger/functional/membersinject/MembersInjectComponent.java
new file mode 100644
index 0000000..ed26113
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersInjectComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import dagger.Component;
+
+@Component(modules = {MembersInjectModule.class})
+interface MembersInjectComponent {
+
+ void inject(ChildOfStringArray subfoo);
+ void inject(ChildOfArrayOfParentOfStringArray subfoo);
+ void inject(ChildOfPrimitiveIntArray subfoo);
+ void inject(RawFrameworkTypes rawFrameworkTypes);
+
+}
diff --git a/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java b/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java
new file mode 100644
index 0000000..3519348
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import javax.inject.Inject;
+
+class MembersInjectGenericParent<T> {
+
+ @Inject T t;
+
+}
diff --git a/javatests/dagger/functional/membersinject/MembersInjectModule.java b/javatests/dagger/functional/membersinject/MembersInjectModule.java
new file mode 100644
index 0000000..d60a7d1
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersInjectModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+class MembersInjectModule {
+
+ @Provides String[] provideStringArray() { return new String[10]; }
+
+ @Provides int[] provideIntArray() { return new int[10]; }
+
+ @SuppressWarnings("unchecked")
+ @Provides MembersInjectGenericParent<String[]>[] provideFooArrayOfStringArray() { return new MembersInjectGenericParent[10]; }
+
+}
diff --git a/javatests/dagger/functional/membersinject/MembersInjectTest.java b/javatests/dagger/functional/membersinject/MembersInjectTest.java
new file mode 100644
index 0000000..0617fd5
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersInjectTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.MembersInjector;
+import dagger.functional.multipackage.DaggerMembersInjectionVisibilityComponent;
+import dagger.functional.multipackage.MembersInjectionVisibilityComponent;
+import dagger.functional.multipackage.a.AGrandchild;
+import dagger.functional.multipackage.a.AParent;
+import dagger.functional.multipackage.b.BChild;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MembersInjectTest {
+ @Test public void testMembersInject_arrays() {
+ MembersInjectComponent component = DaggerMembersInjectComponent.builder().build();
+
+ ChildOfStringArray childOfStringArray = new ChildOfStringArray();
+ component.inject(childOfStringArray);
+ }
+
+ @Test public void testMembersInject_nestedArrays() {
+ MembersInjectComponent component = DaggerMembersInjectComponent.builder().build();
+
+ ChildOfArrayOfParentOfStringArray childOfArrayOfParentOfStringArray =
+ new ChildOfArrayOfParentOfStringArray();
+ component.inject(childOfArrayOfParentOfStringArray);
+ }
+
+ @Test public void testMembersInject_primitives() {
+ MembersInjectComponent component = DaggerMembersInjectComponent.builder().build();
+
+ ChildOfPrimitiveIntArray childOfPrimitiveIntArray = new ChildOfPrimitiveIntArray();
+ component.inject(childOfPrimitiveIntArray);
+ }
+
+ @Test
+ public void testMembersInject_overrides() {
+ MembersInjectionVisibilityComponent component =
+ DaggerMembersInjectionVisibilityComponent.create();
+ AParent aParent = new AParent();
+ component.inject(aParent);
+ assertThat(aParent.aParentField()).isNotNull();
+ assertThat(aParent.aParentMethod()).isNotNull();
+
+ BChild aChild = new BChild();
+ component.inject(aChild);
+ assertThat(aChild.aParentField()).isNotNull();
+ assertThat(aChild.aParentMethod()).isNull();
+ assertThat(aChild.aChildField()).isNotNull();
+ assertThat(aChild.aChildMethod()).isNotNull();
+
+ AGrandchild aGrandchild = new AGrandchild();
+ component.inject(aGrandchild);
+ assertThat(aGrandchild.aParentField()).isNotNull();
+ assertThat(aGrandchild.aParentMethod()).isNotNull();
+ assertThat(aGrandchild.aChildField()).isNotNull();
+ assertThat(aGrandchild.aChildMethod()).isNull();
+ assertThat(aGrandchild.aGrandchildField()).isNotNull();
+ assertThat(aGrandchild.aGrandchildMethod()).isNotNull();
+ }
+
+ @Test
+ public void testNonRequestedMembersInjector() {
+ NonRequestedChild child = new NonRequestedChild();
+ Provider<String> provider =
+ new Provider<String>() {
+ @Override
+ public String get() {
+ return "field!";
+ }
+ };
+ MembersInjector<NonRequestedChild> injector = new NonRequestedChild_MembersInjector(provider);
+ injector.injectMembers(child);
+ assertThat(child.t).isEqualTo("field!");
+ }
+}
diff --git a/javatests/dagger/functional/membersinject/MembersInjectionOrdering.java b/javatests/dagger/functional/membersinject/MembersInjectionOrdering.java
new file mode 100644
index 0000000..451e0e3
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersInjectionOrdering.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Inject;
+
+/**
+ * This exhibits a regression case, that albeit weird, is valid according to the JSR 330 spec. JSR
+ * 330 specifies a rough ordering by which members should be injected, and it is possible to rely on
+ * such ordering. When members injecting {@link Subtype}, field injection is guaranteed to be
+ * performed on {@link Base} first. The binding for {@code @FirstToString} in {@link
+ * OrderingModule#provideToString()} relies on this ordering, and thus uses the value in {@link
+ * Base#first} to satisfy the binding.
+ */
+class MembersInjectionOrdering {
+ static class Base {
+ @Inject First first;
+ }
+
+ static class Subtype extends Base {
+ @Inject String firstToString;
+ }
+
+ @Module
+ static class OrderingModule {
+ private final Subtype subtype;
+
+ OrderingModule(Subtype subtype) {
+ this.subtype = subtype;
+ }
+
+ @Provides
+ String provideToString() {
+ return subtype.first.toString();
+ }
+ }
+
+ @Component(modules = OrderingModule.class)
+ interface TestComponent {
+ void inject(Subtype subtype);
+ }
+
+ static class First {
+ @Inject
+ First() {}
+ }
+}
diff --git a/javatests/dagger/functional/membersinject/MembersInjectionOrderingTest.java b/javatests/dagger/functional/membersinject/MembersInjectionOrderingTest.java
new file mode 100644
index 0000000..efd5117
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersInjectionOrderingTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import dagger.functional.membersinject.MembersInjectionOrdering.OrderingModule;
+import dagger.functional.membersinject.MembersInjectionOrdering.Subtype;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MembersInjectionOrderingTest {
+ @Test
+ public void indirection() {
+ Subtype toInject = new Subtype();
+ DaggerMembersInjectionOrdering_TestComponent.builder()
+ .orderingModule(new OrderingModule(toInject))
+ .build()
+ .inject(toInject);
+ }
+}
diff --git a/javatests/dagger/functional/membersinject/MembersWithSameName.java b/javatests/dagger/functional/membersinject/MembersWithSameName.java
new file mode 100644
index 0000000..2b06898
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersWithSameName.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import javax.inject.Inject;
+
+// https://github.com/google/dagger/issues/755
+public class MembersWithSameName {
+ @Inject String sameName;
+ boolean sameNameStringWasInvoked;
+ boolean sameNameObjectWasInvoked;
+
+ @Inject void sameName(String sameName) {
+ sameNameStringWasInvoked = true;
+ }
+
+ @Inject void sameName(Object sameName) {
+ sameNameObjectWasInvoked = true;
+ }
+}
diff --git a/javatests/dagger/functional/membersinject/MembersWithSameNameTest.java b/javatests/dagger/functional/membersinject/MembersWithSameNameTest.java
new file mode 100644
index 0000000..0f22bf3
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersWithSameNameTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.functional.membersinject.subpackage.ExtendsMembersWithSameName;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+// https://github.com/google/dagger/issues/755
+@RunWith(JUnit4.class)
+public class MembersWithSameNameTest {
+ @Test
+ public void injectsMaskedMembers() {
+ MembersWithSameName membersWithSameName = new MembersWithSameName();
+ TestComponent component = DaggerMembersWithSameNameTest_TestComponent.create();
+ component.inject(membersWithSameName);
+ verifyBaseClassInjection(membersWithSameName);
+ }
+
+ @Test
+ public void subclassInjectsMaskedMembers() {
+ ExtendsMembersWithSameName extendsMembersWithSameName = new ExtendsMembersWithSameName();
+ TestComponent component = DaggerMembersWithSameNameTest_TestComponent.create();
+ component.inject(extendsMembersWithSameName);
+ verifyBaseClassInjection(extendsMembersWithSameName);
+ verifySubclassInjection(extendsMembersWithSameName);
+ }
+
+ private void verifyBaseClassInjection(MembersWithSameName membersWithSameName) {
+ assertThat(membersWithSameName.sameName).isNotNull();
+ assertThat(membersWithSameName.sameNameStringWasInvoked).isTrue();
+ assertThat(membersWithSameName.sameNameObjectWasInvoked).isTrue();
+ }
+
+ private void verifySubclassInjection(ExtendsMembersWithSameName extendsMembersWithSameName) {
+ assertThat(extendsMembersWithSameName.sameName()).isNotNull();
+ assertThat(extendsMembersWithSameName.sameNameStringWasInvoked()).isTrue();
+ assertThat(extendsMembersWithSameName.sameNameObjectWasInvoked()).isTrue();
+ }
+
+ @Module
+ abstract static class TestModule {
+ @Provides
+ static String provideString() {
+ return "";
+ }
+
+ @Binds
+ abstract Object bindObject(String string);
+ }
+
+ @Component(modules = TestModule.class)
+ interface TestComponent {
+ void inject(MembersWithSameName membersWithSameName);
+ void inject(ExtendsMembersWithSameName extendsMembersWithSameName);
+ }
+}
diff --git a/javatests/dagger/functional/membersinject/NonRequestedChild.java b/javatests/dagger/functional/membersinject/NonRequestedChild.java
new file mode 100644
index 0000000..a060473
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/NonRequestedChild.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import javax.inject.Inject;
+
+/**
+ * A class that should not be requested by any component, to ensure that we still generate a members
+ * injector for it.
+ */
+class NonRequestedChild extends MembersInjectGenericParent<String> {
+ @Inject
+ NonRequestedChild() {}
+}
diff --git a/javatests/dagger/functional/membersinject/RawFrameworkTypes.java b/javatests/dagger/functional/membersinject/RawFrameworkTypes.java
new file mode 100644
index 0000000..2180613
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/RawFrameworkTypes.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import dagger.Lazy;
+import dagger.MembersInjector;
+import javax.inject.Provider;
+
+// https://github.com/google/dagger/issues/419
+@SuppressWarnings({"rawtypes", "unused"})
+class RawFrameworkTypes {
+ void nonInjectMethodWithARawProvider(Provider rawProvider) {}
+ void nonInjectMethodWithARawLazy(Lazy rawLazy) {}
+ void nonInjectMethodWithARawMembersInjector(MembersInjector rawMembersInjector) {}
+}
diff --git a/javatests/dagger/functional/membersinject/subpackage/ExtendsMembersWithSameName.java b/javatests/dagger/functional/membersinject/subpackage/ExtendsMembersWithSameName.java
new file mode 100644
index 0000000..1eb1b15
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/subpackage/ExtendsMembersWithSameName.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject.subpackage;
+
+import dagger.functional.membersinject.MembersWithSameName;
+import javax.inject.Inject;
+
+// https://github.com/google/dagger/issues/755
+public class ExtendsMembersWithSameName extends MembersWithSameName {
+ @Inject String sameName;
+ private boolean sameNameStringWasInvoked;
+ private boolean sameNameObjectWasInvoked;
+
+ @Inject void sameName(String sameName) {
+ sameNameStringWasInvoked = true;
+ }
+
+ @Inject void sameName(Object sameName) {
+ sameNameObjectWasInvoked = true;
+ }
+
+ public String sameName() {
+ return sameName;
+ }
+ public boolean sameNameStringWasInvoked() {
+ return sameNameStringWasInvoked;
+ }
+ public boolean sameNameObjectWasInvoked() {
+ return sameNameObjectWasInvoked;
+ }
+}
diff --git a/javatests/dagger/functional/modules/ModuleIncludesTest.java b/javatests/dagger/functional/modules/ModuleIncludesTest.java
new file mode 100644
index 0000000..38f7d97
--- /dev/null
+++ b/javatests/dagger/functional/modules/ModuleIncludesTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.modules;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.functional.modules.subpackage.PublicModule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ModuleIncludesTest {
+
+ @Component(modules = PublicModule.class)
+ interface TestComponent {
+ Object object();
+ }
+
+ @Test
+ public void publicModuleIncludingPackagePrivateModuleThatDoesNotRequireInstance() {
+ TestComponent component = DaggerModuleIncludesTest_TestComponent.create();
+ assertThat(component.object()).isEqualTo("foo42");
+ }
+}
diff --git a/javatests/dagger/functional/modules/subpackage/PackagePrivateModule.java b/javatests/dagger/functional/modules/subpackage/PackagePrivateModule.java
new file mode 100644
index 0000000..66852d7
--- /dev/null
+++ b/javatests/dagger/functional/modules/subpackage/PackagePrivateModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.modules.subpackage;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+abstract class PackagePrivateModule {
+ @Binds
+ abstract Object bindObject(String string);
+
+ @Provides
+ static String provideString(int i) {
+ return "foo" + i;
+ }
+}
diff --git a/javatests/dagger/functional/modules/subpackage/PublicModule.java b/javatests/dagger/functional/modules/subpackage/PublicModule.java
new file mode 100644
index 0000000..7c5fd29
--- /dev/null
+++ b/javatests/dagger/functional/modules/subpackage/PublicModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.modules.subpackage;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module(includes = PackagePrivateModule.class)
+public abstract class PublicModule {
+ @Provides
+ static int provideInt() {
+ return 42;
+ }
+}
diff --git a/javatests/dagger/functional/multibindings/BindsInaccessibleMapKey.java b/javatests/dagger/functional/multibindings/BindsInaccessibleMapKey.java
new file mode 100644
index 0000000..6256812
--- /dev/null
+++ b/javatests/dagger/functional/multibindings/BindsInaccessibleMapKey.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multibindings;
+
+import dagger.Component;
+import dagger.functional.multibindings.subpackage.BindsInaccessibleMapKeyModule;
+import java.util.Map;
+
+// b/73820357
+@Component(modules = BindsInaccessibleMapKeyModule.class)
+interface BindsInaccessibleMapKey {
+ Map<Class<?>, Object> mapWithAnInaccessibleMapKey();
+}
diff --git a/javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java b/javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java
new file mode 100644
index 0000000..f2b5598
--- /dev/null
+++ b/javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multibindings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.value.AutoAnnotation;
+import dagger.Component;
+import dagger.MapKey;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ComplexMapKeysInDifferentOrderTest {
+ @MapKey(unwrapValue = false)
+ @interface ComplexMapKey {
+ int i();
+ int j();
+ }
+
+ @Module
+ interface TestModule {
+ @Provides
+ @IntoMap
+ @ComplexMapKey(i = 1, j = 2)
+ static int inOrder() {
+ return 3;
+ }
+
+ @Provides
+ @IntoMap
+ @ComplexMapKey(j = 4, i = 5)
+ static int backwardsOrder() {
+ return 6;
+ }
+ }
+
+ @Component(modules = TestModule.class)
+ interface TestComponent {
+ Map<ComplexMapKey, Integer> map();
+ }
+
+ @Test
+ public void test() {
+ Map<ComplexMapKey, Integer> map =
+ DaggerComplexMapKeysInDifferentOrderTest_TestComponent.create().map();
+ assertThat(map.get(mapKey(1, 2))).isEqualTo(3);
+ assertThat(map.get(mapKey(5, 4))).isEqualTo(6);
+ }
+
+ @AutoAnnotation
+ static ComplexMapKey mapKey(int i, int j) {
+ return new AutoAnnotation_ComplexMapKeysInDifferentOrderTest_mapKey(i, j);
+ }
+}
diff --git a/javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java b/javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java
new file mode 100644
index 0000000..f188405
--- /dev/null
+++ b/javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multibindings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.value.AutoAnnotation;
+import dagger.Component;
+import dagger.MapKey;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MapKeyWithDefaultTest {
+ @MapKey(unwrapValue = false)
+ @interface MapKeyWithDefault {
+ boolean hasDefault() default true;
+ boolean required();
+ }
+
+ @Module
+ interface TestModule {
+ @Provides
+ @IntoMap
+ @MapKeyWithDefault(required = false)
+ static int justRequired() {
+ return 1;
+ }
+
+ @Provides
+ @IntoMap
+ @MapKeyWithDefault(required = false, hasDefault = false)
+ static int both() {
+ return 2;
+ }
+ }
+
+ @Component(modules = TestModule.class)
+ interface TestComponent {
+ Map<MapKeyWithDefault, Integer> map();
+ }
+
+ @Test
+ public void test() {
+ Map<MapKeyWithDefault, Integer> map = DaggerMapKeyWithDefaultTest_TestComponent.create().map();
+ assertThat(map).hasSize(2);
+ assertThat(map.get(mapKey(true, false))).isEqualTo(1);
+ assertThat(map.get(mapKey(false, false))).isEqualTo(2);
+ }
+
+ @AutoAnnotation
+ static MapKeyWithDefault mapKey(boolean hasDefault, boolean required) {
+ return new AutoAnnotation_MapKeyWithDefaultTest_mapKey(hasDefault, required);
+ }
+}
diff --git a/javatests/dagger/functional/multibindings/subpackage/BindsInaccessibleMapKeyModule.java b/javatests/dagger/functional/multibindings/subpackage/BindsInaccessibleMapKeyModule.java
new file mode 100644
index 0000000..62df4ae
--- /dev/null
+++ b/javatests/dagger/functional/multibindings/subpackage/BindsInaccessibleMapKeyModule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multibindings.subpackage;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import javax.inject.Inject;
+
+@Module
+public abstract class BindsInaccessibleMapKeyModule {
+ @Binds
+ @IntoMap
+ @ClassKey(Inaccessible.class)
+ abstract Object bindInaccessibleMapKey(Inaccessible inaccessible);
+
+ static class Inaccessible {
+ @Inject Inaccessible() {}
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/FooComponent.java b/javatests/dagger/functional/multipackage/FooComponent.java
new file mode 100644
index 0000000..ef5b8d5
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/FooComponent.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage;
+
+import dagger.Component;
+import dagger.functional.multipackage.a.AModule;
+import dagger.functional.multipackage.a.UsesInaccessible;
+import dagger.functional.multipackage.a.UsesInaccessibleInGenericsOnly;
+import dagger.functional.multipackage.sub.FooChildComponent;
+import java.util.Set;
+
+/**
+ * A component that tests the interaction between subcomponents, multiple packages, and
+ * multibindings. Specifically, we want:
+ * <ul>
+ * <li>A set binding with some contributions in the parent component, and some in the subcomponent.
+ * <li>The contributions come from different packages, but not the package of either component.
+ * <li>The set binding is requested in the subcomponent through a binding from a separate package.
+ * <li>No binding in the subcomponent, that's in the subcomponent's package, directly uses any
+ * binding from the component's package.
+ * </ul>
+ */
+// NOTE(beder): Be careful about changing any bindings in either this component or the subcomponent.
+// Even adding a binding might stop this test from testing what it's supposed to test.
+@Component(modules = {AModule.class})
+interface FooComponent {
+ Set<String> setOfString();
+
+ FooChildComponent fooChildComponent();
+
+ UsesInaccessible usesInaccessible();
+
+ UsesInaccessibleInGenericsOnly accessibleConstructorUsesInaccessibleInGenericsOnly();
+}
diff --git a/javatests/dagger/functional/multipackage/MembersInjectionVisibilityComponent.java b/javatests/dagger/functional/multipackage/MembersInjectionVisibilityComponent.java
new file mode 100644
index 0000000..32b9c5e
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/MembersInjectionVisibilityComponent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage;
+
+import dagger.Component;
+import dagger.functional.multipackage.a.AGrandchild;
+import dagger.functional.multipackage.a.AModule;
+import dagger.functional.multipackage.a.AParent;
+import dagger.functional.multipackage.b.BChild;
+
+/**
+ * A component that tests members injection across packages and subclasses.
+ */
+@Component(modules = {AModule.class})
+public interface MembersInjectionVisibilityComponent {
+ void inject(AParent aParent);
+
+ void inject(BChild aChild);
+
+ void inject(AGrandchild aGrandchild);
+}
diff --git a/javatests/dagger/functional/multipackage/MultibindsComponent.java b/javatests/dagger/functional/multipackage/MultibindsComponent.java
new file mode 100644
index 0000000..dbeeed8
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/MultibindsComponent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage;
+
+import dagger.Component;
+import dagger.functional.multipackage.a.AMultibindsModule;
+import dagger.functional.multipackage.a.UsesInaccessible;
+
+/**
+ * A component that tests the interaction between multiple packages and {@code @Multibinding}s.
+ * Specifically, we want:
+ *
+ * <ul>
+ * <li>A {@code @Multibinding} for an empty set of a type not accessible from this package.
+ * <li>A {@code @Multibinding} for an empty map of a type not accessible from this package.
+ * <li>A public type that injects the empty set and map of inaccessible objects.
+ * </ul>
+ */
+@Component(modules = {AMultibindsModule.class})
+interface MultibindsComponent {
+ UsesInaccessible usesInaccessible();
+}
diff --git a/javatests/dagger/functional/multipackage/PrimitivesAcrossPackagesComponent.java b/javatests/dagger/functional/multipackage/PrimitivesAcrossPackagesComponent.java
new file mode 100644
index 0000000..639558f
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/PrimitivesAcrossPackagesComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage;
+
+import dagger.Component;
+import dagger.functional.multipackage.primitives.PrimitiveAcrossPackagesModule;
+import javax.inject.Provider;
+
+// b/77150738#comment11
+@Component(modules = PrimitiveAcrossPackagesModule.class)
+interface PrimitivesAcrossPackagesComponent {
+ boolean primitive();
+
+ Provider<Boolean> boxedPrimitive();
+}
diff --git a/javatests/dagger/functional/multipackage/UsesModuleWithInaccessibleConstructor.java b/javatests/dagger/functional/multipackage/UsesModuleWithInaccessibleConstructor.java
new file mode 100644
index 0000000..7764112
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/UsesModuleWithInaccessibleConstructor.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage;
+
+import dagger.Component;
+import dagger.functional.multipackage.moduleconstructor.ModuleWithInaccessibleConstructor;
+
+@Component(modules = ModuleWithInaccessibleConstructor.class)
+public interface UsesModuleWithInaccessibleConstructor {
+ int i();
+}
diff --git a/javatests/dagger/functional/multipackage/a/AGrandchild.java b/javatests/dagger/functional/multipackage/a/AGrandchild.java
new file mode 100644
index 0000000..ffb03d5
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/AGrandchild.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import dagger.functional.multipackage.b.BChild;
+import javax.inject.Inject;
+
+public class AGrandchild extends BChild {
+
+ @Inject APackagePrivateObject aGrandchildField;
+
+ private APackagePrivateObject aGrandchildMethod;
+
+ @Inject
+ void aGrandchildMethod(APackagePrivateObject aGrandchildMethod) {
+ this.aGrandchildMethod = aGrandchildMethod;
+ }
+
+ @Override
+ @Inject
+ protected void aParentMethod(APublicObject aParentMethod) {
+ super.aParentMethod(aParentMethod);
+ }
+
+ @SuppressWarnings("OverridesJavaxInjectableMethod")
+ @Override
+ protected void aChildMethod(APublicObject aChildMethod) {
+ super.aChildMethod(aChildMethod);
+ }
+
+ public APackagePrivateObject aGrandchildField() {
+ return aGrandchildField;
+ }
+
+ public APackagePrivateObject aGrandchildMethod() {
+ return aGrandchildMethod;
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/a/AModule.java b/javatests/dagger/functional/multipackage/a/AModule.java
new file mode 100644
index 0000000..9f6a7c5
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/AModule.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import dagger.multibindings.StringKey;
+import java.util.HashSet;
+import java.util.Set;
+
+@Module
+public abstract class AModule {
+ @Provides
+ @IntoSet
+ static String provideString() {
+ return "a";
+ }
+
+ @Binds
+ @IntoSet
+ abstract Inaccessible provideInaccessible(Inaccessible inaccessible);
+
+ @Provides
+ @ElementsIntoSet
+ static Set<Inaccessible> provideSetOfInaccessibles() {
+ return new HashSet<>();
+ }
+
+ @Binds
+ @IntoMap
+ @StringKey("inaccessible")
+ abstract Inaccessible provideInaccessibleToMap(Inaccessible inaccessible);
+}
diff --git a/javatests/dagger/functional/multipackage/a/AMultibindsModule.java b/javatests/dagger/functional/multipackage/a/AMultibindsModule.java
new file mode 100644
index 0000000..bcf28c9
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/AMultibindsModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import dagger.Module;
+import dagger.multibindings.Multibinds;
+import java.util.Map;
+import java.util.Set;
+
+/** A module that {@code @Multibinds} a set and a map of {@link Inaccessible}. */
+@Module
+public abstract class AMultibindsModule {
+ @Multibinds
+ abstract Set<Inaccessible> inaccessibleSet();
+
+ @Multibinds
+ abstract Map<String, Inaccessible> inaccessibleMap();
+}
diff --git a/javatests/dagger/functional/multipackage/a/APackagePrivateObject.java b/javatests/dagger/functional/multipackage/a/APackagePrivateObject.java
new file mode 100644
index 0000000..f674b82
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/APackagePrivateObject.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import javax.inject.Inject;
+
+class APackagePrivateObject {
+
+ @Inject
+ APackagePrivateObject() {}
+}
diff --git a/javatests/dagger/functional/multipackage/a/AParent.java b/javatests/dagger/functional/multipackage/a/AParent.java
new file mode 100644
index 0000000..de99ffd
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/AParent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import javax.inject.Inject;
+
+public class AParent {
+
+ @Inject APackagePrivateObject aParentField;
+
+ private APublicObject aParentMethod;
+
+ @Inject
+ protected void aParentMethod(APublicObject aParentMethod) {
+ this.aParentMethod = aParentMethod;
+ }
+
+ public APackagePrivateObject aParentField() {
+ return aParentField;
+ }
+
+ public APublicObject aParentMethod() {
+ return aParentMethod;
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/a/APublicObject.java b/javatests/dagger/functional/multipackage/a/APublicObject.java
new file mode 100644
index 0000000..6c85721
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/APublicObject.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import javax.inject.Inject;
+
+public class APublicObject {
+
+ @Inject
+ APublicObject() {}
+}
diff --git a/javatests/dagger/functional/multipackage/a/Inaccessible.java b/javatests/dagger/functional/multipackage/a/Inaccessible.java
new file mode 100644
index 0000000..30e2861
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/Inaccessible.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import javax.inject.Inject;
+
+final class Inaccessible {
+ @Inject Inaccessible() {}
+}
\ No newline at end of file
diff --git a/javatests/dagger/functional/multipackage/a/InaccessibleGeneric.java b/javatests/dagger/functional/multipackage/a/InaccessibleGeneric.java
new file mode 100644
index 0000000..f5da79d
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/InaccessibleGeneric.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import javax.inject.Inject;
+
+final class InaccessibleGeneric<T> {
+ @Inject
+ InaccessibleGeneric() {}
+}
diff --git a/javatests/dagger/functional/multipackage/a/UsesInaccessible.java b/javatests/dagger/functional/multipackage/a/UsesInaccessible.java
new file mode 100644
index 0000000..7a8ed7f
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/UsesInaccessible.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+
+@SuppressWarnings("unused")
+public class UsesInaccessible {
+ @Inject
+ public UsesInaccessible(
+ Inaccessible inaccessible,
+ Set<Inaccessible> inaccessibleSet,
+ Map<String, Inaccessible> inaccessibleMap,
+ InaccessibleGeneric<Integer> inaccessibleGeneric) {}
+}
diff --git a/javatests/dagger/functional/multipackage/a/UsesInaccessibleInGenericsOnly.java b/javatests/dagger/functional/multipackage/a/UsesInaccessibleInGenericsOnly.java
new file mode 100644
index 0000000..a69aa56
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/a/UsesInaccessibleInGenericsOnly.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.a;
+
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+
+@SuppressWarnings("unused")
+public class UsesInaccessibleInGenericsOnly {
+ @Inject
+ public UsesInaccessibleInGenericsOnly(
+ Set<Inaccessible> inaccessibleSet, Map<String, Inaccessible> inaccessibleMap) {}
+}
diff --git a/javatests/dagger/functional/multipackage/b/BChild.java b/javatests/dagger/functional/multipackage/b/BChild.java
new file mode 100644
index 0000000..fe5da66
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/b/BChild.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.b;
+
+import dagger.functional.multipackage.a.AParent;
+import dagger.functional.multipackage.a.APublicObject;
+import javax.inject.Inject;
+
+public class BChild extends AParent {
+
+ @Inject BPackagePrivateObject aChildField;
+
+ private APublicObject aChildMethod;
+
+ @Inject
+ protected void aChildMethod(APublicObject aChildMethod) {
+ this.aChildMethod = aChildMethod;
+ }
+
+ @SuppressWarnings("OverridesJavaxInjectableMethod")
+ @Override
+ protected void aParentMethod(APublicObject aParentMethod) {
+ super.aParentMethod(aParentMethod);
+ }
+
+ public BPackagePrivateObject aChildField() {
+ return aChildField;
+ }
+
+ public APublicObject aChildMethod() {
+ return aChildMethod;
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/b/BModule.java b/javatests/dagger/functional/multipackage/b/BModule.java
new file mode 100644
index 0000000..6edf4bf
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/b/BModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.b;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+public final class BModule {
+ @Provides
+ @IntoSet
+ static String provideString() {
+ return "b";
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/b/BPackagePrivateObject.java b/javatests/dagger/functional/multipackage/b/BPackagePrivateObject.java
new file mode 100644
index 0000000..b785a4a
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/b/BPackagePrivateObject.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.b;
+
+import javax.inject.Inject;
+
+class BPackagePrivateObject {
+
+ @Inject
+ BPackagePrivateObject() {}
+}
diff --git a/javatests/dagger/functional/multipackage/c/CModule.java b/javatests/dagger/functional/multipackage/c/CModule.java
new file mode 100644
index 0000000..df257ad
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/c/CModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.c;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+public final class CModule {
+ @Provides
+ @IntoSet
+ static String provideString() {
+ return "c";
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/d/DModule.java b/javatests/dagger/functional/multipackage/d/DModule.java
new file mode 100644
index 0000000..c98d647
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/d/DModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.d;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+public final class DModule {
+ @Provides
+ @IntoSet
+ static String provideString() {
+ return "d";
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/foo/Foo.java b/javatests/dagger/functional/multipackage/foo/Foo.java
new file mode 100644
index 0000000..1c06aa5
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/foo/Foo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.foo;
+
+import java.util.Set;
+import javax.inject.Inject;
+
+public final class Foo<T> {
+ public final Set<String> strings;
+
+ @Inject Foo(Set<String> strings) {
+ this.strings = strings;
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/grandsub/FooGrandchildComponent.java b/javatests/dagger/functional/multipackage/grandsub/FooGrandchildComponent.java
new file mode 100644
index 0000000..171b92a
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/grandsub/FooGrandchildComponent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.grandsub;
+
+import dagger.Subcomponent;
+import dagger.functional.multipackage.d.DModule;
+import dagger.functional.multipackage.foo.Foo;
+
+@Subcomponent(modules = DModule.class)
+public interface FooGrandchildComponent {
+ Foo<FooGrandchildComponent> foo();
+}
diff --git a/javatests/dagger/functional/multipackage/moduleconstructor/ModuleWithInaccessibleConstructor.java b/javatests/dagger/functional/multipackage/moduleconstructor/ModuleWithInaccessibleConstructor.java
new file mode 100644
index 0000000..6ca0074
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/moduleconstructor/ModuleWithInaccessibleConstructor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.moduleconstructor;
+
+import dagger.Module;
+import dagger.Provides;
+import java.util.Random;
+
+@Module
+public class ModuleWithInaccessibleConstructor {
+ private final int i;
+
+ /* intentionally package private */ModuleWithInaccessibleConstructor() {
+ i = new Random().nextInt();
+ }
+
+ @Provides
+ int i() {
+ return i;
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/primitives/PrimitiveAcrossPackagesModule.java b/javatests/dagger/functional/multipackage/primitives/PrimitiveAcrossPackagesModule.java
new file mode 100644
index 0000000..1e6e362
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/primitives/PrimitiveAcrossPackagesModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.primitives;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public final class PrimitiveAcrossPackagesModule {
+ // This method should be package-private so that a proxy method is created
+ @Provides
+ static boolean primitive() {
+ return false;
+ }
+}
diff --git a/javatests/dagger/functional/multipackage/sub/FooChildComponent.java b/javatests/dagger/functional/multipackage/sub/FooChildComponent.java
new file mode 100644
index 0000000..b4f1389
--- /dev/null
+++ b/javatests/dagger/functional/multipackage/sub/FooChildComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.multipackage.sub;
+
+import dagger.Subcomponent;
+import dagger.functional.multipackage.b.BModule;
+import dagger.functional.multipackage.c.CModule;
+import dagger.functional.multipackage.foo.Foo;
+import dagger.functional.multipackage.grandsub.FooGrandchildComponent;
+
+@Subcomponent(modules = {BModule.class, CModule.class})
+public interface FooChildComponent {
+ Foo<FooChildComponent> foo();
+
+ FooGrandchildComponent fooGrandchildComponent();
+}
diff --git a/javatests/dagger/functional/nullables/NullComponent.java b/javatests/dagger/functional/nullables/NullComponent.java
new file mode 100644
index 0000000..39f0d34
--- /dev/null
+++ b/javatests/dagger/functional/nullables/NullComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.nullables;
+
+import dagger.Component;
+import javax.inject.Provider;
+
+@Component(modules = NullModule.class)
+interface NullComponent {
+ NullFoo nullFoo();
+ @Nullable String string();
+ Provider<String> stringProvider();
+ Number number();
+ Provider<Number> numberProvider();
+ @Nullable Integer integer();
+}
diff --git a/javatests/dagger/functional/nullables/NullComponentWithDependency.java b/javatests/dagger/functional/nullables/NullComponentWithDependency.java
new file mode 100644
index 0000000..dfb4b82
--- /dev/null
+++ b/javatests/dagger/functional/nullables/NullComponentWithDependency.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.nullables;
+
+import dagger.Component;
+import javax.inject.Provider;
+
+@Component(dependencies = NullComponent.class)
+interface NullComponentWithDependency {
+ @Nullable String string();
+ Provider<String> stringProvider();
+ Number number();
+ Provider<Number> numberProvider();
+}
diff --git a/javatests/dagger/functional/nullables/NullFoo.java b/javatests/dagger/functional/nullables/NullFoo.java
new file mode 100644
index 0000000..b3f9a67
--- /dev/null
+++ b/javatests/dagger/functional/nullables/NullFoo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.nullables;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+class NullFoo {
+ final String string;
+ final Provider<String> stringProvider;
+ final Number number;
+ final Provider<Number> numberProvider;
+
+ @Inject
+ NullFoo(@Nullable String string,
+ Provider<String> stringProvider,
+ Number number,
+ Provider<Number> numberProvider) {
+ this.string = string;
+ this.stringProvider = stringProvider;
+ this.number = number;
+ this.numberProvider = numberProvider;
+ }
+
+ String methodInjectedString;
+ Provider<String> methodInjectedStringProvider;
+ Number methodInjectedNumber;
+ Provider<Number> methodInjectedNumberProvider;
+ @Inject void inject(@Nullable String string,
+ Provider<String> stringProvider,
+ Number number,
+ Provider<Number> numberProvider) {
+ this.methodInjectedString = string;
+ this.methodInjectedStringProvider = stringProvider;
+ this.methodInjectedNumber = number;
+ this.methodInjectedNumberProvider = numberProvider;
+ }
+
+ @Nullable @Inject String fieldInjectedString;
+ @Inject Provider<String> fieldInjectedStringProvider;
+ @Inject Number fieldInjectedNumber;
+ @Inject Provider<Number> fieldInjectedNumberProvider;
+}
diff --git a/javatests/dagger/functional/nullables/NullModule.java b/javatests/dagger/functional/nullables/NullModule.java
new file mode 100644
index 0000000..0219165
--- /dev/null
+++ b/javatests/dagger/functional/nullables/NullModule.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.nullables;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Reusable;
+
+@Module
+class NullModule {
+ Number numberValue = null;
+ Integer integerCallCount = 0;
+
+ @Nullable
+ @Provides
+ String provideNullableString() {
+ return null;
+ }
+
+ @Provides
+ Number provideNumber() {
+ return numberValue;
+ }
+
+ @Nullable
+ @Provides
+ @Reusable
+ Integer provideNullReusableInteger() {
+ integerCallCount++;
+ return null;
+ }
+}
diff --git a/javatests/dagger/functional/nullables/NullabilityTest.java b/javatests/dagger/functional/nullables/NullabilityTest.java
new file mode 100644
index 0000000..6706ece
--- /dev/null
+++ b/javatests/dagger/functional/nullables/NullabilityTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.nullables;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NullabilityTest {
+ @Test public void testNullability_provides() {
+ NullModule module = new NullModule();
+ NullComponent component = DaggerNullComponent.builder().nullModule(module).build();
+
+ // Can't construct NullFoo because it depends on Number, and Number was null.
+ try {
+ component.nullFoo();
+ fail();
+ } catch (NullPointerException npe) {
+ assertThat(npe)
+ .hasMessageThat()
+ .isEqualTo("Cannot return null from a non-@Nullable @Provides method");
+ }
+
+ // set number to non-null so we can create
+ module.numberValue = 1;
+ NullFoo nullFoo = component.nullFoo();
+
+ // Then set it back to null so we can test its providers.
+ module.numberValue = null;
+ validate(true, nullFoo.string, nullFoo.stringProvider, nullFoo.numberProvider);
+ validate(true, nullFoo.methodInjectedString, nullFoo.methodInjectedStringProvider,
+ nullFoo.methodInjectedNumberProvider);
+ validate(true, nullFoo.fieldInjectedString, nullFoo.fieldInjectedStringProvider,
+ nullFoo.fieldInjectedNumberProvider);
+ }
+
+ @Test public void testNullability_reusuable() {
+ NullModule module = new NullModule();
+ NullComponent component = DaggerNullComponent.builder().nullModule(module).build();
+
+ // Test that the @Nullable @Reusuable binding is cached properly even when the value is null.
+ assertThat(module.integerCallCount).isEqualTo(0);
+ assertThat(component.integer()).isNull();
+ assertThat(module.integerCallCount).isEqualTo(1);
+ assertThat(component.integer()).isNull();
+ assertThat(module.integerCallCount).isEqualTo(1);
+ }
+
+ @Test public void testNullability_components() {
+ NullComponent nullComponent = new NullComponent() {
+ @Override public Provider<String> stringProvider() {
+ return new Provider<String>() {
+ @Override public String get() {
+ return null;
+ }
+ };
+ }
+
+ @Override public String string() {
+ return null;
+ }
+
+ @Override public Provider<Number> numberProvider() {
+ return new Provider<Number>() {
+ @Override public Number get() {
+ return null;
+ }
+ };
+ }
+
+ @Override public Number number() {
+ return null;
+ }
+
+ @Override public NullFoo nullFoo() {
+ return null;
+ }
+
+ @Override public Integer integer() {
+ return null;
+ }
+ };
+ NullComponentWithDependency component =
+ DaggerNullComponentWithDependency.builder().nullComponent(nullComponent).build();
+ validate(false, component.string(), component.stringProvider(), component.numberProvider());
+
+ // Also validate that the component's number() method fails
+ try {
+ component.number();
+ fail();
+ } catch (NullPointerException npe) {
+ assertThat(npe)
+ .hasMessageThat()
+ .isEqualTo("Cannot return null from a non-@Nullable component method");
+ }
+ }
+
+ private void validate(boolean fromProvides,
+ String string,
+ Provider<String> stringProvider,
+ Provider<Number> numberProvider) {
+ assertThat(string).isNull();
+ assertThat(numberProvider).isNotNull();
+ try {
+ numberProvider.get();
+ fail();
+ } catch (NullPointerException npe) {
+ assertThat(npe)
+ .hasMessageThat()
+ .isEqualTo(
+ "Cannot return null from a non-@Nullable "
+ + (fromProvides ? "@Provides" : "component")
+ + " method");
+ }
+ assertThat(stringProvider.get()).isNull();
+ }
+}
diff --git a/javatests/dagger/functional/nullables/Nullable.java b/javatests/dagger/functional/nullables/Nullable.java
new file mode 100644
index 0000000..cbfb98a
--- /dev/null
+++ b/javatests/dagger/functional/nullables/Nullable.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.nullables;
+
+@interface Nullable {}
diff --git a/javatests/dagger/functional/producers/BUILD b/javatests/dagger/functional/producers/BUILD
new file mode 100644
index 0000000..36c9701
--- /dev/null
+++ b/javatests/dagger/functional/producers/BUILD
@@ -0,0 +1,45 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Functional tests for Dagger Producers
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+ "SOURCE_7_TARGET_7",
+)
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "producers-functional-tests",
+ srcs = glob(["**/*.java"]),
+ javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ deps = [
+ "//:producers_with_compiler",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/mockito",
+ "@google_bazel_common//third_party/java/truth",
+ "@google_bazel_common//third_party/java/truth:truth8",
+ ],
+)
+
+test_suite(name = "AllTests")
diff --git a/javatests/dagger/functional/producers/DependedComponent.java b/javatests/dagger/functional/producers/DependedComponent.java
new file mode 100644
index 0000000..9a4947a
--- /dev/null
+++ b/javatests/dagger/functional/producers/DependedComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import dagger.Component;
+
+@Component(modules = DependedModule.class)
+interface DependedComponent {
+ String getGreeting();
+}
+
diff --git a/javatests/dagger/functional/producers/DependedModule.java b/javatests/dagger/functional/producers/DependedModule.java
new file mode 100644
index 0000000..89ca4fc
--- /dev/null
+++ b/javatests/dagger/functional/producers/DependedModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class DependedModule {
+ @Provides
+ String provideGreeting() {
+ return "Hello world!";
+ }
+}
diff --git a/javatests/dagger/functional/producers/DependedProducerModule.java b/javatests/dagger/functional/producers/DependedProducerModule.java
new file mode 100644
index 0000000..c25d4e8
--- /dev/null
+++ b/javatests/dagger/functional/producers/DependedProducerModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+@ProducerModule
+final class DependedProducerModule {
+
+ @Produces
+ int produceNumberOfGreetings() {
+ return 2;
+ }
+}
diff --git a/javatests/dagger/functional/producers/DependedProductionComponent.java b/javatests/dagger/functional/producers/DependedProductionComponent.java
new file mode 100644
index 0000000..d5ebc4f
--- /dev/null
+++ b/javatests/dagger/functional/producers/DependedProductionComponent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionComponent;
+
+@ProductionComponent(modules = {ExecutorModule.class, DependedProducerModule.class})
+interface DependedProductionComponent {
+ ListenableFuture<Integer> numGreetings();
+}
+
diff --git a/javatests/dagger/functional/producers/DependentComponent.java b/javatests/dagger/functional/producers/DependentComponent.java
new file mode 100644
index 0000000..364676c
--- /dev/null
+++ b/javatests/dagger/functional/producers/DependentComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionComponent;
+import java.util.List;
+
+@ProductionComponent(
+ modules = {ExecutorModule.class, DependentProducerModule.class},
+ dependencies = {DependedComponent.class, DependedProductionComponent.class}
+)
+interface DependentComponent {
+ ListenableFuture<List<String>> greetings();
+}
diff --git a/javatests/dagger/functional/producers/DependentProducerModule.java b/javatests/dagger/functional/producers/DependentProducerModule.java
new file mode 100644
index 0000000..56b66f5
--- /dev/null
+++ b/javatests/dagger/functional/producers/DependentProducerModule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.util.List;
+
+@ProducerModule
+final class DependentProducerModule {
+ @Produces
+ ListenableFuture<List<String>> greetings(Integer numGreetings, String greeting) {
+ List<String> greetings = ImmutableList.of(
+ String.valueOf(numGreetings), greeting, Ascii.toUpperCase(greeting));
+ return Futures.immediateFuture(greetings);
+ }
+}
diff --git a/javatests/dagger/functional/producers/DependentTest.java b/javatests/dagger/functional/producers/DependentTest.java
new file mode 100644
index 0000000..920d753
--- /dev/null
+++ b/javatests/dagger/functional/producers/DependentTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DependentTest {
+ @Test public void dependentComponent() throws Exception {
+ DependentComponent dependentComponent =
+ DaggerDependentComponent.builder()
+ .dependedProductionComponent(DaggerDependedProductionComponent.create())
+ .dependedComponent(DaggerDependedComponent.create())
+ .build();
+ assertThat(dependentComponent).isNotNull();
+ assertThat(dependentComponent.greetings().get()).containsExactly(
+ "2", "Hello world!", "HELLO WORLD!");
+ }
+
+ @Test public void reuseBuilderWithDependentComponent() throws Exception {
+ DaggerDependentComponent.Builder dependentComponentBuilder = DaggerDependentComponent.builder();
+
+ DependentComponent componentUsingComponents =
+ dependentComponentBuilder
+ .dependedProductionComponent(DaggerDependedProductionComponent.create())
+ .dependedComponent(DaggerDependedComponent.create())
+ .build();
+
+ DependentComponent componentUsingJavaImpls = dependentComponentBuilder
+ .dependedProductionComponent(new DependedProductionComponent() {
+ @Override public ListenableFuture<Integer> numGreetings() {
+ return Futures.immediateFuture(3);
+ }
+ })
+ .dependedComponent(new DependedComponent() {
+ @Override public String getGreeting() {
+ return "Goodbye world!";
+ }
+ })
+ .build();
+
+ assertThat(componentUsingJavaImpls.greetings().get()).containsExactly(
+ "3", "Goodbye world!", "GOODBYE WORLD!");
+ assertThat(componentUsingComponents.greetings().get()).containsExactly(
+ "2", "Hello world!", "HELLO WORLD!");
+
+ }
+}
diff --git a/javatests/dagger/functional/producers/ExecutorModule.java b/javatests/dagger/functional/producers/ExecutorModule.java
new file mode 100644
index 0000000..5f8bfbf
--- /dev/null
+++ b/javatests/dagger/functional/producers/ExecutorModule.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.Module;
+import dagger.Provides;
+import dagger.producers.Production;
+import java.util.concurrent.Executor;
+
+/**
+ * A module that provides an optionally user-defined executor for a production component, defaulting
+ * to the direct executor.
+ */
+@Module
+public final class ExecutorModule {
+ private final Executor executor;
+
+ public ExecutorModule() {
+ this(MoreExecutors.directExecutor());
+ }
+
+ public ExecutorModule(Executor executor) {
+ this.executor = executor;
+ }
+
+ @Provides
+ @Production
+ Executor executor() {
+ return executor;
+ }
+}
diff --git a/javatests/dagger/functional/producers/GenericComponent.java b/javatests/dagger/functional/producers/GenericComponent.java
new file mode 100644
index 0000000..775ac2a
--- /dev/null
+++ b/javatests/dagger/functional/producers/GenericComponent.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.GenericComponent.NongenericModule;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+import java.util.Arrays;
+import java.util.List;
+
+@ProductionComponent(modules = {ExecutorModule.class, NongenericModule.class})
+interface GenericComponent {
+
+ ListenableFuture<List<String>> list(); // b/71595104
+
+ // b/71595104
+ @ProducerModule
+ abstract class GenericModule<T> {
+
+ @Produces
+ List<T> list(T t, String string) {
+ return Arrays.asList(t);
+ }
+ }
+
+ // b/71595104
+ @ProducerModule
+ class NongenericModule extends GenericModule<String> {
+ @Produces
+ static String string() {
+ return "string";
+ }
+ }
+}
diff --git a/javatests/dagger/functional/producers/ProducerFactoryTest.java b/javatests/dagger/functional/producers/ProducerFactoryTest.java
new file mode 100644
index 0000000..c85e342
--- /dev/null
+++ b/javatests/dagger/functional/producers/ProducerFactoryTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.when;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import dagger.producers.Producer;
+import dagger.producers.internal.AbstractProducer;
+import dagger.producers.internal.CancellableProducer;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import javax.inject.Provider;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class ProducerFactoryTest {
+ @Mock private ProductionComponentMonitor componentMonitor;
+ private ProducerMonitor monitor;
+ private Provider<Executor> executorProvider;
+ private Provider<ProductionComponentMonitor> componentMonitorProvider;
+
+ @Before
+ public void setUpMocks() {
+ MockitoAnnotations.initMocks(this);
+ monitor = Mockito.mock(ProducerMonitor.class, Mockito.CALLS_REAL_METHODS);
+ when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor);
+ // TODO(beder): Use Providers.of when available.
+ executorProvider =
+ new Provider<Executor>() {
+ @Override
+ public Executor get() {
+ return MoreExecutors.directExecutor();
+ }
+ };
+ componentMonitorProvider =
+ new Provider<ProductionComponentMonitor>() {
+ @Override
+ public ProductionComponentMonitor get() {
+ return componentMonitor;
+ }
+ };
+ }
+
+ @Test
+ public void noArgMethod() throws Exception {
+ ProducerToken token = ProducerToken.create(SimpleProducerModule_StrFactory.class);
+ Producer<String> producer =
+ SimpleProducerModule_StrFactory.create(executorProvider, componentMonitorProvider);
+ assertThat(producer.get().get()).isEqualTo("str");
+ InOrder order = inOrder(componentMonitor, monitor);
+ order.verify(componentMonitor).producerMonitorFor(token);
+ order.verify(monitor).methodStarting();
+ order.verify(monitor).methodFinished();
+ order.verify(monitor).succeeded("str");
+ order.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void singleArgMethod() throws Exception {
+ SettableFuture<Integer> intFuture = SettableFuture.create();
+ CancellableProducer<Integer> intProducer = producerOfFuture(intFuture);
+ Producer<String> producer =
+ SimpleProducerModule_StrWithArgFactory.create(
+ executorProvider, componentMonitorProvider, intProducer);
+ assertThat(producer.get().isDone()).isFalse();
+ intFuture.set(42);
+ assertThat(producer.get().get()).isEqualTo("str with arg");
+ }
+
+ @Test
+ public void successMonitor() throws Exception {
+ ProducerToken token = ProducerToken.create(SimpleProducerModule_SettableFutureStrFactory.class);
+
+ SettableFuture<String> strFuture = SettableFuture.create();
+ @SuppressWarnings("FutureReturnValueIgnored")
+ SettableFuture<SettableFuture<String>> strFutureFuture = SettableFuture.create();
+ CancellableProducer<SettableFuture<String>> strFutureProducer =
+ producerOfFuture(strFutureFuture);
+ Producer<String> producer =
+ SimpleProducerModule_SettableFutureStrFactory.create(
+ executorProvider, componentMonitorProvider, strFutureProducer);
+ assertThat(producer.get().isDone()).isFalse();
+
+ InOrder order = inOrder(componentMonitor, monitor);
+ order.verify(componentMonitor).producerMonitorFor(token);
+
+ strFutureFuture.set(strFuture);
+ order.verify(monitor).methodStarting();
+ order.verify(monitor).methodFinished();
+ assertThat(producer.get().isDone()).isFalse();
+
+ strFuture.set("monkey");
+ assertThat(producer.get().get()).isEqualTo("monkey");
+ order.verify(monitor).succeeded("monkey");
+
+ order.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void failureMonitor() throws Exception {
+ ProducerToken token = ProducerToken.create(SimpleProducerModule_SettableFutureStrFactory.class);
+
+ SettableFuture<String> strFuture = SettableFuture.create();
+ @SuppressWarnings("FutureReturnValueIgnored")
+ SettableFuture<SettableFuture<String>> strFutureFuture = SettableFuture.create();
+ CancellableProducer<SettableFuture<String>> strFutureProducer =
+ producerOfFuture(strFutureFuture);
+ Producer<String> producer =
+ SimpleProducerModule_SettableFutureStrFactory.create(
+ executorProvider, componentMonitorProvider, strFutureProducer);
+ assertThat(producer.get().isDone()).isFalse();
+
+ InOrder order = inOrder(componentMonitor, monitor);
+ order.verify(componentMonitor).producerMonitorFor(token);
+
+ strFutureFuture.set(strFuture);
+ order.verify(monitor).methodStarting();
+ order.verify(monitor).methodFinished();
+ assertThat(producer.get().isDone()).isFalse();
+
+ Throwable t = new RuntimeException("monkey");
+ strFuture.setException(t);
+ try {
+ producer.get().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isSameInstanceAs(t);
+ order.verify(monitor).failed(t);
+ }
+
+ order.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void failureMonitorDueToThrowingProducer() throws Exception {
+ ProducerToken token = ProducerToken.create(SimpleProducerModule_ThrowingProducerFactory.class);
+
+ Producer<String> producer =
+ SimpleProducerModule_ThrowingProducerFactory.create(
+ executorProvider, componentMonitorProvider);
+ assertThat(producer.get().isDone()).isTrue();
+
+ InOrder order = inOrder(componentMonitor, monitor);
+ order.verify(componentMonitor).producerMonitorFor(token);
+
+ order.verify(monitor).methodStarting();
+ order.verify(monitor).methodFinished();
+
+ try {
+ producer.get().get();
+ fail();
+ } catch (ExecutionException e) {
+ order.verify(monitor).failed(e.getCause());
+ }
+
+ order.verifyNoMoreInteractions();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void nullComponentMonitorProvider() throws Exception {
+ SimpleProducerModule_StrFactory.create(executorProvider, null);
+ }
+
+ private static <T> CancellableProducer<T> producerOfFuture(final ListenableFuture<T> future) {
+ return new AbstractProducer<T>() {
+ @Override
+ public ListenableFuture<T> compute() {
+ return future;
+ }
+ };
+ }
+}
diff --git a/javatests/dagger/functional/producers/ProvidesInProducerModule.java b/javatests/dagger/functional/producers/ProvidesInProducerModule.java
new file mode 100644
index 0000000..6e3b41f
--- /dev/null
+++ b/javatests/dagger/functional/producers/ProvidesInProducerModule.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.Provides;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.Production;
+import dagger.producers.ProductionComponent;
+import java.util.concurrent.Executor;
+
+final class ProvidesInProducerModule {
+ @ProducerModule
+ static class OnlyModule {
+ @Provides
+ @Production
+ static Executor provideExecutor() {
+ return MoreExecutors.directExecutor();
+ }
+
+ @Produces
+ static String produceString() {
+ return "produced";
+ }
+ }
+
+ @ProductionComponent(modules = OnlyModule.class)
+ interface C {
+ ListenableFuture<String> string();
+ }
+}
diff --git a/javatests/dagger/functional/producers/Request.java b/javatests/dagger/functional/producers/Request.java
new file mode 100644
index 0000000..d4020c8
--- /dev/null
+++ b/javatests/dagger/functional/producers/Request.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import javax.inject.Inject;
+
+final class Request {
+ private final String name;
+
+ @Inject
+ Request() {
+ this.name = "Request";
+ }
+
+ String name() {
+ return this.name;
+ }
+}
diff --git a/javatests/dagger/functional/producers/Response.java b/javatests/dagger/functional/producers/Response.java
new file mode 100644
index 0000000..ca5c0c2
--- /dev/null
+++ b/javatests/dagger/functional/producers/Response.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+final class Response {
+ private final String data;
+
+ Response(String data) {
+ this.data = data;
+ }
+
+ String data() {
+ return this.data;
+ }
+}
diff --git a/javatests/dagger/functional/producers/ResponseModule.java b/javatests/dagger/functional/producers/ResponseModule.java
new file mode 100644
index 0000000..9a5e25d
--- /dev/null
+++ b/javatests/dagger/functional/producers/ResponseModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class ResponseModule {
+ @Provides
+ static int requestNumber() {
+ return 5;
+ }
+}
diff --git a/javatests/dagger/functional/producers/ResponseProducerModule.java b/javatests/dagger/functional/producers/ResponseProducerModule.java
new file mode 100644
index 0000000..7b76ae2
--- /dev/null
+++ b/javatests/dagger/functional/producers/ResponseProducerModule.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Lazy;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+
+@ProducerModule(includes = ResponseModule.class)
+final class ResponseProducerModule {
+ @Qualifier
+ @interface RequestsProducerAndProduced {}
+
+ @Produces
+ static ListenableFuture<String> greeting() {
+ return Futures.immediateFuture("Hello");
+ }
+
+ @Produces
+ @RequestsProducerAndProduced
+ static ListenableFuture<String> intermediateGreeting(
+ // TODO(beder): Allow Producer and Provider of the same type (which would force the binding
+ // to be a provision binding), and add validation for that.
+ @SuppressWarnings("unused") String greeting,
+ Producer<String> greetingProducer,
+ @SuppressWarnings("unused") Produced<String> greetingProduced,
+ @SuppressWarnings("unused") Provider<Integer> requestNumberProvider,
+ @SuppressWarnings("unused") Lazy<Integer> requestNumberLazy) {
+ return greetingProducer.get();
+ }
+
+ @Produces
+ static Response response(
+ @RequestsProducerAndProduced String greeting, Request request, int requestNumber) {
+ return new Response(String.format("%s, %s #%d!", greeting, request.name(), requestNumber));
+ }
+}
diff --git a/javatests/dagger/functional/producers/SimpleComponent.java b/javatests/dagger/functional/producers/SimpleComponent.java
new file mode 100644
index 0000000..4b0af7f
--- /dev/null
+++ b/javatests/dagger/functional/producers/SimpleComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionComponent;
+
+@ProductionComponent(modules = {ExecutorModule.class, ResponseProducerModule.class})
+interface SimpleComponent {
+ ListenableFuture<Response> response();
+}
diff --git a/javatests/dagger/functional/producers/SimpleProducerModule.java b/javatests/dagger/functional/producers/SimpleProducerModule.java
new file mode 100644
index 0000000..b0c523d
--- /dev/null
+++ b/javatests/dagger/functional/producers/SimpleProducerModule.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import dagger.Lazy;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoSet;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.io.IOException;
+import java.util.Set;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+
+/**
+ * A module that contains various signatures of produces methods. This is not used in any
+ * components.
+ */
+@ProducerModule
+final class SimpleProducerModule {
+ @Qualifier @interface Qual {
+ int value();
+ }
+
+ // Unique bindings.
+
+ @Produces
+ @Qual(-2)
+ static ListenableFuture<String> throwingProducer() {
+ throw new RuntimeException("monkey");
+ }
+
+ @Produces
+ @Qual(-1)
+ static ListenableFuture<String> settableFutureStr(SettableFuture<String> future) {
+ return future;
+ }
+
+ @Produces
+ @Qual(0)
+ static String str() {
+ return "str";
+ }
+
+ @Produces
+ @Qual(1)
+ static ListenableFuture<String> futureStr() {
+ return Futures.immediateFuture("future str");
+ }
+
+ @Produces
+ @Qual(2)
+ static String strWithArg(@SuppressWarnings("unused") int i) {
+ return "str with arg";
+ }
+
+ @Produces
+ @Qual(3)
+ static ListenableFuture<String> futureStrWithArg(@SuppressWarnings("unused") int i) {
+ return Futures.immediateFuture("future str with arg");
+ }
+
+ @Produces
+ @Qual(4)
+ @SuppressWarnings("unused") // unthrown exception for test
+ static String strThrowingException() throws IOException {
+ return "str throwing exception";
+ }
+
+ @Produces
+ @Qual(5)
+ @SuppressWarnings("unused") // unthrown exception for test
+ static ListenableFuture<String> futureStrThrowingException() throws IOException {
+ return Futures.immediateFuture("future str throwing exception");
+ }
+
+ @Produces
+ @Qual(6)
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameter for test
+ static String strWithArgThrowingException(int i) throws IOException {
+ return "str with arg throwing exception";
+ }
+
+ @Produces
+ @Qual(7)
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameter for test
+ static ListenableFuture<String> futureStrWithArgThrowingException(int i) throws IOException {
+ return Futures.immediateFuture("future str with arg throwing exception");
+ }
+
+ @Produces
+ @Qual(8)
+ static String strWithArgs(
+ @SuppressWarnings("unused") int i,
+ @SuppressWarnings("unused") Produced<Double> b,
+ @SuppressWarnings("unused") Producer<Object> c,
+ @SuppressWarnings("unused") Provider<Boolean> d) {
+ return "str with args";
+ }
+
+ @Produces
+ @Qual(9)
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameters for test
+ static String strWithArgsThrowingException(
+ int i, Produced<Double> b, Producer<Object> c, Provider<Boolean> d) throws IOException {
+ return "str with args throwing exception";
+ }
+
+ @Produces
+ @Qual(10)
+ static ListenableFuture<String> futureStrWithArgs(
+ @SuppressWarnings("unused") int i,
+ @SuppressWarnings("unused") Produced<Double> b,
+ @SuppressWarnings("unused") Producer<Object> c,
+ @SuppressWarnings("unused") Provider<Boolean> d) {
+ return Futures.immediateFuture("future str with args");
+ }
+
+ @Produces
+ @Qual(11)
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameter for test
+ static ListenableFuture<String> futureStrWithArgsThrowingException(
+ int i, Produced<Double> b, Producer<Object> c, Provider<Boolean> d) throws IOException {
+ return Futures.immediateFuture("str with args throwing exception");
+ }
+
+ @Produces
+ @Qual(12)
+ static String strWithFrameworkTypeArgs(
+ @SuppressWarnings("unused") @Qual(1) int i,
+ @SuppressWarnings("unused") @Qual(1) Provider<Integer> iProvider,
+ @SuppressWarnings("unused") @Qual(1) Lazy<Integer> iLazy,
+ @SuppressWarnings("unused") @Qual(2) int j,
+ @SuppressWarnings("unused") @Qual(2) Produced<Integer> jProduced,
+ @SuppressWarnings("unused") @Qual(2) Producer<Integer> jProducer,
+ @SuppressWarnings("unused") @Qual(3) Produced<Integer> kProduced,
+ @SuppressWarnings("unused") @Qual(3) Producer<Integer> kProducer) {
+ return "str with framework type args";
+ }
+
+ // Set bindings.
+
+ @Produces
+ @IntoSet
+ static String setOfStrElement() {
+ return "set of str element";
+ }
+
+ @Produces
+ @IntoSet
+ @SuppressWarnings("unused") // unthrown exception for test
+ static String setOfStrElementThrowingException() throws IOException {
+ return "set of str element throwing exception";
+ }
+
+ @Produces
+ @IntoSet
+ static ListenableFuture<String> setOfStrFutureElement() {
+ return Futures.immediateFuture("set of str element");
+ }
+
+ @Produces
+ @IntoSet
+ @SuppressWarnings("unused") // unthrown exception for test
+ static ListenableFuture<String> setOfStrFutureElementThrowingException() throws IOException {
+ return Futures.immediateFuture("set of str element throwing exception");
+ }
+
+ @Produces
+ @IntoSet
+ static String setOfStrElementWithArg(@SuppressWarnings("unused") int i) {
+ return "set of str element with arg";
+ }
+
+ @Produces
+ @IntoSet
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameter for test
+ static String setOfStrElementWithArgThrowingException(int i) throws IOException {
+ return "set of str element with arg throwing exception";
+ }
+
+ @Produces
+ @IntoSet
+ static ListenableFuture<String> setOfStrFutureElementWithArg(@SuppressWarnings("unused") int i) {
+ return Futures.immediateFuture("set of str element with arg");
+ }
+
+ @Produces
+ @IntoSet
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameter for test
+ static ListenableFuture<String> setOfStrFutureElementWithArgThrowingException(int i)
+ throws IOException {
+ return Futures.immediateFuture("set of str element with arg throwing exception");
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static Set<String> setOfStrValues() {
+ return ImmutableSet.of("set of str 1", "set of str 2");
+ }
+
+ @Produces
+ @ElementsIntoSet
+ @SuppressWarnings("unused") // unthrown exception for test
+ static Set<String> setOfStrValuesThrowingException() throws IOException {
+ return ImmutableSet.of("set of str 1", "set of str 2 throwing exception");
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static ListenableFuture<Set<String>> setOfStrFutureValues() {
+ return Futures.<Set<String>>immediateFuture(ImmutableSet.of("set of str 1", "set of str 2"));
+ }
+
+ @Produces
+ @ElementsIntoSet
+ @SuppressWarnings("unused") // unthrown exception for test
+ static ListenableFuture<Set<String>> setOfStrFutureValuesThrowingException() throws IOException {
+ return Futures.<Set<String>>immediateFuture(
+ ImmutableSet.of("set of str 1", "set of str 2 throwing exception"));
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static Set<String> setOfStrValuesWithArg(@SuppressWarnings("unused") int i) {
+ return ImmutableSet.of("set of str with arg 1", "set of str with arg 2");
+ }
+
+ @Produces
+ @ElementsIntoSet
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameter for test
+ static Set<String> setOfStrValuesWithArgThrowingException(int i) throws IOException {
+ return ImmutableSet.of("set of str with arg 1", "set of str with arg 2 throwing exception");
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static ListenableFuture<Set<String>> setOfStrFutureValuesWithArg(
+ @SuppressWarnings("unused") int i) {
+ return Futures.<Set<String>>immediateFuture(
+ ImmutableSet.of("set of str with arg 1", "set of str with arg 2"));
+ }
+
+ @Produces
+ @ElementsIntoSet
+ @SuppressWarnings("unused") // unthrown exception for test, unused parameter for test
+ static ListenableFuture<Set<String>> setOfStrFutureValuesWithArgThrowingException(int i)
+ throws IOException {
+ return Futures.<Set<String>>immediateFuture(
+ ImmutableSet.of("set of str with arg 1", "set of str with arg 2 throwing exception"));
+ }
+
+ /**
+ * A binding method that might result in a generated factory with conflicting field and parameter
+ * names.
+ */
+ @Produces
+ static Object object(int foo, Provider<String> fooProvider) {
+ return foo + fooProvider.get();
+ }
+}
diff --git a/javatests/dagger/functional/producers/SimpleTest.java b/javatests/dagger/functional/producers/SimpleTest.java
new file mode 100644
index 0000000..e20d098
--- /dev/null
+++ b/javatests/dagger/functional/producers/SimpleTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SimpleTest {
+ @Test public void testSimpleComponent() throws Exception {
+ SimpleComponent simpleComponent = DaggerSimpleComponent.create();
+ assertThat(simpleComponent).isNotNull();
+ assertThat(simpleComponent.response().get().data()).isEqualTo("Hello, Request #5!");
+ }
+}
diff --git a/javatests/dagger/functional/producers/aot/ProducesMethodShadowsInjectConstructorTest.java b/javatests/dagger/functional/producers/aot/ProducesMethodShadowsInjectConstructorTest.java
new file mode 100644
index 0000000..ef37df2
--- /dev/null
+++ b/javatests/dagger/functional/producers/aot/ProducesMethodShadowsInjectConstructorTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.aot;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.BindsOptionalOf;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.Production;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import javax.inject.Inject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ProducesMethodShadowsInjectConstructorTest {
+ static class Multibound {}
+ static class Maybe {}
+
+ static class HasInjectConstructor {
+ @Inject HasInjectConstructor() {}
+ }
+
+ static class DependsOnShadowingProducer {}
+
+ @ProducerModule
+ abstract static class LeafModule {
+ @Produces
+ static DependsOnShadowingProducer dependsOnShadowingProducer(
+ // When viewed just within the leaf, this will resolve HasInjectConstructor to the @Inject
+ // constructor (and will receive a producerFromProvider), but when viewed within an ancestor
+ // that defines a @Produces method for HasInjectConstructor, the binding will be a regular
+ // Producer
+ HasInjectConstructor hasInjectConstructor,
+ Optional<Maybe> maybe) {
+ return new DependsOnShadowingProducer();
+ }
+
+ @Provides
+ @IntoSet
+ static Multibound provisionContribution() {
+ return new Multibound();
+ }
+
+ @BindsOptionalOf
+ abstract Maybe maybe();
+ }
+
+ @ProductionSubcomponent(modules = LeafModule.class)
+ interface Leaf {
+ ListenableFuture<DependsOnShadowingProducer> dependsOnShadowingProducer();
+ ListenableFuture<Set<Multibound>> shadowedProvisionMultibinding();
+ ListenableFuture<Optional<Maybe>> emptyProvisionBindingToPresentProductionBinding();
+ }
+
+ @ProducerModule
+ static class RootModule {
+ @Produces
+ static HasInjectConstructor shadowInjectConstructor() {
+ return new HasInjectConstructor();
+ }
+
+ @Produces
+ @IntoSet
+ static Multibound productionContribution() {
+ return new Multibound();
+ }
+
+ @Provides
+ @Production
+ static Executor executor() {
+ return MoreExecutors.directExecutor();
+ }
+
+ @Produces
+ static Maybe presentMaybeInParent() {
+ return new Maybe();
+ }
+ }
+
+ @ProductionComponent(modules = RootModule.class)
+ interface Root {
+ Leaf leaf();
+ }
+
+ @Test
+ public void shadowedInjectConstructorDoesNotCauseClassCast() throws Exception {
+ Leaf leaf = DaggerProducesMethodShadowsInjectConstructorTest_Root.create().leaf();
+ leaf.dependsOnShadowingProducer().get();
+ assertThat(leaf.shadowedProvisionMultibinding().get()).hasSize(2);
+ assertThat(leaf.emptyProvisionBindingToPresentProductionBinding().get()).isPresent();
+ }
+}
diff --git a/javatests/dagger/functional/producers/badexecutor/BadExecutorTest.java b/javatests/dagger/functional/producers/badexecutor/BadExecutorTest.java
new file mode 100644
index 0000000..e8fb3c5
--- /dev/null
+++ b/javatests/dagger/functional/producers/badexecutor/BadExecutorTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.badexecutor;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.functional.producers.ExecutorModule;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.RejectedExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** This test verifies behavior when the executor throws {@link RejectedExecutionException}. */
+@RunWith(JUnit4.class)
+public final class BadExecutorTest {
+ private SimpleComponent component;
+
+ @Before
+ public void setUpComponent() {
+ ComponentDependency dependency =
+ new ComponentDependency() {
+ @Override
+ public ListenableFuture<Double> doubleDep() {
+ return Futures.immediateFuture(42.0);
+ }
+ };
+ ListeningExecutorService executorService = MoreExecutors.newDirectExecutorService();
+ component =
+ DaggerSimpleComponent.builder()
+ .executorModule(new ExecutorModule(executorService))
+ .componentDependency(dependency)
+ .build();
+ executorService.shutdown();
+ }
+
+ @Test
+ public void rejectNoArgMethod() throws Exception {
+ try {
+ component.noArgStr().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isInstanceOf(RejectedExecutionException.class);
+ }
+ }
+
+ @Test
+ public void rejectSingleArgMethod() throws Exception {
+ try {
+ component.singleArgInt().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isInstanceOf(RejectedExecutionException.class);
+ }
+ }
+
+ @Test
+ public void rejectSingleArgFromComponentDepMethod() throws Exception {
+ try {
+ component.singleArgBool().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isInstanceOf(RejectedExecutionException.class);
+ }
+ }
+
+ @Test
+ public void doNotRejectComponentDepMethod() throws Exception {
+ assertThat(component.doubleDep().get()).isEqualTo(42.0);
+ }
+}
diff --git a/javatests/dagger/functional/producers/badexecutor/ComponentDependency.java b/javatests/dagger/functional/producers/badexecutor/ComponentDependency.java
new file mode 100644
index 0000000..97974e5
--- /dev/null
+++ b/javatests/dagger/functional/producers/badexecutor/ComponentDependency.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.badexecutor;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+interface ComponentDependency {
+ ListenableFuture<Double> doubleDep();
+}
diff --git a/javatests/dagger/functional/producers/badexecutor/SimpleComponent.java b/javatests/dagger/functional/producers/badexecutor/SimpleComponent.java
new file mode 100644
index 0000000..ea58125
--- /dev/null
+++ b/javatests/dagger/functional/producers/badexecutor/SimpleComponent.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.badexecutor;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.ProductionComponent;
+
+/**
+ * A component that contains entry points that exercise different execution paths, for verifying the
+ * behavior when the executor throws a {@link java.util.concurrent.RejectedExecutionException}.
+ */
+@ProductionComponent(
+ dependencies = ComponentDependency.class,
+ modules = {ExecutorModule.class, SimpleProducerModule.class}
+)
+interface SimpleComponent {
+ /** An entry point exposing a producer method with no args. */
+ ListenableFuture<String> noArgStr();
+
+ /** An entry point exposing a producer method that depends on another producer method. */
+ ListenableFuture<Integer> singleArgInt();
+
+ /** An entry point exposing a producer method that depends on a component dependency method. */
+ ListenableFuture<Boolean> singleArgBool();
+
+ /** An entry point exposing a component dependency method. */
+ ListenableFuture<Double> doubleDep();
+}
diff --git a/javatests/dagger/functional/producers/badexecutor/SimpleProducerModule.java b/javatests/dagger/functional/producers/badexecutor/SimpleProducerModule.java
new file mode 100644
index 0000000..f907fda
--- /dev/null
+++ b/javatests/dagger/functional/producers/badexecutor/SimpleProducerModule.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.badexecutor;
+
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+@ProducerModule
+final class SimpleProducerModule {
+ @Produces
+ static String noArgStr() {
+ return "no arg string";
+ }
+
+ @Produces
+ static int singleArgInt(String arg) {
+ return arg.length();
+ }
+
+ @Produces
+ static boolean singleArgBool(double arg) {
+ return arg > 0.0;
+ }
+}
diff --git a/javatests/dagger/functional/producers/binds/BindsProducersTest.java b/javatests/dagger/functional/producers/binds/BindsProducersTest.java
new file mode 100644
index 0000000..0949f02
--- /dev/null
+++ b/javatests/dagger/functional/producers/binds/BindsProducersTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.binds;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BindsProducersTest {
+
+ private SimpleBindsProductionComponent component;
+
+ @Before
+ public void setUp() {
+ component = DaggerSimpleBindsProductionComponent.create();
+ }
+
+ @Test
+ public void bindDelegates() throws Exception {
+ assertThat(component.object().get()).isInstanceOf(FooOfStrings.class);
+ assertThat(component.fooOfStrings().get()).isInstanceOf(FooOfStrings.class);
+ assertThat(component.fooOfIntegers().get()).isNotNull();
+ }
+
+ @Test
+ public void bindWithScope() throws Exception {
+ assertThat(component.qualifiedFooOfStrings().get())
+ .isSameInstanceAs(component.qualifiedFooOfStrings().get());
+ }
+
+ @Test
+ public void multibindings() throws Exception {
+ assertThat(component.foosOfNumbers().get()).hasSize(2);
+ assertThat(component.objects().get()).hasSize(3);
+ assertThat(component.charSequences().get()).hasSize(5);
+
+ assertThat(component.integerObjectMap().get())
+ .containsExactly(
+ 123, "123-string", 456, "456-string", 789, "789-string", -1, "provision-string");
+
+ Map<Integer, Producer<Object>> integerProducerOfObjectMap =
+ component.integerProducerOfObjectMap().get();
+ assertThat(integerProducerOfObjectMap).hasSize(4);
+ assertThat(integerProducerOfObjectMap.get(123).get().get()).isEqualTo("123-string");
+ assertThat(integerProducerOfObjectMap.get(456).get().get()).isEqualTo("456-string");
+ assertThat(integerProducerOfObjectMap.get(789).get().get()).isEqualTo("789-string");
+ assertThat(integerProducerOfObjectMap.get(-1).get().get()).isEqualTo("provision-string");
+
+ assertThat(component.integerProducedOfObjectMap().get())
+ .containsExactly(
+ 123, Produced.successful("123-string"),
+ 456, Produced.successful("456-string"),
+ 789, Produced.successful("789-string"),
+ -1, Produced.successful("provision-string"));
+
+ assertThat(component.qualifiedIntegerObjectMap().get()).hasSize(1);
+ }
+}
diff --git a/javatests/dagger/functional/producers/binds/Foo.java b/javatests/dagger/functional/producers/binds/Foo.java
new file mode 100644
index 0000000..4eb71fd
--- /dev/null
+++ b/javatests/dagger/functional/producers/binds/Foo.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.binds;
+
+/**
+ * This is the type that will be bound. We throw in generics just to complicate the test.
+ */
+interface Foo<T> {}
diff --git a/javatests/dagger/functional/producers/binds/FooOfStrings.java b/javatests/dagger/functional/producers/binds/FooOfStrings.java
new file mode 100644
index 0000000..b94d907
--- /dev/null
+++ b/javatests/dagger/functional/producers/binds/FooOfStrings.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.binds;
+
+/**
+ * This is not marked with {@link javax.inject.Inject @Inject} in order to test that {@link
+ * dagger.Binds @Binds} properly translate to {@code dagger.internal.codegen.ProductionBinding}s
+ * when the right-hand-side of the method is also a production binding. We force this by adding a
+ * {@link dagger.producers.Produces @Produces} method to add it to the graph instead of relying on
+ * the {@code dagger.internal.codegen.ProvisionBinding} that would be created by default with an
+ * {@code @Inject} constructor.
+ */
+final class FooOfStrings implements Foo<String> {}
diff --git a/javatests/dagger/functional/producers/binds/SimpleBindingModule.java b/javatests/dagger/functional/producers/binds/SimpleBindingModule.java
new file mode 100644
index 0000000..f2af277
--- /dev/null
+++ b/javatests/dagger/functional/producers/binds/SimpleBindingModule.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.binds;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.Production;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Executor;
+import javax.inject.Named;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+
+@ProducerModule(includes = {
+ SimpleBindingModule.ExecutorModule.class,
+ SimpleBindingModule.ProvisionModuleForMap.class
+})
+abstract class SimpleBindingModule {
+ @Binds
+ abstract Object bindObject(FooOfStrings impl);
+
+ @Binds
+ abstract Foo<String> bindFooOfStrings(FooOfStrings impl);
+
+ @Binds
+ abstract Foo<? extends Number> bindFooOfNumbers(Foo<Integer> fooOfIntegers);
+
+ @Binds
+ @Singleton
+ @SomeQualifier
+ abstract Foo<String> bindQualifiedFooOfStrings(FooOfStrings impl);
+
+ @Produces
+ static FooOfStrings produceFooOfStrings() {
+ return new FooOfStrings();
+ }
+
+ @Produces
+ static Foo<Integer> produceFooOfIntegers() {
+ return new Foo<Integer>() {};
+ }
+
+ @Produces
+ static Foo<Double> produceFooOfDoubles() {
+ return new Foo<Double>() {};
+ }
+
+ @Binds
+ @IntoSet
+ abstract Foo<? extends Number> bindFooOfIntegersIntoSet(Foo<Integer> fooOfIntegers);
+
+ @Binds
+ @IntoSet
+ abstract Foo<? extends Number> bindFooExtendsNumberIntoSet(Foo<Double> fooOfDoubles);
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<Object> bindSetOfFooNumbersToObjects(Set<Foo<? extends Number>> setOfFooNumbers);
+
+ @Binds
+ @IntoSet
+ abstract Object bindFooOfStringsIntoSetOfObjects(FooOfStrings impl);
+
+ @Produces
+ static HashSet<String> produceStringHashSet() {
+ return new HashSet<>(Arrays.asList("hash-string1", "hash-string2"));
+ }
+
+ @Produces
+ static TreeSet<CharSequence> produceCharSequenceTreeSet() {
+ return new TreeSet<CharSequence>(Arrays.asList("tree-charSequence1", "tree-charSequence2"));
+ }
+
+ @Produces
+ static Collection<CharSequence> produceCharSequenceCollection() {
+ return Arrays.<CharSequence>asList("list-charSequence");
+ }
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<CharSequence> bindHashSetOfStrings(HashSet<String> set);
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<CharSequence> bindTreeSetOfCharSequences(TreeSet<CharSequence> set);
+
+ @Binds
+ @ElementsIntoSet
+ abstract Set<CharSequence> bindCollectionOfCharSequences(Collection<CharSequence> collection);
+
+ @Qualifier
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface SomeQualifier {}
+
+ @Module
+ static final class ExecutorModule {
+ @Provides @Production
+ static Executor provideExecutor() {
+ return MoreExecutors.directExecutor();
+ }
+ }
+
+ @Binds
+ @IntoMap
+ @IntKey(123)
+ abstract Object bind123ForMap(@Named("For-123") String string);
+
+ @Binds
+ @IntoMap
+ @IntKey(456)
+ abstract Object bind456ForMap(@Named("For-456") String string);
+
+ @Produces
+ @IntoMap
+ @IntKey(789)
+ static Object produce789ForMap() {
+ return "789-string";
+ }
+
+ @Module
+ abstract static class ProvisionModuleForMap {
+ @Provides @Named("Provision string") static String provideProvisionString() {
+ return "provision-string";
+ }
+
+ @Binds
+ @IntoMap
+ @IntKey(-1)
+ abstract Object bindNegative1ForMap(@Named("Provision string") String string);
+ }
+
+ @Binds
+ @IntoMap
+ @IntKey(123)
+ @SomeQualifier
+ abstract Object bindFooOfStringsIntoQualifiedMap(FooOfStrings fooOfStrings);
+
+ @Produces
+ @Named("For-123")
+ static String produce123String() {
+ return "123-string";
+ }
+
+ @Produces
+ @Named("For-456")
+ static String produce456String() {
+ return "456-string";
+ }
+}
diff --git a/javatests/dagger/functional/producers/binds/SimpleBindsProductionComponent.java b/javatests/dagger/functional/producers/binds/SimpleBindsProductionComponent.java
new file mode 100644
index 0000000..21cf661
--- /dev/null
+++ b/javatests/dagger/functional/producers/binds/SimpleBindsProductionComponent.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.binds;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.binds.SimpleBindingModule.SomeQualifier;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.ProductionComponent;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Singleton;
+
+@Singleton
+@ProductionComponent(modules = SimpleBindingModule.class)
+public interface SimpleBindsProductionComponent {
+ ListenableFuture<Object> object();
+
+ ListenableFuture<Foo<String>> fooOfStrings();
+
+ @SomeQualifier
+ ListenableFuture<Foo<String>> qualifiedFooOfStrings();
+
+ ListenableFuture<Foo<Integer>> fooOfIntegers();
+
+ ListenableFuture<Set<Foo<? extends Number>>> foosOfNumbers();
+
+ ListenableFuture<Set<Object>> objects();
+
+ ListenableFuture<Set<CharSequence>> charSequences();
+
+ ListenableFuture<Map<Integer, Object>> integerObjectMap();
+
+ ListenableFuture<Map<Integer, Producer<Object>>> integerProducerOfObjectMap();
+
+ ListenableFuture<Map<Integer, Produced<Object>>> integerProducedOfObjectMap();
+
+ @SomeQualifier ListenableFuture<Map<Integer, Object>> qualifiedIntegerObjectMap();
+}
diff --git a/javatests/dagger/functional/producers/builder/DepComponent.java b/javatests/dagger/functional/producers/builder/DepComponent.java
new file mode 100644
index 0000000..c2811f1
--- /dev/null
+++ b/javatests/dagger/functional/producers/builder/DepComponent.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.builder;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+interface DepComponent {
+ ListenableFuture<Double> d();
+}
diff --git a/javatests/dagger/functional/producers/builder/IntModule.java b/javatests/dagger/functional/producers/builder/IntModule.java
new file mode 100644
index 0000000..ed2bb95
--- /dev/null
+++ b/javatests/dagger/functional/producers/builder/IntModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class IntModule {
+ @Provides
+ static int i() {
+ return 42;
+ }
+}
diff --git a/javatests/dagger/functional/producers/builder/ProductionComponentBuilderTest.java b/javatests/dagger/functional/producers/builder/ProductionComponentBuilderTest.java
new file mode 100644
index 0000000..14f153e
--- /dev/null
+++ b/javatests/dagger/functional/producers/builder/ProductionComponentBuilderTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link dagger.producers.ProductionComponent.Builder}. */
+@RunWith(JUnit4.class)
+public final class ProductionComponentBuilderTest {
+
+ @Test
+ public void successfulBuild() throws Exception {
+ TestComponentWithBuilder component =
+ DaggerTestComponentWithBuilder.builder()
+ .depComponent(depComponent(15.3))
+ .strModule(new StringModule())
+ .build();
+ assertThat(component.s().get()).isEqualTo("arg: 42");
+ assertThat(component.d().get()).isEqualTo(15.3);
+ }
+
+ @Test
+ public void successfulBuild_withMissingZeroArgModule() throws Exception {
+ TestComponentWithBuilder component =
+ DaggerTestComponentWithBuilder.builder()
+ .depComponent(depComponent(15.3))
+ .build();
+ assertThat(component.s().get()).isEqualTo("arg: 42");
+ assertThat(component.d().get()).isEqualTo(15.3);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void missingDepComponent() {
+ DaggerTestComponentWithBuilder.builder()
+ .strModule(new StringModule())
+ .build();
+ }
+
+ private static DepComponent depComponent(final double value) {
+ return new DepComponent() {
+ @Override
+ public ListenableFuture<Double> d() {
+ return Futures.immediateFuture(value);
+ }
+ };
+ }
+}
diff --git a/javatests/dagger/functional/producers/builder/StringModule.java b/javatests/dagger/functional/producers/builder/StringModule.java
new file mode 100644
index 0000000..6ad748f
--- /dev/null
+++ b/javatests/dagger/functional/producers/builder/StringModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.builder;
+
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+@ProducerModule
+final class StringModule {
+ @Produces
+ static String str(int i) {
+ return "arg: " + i;
+ }
+}
diff --git a/javatests/dagger/functional/producers/builder/TestComponentWithBuilder.java b/javatests/dagger/functional/producers/builder/TestComponentWithBuilder.java
new file mode 100644
index 0000000..91fb326
--- /dev/null
+++ b/javatests/dagger/functional/producers/builder/TestComponentWithBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.builder;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.ProductionComponent;
+
+@ProductionComponent(
+ modules = {ExecutorModule.class, StringModule.class, IntModule.class},
+ dependencies = DepComponent.class
+)
+interface TestComponentWithBuilder {
+ ListenableFuture<String> s();
+ ListenableFuture<Double> d();
+
+ @ProductionComponent.Builder
+ interface Builder {
+ Builder depComponent(DepComponent depComponent);
+ Builder strModule(StringModule strModule);
+ TestComponentWithBuilder build();
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/CancellationComponent.java b/javatests/dagger/functional/producers/cancellation/CancellationComponent.java
new file mode 100644
index 0000000..f06829e
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/CancellationComponent.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.BindsInstance;
+import dagger.functional.producers.cancellation.CancellationComponent.Dependency;
+import dagger.producers.Producer;
+import dagger.producers.Production;
+import dagger.producers.ProductionComponent;
+import java.util.concurrent.Executor;
+import javax.inject.Named;
+
+@ProductionComponent(modules = CancellationModule.class, dependencies = Dependency.class)
+interface CancellationComponent {
+
+ @Named("ep1")
+ ListenableFuture<String> entryPoint1();
+
+ @Named("ep2")
+ Producer<String> entryPoint2();
+
+ @Named("ep3")
+ ListenableFuture<String> entryPoint3();
+
+ CancellationSubcomponent.Builder subcomponentBuilder();
+
+ @ProductionComponent.Builder
+ interface Builder {
+ Builder module(CancellationModule module);
+
+ Builder dependency(Dependency dependency);
+
+ @BindsInstance
+ Builder executor(@Production Executor executor);
+
+ CancellationComponent build();
+ }
+
+ final class Dependency {
+
+ final ProducerTester tester;
+
+ Dependency(ProducerTester tester) {
+ this.tester = checkNotNull(tester);
+ }
+
+ @SuppressWarnings("unused") // Dagger uses it
+ ListenableFuture<String> getDependencyFuture() {
+ return tester.start("dependencyFuture");
+ }
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/CancellationModule.java b/javatests/dagger/functional/producers/cancellation/CancellationModule.java
new file mode 100644
index 0000000..ff7ee79
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/CancellationModule.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Provides;
+import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import javax.inject.Named;
+
+@SuppressWarnings("unused") // not actually using dependencies
+@ProducerModule(subcomponents = CancellationSubcomponent.class)
+final class CancellationModule {
+
+ private final ProducerTester tester;
+
+ CancellationModule(ProducerTester tester) {
+ this.tester = checkNotNull(tester);
+ }
+
+ @Produces
+ @Named("leaf1")
+ ListenableFuture<String> produceLeaf1() {
+ return tester.start("leaf1");
+ }
+
+ @Produces
+ @Named("leaf2")
+ ListenableFuture<String> produceLeaf2() {
+ return tester.start("leaf2");
+ }
+
+ @Produces
+ @Named("leaf3")
+ ListenableFuture<String> produceLeaf3() {
+ return tester.start("leaf3");
+ }
+
+ @Produces
+ @Named("foo")
+ ListenableFuture<String> produceFoo(@Named("leaf1") String leaf1, @Named("leaf2") String leaf2) {
+ return tester.start("foo");
+ }
+
+ @Produces
+ @Named("bar")
+ ListenableFuture<String> produceBar(@Named("leaf2") String leaf2, @Named("leaf3") String leaf3) {
+ return tester.start("bar");
+ }
+
+ @Produces
+ @Named("baz")
+ ListenableFuture<String> produceBaz(
+ @Named("foo") Producer<String> foo, @Named("bar") String bar) {
+ ListenableFuture<String> fooFuture = foo.get();
+ if (!fooFuture.isDone()) {
+ assertThat(fooFuture.cancel(true)).isTrue();
+ assertThat(fooFuture.isCancelled()).isTrue();
+ }
+ return tester.start("baz");
+ }
+
+ @Provides
+ @Named("providesDep")
+ static String provideProvidesDep() {
+ return "providesDep";
+ }
+
+ @Produces
+ @Named("qux")
+ ListenableFuture<String> produceQux(
+ @Named("baz") String baz, @Named("providesDep") String providesDep) {
+ return tester.start("qux");
+ }
+
+ @Produces
+ @Named("ep1")
+ ListenableFuture<String> produceEntryPoint1(@Named("qux") String qux) {
+ return tester.start("entryPoint1");
+ }
+
+ @Produces
+ @Named("ep2")
+ ListenableFuture<String> produceEntryPoint2(@Named("bar") String bar, String dependency) {
+ return tester.start("entryPoint2");
+ }
+
+ @Produces
+ @Named("ep3")
+ static ListenableFuture<String> produceEntryPoint3(Producer<String> dependencyProducer) {
+ ListenableFuture<String> dependencyFuture = dependencyProducer.get();
+ assertThat(dependencyFuture.isDone()).isFalse();
+ assertThat(dependencyFuture.cancel(true)).isTrue();
+ assertThat(dependencyFuture.isCancelled()).isTrue();
+ return dependencyFuture;
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/CancellationPolicyTest.java b/javatests/dagger/functional/producers/cancellation/CancellationPolicyTest.java
new file mode 100644
index 0000000..936072a
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/CancellationPolicyTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.BindsInstance;
+import dagger.producers.CancellationPolicy;
+import dagger.producers.CancellationPolicy.Propagation;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.Production;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.util.concurrent.Executor;
+import javax.inject.Named;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for parent production components with a {@code CancellationPolicy} that allows subcomponent
+ * cancellation to propagate to them
+ */
+@RunWith(JUnit4.class)
+public final class CancellationPolicyTest {
+
+ @ProducerModule(subcomponents = Child.class)
+ static class ParentModule {
+ private final ProducerTester tester;
+
+ ParentModule(ProducerTester tester) {
+ this.tester = tester;
+ }
+
+ @Produces
+ @Named("a")
+ ListenableFuture<String> produceA() {
+ return tester.start("a");
+ }
+ }
+
+ interface Parent {
+ @Named("a")
+ ListenableFuture<String> a();
+
+ Child.Builder childBuilder();
+
+ interface Builder<P extends Parent, B extends Builder<P, B>> {
+ B module(ParentModule module);
+
+ @BindsInstance
+ B executor(@Production Executor executor);
+
+ P build();
+ }
+ }
+
+ @CancellationPolicy(fromSubcomponents = Propagation.PROPAGATE)
+ @ProductionComponent(modules = ParentModule.class)
+ interface PropagatingParent extends Parent {
+ @ProductionComponent.Builder
+ interface Builder extends Parent.Builder<PropagatingParent, Builder> {}
+ }
+
+ @CancellationPolicy(fromSubcomponents = Propagation.IGNORE)
+ @ProductionComponent(modules = ParentModule.class)
+ interface NonPropagatingParent extends Parent {
+ @ProductionComponent.Builder
+ interface Builder extends Parent.Builder<NonPropagatingParent, Builder> {}
+ }
+
+ @ProducerModule
+ static class ChildModule {
+ private final ProducerTester tester;
+
+ ChildModule(ProducerTester tester) {
+ this.tester = tester;
+ }
+
+ @Produces
+ @Named("b")
+ ListenableFuture<String> b(@Named("a") String a) {
+ return tester.start("b");
+ }
+ }
+
+ @ProductionSubcomponent(modules = ChildModule.class)
+ interface Child {
+ @Named("b")
+ ListenableFuture<String> b();
+
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ Builder module(ChildModule module);
+
+ Child build();
+ }
+ }
+
+ private final ProducerTester tester = new ProducerTester();
+
+ @Test
+ public void propagatingParent_childCancellationPropagatesToParent() {
+ PropagatingParent parent =
+ DaggerCancellationPolicyTest_PropagatingParent.builder()
+ .module(new ParentModule(tester))
+ .executor(MoreExecutors.directExecutor())
+ .build();
+ ListenableFuture<String> a = parent.a();
+
+ Child child = parent.childBuilder().module(new ChildModule(tester)).build();
+
+ ListenableFuture<String> b = child.b();
+
+ tester.assertStarted("a").only();
+
+ assertThat(a.isDone()).isFalse();
+ assertThat(b.isDone()).isFalse();
+
+ assertThat(b.cancel(true)).isTrue();
+ assertThat(b.isCancelled()).isTrue();
+
+ tester.assertCancelled("a");
+
+ assertThat(a.isCancelled()).isTrue();
+ }
+
+ @Test
+ public void nonPropagatingParent_childCancellationDoesNotPropagateToParent() throws Exception {
+ // This test is basically just checking that when the parent has fromSubcomponents = IGNORE, it
+ // behaves the same as having no @CancellationPolicy at all (as tested in
+ // ProducerSubcomponentCancellationTester)
+ NonPropagatingParent parent =
+ DaggerCancellationPolicyTest_NonPropagatingParent.builder()
+ .module(new ParentModule(tester))
+ .executor(MoreExecutors.directExecutor())
+ .build();
+ ListenableFuture<String> a = parent.a();
+
+ Child child = parent.childBuilder().module(new ChildModule(tester)).build();
+
+ ListenableFuture<String> b = child.b();
+
+ tester.assertStarted("a").only();
+
+ assertThat(a.isDone()).isFalse();
+ assertThat(b.isDone()).isFalse();
+
+ assertThat(b.cancel(true)).isTrue();
+ assertThat(b.isCancelled()).isTrue();
+
+ tester.assertNotCancelled("a");
+
+ assertThat(a.isDone()).isFalse();
+
+ tester.complete("a");
+ assertThat(a.isDone()).isTrue();
+ assertThat(a.get(1, MILLISECONDS)).isEqualTo("completed");
+
+ tester.assertNotStarted("b");
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/CancellationSubcomponent.java b/javatests/dagger/functional/producers/cancellation/CancellationSubcomponent.java
new file mode 100644
index 0000000..63b1a9d
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/CancellationSubcomponent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionSubcomponent;
+import javax.inject.Named;
+
+@ProductionSubcomponent(modules = CancellationSubcomponentModule.class)
+interface CancellationSubcomponent {
+
+ @Named("subEntryPoint")
+ ListenableFuture<String> subcomponentEntryPoint();
+
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ Builder module(CancellationSubcomponentModule module);
+
+ CancellationSubcomponent build();
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/CancellationSubcomponentModule.java b/javatests/dagger/functional/producers/cancellation/CancellationSubcomponentModule.java
new file mode 100644
index 0000000..9cedad4
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/CancellationSubcomponentModule.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import javax.inject.Named;
+
+@SuppressWarnings("unused") // not actually using dependencies
+@ProducerModule
+final class CancellationSubcomponentModule {
+
+ private final ProducerTester tester;
+
+ CancellationSubcomponentModule(ProducerTester tester) {
+ this.tester = checkNotNull(tester);
+ }
+
+ @Produces
+ @Named("subLeaf")
+ ListenableFuture<String> produceSubLeaf() {
+ return tester.start("subLeaf");
+ }
+
+ @Produces
+ @Named("subTask1")
+ ListenableFuture<String> produceSubTask1(
+ @Named("subLeaf") String subLeaf, @Named("qux") String qux) {
+ return tester.start("subTask1");
+ }
+
+ @Produces
+ @Named("subTask2")
+ ListenableFuture<String> produceSubTask2(@Named("foo") String foo, Producer<String> dependency) {
+ ListenableFuture<String> dependencyFuture = dependency.get();
+ assertThat(dependencyFuture.cancel(true)).isTrue();
+ assertThat(dependencyFuture.isCancelled()).isTrue();
+ return tester.start("subTask2");
+ }
+
+ @Produces
+ @Named("subEntryPoint")
+ ListenableFuture<String> produceSubEntryPoint(
+ @Named("subTask1") String subTask1, @Named("subTask2") String subTask2) {
+ return tester.start("subEntryPoint");
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/ProducerCancellationTest.java b/javatests/dagger/functional/producers/cancellation/ProducerCancellationTest.java
new file mode 100644
index 0000000..23b31d2
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/ProducerCancellationTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.functional.producers.cancellation.CancellationComponent.Dependency;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests cancellation of tasks in production components. */
+@RunWith(JUnit4.class)
+public class ProducerCancellationTest {
+
+ private final ProducerTester tester = new ProducerTester();
+ private final CancellationComponent component =
+ DaggerCancellationComponent.builder()
+ .module(new CancellationModule(tester))
+ .dependency(new Dependency(tester))
+ .executor(MoreExecutors.directExecutor())
+ .build();
+
+ @Test
+ public void initialState() {
+ tester.assertNoStartedNodes();
+ }
+
+ @Test
+ public void cancellingOneEntryPoint_cancelsAllRunningNodes() {
+ ListenableFuture<String> entryPoint1 = component.entryPoint1();
+ tester.assertStarted("leaf2", "leaf3").only();
+
+ assertThat(entryPoint1.cancel(true)).isTrue();
+ assertThat(entryPoint1.isCancelled()).isTrue();
+
+ tester.assertCancelled("leaf2", "leaf3").only();
+
+ // The other entry points were also cancelled in the process, from the user's perspective.
+ assertThat(component.entryPoint2().get().isCancelled()).isTrue();
+ assertThat(component.entryPoint3().isCancelled()).isTrue();
+
+ // The underlying tasks weren't actually started, even though we just requested them above,
+ // because the node was cancelled already along with the component.
+ tester.assertNotStarted("entryPoint2", "entryPoint3");
+ }
+
+ @SuppressWarnings({"CheckReturnValue", "FutureReturnValueIgnored"})
+ @Test
+ public void cancellingNonEntryPointProducer_doesNotCancelUnderlyingTask() {
+ ListenableFuture<String> entryPoint1 = component.entryPoint1();
+ tester.assertStarted("leaf2", "leaf3").only();
+
+ tester.complete("leaf2", "leaf3");
+
+ tester.assertStarted("bar");
+
+ // foo's dependencies are complete, but it is not yet started because baz depends on
+ // Producer<foo>, so it won't be started until baz calls get() on it.
+ // baz not started yet because it needs bar to complete first.
+ tester.assertNotStarted("foo", "baz");
+
+ // Complete bar, triggering baz to run. It calls get() on the foo Producer, so that also starts
+ // once its dependency leaf1 is complete.
+ tester.complete("bar", "leaf1");
+ tester.assertStarted("baz", "foo");
+
+ // baz then cancelled the foo Producer's future, but that didn't cancel the underlying task.
+ tester.assertNotCancelled("foo");
+
+ // If we cancel the entry point, that does cancel the task.
+ entryPoint1.cancel(true);
+ tester.assertCancelled("foo");
+ }
+
+ @SuppressWarnings({"CheckReturnValue", "FutureReturnValueIgnored"})
+ @Test
+ public void cancellingProducerFromComponentDependency_cancelsUnderlyingTask() {
+ // Start leaf2/leaf3 tasks.
+ component.entryPoint1();
+ tester.assertStarted("leaf2", "leaf3").only();
+ tester.assertNotCancelled("leaf2", "leaf3");
+
+ // Nothing's requested dependencyFuture yet.
+ tester.assertNotStarted("dependencyFuture");
+
+ // entryPoint3 injects Producer of dependency future, then cancels that future. Then also
+ // returns that future as the entry point.
+ ListenableFuture<String> entryPoint = component.entryPoint3();
+
+ tester.assertStarted("dependencyFuture");
+ tester.assertCancelled("dependencyFuture");
+
+ // Even though the entry point future returned from the component is not the dependency future
+ // itself, the cancellation should have propagated out to it and cancelled it.
+ assertThat(entryPoint.isCancelled()).isTrue();
+
+ // And that cancellation should have cancelled the other tasks running in the component.
+ tester.assertCancelled("leaf2", "leaf3");
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/ProducerSubcomponentCancellationTest.java b/javatests/dagger/functional/producers/cancellation/ProducerSubcomponentCancellationTest.java
new file mode 100644
index 0000000..246bf9f
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/ProducerSubcomponentCancellationTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.functional.producers.cancellation.CancellationComponent.Dependency;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests cancellation of tasks in production subcomponents. */
+@RunWith(JUnit4.class)
+public class ProducerSubcomponentCancellationTest {
+
+ private final ProducerTester tester = new ProducerTester();
+ private final CancellationComponent component =
+ DaggerCancellationComponent.builder()
+ .module(new CancellationModule(tester))
+ .dependency(new Dependency(tester))
+ .executor(MoreExecutors.directExecutor())
+ .build();
+ private final CancellationSubcomponent subcomponent =
+ component.subcomponentBuilder().module(new CancellationSubcomponentModule(tester)).build();
+
+ @Test
+ public void initialState() {
+ tester.assertNoStartedNodes();
+ }
+
+ @Test
+ public void cancellingSubcomponent_doesNotCancelParent() throws Exception {
+ ListenableFuture<String> subcomponentEntryPoint = subcomponent.subcomponentEntryPoint();
+
+ // Subcomponent entry point depends on all leaves from the parent component and on the single
+ // leaf in the subcomponent itself, so they should all have started.
+ tester.assertStarted("leaf1", "leaf2", "leaf3", "subLeaf").only();
+
+ assertThat(subcomponentEntryPoint.cancel(true)).isTrue();
+ assertThat(subcomponentEntryPoint.isCancelled()).isTrue();
+
+ // None of the tasks running in the parent were cancelled.
+ tester.assertNotCancelled("leaf1", "leaf2", "leaf3");
+ tester.assertCancelled("subLeaf").only();
+
+ // Finish all the parent tasks to ensure that it can still complete normally.
+ tester.complete(
+ "dependencyFuture",
+ "leaf1",
+ "leaf2",
+ "leaf3",
+ "foo",
+ "bar",
+ "baz",
+ "qux",
+ "entryPoint1",
+ "entryPoint2");
+
+ assertThat(component.entryPoint1().get(1, MILLISECONDS)).isEqualTo("completed");
+ assertThat(component.entryPoint2().get().get(1, MILLISECONDS)).isEqualTo("completed");
+ }
+
+ @Test
+ public void cancellingSubcomponent_preventsUnstartedNodesFromStarting() {
+ ListenableFuture<String> subcomponentEntryPoint = subcomponent.subcomponentEntryPoint();
+
+ tester.complete("subLeaf");
+ tester.assertNotStarted("subTask1", "subTask2");
+
+ subcomponentEntryPoint.cancel(true);
+
+ // Complete the remaining dependencies of subTask1 and subTask2.
+ tester.complete("leaf1", "leaf2", "leaf3", "foo", "bar", "baz", "qux");
+
+ // Since the subcomponent was cancelled, they are not started.
+ tester.assertNotStarted("subTask1", "subTask2");
+ }
+
+ @Test
+ public void cancellingProducerFromComponentDependency_inSubcomponent_cancelsUnderlyingTask()
+ throws Exception {
+ // Request subcomponent's entry point.
+ ListenableFuture<String> subcomponentEntryPoint = subcomponent.subcomponentEntryPoint();
+
+ // Finish all parent tasks so that the subcomponent's tasks can start.
+ tester.complete("leaf1", "leaf2", "leaf3", "foo", "bar", "baz", "qux", "subLeaf");
+
+ tester.assertStarted("subTask1", "subTask2");
+ tester.assertNotCancelled("subTask1", "subTask2");
+
+ // When subTask2 runs, it cancels the dependency future.
+ // TODO(cgdecker): Is this what we want to happen?
+ // On the one hand, there's a policy of "futures from component dependencies come from outside
+ // our control and should be cancelled unconditionally". On the other hand, the dependency is
+ // coming from the parent component, and the policy is also not to cancel things belonging to
+ // the parent unless it allows that.
+ tester.assertCancelled("dependencyFuture");
+
+ // The future it returns didn't depend directly on that future, though, so the subcomponent
+ // should be able to complete normally.
+ tester.complete("subTask1", "subTask2", "subEntryPoint");
+
+ assertThat(subcomponentEntryPoint.get(1, MILLISECONDS)).isEqualTo("completed");
+ }
+}
diff --git a/javatests/dagger/functional/producers/cancellation/ProducerTester.java b/javatests/dagger/functional/producers/cancellation/ProducerTester.java
new file mode 100644
index 0000000..35cf8e9
--- /dev/null
+++ b/javatests/dagger/functional/producers/cancellation/ProducerTester.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.cancellation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.AbstractFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Predicate;
+
+/**
+ * Helper for testing producers.
+ *
+ * <p>Maintains a set of nodes (futures mapped to names) representing the results of different
+ * producer nodes and allows those nodes to be "started" (when returned from a producer method),
+ * completed, and cancelled, as well as to be queried for their state. Additionally, provides
+ * assertions about the state of nodes.
+ */
+final class ProducerTester {
+
+ private final Map<String, TestFuture> futures = new HashMap<>();
+
+ /** Starts the given node. */
+ ListenableFuture<String> start(String node) {
+ return getOrCreate(node).start();
+ }
+
+ private TestFuture getOrCreate(String node) {
+ TestFuture result = futures.get(node);
+ if (result == null) {
+ result = new TestFuture(node);
+ futures.put(node, result);
+ }
+ return result;
+ }
+
+ /** Returns whether or not the given node has been started. */
+ boolean isStarted(String node) {
+ return futures.containsKey(node) && futures.get(node).isStarted();
+ }
+
+ /** Completes of the given nodes. */
+ void complete(String... nodes) {
+ for (String node : nodes) {
+ getOrCreate(node).complete();
+ }
+ }
+
+ /** Returns whether or not the given node has been cancelled. */
+ boolean isCancelled(String node) {
+ TestFuture future = futures.get(node);
+ return future != null && future.isCancelled();
+ }
+
+ /** Asserts that the given nodes have been started. */
+ Only assertStarted(String... nodes) {
+ return assertAboutNodes(STARTED, nodes);
+ }
+
+ /** Asserts that the given nodes have been cancelled. */
+ Only assertCancelled(String... nodes) {
+ return assertAboutNodes(CANCELLED, nodes);
+ }
+
+ /** Asserts that the given nodes have not been started. */
+ Only assertNotStarted(String... nodes) {
+ return assertAboutNodes(not(STARTED), nodes);
+ }
+
+ /** Asserts that the given nodes have not been cancelled. */
+ Only assertNotCancelled(String... nodes) {
+ return assertAboutNodes(not(CANCELLED), nodes);
+ }
+
+ /** Asserts that no nodes in this tester have been started. */
+ void assertNoStartedNodes() {
+ for (TestFuture future : futures.values()) {
+ assertWithMessage("%s is started", future).that(future.isStarted()).isFalse();
+ }
+ }
+
+ private Only assertAboutNodes(Predicate<? super TestFuture> assertion, String... nodes) {
+ ImmutableSet.Builder<TestFuture> builder = ImmutableSet.builder();
+ for (String node : nodes) {
+ TestFuture future = getOrCreate(node);
+ assertWithMessage("%s is %s", future, assertion).that(assertion.test(future)).isTrue();
+ builder.add(future);
+ }
+ return new Only(builder.build(), assertion);
+ }
+
+ /**
+ * Fluent class for making a previous assertion more strict by specifying that whatever was
+ * asserted should be true only for the specified nodes and not for any others.
+ */
+ final class Only {
+
+ private final ImmutableSet<TestFuture> expected;
+ private final Predicate<? super TestFuture> assertion;
+
+ Only(ImmutableSet<TestFuture> expected, Predicate<? super TestFuture> assertion) {
+ this.expected = checkNotNull(expected);
+ this.assertion = checkNotNull(assertion);
+ }
+
+ /**
+ * Asserts that the previous assertion was not true for any node other than those that were
+ * specified.
+ */
+ void only() {
+ for (TestFuture future : futures.values()) {
+ if (!expected.contains(future)) {
+ assertWithMessage("%s is %s", future, assertion).that(assertion.test(future)).isFalse();
+ }
+ }
+ }
+ }
+
+ /**
+ * A simple future for testing that can be marked as having been started and which can be
+ * completed with a result.
+ */
+ private static final class TestFuture extends AbstractFuture<String> {
+
+ private final String name;
+ private volatile boolean started;
+
+ private TestFuture(String name) {
+ this.name = checkNotNull(name);
+ }
+
+ /** Marks this future as having been started and returns it. */
+ TestFuture start() {
+ this.started = true;
+ return this;
+ }
+
+ /** Returns whether or not this future's task was started. */
+ boolean isStarted() {
+ return started;
+ }
+
+ /** Completes this future's task by setting a value for it. */
+ public void complete() {
+ super.set("completed");
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ private static final Predicate<TestFuture> STARTED =
+ new Predicate<TestFuture>() {
+ @Override
+ public boolean test(TestFuture future) {
+ return future.isStarted();
+ }
+
+ @Override
+ public String toString() {
+ return "started";
+ }
+ };
+
+ private static final Predicate<TestFuture> CANCELLED =
+ new Predicate<TestFuture>() {
+ @Override
+ public boolean test(TestFuture future) {
+ return future.isCancelled();
+ }
+
+ @Override
+ public String toString() {
+ return "cancelled";
+ }
+ };
+
+ /** Version of Predicates.not with a toString() that's nicer for our assertion error messages. */
+ private static <T> Predicate<T> not(final Predicate<T> predicate) {
+ return new Predicate<T>() {
+ @Override
+ public boolean test(T input) {
+ return !predicate.test(input);
+ }
+
+ @Override
+ public String toString() {
+ return "not " + predicate;
+ }
+ };
+ }
+}
diff --git a/javatests/dagger/functional/producers/fluentfuture/FluentFutures.java b/javatests/dagger/functional/producers/fluentfuture/FluentFutures.java
new file mode 100644
index 0000000..7bb25f6
--- /dev/null
+++ b/javatests/dagger/functional/producers/fluentfuture/FluentFutures.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.fluentfuture;
+
+import static com.google.common.util.concurrent.Futures.immediateFuture;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.BindsInstance;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.Production;
+import dagger.producers.ProductionComponent;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+final class FluentFutures {
+ interface Dependency {
+ FluentFuture<Float> floatFuture();
+ }
+
+ @ProducerModule
+ static final class Module {
+ @Produces
+ static FluentFuture<Integer> intFuture() {
+ return FluentFuture.from(immediateFuture(5));
+ }
+
+ @Produces
+ static FluentFuture<String> stringFuture(int i) {
+ return FluentFuture.from(immediateFuture("hello"));
+ }
+
+ @Produces
+ @IntoSet
+ static FluentFuture<Double> doubleFuture(int i) {
+ return FluentFuture.from(immediateFuture((double) i));
+ }
+
+ @Produces
+ @IntoSet
+ static double dependencyInput(float f) {
+ return (double) f;
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static Set<FluentFuture<Double>> setOfDoubleFutures(int i) {
+ return ImmutableSet.of(
+ FluentFuture.from(immediateFuture((double) i + 1)),
+ FluentFuture.from(immediateFuture((double) i + 2)));
+ }
+ }
+
+ @ProductionComponent(modules = Module.class, dependencies = Dependency.class)
+ interface Component {
+ ListenableFuture<String> string();
+
+ ListenableFuture<Set<Double>> setOfDouble();
+
+ @ProductionComponent.Builder
+ interface Builder {
+ Builder dependency(Dependency dependency);
+
+ @BindsInstance
+ Builder executor(@Production Executor executor);
+
+ Component build();
+ }
+ }
+}
diff --git a/javatests/dagger/functional/producers/fluentfuture/FluentFuturesTest.java b/javatests/dagger/functional/producers/fluentfuture/FluentFuturesTest.java
new file mode 100644
index 0000000..8745860
--- /dev/null
+++ b/javatests/dagger/functional/producers/fluentfuture/FluentFuturesTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.fluentfuture;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import com.google.common.util.concurrent.FluentFuture;
+import dagger.functional.producers.fluentfuture.FluentFutures.Component;
+import dagger.functional.producers.fluentfuture.FluentFutures.Dependency;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class FluentFuturesTest {
+
+ @Test
+ public void testFluentFutures() throws Exception {
+ Component component =
+ DaggerFluentFutures_Component.builder()
+ .executor(directExecutor())
+ .dependency(
+ new Dependency() {
+ @Override
+ public FluentFuture<Float> floatFuture() {
+ return FluentFuture.from(immediateFuture(42.0f));
+ }
+ })
+ .build();
+ assertThat(component.string().isDone()).isTrue();
+ assertThat(component.string().get()).isEqualTo("hello");
+ assertThat(component.setOfDouble().isDone()).isTrue();
+ assertThat(component.setOfDouble().get()).containsExactly(5.0, 6.0, 7.0, 42.0);
+ }
+}
diff --git a/javatests/dagger/functional/producers/gwt/GwtIncompatibles.java b/javatests/dagger/functional/producers/gwt/GwtIncompatibles.java
new file mode 100644
index 0000000..75efd96
--- /dev/null
+++ b/javatests/dagger/functional/producers/gwt/GwtIncompatibles.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.gwt;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.lang.annotation.Retention;
+
+interface GwtIncompatibles {
+ @Retention(RUNTIME)
+ @interface GwtIncompatible {}
+
+ @GwtIncompatible
+ @ProducerModule
+ class OnModule {
+ @Produces
+ static String onModule() {
+ return "on module";
+ }
+ }
+
+ @ProducerModule
+ class OnMethod {
+ @GwtIncompatible
+ @Produces
+ static String onMethod() {
+ return "on method";
+ }
+ }
+}
diff --git a/javatests/dagger/functional/producers/gwt/GwtIncompatiblesTest.java b/javatests/dagger/functional/producers/gwt/GwtIncompatiblesTest.java
new file mode 100644
index 0000000..d484c8c
--- /dev/null
+++ b/javatests/dagger/functional/producers/gwt/GwtIncompatiblesTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.gwt;
+
+import dagger.functional.producers.gwt.GwtIncompatibles.GwtIncompatible;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@code @GwtIncompatible} bindings. */
+@RunWith(JUnit4.class)
+public class GwtIncompatiblesTest {
+ @Test
+ public void testIncompatible() {
+ assertGwtIncompatible(GwtIncompatibles_OnModule_OnModuleFactory.class);
+ assertGwtIncompatible(GwtIncompatibles_OnMethod_OnMethodFactory.class);
+ }
+
+ private void assertGwtIncompatible(Class<?> clazz) {
+ boolean gwtIncompatible = clazz.isAnnotationPresent(GwtIncompatible.class);
+ if (!gwtIncompatible) {
+ throw new AssertionError(clazz.getCanonicalName() + " is not @GwtIncompatible");
+ }
+ }
+}
diff --git a/javatests/dagger/functional/producers/monitoring/MonitoredComponent.java b/javatests/dagger/functional/producers/monitoring/MonitoredComponent.java
new file mode 100644
index 0000000..2df5645
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/MonitoredComponent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.ProductionComponent;
+
+@ProductionComponent(
+ modules = {ExecutorModule.class, MonitoringModule.class, StubModule.class, ServingModule.class}
+)
+interface MonitoredComponent {
+ ListenableFuture<String> output();
+}
diff --git a/javatests/dagger/functional/producers/monitoring/MonitoringModule.java b/javatests/dagger/functional/producers/monitoring/MonitoringModule.java
new file mode 100644
index 0000000..3c08255
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/MonitoringModule.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+
+@Module
+final class MonitoringModule {
+ private final ProductionComponentMonitor.Factory monitorFactory;
+
+ MonitoringModule(ProductionComponentMonitor.Factory monitorFactory) {
+ this.monitorFactory = monitorFactory;
+ }
+
+ @Provides
+ @IntoSet
+ ProductionComponentMonitor.Factory monitorFactory() {
+ return monitorFactory;
+ }
+}
diff --git a/javatests/dagger/functional/producers/monitoring/MonitoringTest.java b/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
new file mode 100644
index 0000000..543835f
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for production components using monitoring. */
+@RunWith(JUnit4.class)
+public final class MonitoringTest {
+ @Mock private ProductionComponentMonitor.Factory componentMonitorFactory;
+ @Mock private StringStub server1;
+ @Mock private StringStub server2;
+ private SettableFuture<String> server1Future;
+ private SettableFuture<String> server2Future;
+ private FakeProductionComponentMonitor componentMonitor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ componentMonitor = new FakeProductionComponentMonitor();
+ when(componentMonitorFactory.create(any())).thenReturn(componentMonitor);
+ server1Future = SettableFuture.create();
+ server2Future = SettableFuture.create();
+ when(server1.run(any(String.class))).thenReturn(server1Future);
+ when(server2.run(any(String.class))).thenReturn(server2Future);
+ }
+
+ @Test
+ public void basicMonitoring() throws Exception {
+ MonitoredComponent component =
+ DaggerMonitoredComponent.builder()
+ .monitoringModule(new MonitoringModule(componentMonitorFactory))
+ .stubModule(new StubModule(server1, server2))
+ .build();
+ ListenableFuture<String> output = component.output();
+ assertThat(componentMonitor.monitors).hasSize(3);
+ ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
+ ImmutableList.copyOf(componentMonitor.monitors.entrySet());
+ assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
+ assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
+ assertThat(entries.get(2).getKey().toString()).contains("RequestData");
+
+ ProducerMonitor callServer2Monitor = entries.get(0).getValue();
+ ProducerMonitor callServer1Monitor = entries.get(1).getValue();
+ ProducerMonitor requestDataMonitor = entries.get(2).getValue();
+
+ InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ inOrder.verify(callServer2Monitor).requested();
+ inOrder.verify(callServer1Monitor).requested();
+ inOrder.verify(requestDataMonitor).requested();
+ inOrder.verify(requestDataMonitor).ready();
+ inOrder.verify(requestDataMonitor).methodStarting();
+ inOrder.verify(requestDataMonitor).methodFinished();
+ inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
+ inOrder.verify(callServer1Monitor).ready();
+ inOrder.verify(callServer1Monitor).methodStarting();
+ inOrder.verify(callServer1Monitor).methodFinished();
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+
+ server1Future.set("server 1 response");
+ inOrder.verify(callServer1Monitor).succeeded("server 1 response");
+ inOrder.verify(callServer2Monitor).ready();
+ inOrder.verify(callServer2Monitor).methodStarting();
+ inOrder.verify(callServer2Monitor).methodFinished();
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+
+ server2Future.set("server 2 response");
+ inOrder.verify(callServer2Monitor).succeeded("server 2 response");
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ assertThat(output.get()).isEqualTo("server 2 response");
+ }
+
+ @Test
+ public void basicMonitoringWithFailure() throws Exception {
+ MonitoredComponent component =
+ DaggerMonitoredComponent.builder()
+ .monitoringModule(new MonitoringModule(componentMonitorFactory))
+ .stubModule(new StubModule(server1, server2))
+ .build();
+ ListenableFuture<String> output = component.output();
+ assertThat(componentMonitor.monitors).hasSize(3);
+ ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
+ ImmutableList.copyOf(componentMonitor.monitors.entrySet());
+ assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
+ assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
+ assertThat(entries.get(2).getKey().toString()).contains("RequestData");
+
+ ProducerMonitor callServer2Monitor = entries.get(0).getValue();
+ ProducerMonitor callServer1Monitor = entries.get(1).getValue();
+ ProducerMonitor requestDataMonitor = entries.get(2).getValue();
+
+ InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ inOrder.verify(callServer2Monitor).requested();
+ inOrder.verify(callServer1Monitor).requested();
+ inOrder.verify(requestDataMonitor).requested();
+ inOrder.verify(requestDataMonitor).ready();
+ inOrder.verify(requestDataMonitor).methodStarting();
+ inOrder.verify(requestDataMonitor).methodFinished();
+ inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
+ inOrder.verify(callServer1Monitor).ready();
+ inOrder.verify(callServer1Monitor).methodStarting();
+ inOrder.verify(callServer1Monitor).methodFinished();
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+
+ RuntimeException cause = new RuntimeException("monkey");
+ server1Future.setException(cause);
+ inOrder.verify(callServer1Monitor).failed(cause);
+ inOrder.verify(callServer2Monitor).ready();
+ inOrder.verify(callServer2Monitor).failed(any(Throwable.class));
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ try {
+ output.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(Throwables.getRootCause(e)).isSameInstanceAs(cause);
+ }
+ }
+
+ private static final class FakeProductionComponentMonitor extends ProductionComponentMonitor {
+ final Map<ProducerToken, ProducerMonitor> monitors = new LinkedHashMap<>();
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ ProducerMonitor monitor = mock(ProducerMonitor.class);
+ monitors.put(token, monitor);
+ return monitor;
+ }
+ }
+
+ @Test
+ public void monitoringWithThreads() throws Exception {
+ ThreadRecordingProductionComponentMonitor componentMonitor =
+ new ThreadRecordingProductionComponentMonitor();
+ when(componentMonitorFactory.create(any())).thenReturn(componentMonitor);
+
+ ThreadMonitoredComponent component =
+ DaggerThreadMonitoredComponent.builder()
+ .monitoringModule(new MonitoringModule(componentMonitorFactory))
+ .executorModule(new ExecutorModule(Executors.newFixedThreadPool(10)))
+ .build();
+ ThreadAccumulator threadAccumulator = component.threadAccumulator().get();
+
+ assertThat(componentMonitor.monitors).hasSize(3);
+ ImmutableList<Map.Entry<ProducerToken, ThreadRecordingProducerMonitor>> entries =
+ ImmutableList.copyOf(componentMonitor.monitors.entrySet());
+
+ assertThat(entries.get(0).getKey().toString()).contains("EntryPoint");
+ ThreadRecordingProducerMonitor entryPointMonitor = entries.get(0).getValue();
+ assertThat(entries.get(1).getKey().toString()).contains("Required");
+ ThreadRecordingProducerMonitor requiredMonitor = entries.get(1).getValue();
+ assertThat(entries.get(2).getKey().toString()).contains("Deferred");
+ ThreadRecordingProducerMonitor deferredMonitor = entries.get(2).getValue();
+
+ // The entry point producer was requested from the main thread, then ran in its own thread.
+ assertThat(entryPointMonitor.requestedThreadId).isEqualTo(Thread.currentThread().getId());
+ assertThat(entryPointMonitor.startingThreadId)
+ .isEqualTo(threadAccumulator.threadId("entryPoint"));
+ assertThat(entryPointMonitor.finishedThreadId)
+ .isEqualTo(threadAccumulator.threadId("entryPoint"));
+
+ // The deferred producer was requested by the required producer, then ran in its own thread.
+ assertThat(deferredMonitor.requestedThreadId).isEqualTo(threadAccumulator.threadId("required"));
+ assertThat(deferredMonitor.startingThreadId).isEqualTo(threadAccumulator.threadId("deferred"));
+ assertThat(deferredMonitor.finishedThreadId).isEqualTo(threadAccumulator.threadId("deferred"));
+
+ // The required producer was requested by the entry point producer, then ran in its own thread.
+ assertThat(requiredMonitor.requestedThreadId).isEqualTo(entryPointMonitor.requestedThreadId);
+ assertThat(requiredMonitor.startingThreadId).isEqualTo(threadAccumulator.threadId("required"));
+ assertThat(requiredMonitor.finishedThreadId).isEqualTo(threadAccumulator.threadId("required"));
+
+ // Each producer ran in a distinct thread.
+ ImmutableSet<Long> threadIds =
+ ImmutableSet.of(
+ Thread.currentThread().getId(),
+ threadAccumulator.threadId("required"),
+ threadAccumulator.threadId("deferred"),
+ threadAccumulator.threadId("entryPoint"));
+ assertThat(threadIds).hasSize(4);
+ }
+
+ private static final class ThreadRecordingProductionComponentMonitor
+ extends ProductionComponentMonitor {
+ final Map<ProducerToken, ThreadRecordingProducerMonitor> monitors = new LinkedHashMap<>();
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ ThreadRecordingProducerMonitor monitor = new ThreadRecordingProducerMonitor();
+ monitors.put(token, monitor);
+ return monitor;
+ }
+ }
+
+ private static final class ThreadRecordingProducerMonitor extends ProducerMonitor {
+ private long requestedThreadId;
+ private long startingThreadId;
+ private long finishedThreadId;
+
+ @Override
+ public void requested() {
+ requestedThreadId = Thread.currentThread().getId();
+ }
+
+ @Override
+ public void methodStarting() {
+ startingThreadId = Thread.currentThread().getId();
+ }
+
+ @Override
+ public void methodFinished() {
+ finishedThreadId = Thread.currentThread().getId();
+ }
+ }
+}
diff --git a/javatests/dagger/functional/producers/monitoring/ServingModule.java b/javatests/dagger/functional/producers/monitoring/ServingModule.java
new file mode 100644
index 0000000..09c9cd1
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/ServingModule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.monitoring.StubModule.ForServer1;
+import dagger.functional.producers.monitoring.StubModule.ForServer2;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import javax.inject.Qualifier;
+
+@ProducerModule
+final class ServingModule {
+ @Qualifier
+ @interface RequestData {}
+
+ @Qualifier
+ @interface IntermediateData {}
+
+ @Produces
+ @RequestData
+ static String requestData() {
+ return "Hello, World!";
+ }
+
+ @Produces
+ @IntermediateData
+ static ListenableFuture<String> callServer1(
+ @RequestData String data, @ForServer1 StringStub stub) {
+ return stub.run(data);
+ }
+
+ @Produces
+ static ListenableFuture<String> callServer2(
+ @IntermediateData String data, @ForServer2 StringStub stub) {
+ return stub.run(data);
+ }
+}
diff --git a/javatests/dagger/functional/producers/monitoring/StringStub.java b/javatests/dagger/functional/producers/monitoring/StringStub.java
new file mode 100644
index 0000000..2553e88
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/StringStub.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+interface StringStub {
+ ListenableFuture<String> run(String input);
+}
diff --git a/javatests/dagger/functional/producers/monitoring/StubModule.java b/javatests/dagger/functional/producers/monitoring/StubModule.java
new file mode 100644
index 0000000..3c5893c
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/StubModule.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Qualifier;
+
+@Module
+final class StubModule {
+ @Qualifier
+ @interface ForServer1 {}
+
+ @Qualifier
+ @interface ForServer2 {}
+
+ private final StringStub server1;
+ private final StringStub server2;
+
+ StubModule(StringStub server1, StringStub server2) {
+ this.server1 = server1;
+ this.server2 = server2;
+ }
+
+ @Provides
+ @ForServer1
+ StringStub server1() {
+ return server1;
+ }
+
+ @Provides
+ @ForServer2
+ StringStub server2() {
+ return server2;
+ }
+}
diff --git a/javatests/dagger/functional/producers/monitoring/ThreadAccumulator.java b/javatests/dagger/functional/producers/monitoring/ThreadAccumulator.java
new file mode 100644
index 0000000..3d95fb6
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/ThreadAccumulator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
+final class ThreadAccumulator {
+ private final Map<String, Long> threadIds = new ConcurrentHashMap<>();
+
+ @Inject
+ ThreadAccumulator() {}
+
+ void markThread(String name) {
+ threadIds.put(name, Thread.currentThread().getId());
+ }
+
+ long threadId(String name) {
+ return threadIds.get(name);
+ }
+}
diff --git a/javatests/dagger/functional/producers/monitoring/ThreadModule.java b/javatests/dagger/functional/producers/monitoring/ThreadModule.java
new file mode 100644
index 0000000..f55c3e0
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/ThreadModule.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.monitoring.ThreadQualifiers.Deferred;
+import dagger.functional.producers.monitoring.ThreadQualifiers.EntryPoint;
+import dagger.functional.producers.monitoring.ThreadQualifiers.Required;
+import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+@ProducerModule
+final class ThreadModule {
+ @Produces
+ @Deferred
+ Object deferred(ThreadAccumulator acc) {
+ acc.markThread("deferred");
+ return new Object();
+ }
+
+ @Produces
+ @Required
+ ListenableFuture<Object> required(@Deferred Producer<Object> o, ThreadAccumulator acc) {
+ acc.markThread("required");
+ return o.get();
+ }
+
+ @Produces
+ @EntryPoint
+ ThreadAccumulator entryPoint(@Required Object o, ThreadAccumulator acc) {
+ acc.markThread("entryPoint");
+ return acc;
+ }
+}
diff --git a/javatests/dagger/functional/producers/monitoring/ThreadMonitoredComponent.java b/javatests/dagger/functional/producers/monitoring/ThreadMonitoredComponent.java
new file mode 100644
index 0000000..576cd7f
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/ThreadMonitoredComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.functional.producers.monitoring.ThreadQualifiers.EntryPoint;
+import dagger.producers.ProductionComponent;
+import javax.inject.Singleton;
+
+@Singleton
+@ProductionComponent(modules = {ExecutorModule.class, MonitoringModule.class, ThreadModule.class})
+interface ThreadMonitoredComponent {
+ @EntryPoint
+ ListenableFuture<ThreadAccumulator> threadAccumulator();
+}
diff --git a/javatests/dagger/functional/producers/monitoring/ThreadQualifiers.java b/javatests/dagger/functional/producers/monitoring/ThreadQualifiers.java
new file mode 100644
index 0000000..59ccbe4
--- /dev/null
+++ b/javatests/dagger/functional/producers/monitoring/ThreadQualifiers.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.monitoring;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+final class ThreadQualifiers {
+ private ThreadQualifiers() {}
+
+ @Qualifier
+ @Retention(RUNTIME)
+ @Documented
+ @interface EntryPoint {}
+
+ @Qualifier
+ @Retention(RUNTIME)
+ @Documented
+ @interface Required {}
+
+ @Qualifier
+ @Retention(RUNTIME)
+ @Documented
+ @interface Deferred {}
+}
diff --git a/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java b/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java
new file mode 100644
index 0000000..fa5c6ee
--- /dev/null
+++ b/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.multibindings;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.functional.producers.multibindings.Qualifiers.EmptyButDeclaredInModule;
+import dagger.functional.producers.multibindings.Qualifiers.EmptyButDeclaredInModuleAndProducerModule;
+import dagger.functional.producers.multibindings.Qualifiers.ObjCount;
+import dagger.functional.producers.multibindings.Qualifiers.OnlyProvisionMultibindings;
+import dagger.functional.producers.multibindings.Qualifiers.PossiblyThrowingMap;
+import dagger.functional.producers.multibindings.Qualifiers.PossiblyThrowingSet;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.ProductionComponent;
+import java.util.Map;
+import java.util.Set;
+
+@ProductionComponent(
+ modules = {ExecutorModule.class, MultibindingProducerModule.class, MultibindingModule.class}
+)
+interface MultibindingComponent {
+ ListenableFuture<Set<String>> strs();
+ ListenableFuture<Integer> strCount();
+
+ ListenableFuture<Set<Produced<String>>> successfulSet();
+
+ @PossiblyThrowingSet
+ ListenableFuture<Set<Produced<String>>> possiblyThrowingSet();
+
+ ListenableFuture<Map<Integer, String>> map();
+
+ ListenableFuture<Map<Integer, Producer<String>>> mapOfProducer();
+
+ ListenableFuture<Map<Integer, Produced<String>>> mapOfProduced();
+
+ @PossiblyThrowingMap
+ ListenableFuture<Map<Integer, String>> possiblyThrowingMap();
+
+ @PossiblyThrowingMap
+ ListenableFuture<Map<Integer, Producer<String>>> possiblyThrowingMapOfProducer();
+
+ @PossiblyThrowingMap
+ ListenableFuture<Map<Integer, Produced<String>>> possiblyThrowingMapOfProduced();
+
+ ListenableFuture<Set<Object>> objs();
+
+ ListenableFuture<Set<Produced<Object>>> producedObjs();
+
+ ListenableFuture<Map<Object, Object>> objMap();
+
+ ListenableFuture<Map<Object, Produced<Object>>> objMapOfProduced();
+
+ ListenableFuture<Map<Object, Producer<Object>>> objMapOfProducer();
+
+ @ObjCount
+ ListenableFuture<Integer> objCount();
+
+ @EmptyButDeclaredInModuleAndProducerModule
+ ListenableFuture<Map<String, Object>> emptyButDeclaredInModuleAndProducerModule();
+
+ @EmptyButDeclaredInModule
+ ListenableFuture<Map<String, Object>> emptyButDeclaredInModule();
+
+ @OnlyProvisionMultibindings
+ ListenableFuture<Map<String, Object>> onlyProvisionMultibindings();
+}
diff --git a/javatests/dagger/functional/producers/multibindings/MultibindingModule.java b/javatests/dagger/functional/producers/multibindings/MultibindingModule.java
new file mode 100644
index 0000000..08ed6d7
--- /dev/null
+++ b/javatests/dagger/functional/producers/multibindings/MultibindingModule.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.multibindings;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.Provides;
+import dagger.functional.producers.multibindings.Qualifiers.EmptyButDeclaredInModule;
+import dagger.functional.producers.multibindings.Qualifiers.EmptyButDeclaredInModuleAndProducerModule;
+import dagger.functional.producers.multibindings.Qualifiers.OnlyProvisionMultibindings;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import dagger.multibindings.Multibinds;
+import dagger.multibindings.StringKey;
+import java.util.Map;
+import java.util.Set;
+
+@Module
+abstract class MultibindingModule {
+ @Provides
+ @IntoSet
+ static String providedStr() {
+ return "providedStr";
+ }
+
+ @Provides
+ @ElementsIntoSet
+ static Set<String> providedStrs() {
+ return ImmutableSet.of("providedStr1", "providedStr2");
+ }
+
+ @Provides
+ @IntoMap
+ @IntKey(3)
+ static String providedValueFor3() {
+ return "provided three";
+ }
+
+ @Multibinds
+ @EmptyButDeclaredInModuleAndProducerModule
+ abstract Map<String, Object> emptyButDeclaredInModuleAndProducerModule();
+
+ @Multibinds
+ @EmptyButDeclaredInModule
+ abstract Map<String, Object> emptyButDeclaredInModule();
+
+ @Provides
+ @IntoMap
+ @StringKey("a")
+ @OnlyProvisionMultibindings
+ static Object onlyProvisionMultibindings() {
+ return "only multibinding";
+ }
+}
diff --git a/javatests/dagger/functional/producers/multibindings/MultibindingProducerModule.java b/javatests/dagger/functional/producers/multibindings/MultibindingProducerModule.java
new file mode 100644
index 0000000..350733b
--- /dev/null
+++ b/javatests/dagger/functional/producers/multibindings/MultibindingProducerModule.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.multibindings;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.multibindings.Qualifiers.EmptyButDeclaredInModuleAndProducerModule;
+import dagger.functional.producers.multibindings.Qualifiers.ObjCount;
+import dagger.functional.producers.multibindings.Qualifiers.PossiblyThrowingMap;
+import dagger.functional.producers.multibindings.Qualifiers.PossiblyThrowingSet;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import dagger.multibindings.Multibinds;
+import dagger.producers.Produced;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.util.Map;
+import java.util.Set;
+
+@ProducerModule
+abstract class MultibindingProducerModule {
+ @Produces
+ @IntoSet
+ static ListenableFuture<String> futureStr() {
+ return Futures.immediateFuture("foo");
+ }
+
+ @Produces
+ @IntoSet
+ static String str() {
+ return "bar";
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static ListenableFuture<Set<String>> futureStrs() {
+ return Futures.<Set<String>>immediateFuture(ImmutableSet.of("foo1", "foo2"));
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static Set<ListenableFuture<String>> strFutures() {
+ return ImmutableSet.of(Futures.immediateFuture("baz1"), Futures.immediateFuture("baz2"));
+ }
+
+ @Produces
+ @ElementsIntoSet
+ static Set<String> strs() {
+ return ImmutableSet.of("bar1", "bar2");
+ }
+
+ @Produces
+ static int strCount(Set<String> strs) {
+ return strs.size();
+ }
+
+ @Produces
+ @IntoSet
+ @PossiblyThrowingSet
+ static String successfulStringForSet() {
+ return "singleton";
+ }
+
+ @Produces
+ @ElementsIntoSet
+ @PossiblyThrowingSet
+ static Set<String> successfulStringsForSet() {
+ return ImmutableSet.of("double", "ton");
+ }
+
+ @Produces
+ @IntoSet
+ @PossiblyThrowingSet
+ static String throwingStringForSet() {
+ throw new RuntimeException("monkey");
+ }
+
+ @Produces
+ @IntoMap
+ @IntKey(42)
+ static ListenableFuture<String> futureFor42() {
+ return Futures.immediateFuture("forty two");
+ }
+
+ @Produces
+ @IntoMap
+ @IntKey(15)
+ static String valueFor15() {
+ return "fifteen";
+ }
+
+ @Produces
+ @IntoMap
+ @PossiblyThrowingMap
+ @IntKey(42)
+ static ListenableFuture<String> successfulFutureFor42() {
+ return Futures.immediateFuture("forty two");
+ }
+
+ @Produces
+ @IntoMap
+ @PossiblyThrowingMap
+ @IntKey(15)
+ static String throwingValueFor15() {
+ throw new RuntimeException("monkey");
+ }
+
+ @Multibinds
+ abstract Set<Object> objs();
+
+ @Multibinds
+ abstract Map<Object, Object> objMap();
+
+ @Produces
+ @ObjCount
+ static int objCount(Set<Produced<Object>> objs, Map<Object, Produced<Object>> objMap) {
+ return objs.size() + objMap.size();
+ }
+
+ @Multibinds
+ @EmptyButDeclaredInModuleAndProducerModule
+ abstract Map<String, Object> emptyButDeclaredInModuleAndProducerModule();
+}
diff --git a/javatests/dagger/functional/producers/multibindings/MultibindingTest.java b/javatests/dagger/functional/producers/multibindings/MultibindingTest.java
new file mode 100644
index 0000000..81daa88
--- /dev/null
+++ b/javatests/dagger/functional/producers/multibindings/MultibindingTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.multibindings;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MultibindingTest {
+ @Test
+ public void setBinding() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ assertThat(multibindingComponent.strs().get())
+ .containsExactly(
+ "foo",
+ "foo1",
+ "foo2",
+ "baz1",
+ "baz2",
+ "bar",
+ "bar1",
+ "bar2",
+ "providedStr",
+ "providedStr1",
+ "providedStr2");
+ assertThat(multibindingComponent.strCount().get()).isEqualTo(11);
+ }
+
+ @Test
+ public void setBindingOfProduced() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ assertThat(multibindingComponent.successfulSet().get())
+ .containsExactly(
+ Produced.successful("foo"),
+ Produced.successful("foo1"),
+ Produced.successful("foo2"),
+ Produced.successful("baz1"),
+ Produced.successful("baz2"),
+ Produced.successful("bar"),
+ Produced.successful("bar1"),
+ Produced.successful("bar2"),
+ Produced.successful("providedStr"),
+ Produced.successful("providedStr1"),
+ Produced.successful("providedStr2"));
+ }
+
+ @Test
+ public void setBindingOfProducedWithFailures() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ Set<Produced<String>> possiblyThrowingSet = multibindingComponent.possiblyThrowingSet().get();
+ Set<String> successes = new HashSet<>();
+ Set<ExecutionException> failures = new HashSet<>();
+ for (Produced<String> str : possiblyThrowingSet) {
+ try {
+ successes.add(str.get());
+ } catch (ExecutionException e) {
+ failures.add(e);
+ }
+ }
+ assertThat(successes).containsExactly("singleton", "double", "ton");
+ assertThat(failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(failures).getCause()).hasMessageThat().isEqualTo("monkey");
+ }
+
+ @Test
+ public void mapBinding() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ Map<Integer, String> map = multibindingComponent.map().get();
+ assertThat(map).hasSize(3);
+ assertThat(map).containsEntry(15, "fifteen");
+ assertThat(map).containsEntry(42, "forty two");
+ assertThat(map).containsEntry(3, "provided three");
+ }
+
+ @Test
+ public void mapOfProducerBinding() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ Map<Integer, Producer<String>> map = multibindingComponent.mapOfProducer().get();
+ assertThat(map).hasSize(3);
+ assertThat(map).containsKey(15);
+ assertThat(map.get(15).get().get()).isEqualTo("fifteen");
+ assertThat(map).containsKey(42);
+ assertThat(map.get(42).get().get()).isEqualTo("forty two");
+ assertThat(map).containsKey(3);
+ assertThat(map.get(3).get().get()).isEqualTo("provided three");
+ }
+
+ @Test
+ public void mapOfProducedBinding() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ Map<Integer, Produced<String>> map = multibindingComponent.mapOfProduced().get();
+ assertThat(map).hasSize(3);
+ assertThat(map).containsKey(15);
+ assertThat(map.get(15).get()).isEqualTo("fifteen");
+ assertThat(map).containsKey(42);
+ assertThat(map.get(42).get()).isEqualTo("forty two");
+ assertThat(map).containsKey(3);
+ assertThat(map.get(3).get()).isEqualTo("provided three");
+ }
+
+ @Test
+ public void mapBindingWithFailures() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ try {
+ multibindingComponent.possiblyThrowingMap().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause()).hasMessageThat().isEqualTo("monkey");
+ }
+ }
+
+ @Test
+ public void mapOfProducerBindingWithFailures() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ Map<Integer, Producer<String>> map =
+ multibindingComponent.possiblyThrowingMapOfProducer().get();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsKey(42);
+ assertThat(map.get(42).get().get()).isEqualTo("forty two");
+ assertThat(map).containsKey(15);
+ ListenableFuture<String> future = map.get(15).get();
+ try {
+ future.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause()).hasMessageThat().isEqualTo("monkey");
+ }
+ }
+
+ @Test
+ public void mapOfProducedBindingWithFailures() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ Map<Integer, Produced<String>> map =
+ multibindingComponent.possiblyThrowingMapOfProduced().get();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsKey(42);
+ assertThat(map.get(42).get()).isEqualTo("forty two");
+ assertThat(map).containsKey(15);
+ Produced<String> produced = map.get(15);
+ try {
+ produced.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause()).hasMessageThat().isEqualTo("monkey");
+ }
+ }
+
+ @Test
+ public void emptySet() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ assertThat(multibindingComponent.objs().get()).isEmpty();
+ assertThat(multibindingComponent.producedObjs().get()).isEmpty();
+ assertThat(multibindingComponent.objCount().get()).isEqualTo(0);
+ }
+
+ @Test
+ public void emptyMap() throws Exception {
+ MultibindingComponent multibindingComponent = DaggerMultibindingComponent.create();
+ assertThat(multibindingComponent.objMap().get()).isEmpty();
+ assertThat(multibindingComponent.objMapOfProduced().get()).isEmpty();
+ assertThat(multibindingComponent.objMapOfProducer().get()).isEmpty();
+ }
+}
diff --git a/javatests/dagger/functional/producers/multibindings/Qualifiers.java b/javatests/dagger/functional/producers/multibindings/Qualifiers.java
new file mode 100644
index 0000000..640430c
--- /dev/null
+++ b/javatests/dagger/functional/producers/multibindings/Qualifiers.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.multibindings;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+final class Qualifiers {
+ @Documented
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface PossiblyThrowingSet {}
+
+ @Documented
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface PossiblyThrowingMap {}
+
+ @Documented
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface ObjCount {}
+
+ @Documented
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface EmptyButDeclaredInModule {}
+
+ @Documented
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface EmptyButDeclaredInModuleAndProducerModule {}
+
+ @Documented
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface OnlyProvisionMultibindings {}
+
+ private Qualifiers() {}
+}
diff --git a/javatests/dagger/functional/producers/optional/OptionalBindingComponents.java b/javatests/dagger/functional/producers/optional/OptionalBindingComponents.java
new file mode 100644
index 0000000..d3a7aa6
--- /dev/null
+++ b/javatests/dagger/functional/producers/optional/OptionalBindingComponents.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.optional;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.Provides;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.Production;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Retention;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import javax.annotation.Nullable;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+
+/** Classes to support testing {@code BindsOptionalOf} functionality. */
+final class OptionalBindingComponents {
+
+ /** A qualifier. */
+ @Qualifier
+ @Retention(RUNTIME)
+ @interface SomeQualifier {}
+
+ /** A value object that contains various optionally-bound objects. */
+ @AutoValue
+ abstract static class Values {
+ abstract Optional<Value> optionalInstance();
+
+ abstract Optional<Producer<Value>> optionalProducer();
+
+ abstract Optional<Produced<Value>> optionalProduced();
+ }
+
+ enum Value {
+ VALUE,
+ QUALIFIED_VALUE
+ }
+
+ @Module
+ static final class ExecutorModule {
+ @Provides
+ @Production
+ static Executor executor() {
+ return Executors.newSingleThreadExecutor();
+ }
+ }
+
+ /** Binds optionals and {@link Values}. */
+ @ProducerModule
+ abstract static class OptionalBindingModule {
+ @BindsOptionalOf
+ abstract Value value();
+
+ @BindsOptionalOf
+ @SomeQualifier
+ abstract Value qualifiedValue();
+
+ @BindsOptionalOf
+ abstract Object nullableObject();
+
+ @Produces
+ static Values values(
+ Optional<Value> optionalInstance,
+ Optional<Producer<Value>> optionalProducer,
+ Optional<Produced<Value>> optionalProduced) {
+ return new AutoValue_OptionalBindingComponents_Values(
+ optionalInstance, optionalProducer, optionalProduced);
+ }
+
+ @Produces
+ @SomeQualifier
+ static Values qualifiedValues(
+ Optional<Value> optionalInstance,
+ Optional<Producer<Value>> optionalProducer,
+ Optional<Produced<Value>> optionalProduced) {
+ return new AutoValue_OptionalBindingComponents_Values(
+ optionalInstance, optionalProducer, optionalProduced);
+ }
+ }
+
+ /** Binds {@link Value} using {@link Producer}s. */
+ @ProducerModule
+ abstract static class ConcreteBindingProducerModule {
+ @Produces
+ static Value value() {
+ return Value.VALUE;
+ }
+
+ @Produces
+ @SomeQualifier
+ static Value qualifiedValue() {
+ return Value.QUALIFIED_VALUE;
+ }
+
+ // @Produces @Nullable has no effect (and ProducesMethodValidator warns when the two are used
+ // together. Use a @Provides method and let it be wrapped into a producerFromProvider for the
+ // purposes of the test
+ @Provides
+ @Nullable
+ static Object nullableObject() {
+ return null;
+ }
+ }
+
+ /** Binds {@link Value} using {@link Provider}s. */
+ @Module
+ abstract static class ConcreteBindingModule {
+ @Provides
+ static Value value() {
+ return Value.VALUE;
+ }
+
+ @Provides
+ @SomeQualifier
+ static Value qualifiedValue() {
+ return Value.QUALIFIED_VALUE;
+ }
+
+ @Provides
+ @Nullable
+ static Object nullableObject() {
+ return null;
+ }
+ }
+
+ interface OptionalBindingComponent {
+ ListenableFuture<Values> values();
+
+ ListenableFuture<Optional<Value>> optionalInstance();
+
+ ListenableFuture<Optional<Producer<Value>>> optionalProducer();
+
+ ListenableFuture<Optional<Produced<Value>>> optionalProduced();
+
+ @SomeQualifier
+ ListenableFuture<Values> qualifiedValues();
+
+ @SomeQualifier
+ ListenableFuture<Optional<Value>> qualifiedOptionalInstance();
+
+ @SomeQualifier
+ ListenableFuture<Optional<Producer<Value>>> qualifiedOptionalProducer();
+
+ @SomeQualifier
+ ListenableFuture<Optional<Produced<Value>>> qualifiedOptionalProduced();
+
+ // Nullable bindings can satisfy optional bindings except for Optional<Foo>.
+ ListenableFuture<Optional<Producer<Object>>> optionalNullableProducer();
+
+ ListenableFuture<Optional<Produced<Object>>> optionalNullableProduced();
+ }
+
+ @ProductionComponent(modules = {ExecutorModule.class, OptionalBindingModule.class})
+ interface AbsentOptionalBindingComponent extends OptionalBindingComponent {
+ PresentOptionalBindingSubcomponent presentChild();
+ }
+
+ @ProductionComponent(
+ modules = {
+ ExecutorModule.class,
+ OptionalBindingModule.class,
+ ConcreteBindingProducerModule.class
+ }
+ )
+ interface PresentOptionalBindingComponent extends OptionalBindingComponent {}
+
+ @ProductionSubcomponent(modules = ConcreteBindingProducerModule.class)
+ interface PresentOptionalBindingSubcomponent extends OptionalBindingComponent {}
+
+ @ProductionComponent(
+ modules = {ExecutorModule.class, OptionalBindingModule.class, ConcreteBindingModule.class}
+ )
+ interface PresentOptionalProvisionBindingComponent extends OptionalBindingComponent {}
+}
diff --git a/javatests/dagger/functional/producers/optional/OptionalBindingComponentsAbsentTest.java b/javatests/dagger/functional/producers/optional/OptionalBindingComponentsAbsentTest.java
new file mode 100644
index 0000000..4bb7234
--- /dev/null
+++ b/javatests/dagger/functional/producers/optional/OptionalBindingComponentsAbsentTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.optional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.producers.optional.OptionalBindingComponents.AbsentOptionalBindingComponent;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for absent optional bindings. */
+@RunWith(JUnit4.class)
+public final class OptionalBindingComponentsAbsentTest {
+ private AbsentOptionalBindingComponent absent;
+
+ @Before
+ public void setUp() {
+ absent = DaggerOptionalBindingComponents_AbsentOptionalBindingComponent.create();
+ }
+
+ @Test
+ public void optional() throws Exception {
+ assertThat(absent.optionalInstance().get()).isAbsent();
+ }
+
+ @Test
+ public void optionalProducer() throws Exception {
+ assertThat(absent.optionalProducer().get()).isAbsent();
+ }
+
+ @Test
+ public void optionalProduced() throws Exception {
+ assertThat(absent.optionalProduced().get()).isAbsent();
+ }
+
+ @Test
+ public void qualifiedOptional() throws Exception {
+ assertThat(absent.qualifiedOptionalInstance().get()).isAbsent();
+ }
+
+ @Test
+ public void qualifiedOptionalProducer() throws Exception {
+ assertThat(absent.qualifiedOptionalProducer().get()).isAbsent();
+ }
+
+ @Test
+ public void qualifiedOptionalProduced() throws Exception {
+ assertThat(absent.qualifiedOptionalProduced().get()).isAbsent();
+ }
+}
diff --git a/javatests/dagger/functional/producers/optional/OptionalBindingComponentsPresentTest.java b/javatests/dagger/functional/producers/optional/OptionalBindingComponentsPresentTest.java
new file mode 100644
index 0000000..3e2afd4
--- /dev/null
+++ b/javatests/dagger/functional/producers/optional/OptionalBindingComponentsPresentTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.optional;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.functional.producers.optional.OptionalBindingComponents.Value.QUALIFIED_VALUE;
+import static dagger.functional.producers.optional.OptionalBindingComponents.Value.VALUE;
+
+import com.google.common.collect.ImmutableList;
+import dagger.functional.producers.optional.OptionalBindingComponents.OptionalBindingComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for present optional bindings. */
+@RunWith(Parameterized.class)
+public final class OptionalBindingComponentsPresentTest {
+
+ @Parameters(name = "{0}")
+ public static Iterable<Object[]> parameters() {
+ return ImmutableList.copyOf(
+ new Object[][] {
+ {DaggerOptionalBindingComponents_PresentOptionalBindingComponent.create()},
+ {DaggerOptionalBindingComponents_AbsentOptionalBindingComponent.create().presentChild()},
+ {DaggerOptionalBindingComponents_PresentOptionalProvisionBindingComponent.create()}
+ });
+ }
+
+ private final OptionalBindingComponent component;
+
+ public OptionalBindingComponentsPresentTest(OptionalBindingComponent component) {
+ this.component = component;
+ }
+
+ @Test
+ public void optional() throws Exception {
+ assertThat(component.optionalInstance().get()).hasValue(VALUE);
+ }
+
+ @Test
+ public void optionalProducer() throws Exception {
+ assertThat(component.optionalProducer().get().get().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void optionalProduced() throws Exception {
+ assertThat(component.optionalProduced().get().get().get()).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void qualifiedOptional() throws Exception {
+ assertThat(component.qualifiedOptionalInstance().get()).hasValue(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalProducer() throws Exception {
+ assertThat(component.qualifiedOptionalProducer().get().get().get().get())
+ .isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void qualifiedOptionalProduced() throws Exception {
+ assertThat(component.qualifiedOptionalProduced().get().get().get()).isEqualTo(QUALIFIED_VALUE);
+ }
+
+ @Test
+ public void optionalNullableProducer() throws Exception {
+ assertThat(component.optionalNullableProducer().get().get().get().get()).isNull();
+ }
+
+ @Test
+ public void optionalNullableProduced() throws Exception {
+ assertThat(component.optionalNullableProduced().get().get().get()).isNull();
+ }
+}
diff --git a/javatests/dagger/functional/producers/provisions/Provisions.java b/javatests/dagger/functional/producers/provisions/Provisions.java
new file mode 100644
index 0000000..16f3337
--- /dev/null
+++ b/javatests/dagger/functional/producers/provisions/Provisions.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.provisions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+
+/** Tests for requesting provisions from producers. */
+final class Provisions {
+ static final class InjectedClass {
+ @Inject InjectedClass() {}
+ }
+
+ static final class WrappedProducer<T> {
+ final Producer<T> producer;
+
+ WrappedProducer(Producer<T> producer) {
+ this.producer = producer;
+ }
+ }
+
+ static final class Output {
+ final Producer<InjectedClass> injectedClass1;
+ final Producer<InjectedClass> injectedClass2;
+
+ Output(Producer<InjectedClass> injectedClass1, Producer<InjectedClass> injectedClass2) {
+ this.injectedClass1 = injectedClass1;
+ this.injectedClass2 = injectedClass2;
+ }
+ }
+
+ @Qualifier @interface First {}
+ @Qualifier @interface Second {}
+
+ @ProducerModule
+ static final class TestModule {
+ @Produces @First static WrappedProducer<InjectedClass> firstProducer(
+ Producer<InjectedClass> injectedClass) {
+ return new WrappedProducer<>(injectedClass);
+ }
+
+ @Produces @Second static WrappedProducer<InjectedClass> secondProducer(
+ Producer<InjectedClass> injectedClass) {
+ return new WrappedProducer<>(injectedClass);
+ }
+
+ @Produces static Output output(
+ @First WrappedProducer<InjectedClass> producer1,
+ @Second WrappedProducer<InjectedClass> producer2) {
+ return new Output(producer1.producer, producer2.producer);
+ }
+ }
+
+ @ProductionComponent(modules = {ExecutorModule.class, TestModule.class})
+ interface TestComponent {
+ ListenableFuture<Output> output();
+ }
+
+ private Provisions() {}
+}
diff --git a/javatests/dagger/functional/producers/provisions/ProvisionsTest.java b/javatests/dagger/functional/producers/provisions/ProvisionsTest.java
new file mode 100644
index 0000000..47ed2a2
--- /dev/null
+++ b/javatests/dagger/functional/producers/provisions/ProvisionsTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.provisions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.producers.provisions.Provisions.Output;
+import dagger.functional.producers.provisions.Provisions.TestComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ProvisionsTest {
+
+ @Test
+ public void provisionsOnlyAreHeldInOneProducer() throws Exception {
+ TestComponent component = DaggerProvisions_TestComponent.create();
+ Output output = component.output().get();
+ assertThat(output.injectedClass1).isSameInstanceAs(output.injectedClass2);
+ }
+}
diff --git a/javatests/dagger/functional/producers/scope/ScopeTest.java b/javatests/dagger/functional/producers/scope/ScopeTest.java
new file mode 100644
index 0000000..9cfd2c2
--- /dev/null
+++ b/javatests/dagger/functional/producers/scope/ScopeTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.scope;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ScopeTest {
+
+ @Test
+ public void scope() throws Exception {
+ SetComponent component = DaggerSetComponent.create();
+ assertThat(component.set().get()).hasSize(1);
+ assertThat(component.scopedObject()).isSameInstanceAs(component.scopedObject());
+ }
+}
diff --git a/javatests/dagger/functional/producers/scope/ScopedModule.java b/javatests/dagger/functional/producers/scope/ScopedModule.java
new file mode 100644
index 0000000..eec300b
--- /dev/null
+++ b/javatests/dagger/functional/producers/scope/ScopedModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.producers.ProductionScope;
+
+@Module
+final class ScopedModule {
+ @Provides
+ @ProductionScope
+ static Object newScopedObject() {
+ return new Object();
+ }
+}
diff --git a/javatests/dagger/functional/producers/scope/ScopedObject.java b/javatests/dagger/functional/producers/scope/ScopedObject.java
new file mode 100644
index 0000000..07eebd9
--- /dev/null
+++ b/javatests/dagger/functional/producers/scope/ScopedObject.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.scope;
+
+import dagger.producers.ProductionScope;
+import javax.inject.Inject;
+
+@ProductionScope
+final class ScopedObject {
+ @Inject
+ ScopedObject() {}
+}
diff --git a/javatests/dagger/functional/producers/scope/SetComponent.java b/javatests/dagger/functional/producers/scope/SetComponent.java
new file mode 100644
index 0000000..8400b70
--- /dev/null
+++ b/javatests/dagger/functional/producers/scope/SetComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.scope;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.ProductionComponent;
+import java.util.Set;
+
+@ProductionComponent(modules = {ExecutorModule.class, ScopedModule.class, SetProducerModule.class})
+interface SetComponent {
+ ScopedObject scopedObject();
+
+ ListenableFuture<Set<Object>> set();
+}
diff --git a/javatests/dagger/functional/producers/scope/SetProducerModule.java b/javatests/dagger/functional/producers/scope/SetProducerModule.java
new file mode 100644
index 0000000..f88c91b
--- /dev/null
+++ b/javatests/dagger/functional/producers/scope/SetProducerModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.scope;
+
+import dagger.multibindings.IntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+/**
+ * A module that provides two entries into a set; but since the inputs are scoped, the set should
+ * only have one value.
+ */
+@ProducerModule
+final class SetProducerModule {
+ @Produces
+ @IntoSet
+ static Object setValue1(Object value) {
+ return value;
+ }
+
+ @Produces
+ @IntoSet
+ static Object setValue2(Object value) {
+ return value;
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/ModuleSubcomponentsInterop.java b/javatests/dagger/functional/producers/subcomponent/ModuleSubcomponentsInterop.java
new file mode 100644
index 0000000..673be2b
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/ModuleSubcomponentsInterop.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+
+final class ModuleSubcomponentsInterop {
+ @Component(modules = ProvisionTestModule.class)
+ interface ProvisionParent {
+ ProductionChild.Builder productionChild();
+ }
+
+ @Module(subcomponents = ProductionChild.class)
+ static class ProvisionTestModule {}
+
+ @ProductionSubcomponent
+ interface ProductionChild {
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ ProductionChild build();
+ }
+ }
+
+ @ProductionComponent(modules = ProductionTestModule.class)
+ interface ProductionParent {
+ ProvisionChild.Builder provisionBuilder();
+ }
+
+ @ProducerModule(subcomponents = ProvisionChild.class)
+ static class ProductionTestModule {}
+
+ @Subcomponent
+ interface ProvisionChild {
+ @Subcomponent.Builder
+ interface Builder {
+ ProvisionChild build();
+ }
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/MultiPackageSubcomponentTest.java b/javatests/dagger/functional/producers/subcomponent/MultiPackageSubcomponentTest.java
new file mode 100644
index 0000000..482991d
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/MultiPackageSubcomponentTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.producers.subcomponent.MultiPackageSubcomponents.ParentComponent;
+import dagger.functional.producers.subcomponent.sub.ChildComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MultiPackageSubcomponentTest {
+
+ @Test
+ public void childComponent() throws Exception {
+ ParentComponent parent = DaggerMultiPackageSubcomponents_ParentComponent.create();
+ ChildComponent child = parent.childComponentBuilder().build();
+ assertThat(child.str().get()).isEqualTo("Hello, World 42");
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/MultiPackageSubcomponents.java b/javatests/dagger/functional/producers/subcomponent/MultiPackageSubcomponents.java
new file mode 100644
index 0000000..953bfff
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/MultiPackageSubcomponents.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.functional.producers.subcomponent.sub.ChildComponent;
+
+final class MultiPackageSubcomponents {
+ @Component(modules = IntModule.class)
+ interface ParentComponent {
+ ChildComponent.Builder childComponentBuilder();
+ }
+
+ @Module
+ static final class IntModule {
+ @Provides
+ static int i() {
+ return 42;
+ }
+ }
+
+ private MultiPackageSubcomponents() {}
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/ProducerModuleWithSubcomponentsTest.java b/javatests/dagger/functional/producers/subcomponent/ProducerModuleWithSubcomponentsTest.java
new file mode 100644
index 0000000..7568071
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/ProducerModuleWithSubcomponentsTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.producers.subcomponent.UsesProducerModuleSubcomponents.ParentIncludesProductionSubcomponentTransitively;
+import dagger.producers.ProducerModule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link ProducerModule#subcomponents()}. */
+@RunWith(JUnit4.class)
+public class ProducerModuleWithSubcomponentsTest {
+
+ @Test
+ public void subcomponentFromModules() throws Exception {
+ UsesProducerModuleSubcomponents parent = DaggerUsesProducerModuleSubcomponents.create();
+ assertThat(parent.strings().get()).containsExactly("from parent");
+ assertThat(parent.stringsFromChild().get()).containsExactly("from parent", "from child");
+ }
+
+ @Test
+ public void subcomponentFromModules_transitively() throws Exception {
+ ParentIncludesProductionSubcomponentTransitively parent =
+ DaggerUsesProducerModuleSubcomponents_ParentIncludesProductionSubcomponentTransitively
+ .create();
+ assertThat(parent.strings().get()).containsExactly("from parent");
+ assertThat(parent.stringsFromChild().get()).containsExactly("from parent", "from child");
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/ProductionSubcomponentFromModuleAndFactoryMethod.java b/javatests/dagger/functional/producers/subcomponent/ProductionSubcomponentFromModuleAndFactoryMethod.java
new file mode 100644
index 0000000..1286247
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/ProductionSubcomponentFromModuleAndFactoryMethod.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+
+/**
+ * Tests for {@link Subcomponent}s which are defined with {@link Module#subcomponents()} and are
+ * also requested as component factory methods.
+ */
+public class ProductionSubcomponentFromModuleAndFactoryMethod {
+ @ProductionSubcomponent
+ interface Sub {
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ Sub sub();
+ }
+ }
+
+ @ProducerModule(subcomponents = Sub.class)
+ static class ModuleWithSubcomponent {}
+
+ @ProductionComponent(modules = {ModuleWithSubcomponent.class, ExecutorModule.class})
+ interface ExposesBuilder {
+ Sub.Builder subcomponentBuilder();
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/SubcomponentWithBoundExecutorTest.java b/javatests/dagger/functional/producers/subcomponent/SubcomponentWithBoundExecutorTest.java
new file mode 100644
index 0000000..6d176d7
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/SubcomponentWithBoundExecutorTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.producers.subcomponent.SubcomponentsWithBoundExecutor.ChildComponent;
+import dagger.functional.producers.subcomponent.SubcomponentsWithBoundExecutor.ExecutorModule;
+import dagger.functional.producers.subcomponent.SubcomponentsWithBoundExecutor.GrandchildComponent;
+import dagger.functional.producers.subcomponent.SubcomponentsWithBoundExecutor.GrandchildComponentWithoutBuilder;
+import dagger.functional.producers.subcomponent.SubcomponentsWithBoundExecutor.ParentComponent;
+import dagger.functional.producers.subcomponent.SubcomponentsWithBoundExecutor.ParentProductionComponent;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class SubcomponentWithBoundExecutorTest {
+ private ParentComponent parentComponent;
+ private ParentProductionComponent parentProductionComponent;
+ private final AtomicInteger executorConstructionCount = new AtomicInteger();
+ private final AtomicInteger executionCount = new AtomicInteger();
+
+ @Before
+ public void setUp() {
+ parentComponent =
+ DaggerSubcomponentsWithBoundExecutor_ParentComponent.builder()
+ .executorModule(new ExecutorModule(executorConstructionCount, executionCount))
+ .build();
+ parentProductionComponent =
+ DaggerSubcomponentsWithBoundExecutor_ParentProductionComponent.builder()
+ .executorModule(new ExecutorModule(executorConstructionCount, executionCount))
+ .build();
+ }
+
+ @Test
+ public void topLevelComponent_child() throws Exception {
+ ChildComponent child = parentComponent.newChildComponentBuilder().build();
+ assertThat(child.fromChild().get()).isEqualTo("child:parent");
+ assertThat(executorConstructionCount.get()).isEqualTo(1);
+ assertThat(executionCount.get()).isEqualTo(1);
+ }
+
+ @Test
+ public void topLevelComponent_injectsChildBuilder() throws Exception {
+ ChildComponent child = parentComponent.injectsChildBuilder().childBuilder().build();
+ assertThat(child.fromChild().get()).isEqualTo("child:parent");
+ assertThat(executorConstructionCount.get()).isEqualTo(1);
+ assertThat(executionCount.get()).isEqualTo(1);
+ }
+
+ @Test
+ public void topLevelComponent_grandchild() throws Exception {
+ ChildComponent child = parentComponent.newChildComponentBuilder().build();
+ GrandchildComponent grandchild = child.newGrandchildComponentBuilder().build();
+ assertThat(grandchild.fromGrandchild().get()).isEqualTo("grandchild:child:parent");
+ assertThat(executorConstructionCount.get()).isEqualTo(1);
+ assertThat(executionCount.get()).isEqualTo(2);
+ }
+
+ @Test
+ public void topLevelComponent_grandchildWithoutBuilder() throws Exception {
+ ChildComponent child = parentComponent.newChildComponentBuilder().build();
+ GrandchildComponentWithoutBuilder grandchild = child.newGrandchildComponent();
+ assertThat(grandchild.fromGrandchild().get()).isEqualTo("grandchild:child:parent");
+ assertThat(executorConstructionCount.get()).isEqualTo(1);
+ assertThat(executionCount.get()).isEqualTo(2);
+ }
+
+ @Test
+ public void topLevelProductionComponent_child() throws Exception {
+ ChildComponent child = parentProductionComponent.newChildComponentBuilder().build();
+ assertThat(child.fromChild().get()).isEqualTo("child:parentproduction");
+ assertThat(executorConstructionCount.get()).isEqualTo(1);
+ assertThat(executionCount.get()).isEqualTo(2);
+ }
+
+ @Test
+ public void topLevelProductionComponent_grandchild() throws Exception {
+ ChildComponent child = parentProductionComponent.newChildComponentBuilder().build();
+ GrandchildComponent grandchild = child.newGrandchildComponentBuilder().build();
+ assertThat(grandchild.fromGrandchild().get()).isEqualTo("grandchild:child:parentproduction");
+ assertThat(executorConstructionCount.get()).isEqualTo(1);
+ assertThat(executionCount.get()).isEqualTo(3);
+ }
+
+ @Test
+ public void topLevelProductionComponent_grandchildWithoutBuilder() throws Exception {
+ ChildComponent child = parentProductionComponent.newChildComponentBuilder().build();
+ GrandchildComponentWithoutBuilder grandchild = child.newGrandchildComponent();
+ assertThat(grandchild.fromGrandchild().get()).isEqualTo("grandchild:child:parentproduction");
+ assertThat(executorConstructionCount.get()).isEqualTo(1);
+ assertThat(executionCount.get()).isEqualTo(3);
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/SubcomponentsWithBoundExecutor.java b/javatests/dagger/functional/producers/subcomponent/SubcomponentsWithBoundExecutor.java
new file mode 100644
index 0000000..f7059c7
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/SubcomponentsWithBoundExecutor.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.Production;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Qualifier;
+
+final class SubcomponentsWithBoundExecutor {
+ @Qualifier
+ @interface FromParent {}
+
+ @Qualifier
+ @interface FromChild {}
+
+ @Qualifier
+ @interface FromGrandchild {}
+
+ static final class CountingExecutor implements Executor {
+ private final AtomicInteger executionCount;
+
+ CountingExecutor(AtomicInteger executionCount) {
+ this.executionCount = executionCount;
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ executionCount.incrementAndGet();
+ runnable.run();
+ }
+ }
+
+ @Module
+ static final class ExecutorModule {
+ private final AtomicInteger constructionCount;
+ private final AtomicInteger executionCount;
+
+ ExecutorModule(AtomicInteger constructionCount, AtomicInteger executionCount) {
+ this.constructionCount = constructionCount;
+ this.executionCount = executionCount;
+ }
+
+ @Provides
+ @Production
+ Executor executor() {
+ constructionCount.incrementAndGet();
+ return new CountingExecutor(executionCount);
+ }
+ }
+
+ @Module
+ static final class ParentModule {
+ @Provides
+ @FromParent
+ static String fromParent() {
+ return "parent";
+ }
+ }
+
+ @Component(modules = {ParentModule.class, ExecutorModule.class})
+ interface ParentComponent {
+ InjectsChildBuilder injectsChildBuilder();
+
+ ChildComponent.Builder newChildComponentBuilder();
+ }
+
+ @ProducerModule
+ static final class ParentProducerModule {
+ @Produces
+ @FromParent
+ static String fromParent() {
+ return "parentproduction";
+ }
+ }
+
+ @ProductionComponent(modules = {ParentProducerModule.class, ExecutorModule.class})
+ interface ParentProductionComponent {
+ ChildComponent.Builder newChildComponentBuilder();
+
+ @ProductionComponent.Builder
+ interface Builder {
+ Builder executorModule(ExecutorModule executorModule);
+
+ ParentProductionComponent build();
+ }
+ }
+
+ @ProducerModule
+ static final class ChildProducerModule {
+ @Produces
+ @FromChild
+ static String fromChild(@FromParent String fromParent) {
+ return "child:" + fromParent;
+ }
+ }
+
+ @ProductionSubcomponent(modules = ChildProducerModule.class)
+ interface ChildComponent {
+ @FromChild
+ ListenableFuture<String> fromChild();
+
+ GrandchildComponent.Builder newGrandchildComponentBuilder();
+ GrandchildComponentWithoutBuilder newGrandchildComponent();
+
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ ChildComponent build();
+ }
+ }
+
+ static final class InjectsChildBuilder {
+ private final Provider<ChildComponent.Builder> childBuilder;
+
+ @Inject
+ InjectsChildBuilder(Provider<ChildComponent.Builder> childBuilder) {
+ this.childBuilder = childBuilder;
+ }
+
+ ChildComponent.Builder childBuilder() {
+ return childBuilder.get();
+ }
+ }
+
+ @ProducerModule
+ static final class GrandchildProducerModule {
+ @Produces
+ @FromGrandchild
+ static String fromGranchild(@FromChild String fromChild) {
+ return "grandchild:" + fromChild;
+ }
+ }
+
+ @ProductionSubcomponent(modules = GrandchildProducerModule.class)
+ interface GrandchildComponent {
+ @FromGrandchild
+ ListenableFuture<String> fromGrandchild();
+
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ GrandchildComponent build();
+ }
+ }
+
+ @ProductionSubcomponent(modules = GrandchildProducerModule.class)
+ interface GrandchildComponentWithoutBuilder {
+ @FromGrandchild
+ ListenableFuture<String> fromGrandchild();
+ }
+
+ private SubcomponentsWithBoundExecutor() {}
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/UsesProducerModuleSubcomponents.java b/javatests/dagger/functional/producers/subcomponent/UsesProducerModuleSubcomponents.java
new file mode 100644
index 0000000..31253cc
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/UsesProducerModuleSubcomponents.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.multibindings.IntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.util.Set;
+import javax.inject.Qualifier;
+
+/** Supporting types for {@code ProducerModuleWithSubcomponentsTest}. */
+@ProductionComponent(
+ modules = UsesProducerModuleSubcomponents.ProducerModuleWithSubcomponents.class
+)
+public interface UsesProducerModuleSubcomponents {
+
+ ListenableFuture<Set<String>> strings();
+
+ @FromChild
+ ListenableFuture<Set<String>> stringsFromChild();
+
+ @ProducerModule(
+ subcomponents = Child.class,
+ includes = {AlsoIncludesSubcomponents.class, ExecutorModule.class}
+ )
+ class ProducerModuleWithSubcomponents {
+ @Produces
+ @IntoSet
+ static String produceStringInParent() {
+ return "from parent";
+ }
+
+ @Produces
+ @FromChild
+ static Set<String> stringsFromChild(Child.Builder childBuilder) throws Exception {
+ return childBuilder.build().strings().get();
+ }
+ }
+
+ @ProducerModule(subcomponents = Child.class)
+ class AlsoIncludesSubcomponents {}
+
+ @ProductionSubcomponent(modules = ChildModule.class)
+ interface Child {
+ ListenableFuture<Set<String>> strings();
+
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ Child build();
+ }
+ }
+
+ @ProducerModule
+ class ChildModule {
+ @Produces
+ @IntoSet
+ static String produceStringInChild() {
+ return "from child";
+ }
+ }
+
+ @Qualifier
+ @interface FromChild {}
+
+ @ProducerModule(includes = ProducerModuleWithSubcomponents.class)
+ class OnlyIncludesProducerModuleWithSubcomponents {}
+
+ @ProductionComponent(modules = OnlyIncludesProducerModuleWithSubcomponents.class)
+ interface ParentIncludesProductionSubcomponentTransitively
+ extends UsesProducerModuleSubcomponents {}
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/pruning/ParentDoesntUseProductionSubcomponent.java b/javatests/dagger/functional/producers/subcomponent/pruning/ParentDoesntUseProductionSubcomponent.java
new file mode 100644
index 0000000..bf931c1
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/pruning/ParentDoesntUseProductionSubcomponent.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent.pruning;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.multibindings.IntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.util.Set;
+import javax.inject.Qualifier;
+
+/**
+ * Supporting types for {@code ProductionSubcomponentOnlyRequestedBySiblingTest}. {@link ChildA} is
+ * a direct child of the top level component, but is only requested within its sibling, not directly
+ * from its parent.
+ */
+@ProductionComponent(
+ modules = {
+ ParentDoesntUseProductionSubcomponent.ParentModule.class,
+ dagger.functional.producers.ExecutorModule.class
+ }
+)
+interface ParentDoesntUseProductionSubcomponent {
+
+ ChildB.Builder childBBuilder();
+
+ @ProductionSubcomponent(modules = ChildAModule.class)
+ interface ChildA {
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ ChildA build();
+ }
+
+ ListenableFuture<Set<Class<?>>> componentHierarchy();
+ }
+
+ @ProductionSubcomponent(modules = ChildBModule.class)
+ interface ChildB {
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ ChildB build();
+ }
+
+ ListenableFuture<Set<Class<?>>> componentHierarchy();
+
+ @FromChildA
+ ListenableFuture<Set<Class<?>>> componentHierarchyFromChildA();
+ }
+
+ @ProducerModule(subcomponents = {ChildA.class, ChildB.class})
+ class ParentModule {
+ @Produces
+ @IntoSet
+ static Class<?> produceComponentType() {
+ return ParentDoesntUseProductionSubcomponent.class;
+ }
+ }
+
+ @ProducerModule
+ class ChildAModule {
+ @Produces
+ @IntoSet
+ static Class<?> produceComponentType() {
+ return ChildA.class;
+ }
+ }
+
+ @ProducerModule
+ class ChildBModule {
+ @Produces
+ @IntoSet
+ static Class<?> produceComponentType() {
+ return ChildB.class;
+ }
+
+ @Produces
+ @FromChildA
+ Set<Class<?>> fromChildA(ChildA.Builder childABuilder) throws Exception {
+ return childABuilder.build().componentHierarchy().get();
+ }
+ }
+
+ @Qualifier
+ @interface FromChildA {}
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/pruning/ProductionSubcomponentOnlyRequestedBySiblingTest.java b/javatests/dagger/functional/producers/subcomponent/pruning/ProductionSubcomponentOnlyRequestedBySiblingTest.java
new file mode 100644
index 0000000..d178061
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/pruning/ProductionSubcomponentOnlyRequestedBySiblingTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent.pruning;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.functional.producers.subcomponent.pruning.ParentDoesntUseProductionSubcomponent.ChildA;
+import dagger.functional.producers.subcomponent.pruning.ParentDoesntUseProductionSubcomponent.ChildB;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionSubcomponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link ProductionSubcomponent}s which are included with {@link
+ * ProducerModule#subcomponents()} but not used directly within the component which adds them.
+ *
+ * <p>This tests to make sure that while resolving one subcomponent (A), another subcomponent (B)
+ * can be requested if they have a shared ancestor component. If that shared ancestor did not
+ * resolve B directly via any of its entry points, B will still be generated since it is requested
+ * by a descendant.
+ */
+@RunWith(JUnit4.class)
+public class ProductionSubcomponentOnlyRequestedBySiblingTest {
+ @Test
+ public void subcomponentAddedInParent_onlyUsedInSibling() throws Exception {
+ ParentDoesntUseProductionSubcomponent parent =
+ DaggerParentDoesntUseProductionSubcomponent.create();
+ ChildB childB = parent.childBBuilder().build();
+ assertThat(childB.componentHierarchy().get())
+ .containsExactly(ParentDoesntUseProductionSubcomponent.class, ChildB.class);
+ assertThat(childB.componentHierarchyFromChildA().get())
+ .containsExactly(ParentDoesntUseProductionSubcomponent.class, ChildA.class);
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/sub/ChildComponent.java b/javatests/dagger/functional/producers/subcomponent/sub/ChildComponent.java
new file mode 100644
index 0000000..4dfb112
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/sub/ChildComponent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent.sub;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.functional.producers.ExecutorModule;
+import dagger.producers.ProductionSubcomponent;
+
+@ProductionSubcomponent(modules = {ExecutorModule.class, ChildModule.class})
+public interface ChildComponent {
+ ListenableFuture<String> str();
+
+ @ProductionSubcomponent.Builder
+ interface Builder {
+ ChildComponent build();
+ }
+}
diff --git a/javatests/dagger/functional/producers/subcomponent/sub/ChildModule.java b/javatests/dagger/functional/producers/subcomponent/sub/ChildModule.java
new file mode 100644
index 0000000..8994a8a
--- /dev/null
+++ b/javatests/dagger/functional/producers/subcomponent/sub/ChildModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers.subcomponent.sub;
+
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+@ProducerModule
+final class ChildModule {
+ @Produces
+ static String str(int n) {
+ return "Hello, World " + n;
+ }
+}
diff --git a/javatests/dagger/functional/rawtypes/RawTypesComponent.java b/javatests/dagger/functional/rawtypes/RawTypesComponent.java
new file mode 100644
index 0000000..e2c69c2
--- /dev/null
+++ b/javatests/dagger/functional/rawtypes/RawTypesComponent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.rawtypes;
+
+import dagger.Component;
+import java.util.Map;
+import java.util.Set;
+
+@SuppressWarnings("rawtypes")
+@Component(modules = RawtypesModule.class)
+interface RawTypesComponent {
+ Set rawSet();
+ Map rawMap();
+}
diff --git a/javatests/dagger/functional/rawtypes/RawtypesModule.java b/javatests/dagger/functional/rawtypes/RawtypesModule.java
new file mode 100644
index 0000000..3bf5f59
--- /dev/null
+++ b/javatests/dagger/functional/rawtypes/RawtypesModule.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.rawtypes;
+
+import dagger.Module;
+import dagger.Provides;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@SuppressWarnings("rawtypes")
+@Module
+abstract class RawtypesModule {
+ @Provides
+ static Map rawMap() {
+ return new HashMap();
+ }
+
+ @Provides
+ static Set rawSet() {
+ return new HashSet();
+ }
+}
diff --git a/javatests/dagger/functional/scope/BlueModule.java b/javatests/dagger/functional/scope/BlueModule.java
new file mode 100644
index 0000000..e04bc7a
--- /dev/null
+++ b/javatests/dagger/functional/scope/BlueModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+final class BlueModule {
+ @Provides
+ @IntoSet
+ @BlueScope
+ static Object blue() {
+ return new Object();
+ }
+}
diff --git a/javatests/dagger/functional/scope/BlueScope.java b/javatests/dagger/functional/scope/BlueScope.java
new file mode 100644
index 0000000..ecd0da5
--- /dev/null
+++ b/javatests/dagger/functional/scope/BlueScope.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.scope;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+@Documented
+@Retention(RUNTIME)
+@Scope
+@interface BlueScope {}
diff --git a/javatests/dagger/functional/scope/GreenModule.java b/javatests/dagger/functional/scope/GreenModule.java
new file mode 100644
index 0000000..3f9248f
--- /dev/null
+++ b/javatests/dagger/functional/scope/GreenModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+final class GreenModule {
+ @Provides
+ @IntoSet
+ @GreenScope
+ static Object green() {
+ return new Object();
+ }
+}
diff --git a/javatests/dagger/functional/scope/GreenScope.java b/javatests/dagger/functional/scope/GreenScope.java
new file mode 100644
index 0000000..92bcc62
--- /dev/null
+++ b/javatests/dagger/functional/scope/GreenScope.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.scope;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+@Documented
+@Retention(RUNTIME)
+@Scope
+@interface GreenScope {}
diff --git a/javatests/dagger/functional/scope/ScopeTest.java b/javatests/dagger/functional/scope/ScopeTest.java
new file mode 100644
index 0000000..40c1fa9
--- /dev/null
+++ b/javatests/dagger/functional/scope/ScopeTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.scope;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ScopeTest {
+
+ @Test
+ public void testScope() {
+ ScopedComponent component = DaggerScopedComponent.create();
+ assertThat(component.set()).hasSize(4);
+ assertThat(component.set()).isEqualTo(component.set());
+ }
+}
diff --git a/javatests/dagger/functional/scope/ScopedComponent.java b/javatests/dagger/functional/scope/ScopedComponent.java
new file mode 100644
index 0000000..fa9b0da
--- /dev/null
+++ b/javatests/dagger/functional/scope/ScopedComponent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.scope;
+
+import dagger.Component;
+import java.util.Set;
+
+@BlueScope
+@GreenScope
+@Component(modules = {BlueModule.class, GreenModule.class, TurquoiseModule.class})
+interface ScopedComponent {
+ Set<Object> set();
+}
diff --git a/javatests/dagger/functional/scope/TurquoiseModule.java b/javatests/dagger/functional/scope/TurquoiseModule.java
new file mode 100644
index 0000000..780ecb2
--- /dev/null
+++ b/javatests/dagger/functional/scope/TurquoiseModule.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+final class TurquoiseModule {
+ @Provides
+ @IntoSet
+ @BlueScope
+ static Object blue() {
+ return new Object();
+ }
+
+ @Provides
+ @IntoSet
+ @GreenScope
+ static Object green() {
+ return new Object();
+ }
+}
diff --git a/javatests/dagger/functional/spi/BUILD b/javatests/dagger/functional/spi/BUILD
new file mode 100644
index 0000000..fffaddb
--- /dev/null
+++ b/javatests/dagger/functional/spi/BUILD
@@ -0,0 +1,55 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Functional tests for the experimental Dagger SPI
+
+package(default_visibility = ["//:src"])
+
+load("//:test_defs.bzl", "GenJavaTests")
+
+java_plugin(
+ name = "test_plugin",
+ srcs = ["TestPlugin.java"],
+ deps = [
+ "//java/dagger/model",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+java_library(
+ name = "test_lib",
+ exported_plugins = [":test_plugin"],
+)
+
+GenJavaTests(
+ name = "tests",
+ srcs = glob(
+ ["*.java"],
+ exclude = ["TestPlugin.java"],
+ ),
+ functional = 0,
+ test_only_deps = [
+ "@google_bazel_common//third_party/java/truth",
+ "@google_bazel_common//third_party/java/junit",
+ ],
+ deps = [
+ ":test_lib",
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/guava",
+ ],
+)
diff --git a/javatests/dagger/functional/spi/SpiTest.java b/javatests/dagger/functional/spi/SpiTest.java
new file mode 100644
index 0000000..05495e5
--- /dev/null
+++ b/javatests/dagger/functional/spi/SpiTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.spi;
+
+import static com.google.common.io.Resources.getResource;
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SpiTest {
+
+ @Component(modules = M.class)
+ interface C {
+ String string();
+ }
+
+ @Module
+ abstract static class M {
+ @Provides
+ static String string() {
+ return "string";
+ }
+ }
+
+ @Test
+ public void testPluginRuns() throws IOException {
+ Properties properties = new Properties();
+ try (InputStream stream = getResource(SpiTest.class, "SpiTest_C.properties").openStream()) {
+ properties.load(stream);
+ }
+ assertThat(properties).containsEntry("component[0]", C.class.getCanonicalName());
+ }
+}
diff --git a/javatests/dagger/functional/spi/TestPlugin.java b/javatests/dagger/functional/spi/TestPlugin.java
new file mode 100644
index 0000000..360bc20
--- /dev/null
+++ b/javatests/dagger/functional/spi/TestPlugin.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.spi;
+
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+
+import com.google.auto.service.AutoService;
+import com.google.common.base.Joiner;
+import com.squareup.javapoet.ClassName;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.Properties;
+import javax.annotation.processing.Filer;
+
+@AutoService(BindingGraphPlugin.class)
+public final class TestPlugin implements BindingGraphPlugin {
+ private Filer filer;
+
+ @Override
+ public void initFiler(Filer filer) {
+ this.filer = filer;
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ Properties properties = new Properties();
+ int i = 0;
+ for (ComponentNode node : bindingGraph.componentNodes()) {
+ properties.setProperty(
+ String.format("component[%s]", i++), node.componentPath().toString());
+ }
+
+ write(bindingGraph, properties);
+ }
+
+ private void write(BindingGraph bindingGraph, Properties properties) {
+ ClassName rootComponentName =
+ ClassName.get(bindingGraph.rootComponentNode().componentPath().currentComponent());
+ try (Writer writer =
+ filer
+ .createResource(
+ CLASS_OUTPUT,
+ rootComponentName.packageName(),
+ Joiner.on('_').join(rootComponentName.simpleNames()) + ".properties")
+ .openWriter()) {
+ properties.store(writer, "");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/javatests/dagger/functional/staticprovides/AllStaticModule.java b/javatests/dagger/functional/staticprovides/AllStaticModule.java
new file mode 100644
index 0000000..fff63ee
--- /dev/null
+++ b/javatests/dagger/functional/staticprovides/AllStaticModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.staticprovides;
+
+import static java.util.Collections.emptySet;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoSet;
+import java.util.Set;
+
+@Module
+final class AllStaticModule {
+ @Provides
+ @IntoSet
+ static String contributeString() {
+ return AllStaticModule.class + ".contributeString";
+ }
+
+ @Provides
+ @ElementsIntoSet
+ static Set<Integer> contibuteEmptyIntegerSet() {
+ return emptySet();
+ }
+}
diff --git a/javatests/dagger/functional/staticprovides/SomeStaticModule.java b/javatests/dagger/functional/staticprovides/SomeStaticModule.java
new file mode 100644
index 0000000..ab65b58
--- /dev/null
+++ b/javatests/dagger/functional/staticprovides/SomeStaticModule.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.staticprovides;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+final class SomeStaticModule {
+ @Provides
+ @IntoSet
+ static String contributeStringFromAStaticMethod() {
+ return SomeStaticModule.class + ".contributeStringFromAStaticMethod";
+ }
+
+ @Provides
+ @IntoSet
+ static String contributeStringFromAnInstanceMethod() {
+ return SomeStaticModule.class + ".contributeStringFromAnInstanceMethod";
+ }
+}
diff --git a/javatests/dagger/functional/staticprovides/StaticProvidesTest.java b/javatests/dagger/functional/staticprovides/StaticProvidesTest.java
new file mode 100644
index 0000000..9d9952a
--- /dev/null
+++ b/javatests/dagger/functional/staticprovides/StaticProvidesTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.staticprovides;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.common.collect.ImmutableSet;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticProvidesTest {
+ @Parameters
+ public static Collection<Object[]> components() {
+ return Arrays.asList(new Object[][] {
+ {DaggerStaticTestComponent.create()},
+ {DaggerStaticTestComponentWithBuilder.builder().build()},
+ {DaggerStaticTestComponentWithBuilder.builder()
+ .allStaticModule(new AllStaticModule())
+ .someStaticModule(new SomeStaticModule())
+ .build()}});
+ }
+
+ @Parameter
+ public StaticTestComponent component;
+
+ @Test public void setMultibinding() {
+ assertThat(component.getMultiboundStrings()).isEqualTo(ImmutableSet.of(
+ AllStaticModule.class + ".contributeString",
+ SomeStaticModule.class + ".contributeStringFromAStaticMethod",
+ SomeStaticModule.class + ".contributeStringFromAnInstanceMethod"));
+ }
+
+ @Test public void allStaticProvidesModules_noFieldInComponentBuilder() {
+ for (Field field : DaggerStaticTestComponent.Builder.class.getDeclaredFields()) {
+ assertWithMessage(field.getName())
+ .that(field.getType()).isNotEqualTo(AllStaticModule.class);
+ }
+ }
+
+ @Test public void allStaticProvidesModules_deprecatedMethodInComponentBuilder() {
+ for (Method method : DaggerStaticTestComponent.Builder.class.getDeclaredMethods()) {
+ if (Arrays.asList(method.getParameterTypes()).contains(AllStaticModule.class)) {
+ assertWithMessage(method.getName())
+ .that(method.isAnnotationPresent(Deprecated.class))
+ .isTrue();
+ }
+ }
+ }
+}
diff --git a/javatests/dagger/functional/staticprovides/StaticTestComponent.java b/javatests/dagger/functional/staticprovides/StaticTestComponent.java
new file mode 100644
index 0000000..4dced91
--- /dev/null
+++ b/javatests/dagger/functional/staticprovides/StaticTestComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.staticprovides;
+
+import dagger.Component;
+import java.util.Set;
+
+/**
+ * A simple component that demonstrates both static and non-static provides methods.
+ */
+@Component(modules = {AllStaticModule.class, SomeStaticModule.class})
+interface StaticTestComponent {
+ Set<String> getMultiboundStrings();
+ Set<Integer> getMultiboundIntegers();
+}
diff --git a/javatests/dagger/functional/staticprovides/StaticTestComponentWithBuilder.java b/javatests/dagger/functional/staticprovides/StaticTestComponentWithBuilder.java
new file mode 100644
index 0000000..c6e8f2a
--- /dev/null
+++ b/javatests/dagger/functional/staticprovides/StaticTestComponentWithBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.staticprovides;
+
+import dagger.Component;
+
+/**
+ * A simple component that demonstrates both static and non-static provides methods with a builder.
+ */
+@Component(modules = {AllStaticModule.class, SomeStaticModule.class})
+interface StaticTestComponentWithBuilder extends StaticTestComponent {
+ @Component.Builder
+ interface Builder {
+ Builder allStaticModule(AllStaticModule allStaticModule);
+ Builder someStaticModule(SomeStaticModule someStaticModule);
+ StaticTestComponentWithBuilder build();
+ }
+}
diff --git a/javatests/dagger/functional/sub/ContributionsModule.java b/javatests/dagger/functional/sub/ContributionsModule.java
new file mode 100644
index 0000000..654e8f7
--- /dev/null
+++ b/javatests/dagger/functional/sub/ContributionsModule.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.sub;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoSet;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+@Module
+public final class ContributionsModule {
+ @Provides
+ @IntoSet
+ static int contributeAnInt(@SuppressWarnings("unused") double doubleDependency) {
+ return 1742;
+ }
+
+ @Provides
+ @IntoSet
+ static int contributeAnotherInt() {
+ return 832;
+ }
+
+ @Provides
+ @ElementsIntoSet
+ static Set<Integer> contributeSomeInts() {
+ return Collections.unmodifiableSet(new LinkedHashSet<Integer>(Arrays.asList(-1, -90, -17)));
+ }
+}
diff --git a/javatests/dagger/functional/sub/Exposed.java b/javatests/dagger/functional/sub/Exposed.java
new file mode 100644
index 0000000..55c2d15
--- /dev/null
+++ b/javatests/dagger/functional/sub/Exposed.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.sub;
+
+import dagger.Lazy;
+import dagger.functional.Generic;
+import dagger.functional.Generic2;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+public class Exposed {
+
+ @Inject public PackagePrivate pp2;
+ @Inject public Provider<PackagePrivate> ppp2;
+ @Inject public Lazy<PackagePrivate> lpp2;
+ @Inject public Provider<Lazy<PackagePrivate>> plpp2;
+ @Inject public Generic2<PackagePrivate> gpp2;
+ @Inject public Generic2<PackagePrivateContainer.PublicEnclosed> gppc2;
+ @Inject public Provider<Generic2<PackagePrivate>> pgpp2;
+ @Inject public Lazy<Generic2<PackagePrivate>> lgpp2;
+ @Inject public Provider<Lazy<Generic2<PackagePrivate>>> plgpp2;
+
+ public PackagePrivate pp;
+ public Provider<PackagePrivate> ppp;
+ public Lazy<PackagePrivate> lpp;
+ public Provider<Lazy<PackagePrivate>> plpp;
+ public Generic<PackagePrivate> gpp;
+ public Generic<PackagePrivateContainer.PublicEnclosed> gppc;
+ public Provider<Generic<PackagePrivate>> pgpp;
+ public Lazy<Generic<PackagePrivate>> lgpp;
+ public Provider<Lazy<Generic<PackagePrivate>>> plgpp;
+
+ /** Injects inaccessible dependencies to test casting of these dependency arguments. */
+ @Inject Exposed(
+ PackagePrivate pp,
+ Provider<PackagePrivate> ppp,
+ Lazy<PackagePrivate> lpp,
+ Provider<Lazy<PackagePrivate>> plpp,
+ Generic<PackagePrivate> gpp,
+ Generic<PackagePrivateContainer.PublicEnclosed> gppc,
+ Provider<Generic<PackagePrivate>> pgpp,
+ Lazy<Generic<PackagePrivate>> lgpp,
+ Provider<Lazy<Generic<PackagePrivate>>> plgpp) {
+ this.pp = pp;
+ this.ppp = ppp;
+ this.lpp = lpp;
+ this.plpp = plpp;
+ this.gpp = gpp;
+ this.gppc = gppc;
+ this.pgpp = pgpp;
+ this.lgpp = lgpp;
+ this.plgpp = plgpp;
+ }
+}
diff --git a/javatests/dagger/functional/sub/OtherThing.java b/javatests/dagger/functional/sub/OtherThing.java
new file mode 100644
index 0000000..9150aab
--- /dev/null
+++ b/javatests/dagger/functional/sub/OtherThing.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.sub;
+
+import javax.inject.Inject;
+
+public final class OtherThing {
+ @Inject
+ public OtherThing(@SuppressWarnings("unused") int i) {}
+}
diff --git a/javatests/dagger/functional/sub/PackagePrivate.java b/javatests/dagger/functional/sub/PackagePrivate.java
new file mode 100644
index 0000000..8220877
--- /dev/null
+++ b/javatests/dagger/functional/sub/PackagePrivate.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.sub;
+
+import javax.inject.Inject;
+
+class PackagePrivate {
+ @Inject PackagePrivate() {}
+}
diff --git a/javatests/dagger/functional/sub/PackagePrivateContainer.java b/javatests/dagger/functional/sub/PackagePrivateContainer.java
new file mode 100644
index 0000000..f1b1033
--- /dev/null
+++ b/javatests/dagger/functional/sub/PackagePrivateContainer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.sub;
+
+import javax.inject.Inject;
+
+class PackagePrivateContainer {
+ public static class PublicEnclosed {
+ @Inject PublicEnclosed() {}
+ }
+}
diff --git a/javatests/dagger/functional/sub/PublicSubclass.java b/javatests/dagger/functional/sub/PublicSubclass.java
new file mode 100644
index 0000000..2ccd42a
--- /dev/null
+++ b/javatests/dagger/functional/sub/PublicSubclass.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.sub;
+
+import dagger.functional.Generic;
+import javax.inject.Inject;
+
+public class PublicSubclass extends Generic<PackagePrivate> {
+ @Inject public PublicSubclass(PackagePrivate pp) {
+ super(pp);
+ }
+}
diff --git a/javatests/dagger/functional/sub/PublicSubclass2.java b/javatests/dagger/functional/sub/PublicSubclass2.java
new file mode 100644
index 0000000..3d5f429
--- /dev/null
+++ b/javatests/dagger/functional/sub/PublicSubclass2.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.sub;
+
+import dagger.functional.Generic;
+import javax.inject.Inject;
+
+public class PublicSubclass2 extends Generic<PackagePrivateContainer.PublicEnclosed> {
+ @Inject public PublicSubclass2(PackagePrivateContainer.PublicEnclosed pp) {
+ super(pp);
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/AnInterface.java b/javatests/dagger/functional/subcomponent/AnInterface.java
new file mode 100644
index 0000000..f0d9b4c
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/AnInterface.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+interface AnInterface {
+}
diff --git a/javatests/dagger/functional/subcomponent/BoundAsSingleton.java b/javatests/dagger/functional/subcomponent/BoundAsSingleton.java
new file mode 100644
index 0000000..9d14465
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/BoundAsSingleton.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+@Documented
+@Retention(RUNTIME)
+@Qualifier
+@interface BoundAsSingleton {}
diff --git a/javatests/dagger/functional/subcomponent/ChildAbstractClassComponent.java b/javatests/dagger/functional/subcomponent/ChildAbstractClassComponent.java
new file mode 100644
index 0000000..b9bcce2
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ChildAbstractClassComponent.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Subcomponent;
+
+@Subcomponent(modules = {ChildModule.class, StaticChildModule.class})
+abstract class ChildAbstractClassComponent implements ChildComponent {
+}
diff --git a/javatests/dagger/functional/subcomponent/ChildComponent.java b/javatests/dagger/functional/subcomponent/ChildComponent.java
new file mode 100644
index 0000000..6d6fd8b
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ChildComponent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Subcomponent;
+import java.util.Set;
+import javax.inject.Provider;
+
+@Subcomponent(modules = {ChildModule.class, StaticChildModule.class})
+interface ChildComponent {
+ Provider<UnscopedType> getUnscopedTypeProvider();
+
+ RequiresSingletons requiresSingleton();
+
+ Set<Object> objectSet();
+
+ GrandchildComponent newGrandchildComponent();
+
+ Object object();
+}
diff --git a/javatests/dagger/functional/subcomponent/ChildComponentRequiringModules.java b/javatests/dagger/functional/subcomponent/ChildComponentRequiringModules.java
new file mode 100644
index 0000000..29b60e2
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ChildComponentRequiringModules.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Subcomponent;
+
+@Subcomponent(modules = {
+ ChildModule.class,
+ ChildModuleWithParameters.class,
+ ChildModuleWithState.class})
+interface ChildComponentRequiringModules {
+ int getInt();
+}
diff --git a/javatests/dagger/functional/subcomponent/ChildModule.java b/javatests/dagger/functional/subcomponent/ChildModule.java
new file mode 100644
index 0000000..f11cefc
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ChildModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+final class ChildModule {
+ @Provides
+ @IntoSet
+ static Object provideUnscopedObject() {
+ return new Object() {
+ @Override public String toString() {
+ return "unscoped in child";
+ }
+ };
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/ChildModuleWithParameters.java b/javatests/dagger/functional/subcomponent/ChildModuleWithParameters.java
new file mode 100644
index 0000000..17f8471
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ChildModuleWithParameters.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Module;
+
+/**
+ * This is a module that can't be constructed with a default constructor.
+ */
+@Module
+final class ChildModuleWithParameters {
+ public ChildModuleWithParameters(@SuppressWarnings("unused") Object whatever) {}
+}
diff --git a/javatests/dagger/functional/subcomponent/ChildModuleWithState.java b/javatests/dagger/functional/subcomponent/ChildModuleWithState.java
new file mode 100644
index 0000000..49b266c
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ChildModuleWithState.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * This is a module that can be constructed with a default constructor, but has state, so callers
+ * might want to pass a reference anyway.
+ */
+@Module
+final class ChildModuleWithState {
+ private int i = 0;
+
+ @Provides int provideInt() {
+ return i++;
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/GenericParentComponent.java b/javatests/dagger/functional/subcomponent/GenericParentComponent.java
new file mode 100644
index 0000000..f49083b
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/GenericParentComponent.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+interface GenericParentComponent<B> {
+ B subcomponent();
+}
diff --git a/javatests/dagger/functional/subcomponent/GrandchildComponent.java b/javatests/dagger/functional/subcomponent/GrandchildComponent.java
new file mode 100644
index 0000000..5a1b772
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/GrandchildComponent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Subcomponent;
+import java.util.Set;
+import javax.inject.Provider;
+
+@Subcomponent(modules = GrandchildModule.class)
+interface GrandchildComponent {
+ Provider<UnscopedType> getUnscopedTypeProvider();
+
+ RequiresSingletons requiresSingleton();
+
+ Set<Object> objectSet();
+
+ NeedsAnInterface needsAnInterface();
+}
diff --git a/javatests/dagger/functional/subcomponent/GrandchildModule.java b/javatests/dagger/functional/subcomponent/GrandchildModule.java
new file mode 100644
index 0000000..c61accf
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/GrandchildModule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+abstract class GrandchildModule {
+ @Provides
+ @IntoSet
+ static Object provideUnscopedObject() {
+ return new Object() {
+ @Override public String toString() {
+ return "unscoped in grandchild";
+ }
+ };
+ }
+
+ @Binds
+ abstract AnInterface provideAnInterface(ImplementsAnInterface implementsAnInterface);
+
+ @Provides
+ static NeedsAnInterface provideNeedsAnInterface(AnInterface anInterface) {
+ return new NeedsAnInterface(anInterface);
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/ImplementsAnInterface.java b/javatests/dagger/functional/subcomponent/ImplementsAnInterface.java
new file mode 100644
index 0000000..a07025c
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ImplementsAnInterface.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import javax.inject.Inject;
+
+class ImplementsAnInterface implements AnInterface {
+ @Inject ImplementsAnInterface() {}
+}
+
diff --git a/javatests/dagger/functional/subcomponent/ModuleWithSubcomponentsTest.java b/javatests/dagger/functional/subcomponent/ModuleWithSubcomponentsTest.java
new file mode 100644
index 0000000..06beae1
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ModuleWithSubcomponentsTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Module;
+import dagger.functional.subcomponent.UsesModuleSubcomponents.ParentIncludesSubcomponentTransitively;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link Module#subcomponents()}. */
+@RunWith(JUnit4.class)
+public class ModuleWithSubcomponentsTest {
+
+ @Test
+ public void subcomponentFromModules() {
+ UsesModuleSubcomponents parent = DaggerUsesModuleSubcomponents.create();
+ assertThat(parent.strings()).containsExactly("from parent");
+ assertThat(parent.usesChild().strings).containsExactly("from parent", "from child");
+ }
+
+ @Test
+ public void subcomponentFromModules_transitively() {
+ ParentIncludesSubcomponentTransitively parent =
+ DaggerUsesModuleSubcomponents_ParentIncludesSubcomponentTransitively.create();
+ assertThat(parent.strings()).containsExactly("from parent");
+ assertThat(parent.usesChild().strings).containsExactly("from parent", "from child");
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/MultibindingSubcomponents.java b/javatests/dagger/functional/subcomponent/MultibindingSubcomponents.java
new file mode 100644
index 0000000..9959771
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/MultibindingSubcomponents.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import dagger.multibindings.StringKey;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import javax.inject.Inject;
+
+final class MultibindingSubcomponents {
+
+ /** Multibindings for this type are bound only in the parent component. */
+ enum BoundInParent {
+ INSTANCE;
+ }
+
+ /** Multibindings for this type are bound only in the child component. */
+ enum BoundInChild {
+ INSTANCE;
+ }
+
+ /** Multibindings for this type are bound in the parent component and the child component. */
+ enum BoundInParentAndChild {
+ IN_PARENT,
+ IN_CHILD;
+ }
+
+ static final class RequiresMultibindings<T> {
+ private final Set<T> set;
+ private final Map<String, T> map;
+
+ @Inject
+ RequiresMultibindings(Set<T> set, Map<String, T> map) {
+ this.set = set;
+ this.map = map;
+ }
+
+ Set<T> set() {
+ return set;
+ }
+
+ Map<String, T> map() {
+ return map;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof RequiresMultibindings<?>
+ && set.equals(((RequiresMultibindings<?>) obj).set)
+ && map.equals(((RequiresMultibindings<?>) obj).map);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(set, map);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "%s{set=%s, map=%s}", RequiresMultibindings.class.getSimpleName(), set, map);
+ }
+ }
+
+ @Module
+ abstract static class ParentMultibindingModule {
+
+ @Provides
+ @IntoSet
+ static BoundInParent onlyInParentElement() {
+ return BoundInParent.INSTANCE;
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("parent key")
+ static BoundInParent onlyInParentEntry() {
+ return BoundInParent.INSTANCE;
+ }
+
+ @Provides
+ @IntoSet
+ static BoundInParentAndChild inParentAndChildElement() {
+ return BoundInParentAndChild.IN_PARENT;
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("parent key")
+ static BoundInParentAndChild inParentAndChildEntry() {
+ return BoundInParentAndChild.IN_PARENT;
+ }
+
+ /* This is not static because otherwise we have no tests that cover the case where a
+ * subcomponent uses a module instance installed onto a parent component. */
+ @Binds
+ @IntoSet
+ abstract RequiresMultibindings<BoundInParentAndChild>
+ requiresMultibindingsInParentAndChildElement(
+ RequiresMultibindings<BoundInParentAndChild> requiresMultibindingsInParentAndChild);
+ }
+
+ @Module
+ static final class ChildMultibindingModule {
+
+ @Provides
+ @IntoSet
+ static BoundInParentAndChild inParentAndChildElement() {
+ return BoundInParentAndChild.IN_CHILD;
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("child key")
+ static BoundInParentAndChild inParentAndChildEntry() {
+ return BoundInParentAndChild.IN_CHILD;
+ }
+
+ @Provides
+ @IntoSet
+ static BoundInChild onlyInChildElement() {
+ return BoundInChild.INSTANCE;
+ }
+
+ @Provides
+ @IntoMap
+ @StringKey("child key")
+ static BoundInChild onlyInChildEntry() {
+ return BoundInChild.INSTANCE;
+ }
+ }
+
+ @Module
+ abstract static class ChildMultibindingModuleWithOnlyBindsMultibindings {
+ @Provides
+ static BoundInParentAndChild provideBoundInParentAndChildForBinds() {
+ return BoundInParentAndChild.IN_CHILD;
+ }
+
+ @Binds
+ @IntoSet
+ abstract BoundInParentAndChild bindsLocalContribution(BoundInParentAndChild instance);
+
+ @Binds
+ @IntoMap
+ @StringKey("child key")
+ abstract BoundInParentAndChild inParentAndChildEntry(BoundInParentAndChild instance);
+
+ @Provides
+ static BoundInChild provideBoundInChildForBinds() {
+ return BoundInChild.INSTANCE;
+ }
+
+ @Binds
+ @IntoSet
+ abstract BoundInChild inChild(BoundInChild instance);
+
+ @Binds
+ @IntoMap
+ @StringKey("child key")
+ abstract BoundInChild inChildEntry(BoundInChild instance);
+ }
+
+ interface ProvidesBoundInParent {
+ RequiresMultibindings<BoundInParent> requiresMultibindingsBoundInParent();
+ }
+
+ interface ProvidesBoundInChild {
+ RequiresMultibindings<BoundInChild> requiresMultibindingsBoundInChild();
+ }
+
+ interface ProvidesBoundInParentAndChild {
+ RequiresMultibindings<BoundInParentAndChild> requiresMultibindingsBoundInParentAndChild();
+ }
+
+ interface ProvidesSetOfRequiresMultibindings {
+ Set<RequiresMultibindings<BoundInParentAndChild>> setOfRequiresMultibindingsInParentAndChild();
+ }
+
+ interface ParentWithProvision
+ extends ProvidesBoundInParent, ProvidesBoundInParentAndChild,
+ ProvidesSetOfRequiresMultibindings {}
+
+ interface HasChildWithProvision {
+ ChildWithProvision childWithProvision();
+ }
+
+ interface HasChildWithoutProvision {
+ ChildWithoutProvision childWithoutProvision();
+ }
+
+ @Component(modules = ParentMultibindingModule.class)
+ interface ParentWithoutProvisionHasChildWithoutProvision extends HasChildWithoutProvision {}
+
+ @Component(modules = ParentMultibindingModule.class)
+ interface ParentWithoutProvisionHasChildWithProvision extends HasChildWithProvision {}
+
+ @Component(modules = ParentMultibindingModule.class)
+ interface ParentWithProvisionHasChildWithoutProvision
+ extends ParentWithProvision, HasChildWithoutProvision {}
+
+ @Component(modules = ParentMultibindingModule.class)
+ interface ParentWithProvisionHasChildWithProvision
+ extends ParentWithProvision, HasChildWithProvision {}
+
+ @Subcomponent(modules = ChildMultibindingModule.class)
+ interface ChildWithoutProvision {
+ Grandchild grandchild();
+ }
+
+ @Subcomponent(modules = ChildMultibindingModule.class)
+ interface ChildWithProvision
+ extends ProvidesBoundInParent, ProvidesBoundInParentAndChild, ProvidesBoundInChild,
+ ProvidesSetOfRequiresMultibindings {
+
+ Grandchild grandchild();
+ }
+
+ @Subcomponent
+ interface Grandchild
+ extends ProvidesBoundInParent, ProvidesBoundInParentAndChild, ProvidesBoundInChild,
+ ProvidesSetOfRequiresMultibindings {}
+
+ @Component(modules = ParentMultibindingModule.class)
+ interface ParentWithProvisionHasChildWithBinds extends ParentWithProvision {
+ ChildWithBinds childWithBinds();
+ }
+
+ @Subcomponent(modules = ChildMultibindingModuleWithOnlyBindsMultibindings.class)
+ interface ChildWithBinds extends ChildWithProvision {}
+
+}
diff --git a/javatests/dagger/functional/subcomponent/NeedsAnInterface.java b/javatests/dagger/functional/subcomponent/NeedsAnInterface.java
new file mode 100644
index 0000000..3efbad7
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/NeedsAnInterface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+class NeedsAnInterface {
+ NeedsAnInterface(@SuppressWarnings("unused") AnInterface anInterface) {}
+}
diff --git a/javatests/dagger/functional/subcomponent/ParentComponent.java b/javatests/dagger/functional/subcomponent/ParentComponent.java
new file mode 100644
index 0000000..4c0b608
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ParentComponent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Component;
+import dagger.functional.SomeQualifier;
+import javax.inject.Singleton;
+
+@Component(modules = {ParentModule.class, UnresolvableChildComponentModule.class})
+@Singleton
+interface ParentComponent extends ParentGetters {
+ ChildComponent newChildComponent();
+
+ ChildAbstractClassComponent newChildAbstractClassComponent();
+
+ ChildComponentRequiringModules newChildComponentRequiringModules(
+ ChildModuleWithParameters cmwp,
+ ChildModuleWithState childModuleWithState);
+
+ /**
+ * Requests a qualified version of this subcomponent builder, which does not install it as a
+ * subcomponent, but instead, uses the explicit binding of this qualified builder.
+ */
+ @SomeQualifier UnresolvableChildComponent.Builder unresolvableChildComponentBuilder();
+}
diff --git a/javatests/dagger/functional/subcomponent/ParentGetters.java b/javatests/dagger/functional/subcomponent/ParentGetters.java
new file mode 100644
index 0000000..928bff2
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ParentGetters.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import java.util.Set;
+import javax.inject.Provider;
+
+interface ParentGetters {
+ Provider<UnscopedType> getUnscopedTypeProvider();
+
+ Set<Object> objectSet();
+
+}
diff --git a/javatests/dagger/functional/subcomponent/ParentModule.java b/javatests/dagger/functional/subcomponent/ParentModule.java
new file mode 100644
index 0000000..8ce4b87
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ParentModule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+import javax.inject.Singleton;
+
+@Module
+abstract class ParentModule {
+ @Provides
+ @IntoSet
+ static Object provideUnscopedObject() {
+ return new Object() {
+ @Override public String toString() {
+ return "unscoped in parent";
+ }
+ };
+ }
+
+ @Provides
+ @IntoSet
+ @Singleton
+ static Object provideSingletonObject() {
+ return new Object() {
+ @Override public String toString() {
+ return "singleton";
+ }
+ };
+ }
+
+ @Binds
+ @Singleton
+ @BoundAsSingleton
+ abstract UnscopedType provideUnscopedTypeBoundAsSingleton(UnscopedType unscopedType);
+}
diff --git a/javatests/dagger/functional/subcomponent/ParentOfGenericComponent.java b/javatests/dagger/functional/subcomponent/ParentOfGenericComponent.java
new file mode 100644
index 0000000..cc701f4
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/ParentOfGenericComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Component;
+import javax.inject.Singleton;
+
+@Component(modules = ParentModule.class)
+@Singleton
+interface ParentOfGenericComponent extends GenericParentComponent<ChildComponent>, ParentGetters {
+}
diff --git a/javatests/dagger/functional/subcomponent/RequiresSingletons.java b/javatests/dagger/functional/subcomponent/RequiresSingletons.java
new file mode 100644
index 0000000..8dc9b3d
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/RequiresSingletons.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import javax.inject.Inject;
+
+final class RequiresSingletons {
+ private final SingletonType singletonType;
+ private final UnscopedType unscopedTypeBoundAsSingleton;
+
+ @Inject RequiresSingletons(SingletonType singletonType,
+ @BoundAsSingleton UnscopedType unscopedTypeBoundAsSingleton) {
+ this.singletonType = singletonType;
+ this.unscopedTypeBoundAsSingleton = unscopedTypeBoundAsSingleton;
+ }
+
+ SingletonType singletonType() {
+ return singletonType;
+ }
+
+ UnscopedType unscopedTypeBoundAsSingleton() {
+ return unscopedTypeBoundAsSingleton;
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/SingletonType.java b/javatests/dagger/functional/subcomponent/SingletonType.java
new file mode 100644
index 0000000..9a6da6c
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/SingletonType.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
+final class SingletonType {
+ @Inject SingletonType() {}
+}
diff --git a/javatests/dagger/functional/subcomponent/StaticChildModule.java b/javatests/dagger/functional/subcomponent/StaticChildModule.java
new file mode 100644
index 0000000..fcd0cd5
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/StaticChildModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class StaticChildModule {
+ private StaticChildModule() {}
+
+ @Provides static Object provideStaticObject() {
+ return "static";
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/SubcomponentFactoryMethodTest.java b/javatests/dagger/functional/subcomponent/SubcomponentFactoryMethodTest.java
new file mode 100644
index 0000000..ca811af
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/SubcomponentFactoryMethodTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import static org.junit.Assert.fail;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for subcomponent factory methods. */
+@RunWith(JUnit4.class)
+public final class SubcomponentFactoryMethodTest {
+
+ @Module
+ static class IntModule {
+ @Provides
+ int provideInt() {
+ return 42;
+ }
+ }
+
+ @Module
+ static class StringModule {
+ final String s;
+
+ StringModule(String s) {
+ this.s = s;
+ }
+
+ @Provides
+ String provideString(int i) {
+ return s + i;
+ }
+ }
+
+ @Component(modules = IntModule.class)
+ interface TestComponent {
+ TestSubcomponent newSubcomponent(StringModule stringModule);
+ }
+
+ @Subcomponent(modules = StringModule.class)
+ interface TestSubcomponent {
+ String string();
+ }
+
+ @Test
+ public void creatingSubcomponentViaFactoryMethod_failsForNullParameter() {
+ TestComponent component = DaggerSubcomponentFactoryMethodTest_TestComponent.create();
+ try {
+ component.newSubcomponent(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/SubcomponentFromModuleAndFactoryMethod.java b/javatests/dagger/functional/subcomponent/SubcomponentFromModuleAndFactoryMethod.java
new file mode 100644
index 0000000..b8b8806
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/SubcomponentFromModuleAndFactoryMethod.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Subcomponent;
+
+/**
+ * Tests for {@link Subcomponent}s which are defined with {@link Module#subcomponents()} and are
+ * also requested as component factory methods.
+ */
+public class SubcomponentFromModuleAndFactoryMethod {
+ @Subcomponent
+ interface Sub {
+ @Subcomponent.Builder
+ interface Builder {
+ Sub sub();
+ }
+ }
+
+ @Module(subcomponents = Sub.class)
+ class ModuleWithSubcomponent {}
+
+ @Component(modules = ModuleWithSubcomponent.class)
+ interface ExposesBuilder {
+ Sub.Builder subcomponentBuilder();
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/SubcomponentMultibindingsTest.java b/javatests/dagger/functional/subcomponent/SubcomponentMultibindingsTest.java
new file mode 100644
index 0000000..eeadbeb
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/SubcomponentMultibindingsTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.functional.subcomponent.MultibindingSubcomponents.BoundInChild;
+import dagger.functional.subcomponent.MultibindingSubcomponents.BoundInParent;
+import dagger.functional.subcomponent.MultibindingSubcomponents.BoundInParentAndChild;
+import dagger.functional.subcomponent.MultibindingSubcomponents.ParentWithProvisionHasChildWithProvision;
+import dagger.functional.subcomponent.MultibindingSubcomponents.ParentWithProvisionHasChildWithoutProvision;
+import dagger.functional.subcomponent.MultibindingSubcomponents.ParentWithoutProvisionHasChildWithProvision;
+import dagger.functional.subcomponent.MultibindingSubcomponents.ParentWithoutProvisionHasChildWithoutProvision;
+import dagger.functional.subcomponent.MultibindingSubcomponents.RequiresMultibindings;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SubcomponentMultibindingsTest {
+
+ private static final RequiresMultibindings<BoundInParent> BOUND_IN_PARENT =
+ new RequiresMultibindings<>(
+ ImmutableSet.of(BoundInParent.INSTANCE),
+ ImmutableMap.of("parent key", BoundInParent.INSTANCE));
+
+ private static final RequiresMultibindings<BoundInChild> BOUND_IN_CHILD =
+ new RequiresMultibindings<>(
+ ImmutableSet.of(BoundInChild.INSTANCE),
+ ImmutableMap.of("child key", BoundInChild.INSTANCE));
+
+ private static final RequiresMultibindings<BoundInParentAndChild> BOUND_IN_PARENT_AND_CHILD =
+ new RequiresMultibindings<>(
+ ImmutableSet.of(BoundInParentAndChild.IN_PARENT, BoundInParentAndChild.IN_CHILD),
+ ImmutableMap.of(
+ "parent key", BoundInParentAndChild.IN_PARENT,
+ "child key", BoundInParentAndChild.IN_CHILD));
+
+ private static final RequiresMultibindings<BoundInParentAndChild>
+ BOUND_IN_PARENT_AND_CHILD_PROVIDED_BY_PARENT =
+ new RequiresMultibindings<>(
+ ImmutableSet.of(BoundInParentAndChild.IN_PARENT),
+ ImmutableMap.of("parent key", BoundInParentAndChild.IN_PARENT));
+
+ private ParentWithoutProvisionHasChildWithoutProvision
+ parentWithoutProvisionHasChildWithoutProvision;
+ private ParentWithoutProvisionHasChildWithProvision parentWithoutProvisionHasChildWithProvision;
+ private ParentWithProvisionHasChildWithoutProvision parentWithProvisionHasChildWithoutProvision;
+ private ParentWithProvisionHasChildWithProvision parentWithProvisionHasChildWithProvision;
+
+ @Before
+ public void setUp() {
+ parentWithoutProvisionHasChildWithoutProvision =
+ DaggerMultibindingSubcomponents_ParentWithoutProvisionHasChildWithoutProvision.create();
+ parentWithoutProvisionHasChildWithProvision =
+ DaggerMultibindingSubcomponents_ParentWithoutProvisionHasChildWithProvision.create();
+ parentWithProvisionHasChildWithoutProvision =
+ DaggerMultibindingSubcomponents_ParentWithProvisionHasChildWithoutProvision.create();
+ parentWithProvisionHasChildWithProvision =
+ DaggerMultibindingSubcomponents_ParentWithProvisionHasChildWithProvision.create();
+ }
+
+ @Test
+ public void testParentWithoutProvisionHasChildWithoutProvision() {
+ // Child
+ assertThat(
+ parentWithoutProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParent())
+ .isEqualTo(BOUND_IN_PARENT);
+
+ // Grandchild
+ assertThat(
+ parentWithoutProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParentAndChild())
+ .isEqualTo(BOUND_IN_PARENT_AND_CHILD);
+ assertThat(
+ parentWithoutProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInChild())
+ .isEqualTo(BOUND_IN_CHILD);
+
+ /*
+ * Even though the multibinding for Set<RequiresMultiboundObjects> does not itself have a
+ * contribution from the child, it must be pushed down to (not duplicated in) the child because
+ * its contribution depends on multibindings that have one contribution from the parent and one
+ * from the child.
+ *
+ */
+ assertThat(
+ parentWithoutProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .setOfRequiresMultibindingsInParentAndChild())
+ .containsExactly(BOUND_IN_PARENT_AND_CHILD);
+ }
+
+ @Test
+ public void testParentWithoutProvisionHasChildWithProvision() {
+ // Child
+ assertThat(
+ parentWithoutProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParent())
+ .isEqualTo(BOUND_IN_PARENT);
+
+ // Grandchild
+ assertThat(
+ parentWithoutProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParentAndChild())
+ .isEqualTo(BOUND_IN_PARENT_AND_CHILD);
+ assertThat(
+ parentWithoutProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInChild())
+ .isEqualTo(BOUND_IN_CHILD);
+
+ /*
+ * Even though the multibinding for Set<RequiresMultiboundObjects> does not itself have a
+ * contribution from the child, it must be pushed down to (not duplicated in) the child because
+ * its contribution depends on multibindings that have one contribution from the parent and one
+ * from the child.
+ *
+ */
+ assertThat(
+ parentWithoutProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .setOfRequiresMultibindingsInParentAndChild())
+ .containsExactly(BOUND_IN_PARENT_AND_CHILD);
+ }
+
+ @Test
+ public void testParentWithProvisionHasChildWithoutProvision() {
+ // Parent
+ assertThat(parentWithProvisionHasChildWithoutProvision.requiresMultibindingsBoundInParent())
+ .isEqualTo(BOUND_IN_PARENT);
+
+ assertThat(
+ parentWithProvisionHasChildWithoutProvision
+ .requiresMultibindingsBoundInParentAndChild())
+ .isEqualTo(BOUND_IN_PARENT_AND_CHILD_PROVIDED_BY_PARENT);
+
+ // Grandchild
+ assertThat(
+ parentWithProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParent())
+ .isEqualTo(BOUND_IN_PARENT);
+ assertThat(
+ parentWithProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInChild())
+ .isEqualTo(BOUND_IN_CHILD);
+
+ assertThat(
+ parentWithProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParentAndChild())
+ .isEqualTo(BOUND_IN_PARENT_AND_CHILD);
+
+ /*
+ * Even though the multibinding for Set<RequiresMultiboundObjects> does not itself have a
+ * contribution from the child, it must be pushed down to (not duplicated in) the child because
+ * its contribution depends on multibindings that have one contribution from the parent and one
+ * from the child.
+ *
+ */
+ assertThat(
+ parentWithProvisionHasChildWithoutProvision
+ .childWithoutProvision()
+ .grandchild()
+ .setOfRequiresMultibindingsInParentAndChild())
+ .containsExactly(BOUND_IN_PARENT_AND_CHILD);
+ }
+
+ @Test
+ public void testParentWithProvisionHasChildWithProvision() {
+ // Parent
+ assertThat(parentWithProvisionHasChildWithProvision.requiresMultibindingsBoundInParent())
+ .isEqualTo(BOUND_IN_PARENT);
+
+ // Child
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .requiresMultibindingsBoundInParent())
+ .isEqualTo(BOUND_IN_PARENT);
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .requiresMultibindingsBoundInChild())
+ .isEqualTo(BOUND_IN_CHILD);
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .requiresMultibindingsBoundInParentAndChild())
+ .isEqualTo(BOUND_IN_PARENT_AND_CHILD);
+
+ // https://github.com/google/dagger/issues/401
+ assertThat(
+ DaggerMultibindingSubcomponents_ParentWithProvisionHasChildWithBinds.create()
+ .childWithBinds()
+ .requiresMultibindingsBoundInParentAndChild())
+ .isEqualTo(BOUND_IN_PARENT_AND_CHILD);
+
+ /*
+ * Even though the multibinding for Set<RequiresMultiboundObjects> does not itself have a
+ * contribution from the child, it must be pushed down to (not duplicated in) the child because
+ * its contribution depends on multibindings that have one contribution from the parent and one
+ * from the child.
+ *
+ */
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .setOfRequiresMultibindingsInParentAndChild())
+ .containsExactly(BOUND_IN_PARENT_AND_CHILD);
+
+ // Grandchild
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParent())
+ .isEqualTo(BOUND_IN_PARENT);
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInChild())
+ .isEqualTo(BOUND_IN_CHILD);
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .requiresMultibindingsBoundInParentAndChild())
+ .isEqualTo(BOUND_IN_PARENT_AND_CHILD);
+
+ /*
+ * Even though the multibinding for Set<RequiresMultiboundObjects> does not itself have a
+ * contribution from the child, it must be pushed down to (not duplicated in) the child because
+ * its contribution depends on multibindings that have one contribution from the parent and one
+ * from the child.
+ *
+ */
+ assertThat(
+ parentWithProvisionHasChildWithProvision
+ .childWithProvision()
+ .grandchild()
+ .setOfRequiresMultibindingsInParentAndChild())
+ .containsExactly(BOUND_IN_PARENT_AND_CHILD);
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/SubcomponentTest.java b/javatests/dagger/functional/subcomponent/SubcomponentTest.java
new file mode 100644
index 0000000..c34de0a
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/SubcomponentTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import static com.google.common.collect.Sets.intersection;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SubcomponentTest {
+ private static final ParentComponent parentComponent = DaggerParentComponent.create();
+ private static final ParentOfGenericComponent parentOfGenericComponent =
+ DaggerParentOfGenericComponent.create();
+
+ @Parameters
+ public static Collection<Object[]> parameters() {
+ return Arrays.asList(new Object[][] {
+ { parentComponent, parentComponent.newChildComponent() },
+ { parentComponent, parentComponent.newChildAbstractClassComponent() },
+ { parentOfGenericComponent, parentOfGenericComponent.subcomponent() }});
+ }
+
+ private final ParentGetters parentGetters;
+ private final ChildComponent childComponent;
+
+ public SubcomponentTest(ParentGetters parentGetters, ChildComponent childComponent) {
+ this.parentGetters = parentGetters;
+ this.childComponent = childComponent;
+ }
+
+ @Test
+ public void scopePropagatesUpward_class() {
+ assertThat(childComponent.requiresSingleton().singletonType())
+ .isSameInstanceAs(childComponent.requiresSingleton().singletonType());
+ assertThat(childComponent.requiresSingleton().singletonType())
+ .isSameInstanceAs(
+ childComponent.newGrandchildComponent().requiresSingleton().singletonType());
+ }
+
+ @Test
+ public void scopePropagatesUpward_provides() {
+ assertThat(childComponent.requiresSingleton().unscopedTypeBoundAsSingleton())
+ .isSameInstanceAs(childComponent.requiresSingleton().unscopedTypeBoundAsSingleton());
+ assertThat(childComponent.requiresSingleton().unscopedTypeBoundAsSingleton())
+ .isSameInstanceAs(
+ childComponent
+ .newGrandchildComponent()
+ .requiresSingleton()
+ .unscopedTypeBoundAsSingleton());
+ }
+
+ @Test
+ public void multibindingContributions() {
+ Set<Object> parentObjectSet = parentGetters.objectSet();
+ assertThat(parentObjectSet).hasSize(2);
+ Set<Object> childObjectSet = childComponent.objectSet();
+ assertThat(childObjectSet).hasSize(3);
+ Set<Object> grandchildObjectSet =
+ childComponent.newGrandchildComponent().objectSet();
+ assertThat(grandchildObjectSet).hasSize(4);
+ assertThat(intersection(parentObjectSet, childObjectSet)).hasSize(1);
+ assertThat(intersection(parentObjectSet, grandchildObjectSet)).hasSize(1);
+ assertThat(intersection(childObjectSet, grandchildObjectSet)).hasSize(1);
+ }
+
+ @Test
+ public void unscopedProviders() {
+ assume().that(System.getProperty("dagger.mode")).doesNotContain("FastInit");
+ assertThat(parentGetters.getUnscopedTypeProvider())
+ .isSameInstanceAs(childComponent.getUnscopedTypeProvider());
+ assertThat(parentGetters.getUnscopedTypeProvider())
+ .isSameInstanceAs(childComponent.newGrandchildComponent().getUnscopedTypeProvider());
+ }
+
+ @Test
+ public void passedModules() {
+ ChildModuleWithState childModuleWithState = new ChildModuleWithState();
+ ChildComponentRequiringModules childComponent1 =
+ parentComponent.newChildComponentRequiringModules(
+ new ChildModuleWithParameters(new Object()),
+ childModuleWithState);
+ ChildComponentRequiringModules childComponent2 =
+ parentComponent.newChildComponentRequiringModules(
+ new ChildModuleWithParameters(new Object()),
+ childModuleWithState);
+ assertThat(childComponent1.getInt()).isEqualTo(0);
+ assertThat(childComponent2.getInt()).isEqualTo(1);
+ }
+
+ @Test
+ public void dependenceisInASubcomponent() {
+ assertThat(childComponent.newGrandchildComponent().needsAnInterface()).isNotNull();
+ }
+
+ @Test
+ public void qualifiedSubcomponentIsBound() {
+ assertThat(parentComponent.unresolvableChildComponentBuilder().build().unboundString())
+ .isEqualTo("unbound");
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/Unbound.java b/javatests/dagger/functional/subcomponent/Unbound.java
new file mode 100644
index 0000000..ae80621
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/Unbound.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A qualifier representing an unbound type, to verify that the compiler does not attempt to
+ * generate code depending on it.
+ */
+@Documented
+@Retention(RUNTIME)
+@Qualifier
+@interface Unbound {}
diff --git a/javatests/dagger/functional/subcomponent/UnresolvableChildComponent.java b/javatests/dagger/functional/subcomponent/UnresolvableChildComponent.java
new file mode 100644
index 0000000..57d9d0d
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/UnresolvableChildComponent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Subcomponent;
+
+/**
+ * A subcomponent that's not resolvable in any parent component, for testing that qualified methods
+ * on components that return subcomponents do not trigger actual subcomponents.
+ */
+@Subcomponent
+interface UnresolvableChildComponent {
+ /**
+ * Requests a type that is never bound in any component that this subcomponent might be installed
+ * in. If this subcomponent is ever attempted to be installed in a component, then it will produce
+ * a compiler error.
+ */
+ @Unbound
+ String unboundString();
+
+ @Subcomponent.Builder
+ interface Builder {
+ UnresolvableChildComponent build();
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/UnresolvableChildComponentModule.java b/javatests/dagger/functional/subcomponent/UnresolvableChildComponentModule.java
new file mode 100644
index 0000000..613e7e3
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/UnresolvableChildComponentModule.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.functional.SomeQualifier;
+
+@Module
+final class UnresolvableChildComponentModule {
+ /**
+ * Provides a qualified version of the {@link UnresolvableChildComponent}'s builder. If the
+ * subcomponent were actually installed in a component, this would be a duplicate binding; but
+ * since that doesn't happen, this binding is OK.
+ */
+ @Provides
+ @SomeQualifier
+ static UnresolvableChildComponent.Builder unresolvableChildComponentBuilder() {
+ return new UnresolvableChildComponent.Builder() {
+ @Override
+ public UnresolvableChildComponent build() {
+ return new UnresolvableChildComponent() {
+ @Override
+ public String unboundString() {
+ return "unbound";
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/UnscopedType.java b/javatests/dagger/functional/subcomponent/UnscopedType.java
new file mode 100644
index 0000000..c167457
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/UnscopedType.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import javax.inject.Inject;
+
+final class UnscopedType {
+ @Inject UnscopedType(@SuppressWarnings("unused") SingletonType singletonType) {}
+}
diff --git a/javatests/dagger/functional/subcomponent/UsesModuleSubcomponents.java b/javatests/dagger/functional/subcomponent/UsesModuleSubcomponents.java
new file mode 100644
index 0000000..bd240af
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/UsesModuleSubcomponents.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.multibindings.IntoSet;
+import java.util.Set;
+import javax.inject.Inject;
+
+/** Supporting types for {@link ModuleWithSubcomponentsTest}. */
+@Component(modules = UsesModuleSubcomponents.ModuleWithSubcomponents.class)
+public interface UsesModuleSubcomponents {
+ UsesChild usesChild();
+
+ Set<String> strings();
+
+ @Module(subcomponents = Child.class, includes = AlsoIncludesSubcomponents.class)
+ class ModuleWithSubcomponents {
+ @Provides
+ @IntoSet
+ static String provideStringInParent() {
+ return "from parent";
+ }
+ }
+
+ @Module(subcomponents = Child.class)
+ class AlsoIncludesSubcomponents {}
+
+ @Subcomponent(modules = ChildModule.class)
+ interface Child {
+ Set<String> strings();
+
+ @Subcomponent.Builder
+ interface Builder {
+ Child build();
+ }
+ }
+
+ @Module
+ class ChildModule {
+ @Provides
+ @IntoSet
+ static String provideStringInChild() {
+ return "from child";
+ }
+ }
+
+ class UsesChild {
+ Set<String> strings;
+
+ @Inject
+ UsesChild(Child.Builder childBuilder) {
+ this.strings = childBuilder.build().strings();
+ }
+ }
+
+ @Module(includes = ModuleWithSubcomponents.class)
+ class OnlyIncludesModuleWithSubcomponents {}
+
+ @Component(modules = OnlyIncludesModuleWithSubcomponents.class)
+ interface ParentIncludesSubcomponentTransitively extends UsesModuleSubcomponents {}
+
+}
diff --git a/javatests/dagger/functional/subcomponent/hiding/ChildComponent.java b/javatests/dagger/functional/subcomponent/hiding/ChildComponent.java
new file mode 100644
index 0000000..7cb4fce
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/hiding/ChildComponent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.hiding;
+
+import dagger.Subcomponent;
+
+@Subcomponent(modules = dagger.functional.subcomponent.hiding.b.CommonModuleName.class)
+interface ChildComponent {
+ //ensure that t.s.h.a.CommonName gets bound in this component
+ dagger.functional.subcomponent.hiding.a.CommonName aCommonName();
+ //ensure that t.s.h.b.CommonName gets bound in this component
+ dagger.functional.subcomponent.hiding.b.CommonName bCommonName();
+}
diff --git a/javatests/dagger/functional/subcomponent/hiding/ParentComponent.java b/javatests/dagger/functional/subcomponent/hiding/ParentComponent.java
new file mode 100644
index 0000000..7458d82
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/hiding/ParentComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.hiding;
+
+import dagger.Component;
+import javax.inject.Singleton;
+
+@Component(modules = dagger.functional.subcomponent.hiding.a.CommonModuleName.class)
+@Singleton
+interface ParentComponent {
+ // ensure that t.s.h.a.CommonName gets bound in this component
+ dagger.functional.subcomponent.hiding.a.CommonName aCommonName();
+
+ ChildComponent newChildComponent();
+}
diff --git a/javatests/dagger/functional/subcomponent/hiding/SubcomponentHidingTest.java b/javatests/dagger/functional/subcomponent/hiding/SubcomponentHidingTest.java
new file mode 100644
index 0000000..398b8a7
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/hiding/SubcomponentHidingTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.hiding;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SubcomponentHidingTest {
+ @Test public void moduleNameHiding() {
+ ParentComponent parent = DaggerParentComponent.create();
+ assertThat(parent.aCommonName().toString()).isEqualTo("a");
+ assertThat(parent.newChildComponent().aCommonName().toString()).isEqualTo("a");
+ assertThat(parent.newChildComponent().bCommonName().toString()).isEqualTo("1");
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/hiding/a/CommonModuleName.java b/javatests/dagger/functional/subcomponent/hiding/a/CommonModuleName.java
new file mode 100644
index 0000000..d13446c
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/hiding/a/CommonModuleName.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.hiding.a;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class CommonModuleName {
+ @Provides String provideString() {
+ return "a";
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/hiding/a/CommonName.java b/javatests/dagger/functional/subcomponent/hiding/a/CommonName.java
new file mode 100644
index 0000000..567fc39
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/hiding/a/CommonName.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.hiding.a;
+
+import javax.inject.Inject;
+
+public final class CommonName {
+ private final String s;
+
+ @Inject CommonName(String s) {
+ this.s = s;
+ }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/hiding/b/CommonModuleName.java b/javatests/dagger/functional/subcomponent/hiding/b/CommonModuleName.java
new file mode 100644
index 0000000..b05356f
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/hiding/b/CommonModuleName.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.hiding.b;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class CommonModuleName {
+ @Provides int provideString() {
+ return 1;
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/hiding/b/CommonName.java b/javatests/dagger/functional/subcomponent/hiding/b/CommonName.java
new file mode 100644
index 0000000..1136f64
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/hiding/b/CommonName.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.hiding.b;
+
+import javax.inject.Inject;
+
+public final class CommonName {
+ private final int i;
+
+ @Inject CommonName(int i) {
+ this.i = i;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(i);
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/pruning/ParentDoesntUseSubcomponent.java b/javatests/dagger/functional/subcomponent/pruning/ParentDoesntUseSubcomponent.java
new file mode 100644
index 0000000..516a52c
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/pruning/ParentDoesntUseSubcomponent.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.pruning;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.multibindings.IntoSet;
+import java.util.Set;
+import javax.inject.Qualifier;
+
+/**
+ * Supporting types for {@link SubcomponentOnlyRequestedBySiblingTest}. {@link ChildA} is a direct
+ * child of the top level component, but is only requested within its sibling, not directly from its
+ * parent.
+ */
+@Component(modules = ParentDoesntUseSubcomponent.ParentModule.class)
+interface ParentDoesntUseSubcomponent {
+
+ ChildB.Builder childBBuilder();
+
+ @Subcomponent(modules = ChildAModule.class)
+ interface ChildA {
+ @Subcomponent.Builder
+ interface Builder {
+ ChildA build();
+ }
+
+ Set<Class<?>> componentHierarchy();
+ }
+
+ @Subcomponent(modules = ChildBModule.class)
+ interface ChildB {
+ @Subcomponent.Builder
+ interface Builder {
+ ChildB build();
+ }
+
+ Set<Class<?>> componentHierarchy();
+
+ @FromChildA
+ Set<Class<?>> componentHierarchyFromChildA();
+ }
+
+ @Module(subcomponents = {ChildA.class, ChildB.class})
+ class ParentModule {
+ @Provides
+ @IntoSet
+ static Class<?> provideComponentType() {
+ return ParentDoesntUseSubcomponent.class;
+ }
+ }
+
+ @Module
+ class ChildAModule {
+ @Provides
+ @IntoSet
+ static Class<?> provideComponentType() {
+ return ChildA.class;
+ }
+ }
+
+ @Module
+ class ChildBModule {
+ @Provides
+ @IntoSet
+ static Class<?> provideComponentType() {
+ return ChildB.class;
+ }
+
+ @Provides
+ @FromChildA
+ Set<Class<?>> fromChildA(ChildA.Builder childABuilder) {
+ return childABuilder.build().componentHierarchy();
+ }
+ }
+
+ @Qualifier
+ @interface FromChildA {}
+}
diff --git a/javatests/dagger/functional/subcomponent/pruning/SubcomponentOnlyRequestedBySiblingTest.java b/javatests/dagger/functional/subcomponent/pruning/SubcomponentOnlyRequestedBySiblingTest.java
new file mode 100644
index 0000000..40a784a
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/pruning/SubcomponentOnlyRequestedBySiblingTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.pruning;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.functional.subcomponent.pruning.ParentDoesntUseSubcomponent.ChildA;
+import dagger.functional.subcomponent.pruning.ParentDoesntUseSubcomponent.ChildB;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link Subcomponent}s which are included with {@link Module#subcomponents()} but not
+ * used directly within the component which adds them.
+ *
+ * <p>This tests to make sure that while resolving one subcomponent (A), another subcomponent (B)
+ * can be requested if they have a shared ancestor component. If that shared ancestor did not
+ * resolve B directly via any of its entry points, B will still be generated since it is requested
+ * by a descendant.
+ */
+@RunWith(JUnit4.class)
+public class SubcomponentOnlyRequestedBySiblingTest {
+ @Test
+ public void subcomponentAddedInParent_onlyUsedInSibling() {
+ ParentDoesntUseSubcomponent parent = DaggerParentDoesntUseSubcomponent.create();
+ ChildB childB = parent.childBBuilder().build();
+ assertThat(childB.componentHierarchy())
+ .containsExactly(ParentDoesntUseSubcomponent.class, ChildB.class);
+ assertThat(childB.componentHierarchyFromChildA())
+ .containsExactly(ParentDoesntUseSubcomponent.class, ChildA.class);
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/OnlyUsedInChild.java b/javatests/dagger/functional/subcomponent/repeat/OnlyUsedInChild.java
new file mode 100644
index 0000000..b3318cf
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/OnlyUsedInChild.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+abstract class OnlyUsedInChild {
+
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/OnlyUsedInParent.java b/javatests/dagger/functional/subcomponent/repeat/OnlyUsedInParent.java
new file mode 100644
index 0000000..1e8d4cd
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/OnlyUsedInParent.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+abstract class OnlyUsedInParent {
+
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/OtherSubcomponentWithRepeatedModule.java b/javatests/dagger/functional/subcomponent/repeat/OtherSubcomponentWithRepeatedModule.java
new file mode 100644
index 0000000..82cd021
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/OtherSubcomponentWithRepeatedModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+import dagger.Subcomponent;
+
+@Subcomponent(modules = RepeatedModule.class)
+interface OtherSubcomponentWithRepeatedModule extends SubcomponentWithRepeatedModule {
+
+ @Subcomponent.Builder
+ interface Builder {
+ Builder repeatedModule(RepeatedModule repeatedModule);
+
+ OtherSubcomponentWithRepeatedModule build();
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/ParentComponent.java b/javatests/dagger/functional/subcomponent/repeat/ParentComponent.java
new file mode 100644
index 0000000..1578596
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/ParentComponent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+import dagger.Component;
+import java.util.Set;
+
+@Component(modules = RepeatedModule.class)
+interface ParentComponent {
+ Object state();
+
+ String getString();
+ Set<String> getMultiboundStrings();
+ OnlyUsedInParent getOnlyUsedInParent();
+
+ SubcomponentWithRepeatedModule.Builder newChildComponentBuilder();
+
+ SubcomponentWithoutRepeatedModule newChildComponentWithoutRepeatedModule();
+
+ @Component.Builder
+ interface Builder {
+ Builder repeatedModule(RepeatedModule repeatedModule);
+
+ ParentComponent build();
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/RepeatedModule.java b/javatests/dagger/functional/subcomponent/repeat/RepeatedModule.java
new file mode 100644
index 0000000..55d7de9
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/RepeatedModule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+@Module
+final class RepeatedModule {
+ private final Object state = new Object();
+
+ @Provides
+ Object state() {
+ return state;
+ }
+
+ @Provides
+ static String provideString() {
+ return "a string";
+ }
+
+ @Provides
+ @IntoSet
+ static String contributeString() {
+ return "a string in a set";
+ }
+
+ @Provides
+ static OnlyUsedInParent provideOnlyUsedInParent() {
+ return new OnlyUsedInParent() {};
+ }
+
+ @Provides
+ static OnlyUsedInChild provideOnlyUsedInChild() {
+ return new OnlyUsedInChild() {};
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/RepeatedModuleTest.java b/javatests/dagger/functional/subcomponent/repeat/RepeatedModuleTest.java
new file mode 100644
index 0000000..9bf3a39
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/RepeatedModuleTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class RepeatedModuleTest {
+ private ParentComponent parentComponent;
+
+ @Before
+ public void initializeParentComponent() {
+ this.parentComponent = DaggerParentComponent.builder().build();
+ }
+
+ @Test
+ public void repeatedModuleHasSameStateInSubcomponent() {
+ SubcomponentWithRepeatedModule childComponent =
+ parentComponent.newChildComponentBuilder().build();
+ assertThat(parentComponent.state()).isSameInstanceAs(childComponent.state());
+ }
+
+ @Test
+ public void repeatedModuleHasSameStateInGrandchildSubcomponent() {
+ SubcomponentWithoutRepeatedModule childComponent =
+ parentComponent.newChildComponentWithoutRepeatedModule();
+ SubcomponentWithRepeatedModule grandchildComponent =
+ childComponent.newGrandchildBuilder().build();
+ assertThat(parentComponent.state()).isSameInstanceAs(grandchildComponent.state());
+ }
+
+ @Test
+ public void repeatedModuleBuilderThrowsInSubcomponent() {
+ SubcomponentWithRepeatedModule.Builder childComponentBuilder =
+ parentComponent.newChildComponentBuilder();
+ try {
+ childComponentBuilder.repeatedModule(new RepeatedModule());
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected)
+ .hasMessageThat()
+ .isEqualTo(
+ "dagger.functional.subcomponent.repeat.RepeatedModule cannot be set "
+ + "because it is inherited from the enclosing component");
+ }
+ }
+
+ @Test
+ public void repeatedModuleBuilderThrowsInGrandchildSubcomponent() {
+ SubcomponentWithoutRepeatedModule childComponent =
+ parentComponent.newChildComponentWithoutRepeatedModule();
+ OtherSubcomponentWithRepeatedModule.Builder grandchildComponentBuilder =
+ childComponent.newGrandchildBuilder();
+ try {
+ grandchildComponentBuilder.repeatedModule(new RepeatedModule());
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected)
+ .hasMessageThat()
+ .isEqualTo(
+ "dagger.functional.subcomponent.repeat.RepeatedModule cannot be set "
+ + "because it is inherited from the enclosing component");
+ }
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/SubcomponentWithRepeatedModule.java b/javatests/dagger/functional/subcomponent/repeat/SubcomponentWithRepeatedModule.java
new file mode 100644
index 0000000..0762374
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/SubcomponentWithRepeatedModule.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+import dagger.Subcomponent;
+import java.util.Set;
+
+@Subcomponent(modules = RepeatedModule.class)
+interface SubcomponentWithRepeatedModule {
+ Object state();
+
+ String getString();
+
+ Set<String> getMultiboundStrings();
+
+ OnlyUsedInChild getOnlyUsedInChild();
+
+ @Subcomponent.Builder
+ interface Builder {
+ Builder repeatedModule(RepeatedModule repeatedModule);
+
+ SubcomponentWithRepeatedModule build();
+ }
+}
diff --git a/javatests/dagger/functional/subcomponent/repeat/SubcomponentWithoutRepeatedModule.java b/javatests/dagger/functional/subcomponent/repeat/SubcomponentWithoutRepeatedModule.java
new file mode 100644
index 0000000..5930f51
--- /dev/null
+++ b/javatests/dagger/functional/subcomponent/repeat/SubcomponentWithoutRepeatedModule.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.subcomponent.repeat;
+
+import dagger.Subcomponent;
+
+@Subcomponent
+interface SubcomponentWithoutRepeatedModule {
+ OtherSubcomponentWithRepeatedModule.Builder newGrandchildBuilder();
+}
diff --git a/javatests/dagger/functional/tck/BUILD b/javatests/dagger/functional/tck/BUILD
new file mode 100644
index 0000000..7526bf0
--- /dev/null
+++ b/javatests/dagger/functional/tck/BUILD
@@ -0,0 +1,42 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# TCK tests for Dagger
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+)
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "tck_tests",
+ srcs = glob(["*.java"]),
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ lib_javacopts = [
+ "-Adagger.privateMemberValidation=warning",
+ "-Adagger.staticMemberValidation=warning",
+ "-Adagger.ignorePrivateAndStaticInjectionForComponent=enabled",
+ ],
+ deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@google_bazel_common//third_party/java/jsr330_inject:tck",
+ "@google_bazel_common//third_party/java/junit",
+ ],
+)
diff --git a/javatests/dagger/functional/tck/CarModule.java b/javatests/dagger/functional/tck/CarModule.java
new file mode 100644
index 0000000..3c44d60
--- /dev/null
+++ b/javatests/dagger/functional/tck/CarModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.tck;
+
+import dagger.Binds;
+import dagger.Module;
+import org.atinject.tck.auto.Car;
+import org.atinject.tck.auto.Convertible;
+
+@Module
+abstract class CarModule {
+ @Binds
+ abstract Car provideConvertible(Convertible convertible);
+}
diff --git a/javatests/dagger/functional/tck/CarShop.java b/javatests/dagger/functional/tck/CarShop.java
new file mode 100644
index 0000000..d9e64d6
--- /dev/null
+++ b/javatests/dagger/functional/tck/CarShop.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.tck;
+
+import dagger.Component;
+import javax.inject.Singleton;
+import org.atinject.tck.auto.Car;
+
+@Singleton
+@Component(
+ modules = {
+ CarModule.class,
+ TireModule.class,
+ SeatModule.class,
+ EngineModule.class,
+ FuelTankModule.class
+ }
+)
+public interface CarShop {
+ @SuppressWarnings("dependency-cycle")
+ Car make();
+}
diff --git a/javatests/dagger/functional/tck/EngineModule.java b/javatests/dagger/functional/tck/EngineModule.java
new file mode 100644
index 0000000..331b064
--- /dev/null
+++ b/javatests/dagger/functional/tck/EngineModule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.tck;
+
+import dagger.MembersInjector;
+import dagger.Module;
+import dagger.Provides;
+import org.atinject.tck.auto.Engine;
+import org.atinject.tck.auto.V8Engine;
+
+@Module
+public class EngineModule {
+ @Provides
+ static Engine provideEngine(MembersInjector<V8Engine> injector) {
+ // This is provided because V8Engine has no @Inject constructor and Dagger requires an @Inject
+ // constructor, however this is a TCK supplied class that we prefer to leave unmodified.
+ V8Engine engine = new V8Engine();
+ injector.injectMembers(engine);
+ return engine;
+ }
+}
diff --git a/javatests/dagger/functional/tck/FuelTankModule.java b/javatests/dagger/functional/tck/FuelTankModule.java
new file mode 100644
index 0000000..b5f2800
--- /dev/null
+++ b/javatests/dagger/functional/tck/FuelTankModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.tck;
+
+import dagger.Module;
+import dagger.Provides;
+import org.atinject.tck.auto.FuelTank;
+
+@Module
+class FuelTankModule {
+ @Provides
+ static FuelTank provideFuelTank() {
+ return new FuelTank();
+ }
+}
diff --git a/javatests/dagger/functional/tck/SeatModule.java b/javatests/dagger/functional/tck/SeatModule.java
new file mode 100644
index 0000000..c3574ea
--- /dev/null
+++ b/javatests/dagger/functional/tck/SeatModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.tck;
+
+import dagger.Binds;
+import dagger.Module;
+import org.atinject.tck.auto.Drivers;
+import org.atinject.tck.auto.DriversSeat;
+import org.atinject.tck.auto.Seat;
+
+@Module
+abstract class SeatModule {
+ @Binds
+ @Drivers
+ abstract Seat provideSeat(DriversSeat seat);
+}
diff --git a/javatests/dagger/functional/tck/TckTest.java b/javatests/dagger/functional/tck/TckTest.java
new file mode 100644
index 0000000..a49972d
--- /dev/null
+++ b/javatests/dagger/functional/tck/TckTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.tck;
+
+import junit.framework.Test;
+import org.atinject.tck.Tck;
+import org.atinject.tck.auto.Car;
+import org.atinject.tck.auto.Convertible;
+
+/**
+ * Test suite to execute the JSR-330 TCK in JUnit.
+ */
+public class TckTest {
+ public static Test suite() {
+ CarShop carShopComponent = DaggerCarShop.create();
+ Car car = carShopComponent.make();
+ Convertible.localConvertible.set((Convertible) car);
+ return Tck.testsFor(car, false, false);
+ }
+}
diff --git a/javatests/dagger/functional/tck/TireModule.java b/javatests/dagger/functional/tck/TireModule.java
new file mode 100644
index 0000000..e6b6d58
--- /dev/null
+++ b/javatests/dagger/functional/tck/TireModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.tck;
+
+import dagger.Binds;
+import dagger.Module;
+import javax.inject.Named;
+import org.atinject.tck.auto.Tire;
+import org.atinject.tck.auto.accessories.SpareTire;
+
+@Module
+abstract class TireModule {
+ @Binds
+ @Named("spare")
+ abstract Tire provideTire(SpareTire sparetire);
+}
diff --git a/javatests/dagger/grpc/functional/server/BUILD b/javatests/dagger/grpc/functional/server/BUILD
new file mode 100644
index 0000000..1e8c2e6
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/BUILD
@@ -0,0 +1,19 @@
+# Functional tests for Dagger-gRPC
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
+load("//:test_defs.bzl", "GenJavaTests")
+
+# TODO(dpb): enable tests once java_grpc_library is ready in bazel:
+# https://github.com/grpc/grpc-java/issues/2756
+
+java_proto_library(
+ name = "coffee_service_java_proto",
+ deps = [":protos"],
+)
+
+proto_library(
+ name = "protos",
+ srcs = glob(["*.proto"]),
+)
diff --git a/javatests/dagger/grpc/functional/server/BaristaTest.java b/javatests/dagger/grpc/functional/server/BaristaTest.java
new file mode 100644
index 0000000..d6c5b42
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/BaristaTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.Futures.getUnchecked;
+import static com.google.protos.test.CoffeeService.CoffeeType.AMERICANO;
+import static com.google.protos.test.CoffeeService.CoffeeType.DRIP;
+import static com.google.protos.test.CoffeeService.CoffeeType.ESPRESSO;
+import static com.google.protos.test.CoffeeService.CoffeeType.LATTE;
+import static com.google.protos.test.CoffeeService.CoffeeType.POUR_OVER;
+import static java.util.Arrays.asList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.SettableFuture;
+import com.google.protos.test.BaristaGrpc;
+import com.google.protos.test.BaristaGrpc.BaristaStub;
+import com.google.protos.test.CoffeeService.CoffeeRequest;
+import com.google.protos.test.CoffeeService.CoffeeResponse;
+import com.google.protos.test.CoffeeService.CoffeeType;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.stub.StreamObserver;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class BaristaTest {
+
+ private static final class CoffeeResponseObserver implements StreamObserver<CoffeeResponse> {
+ private final SettableFuture<Void> completion = SettableFuture.create();
+ private final List<CoffeeResponse> responses = new ArrayList<>();
+
+ List<CoffeeResponse> responses() {
+ getUnchecked(completion);
+ return responses;
+ }
+
+ @Override
+ public void onNext(CoffeeResponse value) {
+ responses.add(value);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ completion.setException(t);
+ }
+
+ @Override
+ public void onCompleted() {
+ completion.set(null);
+ }
+ }
+
+ @ClassRule
+ public static CoffeeServerResource coffeeServerWithCallScope =
+ new CoffeeServerResource("CallScope", DaggerCoffeeServerWithCallScopeService.builder());
+
+ @ClassRule
+ public static CoffeeServerResource coffeeServerWithSingletonScope =
+ new CoffeeServerResource("Unscoped", DaggerCoffeeServerWithUnscopedService.builder());
+
+ @Parameters(name = "{0}")
+ public static Iterable<Object[]> coffeeServers() {
+ return ImmutableList.copyOf(
+ new Object[][] {{coffeeServerWithCallScope}, {coffeeServerWithSingletonScope}});
+ }
+
+ @Rule public final VerifyInterceptor verifyCount;
+ private final CoffeeServerResource coffeeServer;
+ private final CoffeeResponseObserver responseObserver = new CoffeeResponseObserver();
+
+ private BaristaStub barista;
+
+ public BaristaTest(CoffeeServerResource coffeeServer) {
+ this.coffeeServer = coffeeServer;
+ this.verifyCount = new VerifyInterceptor(coffeeServer);
+ }
+
+ @Before
+ public void setUp() {
+ barista = BaristaGrpc.newStub(InProcessChannelBuilder.forName(coffeeServer.name()).build());
+ }
+
+ @Test
+ public void testUnaryGetCoffee() {
+ barista.unaryGetCoffee(request(POUR_OVER, LATTE), responseObserver);
+ assertThat(responseObserver.responses())
+ .containsExactly(response("Here you go!", POUR_OVER, LATTE));
+ }
+
+ @Test
+ public void testClientStreamingGetCoffee() {
+ StreamObserver<CoffeeRequest> requestObserver =
+ barista.clientStreamingGetCoffee(responseObserver);
+ requestObserver.onNext(request(POUR_OVER, LATTE));
+ requestObserver.onNext(request(AMERICANO));
+ requestObserver.onNext(request(DRIP, ESPRESSO));
+ requestObserver.onCompleted();
+ assertThat(responseObserver.responses())
+ .containsExactly(response("All yours!", POUR_OVER, LATTE, AMERICANO, DRIP, ESPRESSO));
+ }
+
+ @Test
+ public void testServerStreamingGetCoffee() {
+ barista.serverStreamingGetCoffee(request(DRIP, AMERICANO), responseObserver);
+ assertThat(responseObserver.responses())
+ .containsExactly(
+ response("Here's a DRIP", DRIP), response("Here's a AMERICANO", AMERICANO));
+ }
+
+ @Test
+ public void testBidiStreamingGetCoffee() {
+ StreamObserver<CoffeeRequest> requestObserver =
+ barista.bidiStreamingGetCoffee(responseObserver);
+ requestObserver.onNext(request(POUR_OVER, LATTE));
+ requestObserver.onNext(request(AMERICANO));
+ requestObserver.onNext(request(DRIP, ESPRESSO));
+ requestObserver.onCompleted();
+ assertThat(responseObserver.responses())
+ .containsExactly(
+ response("Enjoy!", POUR_OVER, LATTE),
+ response("Enjoy!", AMERICANO),
+ response("Enjoy!", DRIP, ESPRESSO));
+ }
+
+ private CoffeeRequest request(CoffeeType... types) {
+ return CoffeeRequest.newBuilder().addAllType(asList(types)).build();
+ }
+
+ private CoffeeResponse response(String message, CoffeeType... types) {
+ return CoffeeResponse.newBuilder().setMessage(message).addAllCup(asList(types)).build();
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/CoffeeServer.java b/javatests/dagger/grpc/functional/server/CoffeeServer.java
new file mode 100644
index 0000000..10a0fd2
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/CoffeeServer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import dagger.grpc.server.InProcessServerModule;
+import io.grpc.Server;
+import java.io.IOException;
+
+abstract class CoffeeServer<T extends CoffeeServer<T>> {
+ protected abstract Server server();
+
+ public void start() throws IOException {
+ server().start();
+ }
+
+ public void shutdown() {
+ server().shutdownNow();
+ }
+
+ abstract CountingInterceptor countingInterceptor();
+
+ interface Builder<T extends CoffeeServer<T>> {
+ Builder<T> inProcessServerModule(InProcessServerModule serverModule);
+
+ T build();
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/CoffeeServerResource.java b/javatests/dagger/grpc/functional/server/CoffeeServerResource.java
new file mode 100644
index 0000000..dc449fb
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/CoffeeServerResource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import dagger.grpc.server.InProcessServerModule;
+import java.io.IOException;
+import org.junit.rules.ExternalResource;
+
+final class CoffeeServerResource extends ExternalResource {
+ private final String name;
+ private final CoffeeServer<?> coffeeServer;
+
+ CoffeeServerResource(String name, CoffeeServer.Builder<?> coffeeServerBuilder) {
+ this.name = name;
+ this.coffeeServer =
+ coffeeServerBuilder.inProcessServerModule(InProcessServerModule.serverNamed(name)).build();
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public int methodCount(String methodName) {
+ return coffeeServer.countingInterceptor().countCalls(methodName);
+ }
+
+ @Override
+ protected void before() throws IOException, InterruptedException {
+ coffeeServer.start();
+ }
+
+ @Override
+ protected void after() {
+ coffeeServer.shutdown();
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/CoffeeServerWithCallScopeService.java b/javatests/dagger/grpc/functional/server/CoffeeServerWithCallScopeService.java
new file mode 100644
index 0000000..5fb06b6
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/CoffeeServerWithCallScopeService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.grpc.functional.server.CoffeeServerWithCallScopeService.CallScopeServiceModule;
+import dagger.grpc.functional.server.CountingInterceptor.CountingInterceptorModule;
+import dagger.grpc.server.CallScoped;
+import dagger.grpc.server.GrpcCallMetadataModule;
+import dagger.grpc.server.InProcessServerModule;
+import javax.inject.Singleton;
+
+@Singleton
+@Component(modules = {InProcessServerModule.class, CallScopeServiceModule.class})
+abstract class CoffeeServerWithCallScopeService
+ extends CoffeeServer<CoffeeServerWithCallScopeService> {
+
+ @Component.Builder
+ interface Builder extends CoffeeServer.Builder<CoffeeServerWithCallScopeService> {}
+
+ abstract BaristaCallScope baristaCallScope(GrpcCallMetadataModule callMetadataModule);
+
+ @CallScoped
+ @Subcomponent(
+ modules = {
+ GrpcCallMetadataModule.class,
+ FriendlyBaristaGrpcServiceModule.class,
+ CountingInterceptorModule.class
+ }
+ )
+ interface BaristaCallScope extends FriendlyBaristaServiceDefinition {}
+
+ @Module(includes = FriendlyBaristaGrpcProxyModule.class)
+ static class CallScopeServiceModule {
+ @Provides
+ static FriendlyBaristaServiceDefinition.Factory friendlyBaristaServiceDefinitionFactory(
+ final CoffeeServerWithCallScopeService testServer) {
+ return new FriendlyBaristaServiceDefinition.Factory() {
+ @Override
+ public FriendlyBaristaServiceDefinition grpcService(
+ GrpcCallMetadataModule grpcCallMetadataModule) {
+ return testServer.baristaCallScope(grpcCallMetadataModule);
+ }
+ };
+ }
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/CoffeeServerWithUnscopedService.java b/javatests/dagger/grpc/functional/server/CoffeeServerWithUnscopedService.java
new file mode 100644
index 0000000..3346f84
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/CoffeeServerWithUnscopedService.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import dagger.grpc.functional.server.CoffeeServerWithUnscopedService.UnscopedServiceModule;
+import dagger.grpc.functional.server.CountingInterceptor.CountingInterceptorModule;
+import dagger.grpc.server.InProcessServerModule;
+import javax.inject.Singleton;
+
+@Singleton
+@Component(
+ modules = {
+ InProcessServerModule.class,
+ UnscopedServiceModule.class,
+ CountingInterceptorModule.class
+ }
+)
+abstract class CoffeeServerWithUnscopedService extends CoffeeServer<CoffeeServerWithUnscopedService>
+ implements FriendlyBaristaServiceDefinition {
+
+ @Component.Builder
+ interface Builder extends CoffeeServer.Builder<CoffeeServerWithUnscopedService> {}
+
+ @Module(includes = FriendlyBaristaUnscopedGrpcServiceModule.class)
+ abstract static class UnscopedServiceModule {
+ @Binds
+ abstract FriendlyBaristaServiceDefinition friendlyBaristaServiceDefinition(
+ CoffeeServerWithUnscopedService testServer);
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/CountingInterceptor.java b/javatests/dagger/grpc/functional/server/CountingInterceptor.java
new file mode 100644
index 0000000..4d53782
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/CountingInterceptor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import static java.util.Arrays.asList;
+
+import com.google.common.collect.ConcurrentHashMultiset;
+import com.google.common.collect.Multiset;
+import com.google.protos.test.BaristaGrpc;
+import dagger.Module;
+import dagger.Provides;
+import dagger.grpc.server.ForGrpcService;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCall.Listener;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
+class CountingInterceptor implements ServerInterceptor {
+ private final Multiset<String> calls = ConcurrentHashMultiset.create();
+
+ @Inject
+ CountingInterceptor() {}
+
+ @Override
+ public <RequestT, ResponseT> Listener<RequestT> interceptCall(
+ ServerCall<RequestT, ResponseT> call,
+ Metadata headers,
+ ServerCallHandler<RequestT, ResponseT> next) {
+ calls.add(call.getMethodDescriptor().getFullMethodName());
+ return next.startCall(call, headers);
+ }
+
+ public int countCalls(String methodName) {
+ return calls.count(methodName);
+ }
+
+ @Module
+ static class CountingInterceptorModule {
+ @Provides
+ @ForGrpcService(BaristaGrpc.class)
+ static List<? extends ServerInterceptor> testServiceInterceptors(
+ CountingInterceptor countingInterceptor) {
+ return asList(countingInterceptor);
+ }
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/FriendlyBarista.java b/javatests/dagger/grpc/functional/server/FriendlyBarista.java
new file mode 100644
index 0000000..2c0246f
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/FriendlyBarista.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import static java.util.Collections.singletonList;
+
+import com.google.protos.test.BaristaGrpc;
+import com.google.protos.test.BaristaGrpc.BaristaImplBase;
+import com.google.protos.test.CoffeeService.CoffeeRequest;
+import com.google.protos.test.CoffeeService.CoffeeResponse;
+import com.google.protos.test.CoffeeService.CoffeeType;
+import dagger.grpc.server.GrpcService;
+import io.grpc.stub.StreamObserver;
+import java.util.List;
+import javax.inject.Inject;
+
+@GrpcService(grpcClass = BaristaGrpc.class)
+class FriendlyBarista extends BaristaImplBase {
+
+ @Inject
+ FriendlyBarista() {}
+
+ @Override
+ public void unaryGetCoffee(
+ CoffeeRequest request, StreamObserver<CoffeeResponse> responseObserver) {
+ responseObserver.onNext(response("Here you go!", request.getTypeList()));
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public StreamObserver<CoffeeRequest> clientStreamingGetCoffee(
+ final StreamObserver<CoffeeResponse> responseObserver) {
+ return new StreamObserver<CoffeeRequest>() {
+
+ private final CoffeeResponse.Builder response = CoffeeResponse.newBuilder();
+
+ @Override
+ public void onNext(CoffeeRequest value) {
+ response.addAllCup(value.getTypeList());
+ }
+
+ @Override
+ public void onError(Throwable t) {}
+
+ @Override
+ public void onCompleted() {
+ response.setMessage("All yours!");
+ responseObserver.onNext(response.build());
+ responseObserver.onCompleted();
+ }
+ };
+ }
+
+ @Override
+ public void serverStreamingGetCoffee(
+ CoffeeRequest request, StreamObserver<CoffeeResponse> responseObserver) {
+ for (CoffeeType type : request.getTypeList()) {
+ responseObserver.onNext(response("Here's a " + type, singletonList(type)));
+ }
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public StreamObserver<CoffeeRequest> bidiStreamingGetCoffee(
+ final StreamObserver<CoffeeResponse> responseObserver) {
+ return new StreamObserver<CoffeeRequest>() {
+
+ private int responses;
+
+ @Override
+ public void onNext(CoffeeRequest value) {
+ responseObserver.onNext(response("Enjoy!", value.getTypeList()));
+ if (responses++ > 10) {
+ responseObserver.onNext(CoffeeResponse.newBuilder().setMessage("We're done.").build());
+ responseObserver.onCompleted();
+ }
+ }
+
+ @Override
+ public void onError(Throwable t) {}
+
+ @Override
+ public void onCompleted() {
+ responseObserver.onCompleted();
+ }
+ };
+ }
+
+ private CoffeeResponse response(String message, List<CoffeeType> types) {
+ return CoffeeResponse.newBuilder().addAllCup(types).setMessage(message).build();
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/VerifyInterceptor.java b/javatests/dagger/grpc/functional/server/VerifyInterceptor.java
new file mode 100644
index 0000000..99ecedb
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/VerifyInterceptor.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.grpc.functional.server;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.protos.test.BaristaGrpc;
+import io.grpc.MethodDescriptor;
+import java.lang.annotation.Retention;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+final class VerifyInterceptor implements TestRule {
+
+ @Retention(RUNTIME)
+ @interface MethodName {
+ String value();
+ }
+
+ private final CoffeeServerResource coffeeServer;
+
+ VerifyInterceptor(CoffeeServerResource coffeeServer) {
+ this.coffeeServer = coffeeServer;
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ MethodName annotation = description.getAnnotation(MethodName.class);
+ if (annotation == null) {
+ return base;
+ }
+ final String fullMethodName =
+ MethodDescriptor.generateFullMethodName(BaristaGrpc.SERVICE_NAME, annotation.value());
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ int calls = coffeeServer.methodCount(fullMethodName);
+ base.evaluate();
+ assertWithMessage("Calls to %s", fullMethodName)
+ .that(coffeeServer.methodCount(fullMethodName))
+ .isEqualTo(calls + 1);
+ }
+ };
+ }
+}
diff --git a/javatests/dagger/grpc/functional/server/coffee_service.proto b/javatests/dagger/grpc/functional/server/coffee_service.proto
new file mode 100644
index 0000000..a14c794
--- /dev/null
+++ b/javatests/dagger/grpc/functional/server/coffee_service.proto
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Dagger Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package test;
+
+enum CoffeeType {
+ UNKNOWN = 0;
+ DRIP = 1;
+ POUR_OVER = 2;
+ ESPRESSO = 3;
+ AMERICANO = 4;
+ LATTE = 5;
+}
+
+message CoffeeRequest {
+ repeated CoffeeType type = 1;
+}
+
+message CoffeeResponse {
+ repeated CoffeeType cup = 1;
+ string message = 2;
+}
+
+service Barista {
+ rpc UnaryGetCoffee(CoffeeRequest) returns (CoffeeResponse) {
+ }
+
+ rpc ClientStreamingGetCoffee(stream CoffeeRequest) returns (CoffeeResponse) {
+ }
+
+ rpc ServerStreamingGetCoffee(CoffeeRequest) returns (stream CoffeeResponse) {
+ }
+
+ rpc BidiStreamingGetCoffee(stream CoffeeRequest)
+ returns (stream CoffeeResponse) {
+ }
+}
diff --git a/javatests/dagger/internal/DoubleCheckTest.java b/javatests/dagger/internal/DoubleCheckTest.java
new file mode 100644
index 0000000..e36c1bc
--- /dev/null
+++ b/javatests/dagger/internal/DoubleCheckTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Uninterruptibles;
+import dagger.Lazy;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DoubleCheckTest {
+ @Test
+ public void provider_nullPointerException() {
+ try {
+ DoubleCheck.provider(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void lazy_nullPointerException() {
+ try {
+ DoubleCheck.lazy(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static final Provider<Object> DOUBLE_CHECK_OBJECT_PROVIDER =
+ DoubleCheck.provider(Object::new);
+
+ @Test
+ public void doubleWrapping_provider() {
+ assertThat(DoubleCheck.provider(DOUBLE_CHECK_OBJECT_PROVIDER))
+ .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER);
+ }
+
+ @Test
+ public void doubleWrapping_lazy() {
+ assertThat(DoubleCheck.lazy(DOUBLE_CHECK_OBJECT_PROVIDER))
+ .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER);
+ }
+
+ @Test
+ public void get() throws Exception {
+ int numThreads = 10;
+ ExecutorService executor = Executors.newFixedThreadPool(numThreads);
+
+ final CountDownLatch latch = new CountDownLatch(numThreads);
+ LatchedProvider provider = new LatchedProvider(latch);
+ final Lazy<Object> lazy = DoubleCheck.lazy(provider);
+
+ List<Callable<Object>> tasks = Lists.newArrayListWithCapacity(numThreads);
+ for (int i = 0; i < numThreads; i++) {
+ tasks.add(
+ () -> {
+ latch.countDown();
+ return lazy.get();
+ });
+ }
+
+ List<Future<Object>> futures = executor.invokeAll(tasks);
+
+ assertThat(provider.provisions.get()).isEqualTo(1);
+ Set<Object> results = Sets.newIdentityHashSet();
+ for (Future<Object> future : futures) {
+ results.add(future.get());
+ }
+ assertThat(results).hasSize(1);
+ }
+
+ private static class LatchedProvider implements Provider<Object> {
+ final AtomicInteger provisions;
+ final CountDownLatch latch;
+
+ LatchedProvider(CountDownLatch latch) {
+ this.latch = latch;
+ this.provisions = new AtomicInteger();
+ }
+
+ @Override
+ public Object get() {
+ if (latch != null) {
+ Uninterruptibles.awaitUninterruptibly(latch);
+ }
+ provisions.incrementAndGet();
+ return new Object();
+ }
+ }
+
+ @Test public void reentranceWithoutCondition_throwsStackOverflow() {
+ final AtomicReference<Provider<Object>> doubleCheckReference =
+ new AtomicReference<>();
+ Provider<Object> doubleCheck = DoubleCheck.provider(() -> doubleCheckReference.get().get());
+ doubleCheckReference.set(doubleCheck);
+ try {
+ doubleCheck.get();
+ fail();
+ } catch (StackOverflowError expected) {}
+ }
+
+ @Test public void reentranceReturningSameInstance() {
+ final AtomicReference<Provider<Object>> doubleCheckReference =
+ new AtomicReference<>();
+ final AtomicInteger invocationCount = new AtomicInteger();
+ final Object object = new Object();
+ Provider<Object> doubleCheck = DoubleCheck.provider(() -> {
+ if (invocationCount.incrementAndGet() == 1) {
+ doubleCheckReference.get().get();
+ }
+ return object;
+ });
+ doubleCheckReference.set(doubleCheck);
+ assertThat(doubleCheck.get()).isSameInstanceAs(object);
+ }
+
+ @Test public void reentranceReturningDifferentInstances_throwsIllegalStateException() {
+ final AtomicReference<Provider<Object>> doubleCheckReference =
+ new AtomicReference<>();
+ final AtomicInteger invocationCount = new AtomicInteger();
+ Provider<Object> doubleCheck = DoubleCheck.provider(() -> {
+ if (invocationCount.incrementAndGet() == 1) {
+ doubleCheckReference.get().get();
+ }
+ return new Object();
+ });
+ doubleCheckReference.set(doubleCheck);
+ try {
+ doubleCheck.get();
+ fail();
+ } catch (IllegalStateException expected) {}
+ }
+
+ @Test
+ public void instanceFactoryAsLazyDoesNotWrap() {
+ Factory<Object> factory = InstanceFactory.create(new Object());
+ assertThat(DoubleCheck.lazy(factory)).isSameInstanceAs(factory);
+ }
+}
diff --git a/javatests/dagger/internal/InstanceFactoryTest.java b/javatests/dagger/internal/InstanceFactoryTest.java
new file mode 100644
index 0000000..82b66e6
--- /dev/null
+++ b/javatests/dagger/internal/InstanceFactoryTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class InstanceFactoryTest {
+ @Test public void instanceFactory() {
+ Object instance = new Object();
+ Factory<Object> factory = InstanceFactory.create(instance);
+ assertThat(factory.get()).isEqualTo(instance);
+ assertThat(factory.get()).isEqualTo(instance);
+ assertThat(factory.get()).isEqualTo(instance);
+ }
+
+ @Test public void create_throwsNullPointerException() {
+ try {
+ InstanceFactory.create(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+}
diff --git a/javatests/dagger/internal/MapProviderFactoryTest.java b/javatests/dagger/internal/MapProviderFactoryTest.java
new file mode 100644
index 0000000..5598ff2
--- /dev/null
+++ b/javatests/dagger/internal/MapProviderFactoryTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.inject.Provider;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SuppressWarnings("unchecked")
+public class MapProviderFactoryTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void nullKey() {
+ thrown.expect(NullPointerException.class);
+ MapProviderFactory.<String, Integer>builder(1).put(null, incrementingIntegerProvider(1));
+ }
+
+ @Test
+ public void nullValue() {
+ thrown.expect(NullPointerException.class);
+ MapProviderFactory.<String, Integer>builder(1).put("Hello", null);
+ }
+
+ @Test
+ public void iterationOrder() {
+ Provider<Integer> p1 = incrementingIntegerProvider(10);
+ Provider<Integer> p2 = incrementingIntegerProvider(20);
+ Provider<Integer> p3 = incrementingIntegerProvider(30);
+ Provider<Integer> p4 = incrementingIntegerProvider(40);
+ Provider<Integer> p5 = incrementingIntegerProvider(50);
+
+ Factory<Map<String, Provider<Integer>>> factory = MapProviderFactory
+ .<String, Integer>builder(4)
+ .put("two", p2)
+ .put("one", p1)
+ .put("three", p3)
+ .put("one", p5)
+ .put("four", p4)
+ .build();
+
+ Map<String, Provider<Integer>> expectedMap = new LinkedHashMap<>();
+ expectedMap.put("two", p2);
+ expectedMap.put("one", p1);
+ expectedMap.put("three", p3);
+ expectedMap.put("one", p5);
+ expectedMap.put("four", p4);
+ assertThat(factory.get().entrySet())
+ .containsExactlyElementsIn(expectedMap.entrySet())
+ .inOrder();
+ }
+
+ private static Provider<Integer> incrementingIntegerProvider(int seed) {
+ return new AtomicInteger(seed)::getAndIncrement;
+ }
+}
diff --git a/javatests/dagger/internal/SetBuilderTest.java b/javatests/dagger/internal/SetBuilderTest.java
new file mode 100644
index 0000000..ac78312
--- /dev/null
+++ b/javatests/dagger/internal/SetBuilderTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SetBuilderTest {
+ private SetBuilder<String> setBuilder;
+
+ @Before
+ public void setUp() {
+ setBuilder = SetBuilder.newSetBuilder(1);
+ }
+
+ @Test
+ public void addNull() {
+ try {
+ setBuilder.add(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void addNullCollection() {
+ try {
+ setBuilder.addAll(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void addNullElement() {
+ try {
+ setBuilder.addAll(Arrays.asList("hello", null, "world"));
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+}
diff --git a/javatests/dagger/internal/SetFactoryTest.java b/javatests/dagger/internal/SetFactoryTest.java
new file mode 100644
index 0000000..0032578
--- /dev/null
+++ b/javatests/dagger/internal/SetFactoryTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.inject.Provider;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SuppressWarnings("unchecked")
+public class SetFactoryTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void providerReturnsNull() {
+ Factory<Set<Integer>> factory =
+ SetFactory.<Integer>builder(0, 1).addCollectionProvider(() -> null).build();
+ thrown.expect(NullPointerException.class);
+ factory.get();
+ }
+
+ @Test
+ public void providerReturnsNullSet() {
+ Factory<Set<Integer>> factory =
+ SetFactory.<Integer>builder(1, 0).addProvider(() -> null).build();
+ thrown.expect(NullPointerException.class);
+ factory.get();
+ }
+
+ @Test
+ public void providerReturnsSetWithNullElement() {
+ Set<Integer> set = new LinkedHashSet<>(Arrays.asList(1, null, 3));
+ Factory<Set<Integer>> factory =
+ SetFactory.<Integer>builder(0, 1).addCollectionProvider(() -> set).build();
+ thrown.expect(NullPointerException.class);
+ factory.get();
+ }
+
+ @Test
+ public void invokesProvidersEveryTime() {
+ Factory<Set<Integer>> factory =
+ SetFactory.<Integer>builder(2, 2)
+ .addProvider(incrementingIntegerProvider(0))
+ .addProvider(incrementingIntegerProvider(10))
+ .addCollectionProvider(incrementingIntegerSetProvider(20))
+ .addCollectionProvider(incrementingIntegerSetProvider(30))
+ .build();
+ assertThat(factory.get()).containsExactly(0, 10, 20, 21, 30, 31);
+ assertThat(factory.get()).containsExactly(1, 11, 22, 23, 32, 33);
+ assertThat(factory.get()).containsExactly(2, 12, 24, 25, 34, 35);
+ }
+
+ private static Provider<Integer> incrementingIntegerProvider(int seed) {
+ final AtomicInteger value = new AtomicInteger(seed);
+ return value::getAndIncrement;
+ }
+
+ private static Provider<Set<Integer>> incrementingIntegerSetProvider(int seed) {
+ final AtomicInteger value = new AtomicInteger(seed);
+ return () -> ImmutableSet.of(value.getAndIncrement(), value.getAndIncrement());
+ }
+}
diff --git a/javatests/dagger/internal/SingleCheckTest.java b/javatests/dagger/internal/SingleCheckTest.java
new file mode 100644
index 0000000..0c043fd
--- /dev/null
+++ b/javatests/dagger/internal/SingleCheckTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link SingleCheck}.
+ */
+@RunWith(JUnit4.class)
+public class SingleCheckTest {
+ @Test(expected = NullPointerException.class)
+ public void create_nullPointerException() {
+ SingleCheck.provider(null);
+ }
+
+ @Test
+ public void get() {
+ AtomicInteger integer = new AtomicInteger();
+ Provider<Integer> provider = SingleCheck.provider(integer::getAndIncrement);
+ assertThat(provider.get()).isEqualTo(0);
+ assertThat(provider.get()).isEqualTo(0);
+ assertThat(provider.get()).isEqualTo(0);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java
new file mode 100644
index 0000000..bb967b1
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java
@@ -0,0 +1,2838 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.AHEAD_OF_TIME_SUBCOMPONENTS_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATION_OPTIONS_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AheadOfTimeSubcomponentsMultibindingsTest {
+ @Test
+ public void setMultibindings_contributionsInLeaf() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Set<InLeaf> contributionsInLeaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoSet",
+ " static InLeaf provideInLeaf() {",
+ " return new InLeaf();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Set<InLeaf> contributionsInLeaf() {",
+ " return ImmutableSet.<InLeaf>of(",
+ " LeafModule_ProvideInLeafFactory.provideInLeaf());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+ }
+
+ @Test
+ public void setMultibindings_contributionsInAncestorOnly() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " Set<InAncestor> contributionsInAncestor();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract Set<InAncestor> contributionsInAncestor();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @ElementsIntoSet",
+ " static Set<InAncestor> provideInAncestors() {",
+ " return ImmutableSet.of(new InAncestor(), new InAncestor());",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Set<InAncestor> contributionsInAncestor() {",
+ " return ImmutableSet.<InAncestor>copyOf(",
+ " AncestorModule_ProvideInAncestorsFactory.provideInAncestors());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void setMultibindings_contributionsInLeafAndAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Set<InEachSubcomponent> contributionsInEachSubcomponent();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoSet",
+ " static InEachSubcomponent provideInLeaf() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoSet",
+ " static InEachSubcomponent provideAnotherInLeaf() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Set<InEachSubcomponent> contributionsInEachSubcomponent() {",
+ " return ImmutableSet.<InEachSubcomponent>of(",
+ " LeafModule_ProvideInLeafFactory.provideInLeaf(),",
+ " LeafModule_ProvideAnotherInLeafFactory.provideAnotherInLeaf());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @ElementsIntoSet",
+ " static Set<InEachSubcomponent> provideInAncestor() {",
+ " return ImmutableSet.of(new InEachSubcomponent(), new InEachSubcomponent());",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Set<InEachSubcomponent> contributionsInEachSubcomponent() {",
+ " return ImmutableSet.<InEachSubcomponent>builderWithExpectedSize(3)",
+ " .addAll(AncestorModule_ProvideInAncestorFactory.provideInAncestor())",
+ " .addAll(super.contributionsInEachSubcomponent())",
+ " .build();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void setMultibindings_contributionsInLeafAndGrandAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InLeafAndGrandAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Set<InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoSet",
+ " static InLeafAndGrandAncestor provideInLeaf() {",
+ " return new InLeafAndGrandAncestor();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoSet",
+ " static InLeafAndGrandAncestor provideAnotherInLeaf() {",
+ " return new InLeafAndGrandAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Set<InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor() {",
+ " return ImmutableSet.<InLeafAndGrandAncestor>of(",
+ " LeafModule_ProvideInLeafFactory.provideInLeaf(),",
+ " LeafModule_ProvideAnotherInLeafFactory.provideAnotherInLeaf());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = GrandAncestorModule.class)",
+ "interface GrandAncestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestorModule",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class GrandAncestorModule {",
+ " @Provides",
+ " @ElementsIntoSet",
+ " static Set<InLeafAndGrandAncestor> provideInGrandAncestor() {",
+ " return ImmutableSet.of(new InLeafAndGrandAncestor(),",
+ " new InLeafAndGrandAncestor());",
+ " }",
+ "}"));
+ JavaFileObject generatedGrandAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerGrandAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerGrandAncestor implements GrandAncestor {",
+ " protected DaggerGrandAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Set<InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor() {",
+ " return ImmutableSet.<InLeafAndGrandAncestor>builderWithExpectedSize(3)",
+ " .addAll(GrandAncestorModule_ProvideInGrandAncestorFactory",
+ " .provideInGrandAncestor())",
+ " .addAll(super.contributionsInLeafAndGrandAncestor())",
+ " .build();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerGrandAncestor")
+ .hasSourceEquivalentTo(generatedGrandAncestor);
+ }
+
+ @Test
+ public void setMultibindings_nonComponentMethodDependency() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(
+ filesToCompile, "InAllSubcomponents", "RequresInAllSubcomponentsSet");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " RequresInAllSubcomponentsSet requiresNonComponentMethod();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoSet",
+ " static InAllSubcomponents provideInAllSubcomponents() {",
+ " return new InAllSubcomponents();",
+ " }",
+ "",
+ " @Provides",
+ " static RequresInAllSubcomponentsSet providesRequresInAllSubcomponentsSet(",
+ " Set<InAllSubcomponents> inAllSubcomponents) {",
+ " return new RequresInAllSubcomponentsSet();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public RequresInAllSubcomponentsSet requiresNonComponentMethod() {",
+ " return LeafModule_ProvidesRequresInAllSubcomponentsSetFactory",
+ " .providesRequresInAllSubcomponentsSet(getSetOfInAllSubcomponents());",
+ " }",
+ "",
+ " protected Set getSetOfInAllSubcomponents() {",
+ " return ImmutableSet.<InAllSubcomponents>of(",
+ " LeafModule_ProvideInAllSubcomponentsFactory",
+ " .provideInAllSubcomponents());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoSet",
+ " static InAllSubcomponents provideInAllSubcomponents() {",
+ " return new InAllSubcomponents();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected Set getSetOfInAllSubcomponents() {",
+ " return ImmutableSet.<InAllSubcomponents>builderWithExpectedSize(2)",
+ " .add(AncestorModule_ProvideInAllSubcomponentsFactory",
+ " .provideInAllSubcomponents())",
+ " .addAll(super.getSetOfInAllSubcomponents())",
+ " .build();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void setMultibindings_newSubclass() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InAncestor", "RequiresInAncestorSet");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " RequiresInAncestorSet missingWithSetDependency();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract RequiresInAncestorSet missingWithSetDependency();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ "",
+ " @Provides",
+ " static RequiresInAncestorSet provideRequiresInAncestorSet(",
+ " Set<InAncestor> inAncestors) {",
+ " return new RequiresInAncestorSet();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoSet",
+ " static InAncestor provideInAncestor() {",
+ " return new InAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " private RequiresInAncestorSet getRequiresInAncestorSet() {",
+ " return AncestorModule_ProvideRequiresInAncestorSetFactory",
+ " .provideRequiresInAncestorSet(getSetOfInAncestor());",
+ " }",
+ "",
+ " protected Set getSetOfInAncestor() {",
+ " return ImmutableSet.<InAncestor>of(",
+ " AncestorModule_ProvideInAncestorFactory.provideInAncestor());",
+ " }",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final RequiresInAncestorSet missingWithSetDependency() {",
+ " return DaggerAncestor.this.getRequiresInAncestorSet();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void setMultibinding_requestedAsInstanceInLeaf_requestedAsFrameworkInstanceFromAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(
+ filesToCompile, "Multibound", "MissingInLeaf_WillDependOnFrameworkInstance");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Set<Multibound> instance();",
+ " MissingInLeaf_WillDependOnFrameworkInstance willDependOnFrameworkInstance();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoSet",
+ " static Multibound contribution() {",
+ " return new Multibound();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Set<Multibound> instance() {",
+ " return ImmutableSet.<Multibound>of(",
+ " LeafModule_ContributionFactory.contribution());",
+ " }",
+ "",
+ " @Override",
+ " public abstract MissingInLeaf_WillDependOnFrameworkInstance",
+ " willDependOnFrameworkInstance();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "interface AncestorModule {",
+ " @Provides",
+ " static MissingInLeaf_WillDependOnFrameworkInstance providedInAncestor(",
+ " Provider<Set<Multibound>> frameworkInstance) {",
+ " return null;",
+ " }",
+ "",
+ " @Multibinds Set<Multibound> multibinds();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.SetFactory;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private Provider<Set<Multibound>> setOfMultiboundProvider;",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " protected void configureInitialization() { ",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() { ",
+ " this.setOfMultiboundProvider =",
+ " SetFactory.<Multibound>builder(1, 0)",
+ " .addProvider(LeafModule_ContributionFactory.create())",
+ " .build();",
+ " }",
+ "",
+ " protected Provider getSetOfMultiboundProvider() {",
+ " return setOfMultiboundProvider;",
+ " }",
+ "",
+ " @Override",
+ " public final MissingInLeaf_WillDependOnFrameworkInstance ",
+ " willDependOnFrameworkInstance() {",
+ " return AncestorModule_ProvidedInAncestorFactory.providedInAncestor(",
+ " getSetOfMultiboundProvider());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void missingMultibindingInLeaf_onlyContributionsInAncestor_notReModifiedInRoot() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " Set<Object> set();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract Set<Object> set();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoSet",
+ " static Object onlyContribution() {",
+ " return new Object();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Set<Object> set() {",
+ " return ImmutableSet.<Object>of(",
+ " AncestorModule_OnlyContributionFactory.onlyContribution());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " Ancestor ancestor();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Ancestor ancestor() {",
+ " return new AncestorImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class AncestorImpl extends DaggerAncestor {",
+ " private AncestorImpl() {}",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " private LeafImpl() {}",
+ // This tests a regression case where Dagger used to reimplement Leaf.set(), even though
+ // there were no new contributions, because the state change from missing ->
+ // multibinding wasn't properly recorded
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void setMultibindings_contributionsInLeafAndAncestor_frameworkInstances() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Provider<Set<InEachSubcomponent>> contributionsInEachSubcomponent();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoSet",
+ " static InEachSubcomponent provideInLeaf() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoSet",
+ " static InEachSubcomponent provideAnotherInLeaf() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.SetFactory;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private Provider<Set<InEachSubcomponent>> setOfInEachSubcomponentProvider;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.setOfInEachSubcomponentProvider =",
+ " SetFactory.<InEachSubcomponent>builder(2, 0)",
+ " .addProvider(LeafModule_ProvideInLeafFactory.create())",
+ " .addProvider(LeafModule_ProvideAnotherInLeafFactory.create())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Set<InEachSubcomponent>> contributionsInEachSubcomponent() {",
+ " return setOfInEachSubcomponentProvider;",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @ElementsIntoSet",
+ " static Set<InEachSubcomponent> provideInAncestor() {",
+ " return ImmutableSet.of(new InEachSubcomponent(), new InEachSubcomponent());",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.DelegateFactory;",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.SetFactory;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private Provider<Set<InEachSubcomponent>> setOfInEachSubcomponentProvider = ",
+ " new DelegateFactory<>();",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected void configureInitialization() {",
+ " super.configureInitialization();",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " DelegateFactory.setDelegate(",
+ " setOfInEachSubcomponentProvider,",
+ " SetFactory.<InEachSubcomponent>builder(0, 2)",
+ " .addCollectionProvider(super.contributionsInEachSubcomponent())",
+ " .addCollectionProvider(",
+ " AncestorModule_ProvideInAncestorFactory.create())",
+ " .build());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Set<InEachSubcomponent>> contributionsInEachSubcomponent() {",
+ " return setOfInEachSubcomponentProvider;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void mapMultibindings_contributionsInLeaf() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Map<String, InLeaf> contributionsInLeaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"leafmodule\")",
+ " static InLeaf provideInLeaf() {",
+ " return new InLeaf();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Map<String, InLeaf> contributionsInLeaf() {",
+ " return ImmutableMap.<String, InLeaf>of(",
+ " \"leafmodule\",",
+ " LeafModule_ProvideInLeafFactory.provideInLeaf());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+ }
+
+ @Test
+ public void mapMultibindings_contributionsInAncestorOnly() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " Map<String, InAncestor> contributionsInAncestor();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract Map<String, InAncestor> contributionsInAncestor();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"ancestormodule\")",
+ " static InAncestor provideInAncestor() {",
+ " return new InAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Map<String, InAncestor> contributionsInAncestor() {",
+ " return ImmutableMap.<String, InAncestor>of(\"ancestormodule\",",
+ " AncestorModule_ProvideInAncestorFactory.provideInAncestor());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void mapMultibindings_contributionsInLeafAndAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Map<String, InEachSubcomponent> contributionsInEachSubcomponent();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"leafmodule\")",
+ " static InEachSubcomponent provideInLeaf() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
+ " return ImmutableMap.<String, InEachSubcomponent>of(",
+ " \"leafmodule\", LeafModule_ProvideInLeafFactory.provideInLeaf());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"ancestormodule\")",
+ " static InEachSubcomponent provideInAncestor() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
+ " return ImmutableMap.<String, InEachSubcomponent>builderWithExpectedSize(2)",
+ " .put(\"ancestormodule\",",
+ " AncestorModule_ProvideInAncestorFactory.provideInAncestor())",
+ " .putAll(super.contributionsInEachSubcomponent())",
+ " .build();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void mapMultibindings_contributionsInLeafAndAncestor_frameworkInstance() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Provider<Map<String, InEachSubcomponent>> contributionsInEachSubcomponent();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"leafmodule\")",
+ " static InEachSubcomponent provideInLeaf() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.MapFactory;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private Provider<Map<String, InEachSubcomponent>> ",
+ " mapOfStringAndInEachSubcomponentProvider;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.mapOfStringAndInEachSubcomponentProvider =",
+ " MapFactory.<String, InEachSubcomponent>builder(1)",
+ " .put(\"leafmodule\", LeafModule_ProvideInLeafFactory.create())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<String, InEachSubcomponent>> ",
+ " contributionsInEachSubcomponent() {",
+ " return mapOfStringAndInEachSubcomponentProvider;",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"ancestormodule\")",
+ " static InEachSubcomponent provideInAncestor() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.DelegateFactory;",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.MapFactory;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private Provider<Map<String, InEachSubcomponent>> ",
+ " mapOfStringAndInEachSubcomponentProvider = new DelegateFactory<>();",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected void configureInitialization() { ",
+ " super.configureInitialization();",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() { ",
+ " DelegateFactory.setDelegate(",
+ " mapOfStringAndInEachSubcomponentProvider,",
+ " MapFactory.<String, InEachSubcomponent>builder(2)",
+ " .putAll(super.contributionsInEachSubcomponent())",
+ " .put(",
+ " \"ancestormodule\",",
+ " AncestorModule_ProvideInAncestorFactory.create())",
+ " .build());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<String, InEachSubcomponent>> ",
+ " contributionsInEachSubcomponent() {",
+ " return mapOfStringAndInEachSubcomponentProvider;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void mapMultibindings_contributionsInLeafAndGrandAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InLeafAndGrandAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Map<String, InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"leafmodule\")",
+ " static InLeafAndGrandAncestor provideInLeaf() {",
+ " return new InLeafAndGrandAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Map<String, InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor() {",
+ " return ImmutableMap.<String, InLeafAndGrandAncestor>of(",
+ " \"leafmodule\", LeafModule_ProvideInLeafFactory.provideInLeaf());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = GrandAncestorModule.class)",
+ "interface GrandAncestor {",
+ " Ancestor ancestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class GrandAncestorModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"grandancestormodule\")",
+ " static InLeafAndGrandAncestor provideInGrandAncestor() {",
+ " return new InLeafAndGrandAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedGrandAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerGrandAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerGrandAncestor implements GrandAncestor {",
+ " protected DaggerGrandAncestor() {}",
+ "",
+ " protected abstract class AncestorImpl extends DaggerAncestor {",
+ " protected AncestorImpl() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Map<String, InLeafAndGrandAncestor>",
+ " contributionsInLeafAndGrandAncestor() {",
+ " return",
+ " ImmutableMap.<String, InLeafAndGrandAncestor>builderWithExpectedSize(2)",
+ " .put(\"grandancestormodule\",",
+ " GrandAncestorModule_ProvideInGrandAncestorFactory",
+ " .provideInGrandAncestor())",
+ " .putAll(super.contributionsInLeafAndGrandAncestor())",
+ " .build();",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerGrandAncestor")
+ .hasSourceEquivalentTo(generatedGrandAncestor);
+ }
+
+ @Test
+ public void mapMultibindings_contributionsInLeafAndAncestorWithoutGuava() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Map<String, InEachSubcomponent> contributionsInEachSubcomponent();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"leafmodule\")",
+ " static InEachSubcomponent provideInLeaf() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Collections;",
+ "import java.util.Map",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
+ " return Collections.<String, InEachSubcomponent>singletonMap(",
+ " \"leafmodule\", LeafModule_ProvideInLeafFactory.provideInLeaf());",
+ " }",
+ "}");
+ Compilation compilation = compileWithoutGuava(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoMap",
+ " @StringKey(\"ancestormodule\")",
+ " static InEachSubcomponent provideInAncestor() {",
+ " return new InEachSubcomponent();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.MapBuilder;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
+ " return MapBuilder.<String, InEachSubcomponent>newMapBuilder(2)",
+ " .put(\"ancestormodule\",",
+ " AncestorModule_ProvideInAncestorFactory.provideInAncestor())",
+ " .putAll(super.contributionsInEachSubcomponent())",
+ " .build();",
+ " }",
+ " }",
+ "}");
+ compilation = compileWithoutGuava(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void mapMultibinding_requestedAsInstanceInLeaf_requestedAsFrameworkInstanceFromAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(
+ filesToCompile, "Multibound", "MissingInLeaf_WillDependOnFrameworkInstance");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Map<Integer, Multibound> instance();",
+ " MissingInLeaf_WillDependOnFrameworkInstance willDependOnFrameworkInstance();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntKey;",
+ "import dagger.multibindings.IntoMap;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " @IntoMap",
+ " @IntKey(111)",
+ " static Multibound contribution() {",
+ " return new Multibound();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Map<Integer, Multibound> instance() {",
+ " return ImmutableMap.<Integer, Multibound>of(",
+ " 111, LeafModule_ContributionFactory.contribution());",
+ " }",
+ "",
+ " @Override",
+ " public abstract MissingInLeaf_WillDependOnFrameworkInstance",
+ " willDependOnFrameworkInstance();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "interface AncestorModule {",
+ " @Provides",
+ " static MissingInLeaf_WillDependOnFrameworkInstance providedInAncestor(",
+ " Provider<Map<Integer, Multibound>> frameworkInstance) {",
+ " return null;",
+ " }",
+ "",
+ " @Multibinds Map<Integer, Multibound> multibinds();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.MapFactory;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private Provider<Map<Integer, Multibound>> mapOfIntegerAndMultiboundProvider;",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " protected void configureInitialization() { ",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() { ",
+ " this.mapOfIntegerAndMultiboundProvider =",
+ " MapFactory.<Integer, Multibound>builder(1)",
+ " .put(111, LeafModule_ContributionFactory.create())",
+ " .build();",
+ " }",
+ "",
+ " protected Provider getMapOfIntegerAndMultiboundProvider() {",
+ " return mapOfIntegerAndMultiboundProvider;",
+ " }",
+ "",
+ " @Override",
+ " public final MissingInLeaf_WillDependOnFrameworkInstance ",
+ " willDependOnFrameworkInstance() {",
+ " return AncestorModule_ProvidedInAncestorFactory.providedInAncestor(",
+ " getMapOfIntegerAndMultiboundProvider());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void emptyMultibinds_set() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Multibound");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Multibinds",
+ " Set<Multibound> set();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Set<Multibound> set();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Set<Multibound> set() {",
+ " return ImmutableSet.<Multibound>of();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoSet",
+ " static Multibound fromAncestor() {",
+ " return new Multibound();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Set<Multibound> set() {",
+ " return ImmutableSet.<Multibound>of(",
+ " AncestorModule_FromAncestorFactory.fromAncestor());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void emptyMultibinds_set_frameworkInstance() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Multibound");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Multibinds",
+ " Set<Multibound> set();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Provider<Set<Multibound>> set();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.SetFactory;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Provider<Set<Multibound>> set() {",
+ " return SetFactory.<Multibound>empty();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoSet",
+ " static Multibound fromAncestor() {",
+ " return new Multibound();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.DelegateFactory;",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.SetFactory;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private Provider<Set<Multibound>> setOfMultiboundProvider =",
+ " new DelegateFactory<>();",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " DelegateFactory.setDelegate(",
+ " setOfMultiboundProvider,",
+ " SetFactory.<Multibound>builder(1, 0)",
+ " .addProvider(AncestorModule_FromAncestorFactory.create())",
+ " .build());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Set<Multibound>> set() {",
+ " return setOfMultiboundProvider;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void emptyMultibinds_map() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Multibound");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Multibinds",
+ " Map<Integer, Multibound> map();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Map<Integer, Multibound> map();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Map<Integer, Multibound> map() {",
+ " return ImmutableMap.<Integer, Multibound>of();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntKey;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoMap",
+ " @IntKey(111)",
+ " static Multibound fromAncestor() {",
+ " return new Multibound();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableMap;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Map<Integer, Multibound> map() {",
+ " return ImmutableMap.<Integer, Multibound>of(",
+ " 111, AncestorModule_FromAncestorFactory.fromAncestor());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void emptyMultibinds_map_frameworkInstance() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Multibound");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Multibinds",
+ " Map<Integer, Multibound> map();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Provider<Map<Integer, Multibound>> map();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.MapFactory;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Provider<Map<Integer, Multibound>> map() {",
+ " return MapFactory.<Integer, Multibound>emptyMapProvider();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntKey;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " @IntoMap",
+ " @IntKey(111)",
+ " static Multibound fromAncestor() {",
+ " return new Multibound();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.DelegateFactory;",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.MapFactory;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private Provider<Map<Integer, Multibound>> mapOfIntegerAndMultiboundProvider =",
+ " new DelegateFactory<>()",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " DelegateFactory.setDelegate(",
+ " mapOfIntegerAndMultiboundProvider,",
+ " MapFactory.<Integer, Multibound>builder(1)",
+ " .put(111, AncestorModule_FromAncestorFactory.create())",
+ " .build());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<Integer, Multibound>> map() {",
+ " return mapOfIntegerAndMultiboundProvider;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void bindsMissingDep_Multibindings() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Binds",
+ " @IntoSet",
+ " CharSequence bindsMultibindingWithMissingDep(String string);",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Set<CharSequence> set();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Set<CharSequence> set() {",
+ " return ImmutableSet.<CharSequence>of(getBindsMultibindingWithMissingDep());",
+ " }",
+ "",
+ // The expected output here is subtle: the Key of
+ // LeafModule.bindsMultibindingWithMissingDep() is Set<CharSequence>, but the binding
+ // method should only be returning an individual CharSequence. Otherwise the
+ // ImmutableSet factory method above will fail.
+ " protected abstract CharSequence getBindsMultibindingWithMissingDep();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+ }
+
+ @Test
+ public void multibindingsAndFastInit() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "PackagePrivate");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.MultibindingModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntKey;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "interface MultibindingModule {",
+ " @Provides",
+ " @IntoSet",
+ " @LeafScope",
+ " static PackagePrivate setContribution() {",
+ " return new PackagePrivate();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoMap",
+ " @IntKey(1)",
+ " @LeafScope",
+ " static PackagePrivate mapContribution() {",
+ " return new PackagePrivate();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface LeafScope {}"),
+ JavaFileObjects.forSourceLines(
+ "test.UsesMultibindings",
+ "package test;",
+ "",
+ "import java.util.Map;",
+ "import java.util.Set;",
+ "import javax.inject.Inject;",
+ "",
+ "class UsesMultibindings {",
+ " @Inject",
+ " UsesMultibindings(Set<PackagePrivate> set, Map<Integer, PackagePrivate> map) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "import java.util.Set;",
+ "",
+ "@LeafScope",
+ "@Subcomponent(modules = MultibindingModule.class)",
+ "interface Leaf {",
+ " UsesMultibindings entryPoint();",
+ "}"));
+
+ Compilation compilation =
+ compilerWithOptions(AHEAD_OF_TIME_SUBCOMPONENTS_MODE, FAST_INIT_MODE)
+ .compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "@GenerationOptions(fastInit = true)",
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " private PackagePrivate getSetContribution() {",
+ " Object local = setContribution;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = setContribution;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = MultibindingModule_SetContributionFactory.setContribution();",
+ " setContribution = DoubleCheck.reentrantCheck(setContribution, local);",
+ " }",
+ " }",
+ " }",
+ " return (PackagePrivate) local;",
+ " }",
+ "",
+ " private PackagePrivate getMapContribution() {",
+ " Object local = mapContribution;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = mapContribution;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = MultibindingModule_MapContributionFactory.mapContribution();",
+ " mapContribution = DoubleCheck.reentrantCheck(mapContribution, local);",
+ " }",
+ " }",
+ " }",
+ " return (PackagePrivate) local;",
+ " }",
+ "",
+ " @Override",
+ " public UsesMultibindings entryPoint() {",
+ " return new UsesMultibindings(",
+ " getSetOfPackagePrivate(), getMapOfIntegerAndPackagePrivate());",
+ " }",
+ "",
+ " protected Set getSetOfPackagePrivate() {",
+ " return ImmutableSet.<PackagePrivate>of(getSetContribution());",
+ " }",
+ "",
+ " protected Map getMapOfIntegerAndPackagePrivate() {",
+ " return ImmutableMap.<Integer, PackagePrivate>of(1, getMapContribution());",
+ " }",
+ "}");
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .containsElementsIn(generatedLeaf);
+ }
+
+ // TODO(ronshapiro): remove copies from AheadOfTimeSubcomponents*Test classes
+ private void createSimplePackagePrivateClasses(
+ ImmutableList.Builder<JavaFileObject> filesBuilder, String... ancillaryClasses) {
+ for (String className : ancillaryClasses) {
+ filesBuilder.add(
+ JavaFileObjects.forSourceLines(
+ String.format("test.%s", className),
+ "package test;",
+ "",
+ String.format("class %s { }", className)));
+ }
+ }
+
+ private static Compilation compile(Iterable<JavaFileObject> files) {
+ return compilerWithOptions(AHEAD_OF_TIME_SUBCOMPONENTS_MODE).compile(files);
+ }
+
+ private static Compilation compileWithoutGuava(Iterable<JavaFileObject> files) {
+ return daggerCompiler()
+ .withOptions(
+ AHEAD_OF_TIME_SUBCOMPONENTS_MODE.javacopts().append(CLASS_PATH_WITHOUT_GUAVA_OPTION))
+ .compile(files);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java
new file mode 100644
index 0000000..1bd221a
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java
@@ -0,0 +1,5677 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.AHEAD_OF_TIME_SUBCOMPONENTS_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATION_OPTIONS_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ObjectArrays;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AheadOfTimeSubcomponentsTest {
+ private static final String PRUNED_METHOD_BODY =
+ "throw new UnsupportedOperationException(\"This binding is not part of the final binding "
+ + "graph. The key was requested by a binding that was believed to possibly be part of "
+ + "the graph, but is no longer requested. If this exception is thrown, it is the result "
+ + "of a Dagger bug.\");";
+
+ @Test
+ public void missingBindings_fromComponentMethod() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " MissingInLeaf missingFromComponentMethod();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract MissingInLeaf missingFromComponentMethod();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " static MissingInLeaf satisfiedInAncestor() { return new MissingInLeaf(); }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final MissingInLeaf missingFromComponentMethod() {",
+ " return AncestorModule_SatisfiedInAncestorFactory.satisfiedInAncestor();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void missingBindings_dependsOnBindingWithMatchingComponentMethod() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " MissingInLeaf missingComponentMethod();",
+ " DependsOnComponentMethod dependsOnComponentMethod();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.DependsOnComponentMethod",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class DependsOnComponentMethod {",
+ " @Inject DependsOnComponentMethod(MissingInLeaf missingInLeaf) {}",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract MissingInLeaf missingComponentMethod();",
+ "",
+ " @Override",
+ " public DependsOnComponentMethod dependsOnComponentMethod() {",
+ " return new DependsOnComponentMethod(missingComponentMethod());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+ }
+
+ @Test
+ public void missingBindings_dependsOnMissingBinding() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " DependsOnMissingBinding dependsOnMissingBinding();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.DependsOnMissingBinding",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class DependsOnMissingBinding {",
+ " @Inject DependsOnMissingBinding(MissingInLeaf missing) {}",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public DependsOnMissingBinding dependsOnMissingBinding() {",
+ " return new DependsOnMissingBinding((MissingInLeaf) getMissingInLeaf());",
+ " }",
+ "",
+ " protected abstract Object getMissingInLeaf();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " static MissingInLeaf satisfiedInAncestor() { return new MissingInLeaf(); }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected final Object getMissingInLeaf() {",
+ " return AncestorModule_SatisfiedInAncestorFactory.satisfiedInAncestor();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void missingBindings_satisfiedInGreatAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " DependsOnMissingBinding dependsOnMissingBinding();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.DependsOnMissingBinding",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class DependsOnMissingBinding {",
+ " @Inject DependsOnMissingBinding(MissingInLeaf missing) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.GreatAncestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = SatisfiesMissingBindingModule.class)",
+ "interface GreatAncestor {",
+ " Ancestor ancestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.SatisfiesMissingBindingModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class SatisfiesMissingBindingModule {",
+ " @Provides",
+ " static MissingInLeaf satisfy() { return new MissingInLeaf(); }",
+ "}"));
+ // DaggerLeaf+DaggerAncestor generated types are ignored - they're not the focus of this test
+ // and are tested elsewhere
+ JavaFileObject generatedGreatAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerGreatAncestor implements GreatAncestor {",
+ " protected DaggerGreatAncestor() {}",
+ "",
+ " protected abstract class AncestorImpl extends DaggerAncestor {",
+ " protected AncestorImpl() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected final Object getMissingInLeaf() {",
+ " return SatisfiesMissingBindingModule_SatisfyFactory.satisfy();",
+ " }",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerGreatAncestor")
+ .hasSourceEquivalentTo(generatedGreatAncestor);
+ }
+
+ @Test
+ public void moduleInstanceDependency() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "interface Leaf {",
+ " String string();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class TestModule {",
+ " @Provides String provideString() { return \"florp\"; }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public String string() {",
+ " return TestModule_ProvideStringFactory.provideString(testModule());",
+ " }",
+ "",
+ " protected abstract TestModule testModule();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " Ancestor ancestor();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Ancestor ancestor() {",
+ " return new AncestorImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class AncestorImpl extends DaggerAncestor {",
+ " private AncestorImpl() {}",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " private final TestModule testModule;",
+ "",
+ " private LeafImpl() {",
+ " this.testModule = new TestModule();",
+ " }",
+ "",
+ " @Override",
+ " protected TestModule testModule() {",
+ " return testModule;",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void moduleInstanceDependency_withModuleParams() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "interface Leaf {",
+ " int getInt();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " private int i;",
+ "",
+ " TestModule(int i) {}",
+ "",
+ " @Provides int provideInt() {",
+ " return i++;",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public int getInt() {",
+ " return testModule().provideInt();",
+ " }",
+ "",
+ " protected abstract TestModule testModule();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf leaf(TestModule module);",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " Ancestor ancestor();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Ancestor ancestor() {",
+ " return new AncestorImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class AncestorImpl extends DaggerAncestor {",
+ " private AncestorImpl() {}",
+ "",
+ " @Override",
+ " public Leaf leaf(TestModule module) {",
+ " Preconditions.checkNotNull(module);",
+ " return new LeafImpl(module);",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " private final TestModule testModule;",
+ "",
+ " private LeafImpl(TestModule module) {",
+ " this.testModule = module;",
+ " }",
+ "",
+ " @Override",
+ " protected TestModule testModule() {",
+ " return testModule;",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void generatedInstanceBinding() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Leaf build();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf.Builder leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " @Override",
+ " public abstract Leaf.Builder leaf();",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " Ancestor ancestor();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Ancestor ancestor() {",
+ " return new AncestorImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class AncestorImpl extends DaggerAncestor {",
+ " private AncestorImpl() {}",
+ "",
+ " @Override",
+ " public Leaf.Builder leaf() {",
+ " return new LeafBuilder();",
+ " }",
+ "",
+ " private final class LeafBuilder implements Leaf.Builder {",
+ " @Override",
+ " public Leaf build() {",
+ " return new LeafImpl();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " private LeafImpl() {}",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void prunedGeneratedInstanceBinding() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.PrunedSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface PrunedSubcomponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " PrunedSubcomponent build();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.InstallsPrunedSubcomponentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(subcomponents = PrunedSubcomponent.class)",
+ "interface InstallsPrunedSubcomponentModule {}"),
+ JavaFileObjects.forSourceLines(
+ "test.DependsOnPrunedSubcomponentBuilder",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class DependsOnPrunedSubcomponentBuilder {",
+ " @Inject DependsOnPrunedSubcomponentBuilder(PrunedSubcomponent.Builder builder) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.MaybeLeaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = InstallsPrunedSubcomponentModule.class)",
+ "interface MaybeLeaf {",
+ " DependsOnPrunedSubcomponentBuilder dependsOnPrunedSubcomponentBuilder();",
+ "}"));
+ JavaFileObject generatedMaybeLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerMaybeLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerMaybeLeaf implements MaybeLeaf {",
+ " protected DaggerMaybeLeaf() {}",
+ "",
+ " @Override",
+ " public DependsOnPrunedSubcomponentBuilder dependsOnPrunedSubcomponentBuilder() {",
+ " return new DependsOnPrunedSubcomponentBuilder(",
+ " (PrunedSubcomponent.Builder) getPrunedSubcomponentBuilder());",
+ " }",
+ "",
+ " protected abstract Object getPrunedSubcomponentBuilder();",
+ "",
+ " protected abstract class PrunedSubcomponentImpl extends DaggerPrunedSubcomponent {",
+ " protected PrunedSubcomponentImpl() {}",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerMaybeLeaf")
+ .hasSourceEquivalentTo(generatedMaybeLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.PrunesGeneratedInstanceModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface PrunesGeneratedInstanceModule {",
+ " @Provides",
+ " static DependsOnPrunedSubcomponentBuilder pruneGeneratedInstance() {",
+ " return new DependsOnPrunedSubcomponentBuilder(null);",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = PrunesGeneratedInstanceModule.class)",
+ "interface Root {",
+ " MaybeLeaf actuallyLeaf();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public MaybeLeaf actuallyLeaf() {",
+ " return new MaybeLeafImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class MaybeLeafImpl extends DaggerMaybeLeaf {",
+ " private MaybeLeafImpl() {}",
+ "",
+ " @Override",
+ " protected Object getPrunedSubcomponentBuilder() {",
+ " " + PRUNED_METHOD_BODY,
+ " }",
+ "",
+ " @Override",
+ " public DependsOnPrunedSubcomponentBuilder dependsOnPrunedSubcomponentBuilder() {",
+ " return PrunesGeneratedInstanceModule_PruneGeneratedInstanceFactory",
+ " .pruneGeneratedInstance();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void optionalBindings_boundAndSatisfiedInSameSubcomponent() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInSub");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Optional;",
+ "",
+ "@Subcomponent(modules = {SubModule.class, BindsSatisfiedInSubModule.class})",
+ "interface Sub {",
+ " Optional<SatisfiedInSub> satisfiedInSub();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.SubModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class SubModule {",
+ " @BindsOptionalOf abstract SatisfiedInSub optionalSatisfiedInSub();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.BindsSatisfiedInSubModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class BindsSatisfiedInSubModule {",
+ " @Provides static SatisfiedInSub provideSatisfiedInSub() {",
+ " return new SatisfiedInSub();",
+ " }",
+ "}"));
+ JavaFileObject generatedSubcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSub",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerSub implements Sub {",
+ " protected DaggerSub() {}",
+ "",
+ " @Override",
+ " public Optional<SatisfiedInSub> satisfiedInSub() {",
+ " return Optional.of(",
+ " BindsSatisfiedInSubModule_ProvideSatisfiedInSubFactory",
+ " .provideSatisfiedInSub());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSub")
+ .hasSourceEquivalentTo(generatedSubcomponent);
+ }
+
+ @Test
+ public void optionalBindings_satisfiedInAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Optional;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Optional<SatisfiedInAncestor> satisfiedInAncestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class LeafModule {",
+ " @BindsOptionalOf abstract SatisfiedInAncestor optionalSatisfiedInAncestor();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Optional<SatisfiedInAncestor> satisfiedInAncestor() {",
+ " return Optional.<SatisfiedInAncestor>empty();",
+ " }",
+ "}");
+ Compilation compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class AncestorModule {",
+ " @Provides",
+ " static SatisfiedInAncestor satisfiedInAncestor(){",
+ " return new SatisfiedInAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final Optional<SatisfiedInAncestor> satisfiedInAncestor() {",
+ " return Optional.of(AncestorModule_SatisfiedInAncestorFactory",
+ " .satisfiedInAncestor());",
+ " }",
+ "",
+ " }",
+ "}");
+ compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void optionalBindings_satisfiedInGrandAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInGrandAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Optional;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Optional<SatisfiedInGrandAncestor> satisfiedInGrandAncestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class LeafModule {",
+ " @BindsOptionalOf",
+ " abstract SatisfiedInGrandAncestor optionalSatisfiedInGrandAncestor();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Optional<SatisfiedInGrandAncestor> satisfiedInGrandAncestor() {",
+ " return Optional.<SatisfiedInGrandAncestor>empty();",
+ " }",
+ "}");
+ Compilation compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ " }",
+ "}");
+ compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.GreatAncestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = GreatAncestorModule.class)",
+ "interface GreatAncestor {",
+ " Ancestor ancestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.GreatAncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class GreatAncestorModule {",
+ " @Provides",
+ " static SatisfiedInGrandAncestor satisfiedInGrandAncestor(){",
+ " return new SatisfiedInGrandAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedGreatAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerGreatAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerGreatAncestor implements GreatAncestor {",
+ " protected DaggerGreatAncestor() {}",
+ "",
+ " protected abstract class AncestorImpl extends DaggerAncestor {",
+ " protected AncestorImpl() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final Optional<SatisfiedInGrandAncestor> satisfiedInGrandAncestor() {",
+ " return Optional.of(",
+ " GreatAncestorModule_SatisfiedInGrandAncestorFactory",
+ " .satisfiedInGrandAncestor());",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerGreatAncestor")
+ .hasSourceEquivalentTo(generatedGreatAncestor);
+ }
+
+ @Test
+ public void optionalBindings_nonComponentMethodDependencySatisfiedInAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(
+ filesToCompile, "SatisfiedInAncestor", "RequiresOptionalSatisfiedInAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Optional;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " RequiresOptionalSatisfiedInAncestor requiresOptionalSatisfiedInAncestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.Optional;",
+ "",
+ "@Module",
+ "abstract class LeafModule {",
+ " @Provides static RequiresOptionalSatisfiedInAncestor",
+ " provideRequiresOptionalSatisfiedInAncestor(",
+ " Optional<SatisfiedInAncestor> satisfiedInAncestor) {",
+ " return new RequiresOptionalSatisfiedInAncestor();",
+ " }",
+ "",
+ " @BindsOptionalOf abstract SatisfiedInAncestor optionalSatisfiedInAncestor();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public RequiresOptionalSatisfiedInAncestor",
+ " requiresOptionalSatisfiedInAncestor() {",
+ " return LeafModule_ProvideRequiresOptionalSatisfiedInAncestorFactory",
+ " .provideRequiresOptionalSatisfiedInAncestor(",
+ " getOptionalOfSatisfiedInAncestor());",
+ " }",
+ "",
+ " protected Optional getOptionalOfSatisfiedInAncestor() {",
+ " return Optional.<SatisfiedInAncestor>empty();",
+ " }",
+ "}");
+ Compilation compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class AncestorModule {",
+ " @Provides",
+ " static SatisfiedInAncestor satisfiedInAncestor(){",
+ " return new SatisfiedInAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected final Optional getOptionalOfSatisfiedInAncestor() {",
+ " return Optional.of(",
+ " AncestorModule_SatisfiedInAncestorFactory.satisfiedInAncestor());",
+ " }",
+ " }",
+ "}");
+ compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void optionalBindings_boundInAncestorAndSatisfiedInGrandAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInGrandAncestor");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Optional;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " Optional<SatisfiedInGrandAncestor> boundInAncestorSatisfiedInGrandAncestor();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract Optional<SatisfiedInGrandAncestor>",
+ " boundInAncestorSatisfiedInGrandAncestor();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class AncestorModule {",
+ " @BindsOptionalOf",
+ " abstract SatisfiedInGrandAncestor optionalSatisfiedInGrandAncestor();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public Optional<SatisfiedInGrandAncestor>",
+ " boundInAncestorSatisfiedInGrandAncestor() {",
+ " return Optional.<SatisfiedInGrandAncestor>empty();",
+ " }",
+ " }",
+ "}");
+ compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = GrandAncestorModule.class)",
+ "interface GrandAncestor {",
+ " Ancestor ancestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class GrandAncestorModule {",
+ " @Provides static SatisfiedInGrandAncestor provideSatisfiedInGrandAncestor() {",
+ " return new SatisfiedInGrandAncestor();",
+ " }",
+ "}"));
+ JavaFileObject generatedGrandAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerGrandAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Optional;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerGrandAncestor implements GrandAncestor {",
+ " protected DaggerGrandAncestor() {}",
+ "",
+ " protected abstract class AncestorImpl extends DaggerAncestor {",
+ " protected AncestorImpl() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final Optional<SatisfiedInGrandAncestor>",
+ " boundInAncestorSatisfiedInGrandAncestor() {",
+ " return Optional.of(",
+ " GrandAncestorModule_ProvideSatisfiedInGrandAncestorFactory",
+ " .provideSatisfiedInGrandAncestor());",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation =
+ compile(
+ filesToCompile.build()
+ , CompilerMode.JAVA7
+ );
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerGrandAncestor")
+ .hasSourceEquivalentTo(generatedGrandAncestor);
+ }
+
+ @Test
+ public void provisionOverInjection_providedInAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.ProvidedInAncestor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class ProvidedInAncestor {",
+ " @Inject",
+ " ProvidedInAncestor(String string) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " ProvidedInAncestor injectedInLeaf();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public ProvidedInAncestor injectedInLeaf() {",
+ " return new ProvidedInAncestor(getString());",
+ " }",
+ "",
+ " protected abstract String getString();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " static ProvidedInAncestor provideProvidedInAncestor() {",
+ " return new ProvidedInAncestor(\"static\");",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final ProvidedInAncestor injectedInLeaf() {",
+ " return AncestorModule_ProvideProvidedInAncestorFactory",
+ " .provideProvidedInAncestor();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void provisionOverInjection_providedInGrandAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.ProvidedInGrandAncestor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class ProvidedInGrandAncestor {",
+ " @Inject",
+ " ProvidedInGrandAncestor(String string) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " ProvidedInGrandAncestor injectedInLeaf();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public ProvidedInGrandAncestor injectedInLeaf() {",
+ " return new ProvidedInGrandAncestor(getString());",
+ " }",
+ "",
+ " protected abstract String getString();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = GrandAncestorModule.class)",
+ "interface GrandAncestor {",
+ " Ancestor ancestor();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.GrandAncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class GrandAncestorModule {",
+ " @Provides",
+ " static ProvidedInGrandAncestor provideProvidedInGrandAncestor() {",
+ " return new ProvidedInGrandAncestor(\"static\");",
+ " }",
+ "}"));
+ JavaFileObject generatedGrandAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerGrandAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerGrandAncestor implements GrandAncestor {",
+ " protected DaggerGrandAncestor() {}",
+ "",
+ " protected abstract class AncestorImpl extends DaggerAncestor {",
+ " protected AncestorImpl() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final ProvidedInGrandAncestor injectedInLeaf() {",
+ " return GrandAncestorModule_ProvideProvidedInGrandAncestorFactory",
+ " .provideProvidedInGrandAncestor();",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerGrandAncestor")
+ .hasSourceEquivalentTo(generatedGrandAncestor);
+ }
+
+ @Test
+ public void provisionOverInjection_indirectDependency() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.ProvidedInAncestor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class ProvidedInAncestor {",
+ " @Inject",
+ " ProvidedInAncestor(String string) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.InjectedInLeaf",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectedInLeaf {",
+ " @Inject",
+ " InjectedInLeaf(ProvidedInAncestor providedInAncestor) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " InjectedInLeaf injectedInLeaf();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public InjectedInLeaf injectedInLeaf() {",
+ " return new InjectedInLeaf((ProvidedInAncestor) getProvidedInAncestor());",
+ " }",
+ "",
+ " protected abstract String getString();",
+ "",
+ " protected Object getProvidedInAncestor() {",
+ " return new ProvidedInAncestor(getString());",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " static ProvidedInAncestor provideProvidedInAncestor() {",
+ " return new ProvidedInAncestor(\"static\");",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected final Object getProvidedInAncestor() {",
+ " return AncestorModule_ProvideProvidedInAncestorFactory",
+ " .provideProvidedInAncestor();",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void provisionOverInjection_prunedIndirectDependency() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "PrunedDependency");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.InjectsPrunedDependency",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectsPrunedDependency {",
+ " @Inject",
+ " InjectsPrunedDependency(PrunedDependency prunedDependency) {}",
+ "",
+ " private InjectsPrunedDependency() { }",
+ "",
+ " static InjectsPrunedDependency create() { return new InjectsPrunedDependency(); }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " InjectsPrunedDependency injectsPrunedDependency();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public InjectsPrunedDependency injectsPrunedDependency() {",
+ " return new InjectsPrunedDependency((PrunedDependency) getPrunedDependency());",
+ " }",
+ "",
+ " protected abstract Object getPrunedDependency();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RootModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.RootModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class RootModule {",
+ " @Provides",
+ " static InjectsPrunedDependency injectsPrunedDependency() {",
+ " return InjectsPrunedDependency.create();",
+ " }",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " @Deprecated",
+ " public Builder rootModule(RootModule rootModule) {",
+ " Preconditions.checkNotNull(rootModule);",
+ " return this;",
+ " }",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " private LeafImpl() {}",
+ "",
+ " @Override",
+ " protected Object getPrunedDependency() {",
+ " " + PRUNED_METHOD_BODY,
+ " }",
+ "",
+ " @Override",
+ " public InjectsPrunedDependency injectsPrunedDependency() {",
+ " return RootModule_InjectsPrunedDependencyFactory",
+ " .injectsPrunedDependency();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void provisionOverInjection_prunedDirectDependency_prunedInConcreteImplementation() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ // The binding for PrunedDependency will always exist, but will change from
+ // ModifiableBindingType.INJECTION to ModifiableBindingType.MISSING. We should correctly
+ // ignore this change leave the modifiable binding method alone
+ JavaFileObjects.forSourceLines(
+ "test.PrunedDependency",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class PrunedDependency {",
+ " @Inject PrunedDependency() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.InjectsPrunedDependency",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectsPrunedDependency {",
+ " @Inject",
+ " InjectsPrunedDependency(PrunedDependency prunedDependency) {}",
+ "",
+ " private InjectsPrunedDependency() { }",
+ "",
+ " static InjectsPrunedDependency create() { return new InjectsPrunedDependency(); }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " InjectsPrunedDependency injectsPrunedDependency();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public InjectsPrunedDependency injectsPrunedDependency() {",
+ " return new InjectsPrunedDependency((PrunedDependency) getPrunedDependency());",
+ " }",
+ "",
+ " protected Object getPrunedDependency() {",
+ " return new PrunedDependency();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RootModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.RootModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class RootModule {",
+ " @Provides",
+ " static InjectsPrunedDependency injectsPrunedDependency() {",
+ " return InjectsPrunedDependency.create();",
+ " }",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " @Deprecated",
+ " public Builder rootModule(RootModule rootModule) {",
+ " Preconditions.checkNotNull(rootModule);",
+ " return this;",
+ " }",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " private LeafImpl() {}",
+ "",
+ " @Override",
+ " public InjectsPrunedDependency injectsPrunedDependency() {",
+ " return RootModule_InjectsPrunedDependencyFactory",
+ " .injectsPrunedDependency();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void provisionOverInjection_prunedDirectDependency_prunedInAbstractImplementation() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ // The binding for PrunedDependency will always exist, but will change from
+ // ModifiableBindingType.INJECTION to ModifiableBindingType.MISSING. We should correctly
+ // ignore this change leave the modifiable binding method alone
+ JavaFileObjects.forSourceLines(
+ "test.PrunedDependency",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class PrunedDependency {",
+ " @Inject PrunedDependency() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.InjectsPrunedDependency",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectsPrunedDependency {",
+ " @Inject",
+ " InjectsPrunedDependency(PrunedDependency prunedDependency) {}",
+ "",
+ " private InjectsPrunedDependency() { }",
+ "",
+ " static InjectsPrunedDependency create() { return new InjectsPrunedDependency(); }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " InjectsPrunedDependency injectsPrunedDependency();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public InjectsPrunedDependency injectsPrunedDependency() {",
+ " return new InjectsPrunedDependency((PrunedDependency) getPrunedDependency());",
+ " }",
+ "",
+ " protected Object getPrunedDependency() {",
+ " return new PrunedDependency();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " static InjectsPrunedDependency injectsPrunedDependency() {",
+ " return InjectsPrunedDependency.create();",
+ " }",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " public final InjectsPrunedDependency injectsPrunedDependency() {",
+ " return AncestorModule_InjectsPrunedDependencyFactory",
+ " .injectsPrunedDependency();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " Ancestor ancestor();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Ancestor ancestor() {",
+ " return new AncestorImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class AncestorImpl extends DaggerAncestor {",
+ " private AncestorImpl() {}",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " private LeafImpl() {}",
+ // even though DaggerAncestor.LeafImpl.getPrunedDependency() was
+ // ModifiableBindingType.MISSING, it doesn't need to be reimplemented because there was
+ // a base implementation
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void productionSubcomponentAndModifiableFrameworkInstance() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Response", "ResponseDependency");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionSubcomponent;",
+ "import java.util.Set;",
+ "",
+ "@ProductionSubcomponent(modules = ResponseProducerModule.class)",
+ "interface Leaf {",
+ " ListenableFuture<Set<Response>> responses();",
+ "",
+ " @ProductionSubcomponent.Builder",
+ " interface Builder {",
+ " Leaf build();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.ResponseProducerModule",
+ "package test;",
+ "",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class ResponseProducerModule {",
+ " @Produces",
+ " @IntoSet",
+ " static Response response(ResponseDependency responseDependency) {",
+ " return new Response();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.internal.CancellationListener;",
+ "import dagger.producers.internal.Producers;",
+ "import dagger.producers.internal.SetProducer;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.Set;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf, CancellationListener {",
+ " private Producer<Set<Response>> responsesEntryPoint;",
+ " private Producer<Response> responseProducer;",
+ " private Producer<Set<Response>> setOfResponseProducer;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.responseProducer =",
+ " ResponseProducerModule_ResponseFactory.create(",
+ " getProductionImplementationExecutorProvider(),",
+ " getProductionComponentMonitorProvider(),",
+ " getResponseDependencyProducer());",
+ " this.setOfResponseProducer =",
+ " SetProducer.<Response>builder(1, 0)",
+ " .addProducer(responseProducer).build();",
+ " this.responsesEntryPoint =",
+ " Producers.entryPointViewOf(getSetOfResponseProducer(), this);",
+ " }",
+ "",
+ " @Override",
+ " public ListenableFuture<Set<Response>> responses() {",
+ " return responsesEntryPoint.get();",
+ " }",
+ "",
+ " protected abstract Provider<Executor>",
+ " getProductionImplementationExecutorProvider();",
+ "",
+ " protected abstract Provider<ProductionComponentMonitor>",
+ " getProductionComponentMonitorProvider();",
+ "",
+ " protected abstract Producer getResponseDependencyProducer();",
+ "",
+ " protected Producer getSetOfResponseProducer() {",
+ " return setOfResponseProducer;",
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
+ " Producers.cancel(getSetOfResponseProducer(), mayInterruptIfRunning);",
+ " Producers.cancel(responseProducer, mayInterruptIfRunning);",
+ " }",
+ "}");
+
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.ExecutorModule",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.MoreExecutors;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.Production;",
+ "import java.util.concurrent.Executor;",
+ "",
+ "@Module",
+ "final class ExecutorModule {",
+ " @Provides",
+ " @Production",
+ " static Executor executor() {",
+ " return MoreExecutors.directExecutor();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(",
+ " modules = {",
+ " ExecutorModule.class,",
+ " ResponseDependencyProducerModule.class,",
+ " RootMultibindingModule.class,",
+ " })",
+ "interface Root {",
+ " Leaf.Builder leaf();",
+ "",
+ " @ProductionComponent.Builder",
+ " interface Builder {",
+ " Root build();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.ResponseDependencyProducerModule",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.Futures;",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class ResponseDependencyProducerModule {",
+ " @Produces",
+ " static ListenableFuture<ResponseDependency> responseDependency() {",
+ " return Futures.immediateFuture(new ResponseDependency());",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.RootMultibindingModule",
+ "package test;",
+ "",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class RootMultibindingModule {",
+ " @Produces",
+ " @IntoSet",
+ " static Response response() {",
+ " return new Response();",
+ " }",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.InstanceFactory;",
+ "import dagger.internal.SetFactory;",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.internal.CancellationListener;",
+ "import dagger.producers.internal.DelegateProducer;",
+ "import dagger.producers.internal.Producers;",
+ "import dagger.producers.internal.SetProducer;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.Set;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root, CancellationListener {",
+ " private Provider<Executor> productionImplementationExecutorProvider;",
+ " private Provider<Root> rootProvider;",
+ " private Provider<ProductionComponentMonitor> monitorProvider;",
+ " private Producer<ResponseDependency> responseDependencyProducer;",
+ " private Producer<Response> responseProducer;",
+ "",
+ " private DaggerRoot() {",
+ " initialize();",
+ " }",
+ "",
+ " public static Root.Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.productionImplementationExecutorProvider =",
+ " DoubleCheck.provider((Provider) ExecutorModule_ExecutorFactory.create());",
+ " this.rootProvider = InstanceFactory.create((Root) this);",
+ " this.monitorProvider =",
+ " DoubleCheck.provider(",
+ " Root_MonitoringModule_MonitorFactory.create(",
+ " rootProvider,",
+ " SetFactory.<ProductionComponentMonitor.Factory>empty()));",
+ " this.responseDependencyProducer =",
+ " ResponseDependencyProducerModule_ResponseDependencyFactory.create(",
+ " productionImplementationExecutorProvider, monitorProvider);",
+ " this.responseProducer =",
+ " RootMultibindingModule_ResponseFactory.create(",
+ " productionImplementationExecutorProvider, monitorProvider);",
+ " }",
+ "",
+ " @Override",
+ " public Leaf.Builder leaf() {",
+ " return new LeafBuilder();",
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
+ " Producers.cancel(responseProducer, mayInterruptIfRunning);",
+ " Producers.cancel(responseDependencyProducer, mayInterruptIfRunning);",
+ " }",
+ "",
+ " private static final class Builder implements Root.Builder {",
+ " @Override",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " private final class LeafBuilder implements Leaf.Builder {",
+ " @Override",
+ " public Leaf build() {",
+ " return new LeafImpl();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf implements CancellationListener {",
+ " private Producer<Set<Response>> setOfResponseProducer = new DelegateProducer<>();",
+ "",
+ " private LeafImpl() {",
+ " configureInitialization();",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " DelegateProducer.setDelegate(",
+ " setOfResponseProducer,",
+ " SetProducer.<Response>builder(1, 1)",
+ " .addCollectionProducer(super.getSetOfResponseProducer())",
+ " .addProducer(DaggerRoot.this.responseProducer)",
+ " .build());",
+ " }",
+ "",
+ " @Override",
+ " protected Provider<Executor> getProductionImplementationExecutorProvider() {",
+ " return DaggerRoot.this.productionImplementationExecutorProvider;",
+ " }",
+ "",
+ " @Override",
+ " protected Provider<ProductionComponentMonitor>",
+ " getProductionComponentMonitorProvider() {",
+ " return DaggerRoot.this.monitorProvider;",
+ " }",
+ "",
+ " @Override",
+ " protected Producer getResponseDependencyProducer() {",
+ " return DaggerRoot.this.responseDependencyProducer;",
+ " }",
+ "",
+ " @Override",
+ " protected Producer getSetOfResponseProducer() {",
+ " return setOfResponseProducer;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void lazyOfModifiableBinding() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " Lazy<MissingInLeaf> lazy();",
+ " Provider<Lazy<MissingInLeaf>> providerOfLazy();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.ProviderOfLazy;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Lazy<MissingInLeaf> lazy() {",
+ " return DoubleCheck.lazy(getMissingInLeafProvider());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Lazy<MissingInLeaf>> providerOfLazy() {",
+ " return ProviderOfLazy.create(getMissingInLeafProvider());",
+ " }",
+ "",
+ " protected abstract Provider getMissingInLeafProvider();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class AncestorModule {",
+ " @Provides",
+ " static MissingInLeaf satisfiedInAncestor() { return new MissingInLeaf(); }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected final Provider getMissingInLeafProvider() {",
+ " return AncestorModule_SatisfiedInAncestorFactory.create();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void missingBindingAccessInLeafAndAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(
+ filesToCompile, "Missing", "DependsOnMissing", "ProvidedInAncestor_InducesSetBinding");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.Provides;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " static DependsOnMissing test(",
+ " Missing missing,",
+ " Provider<Missing> missingProvider,",
+ " ProvidedInAncestor_InducesSetBinding missingInLeaf) {",
+ " return new DependsOnMissing();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoSet",
+ " static Object unresolvedSetBinding(",
+ " Missing missing, Provider<Missing> missingProvider) {",
+ " return new Object();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " DependsOnMissing instance();",
+ " Provider<DependsOnMissing> frameworkInstance();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private Provider<DependsOnMissing> testProvider;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.testProvider =",
+ " LeafModule_TestFactory.create(",
+ " getMissingProvider(), getProvidedInAncestor_InducesSetBindingProvider());",
+ " }",
+ "",
+ " @Override",
+ " public DependsOnMissing instance() {",
+ " return LeafModule_TestFactory.test(",
+ // TODO(b/117833324): remove these unnecessary casts
+ " (Missing) getMissing(),",
+ " getMissingProvider(),",
+ " (ProvidedInAncestor_InducesSetBinding)",
+ " getProvidedInAncestor_InducesSetBinding());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<DependsOnMissing> frameworkInstance() {",
+ " return testProvider;",
+ " }",
+ "",
+ " protected abstract Object getMissing();",
+ " protected abstract Provider getMissingProvider();",
+ " protected abstract Object getProvidedInAncestor_InducesSetBinding();",
+ " protected abstract Provider getProvidedInAncestor_InducesSetBindingProvider();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.Provides;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface AncestorModule {",
+ " @Provides",
+ " static ProvidedInAncestor_InducesSetBinding providedInAncestor(",
+ " Set<Object> setThatInducesMissingBindingInChildSubclassImplementation) {",
+ " return new ProvidedInAncestor_InducesSetBinding();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoSet",
+ " static Object setContribution() {",
+ " return new Object();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.DelegateFactory;",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.SetFactory;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private Provider<Object> unresolvedSetBindingProvider;",
+ " private Provider<Set<Object>> setOfObjectProvider;",
+ " private Provider<ProvidedInAncestor_InducesSetBinding> ",
+ " providedInAncestorProvider = ",
+ " new DelegateFactory<>();",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " @Override",
+ " protected void configureInitialization() {",
+ " super.configureInitialization();",
+ " initialize();",
+ " }",
+ "",
+ " private Object getUnresolvedSetBinding() {",
+ " return LeafModule_UnresolvedSetBindingFactory.unresolvedSetBinding(",
+ // TODO(b/117833324): remove this unnecessary cast
+ " (Missing) getMissing(), getMissingProvider());",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.unresolvedSetBindingProvider =",
+ " LeafModule_UnresolvedSetBindingFactory.create(getMissingProvider());",
+ " this.setOfObjectProvider =",
+ " SetFactory.<Object>builder(2, 0)",
+ " .addProvider(AncestorModule_SetContributionFactory.create())",
+ " .addProvider(unresolvedSetBindingProvider)",
+ " .build();",
+ " DelegateFactory.setDelegate(",
+ " providedInAncestorProvider,",
+ " AncestorModule_ProvidedInAncestorFactory.create(getSetOfObjectProvider()));",
+ " }",
+ "",
+ " protected Set<Object> getSetOfObject() {",
+ " return ImmutableSet.<Object>of(",
+ " AncestorModule_SetContributionFactory.setContribution(),",
+ " getUnresolvedSetBinding());",
+ " }",
+ "",
+ " @Override",
+ " protected final Object getProvidedInAncestor_InducesSetBinding() {",
+ " return AncestorModule_ProvidedInAncestorFactory.providedInAncestor(",
+ " getSetOfObject());",
+ " }",
+ "",
+ " protected Provider<Set<Object>> getSetOfObjectProvider() {",
+ " return setOfObjectProvider;",
+ " }",
+ "",
+ " @Override",
+ " protected final Provider getProvidedInAncestor_InducesSetBindingProvider() {",
+ " return providedInAncestorProvider;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void subcomponentBuilders() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "InducesDependenciesOnBuilderFields");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " private final Object object;",
+ "",
+ " LeafModule(Object object) {",
+ " this.object = object;",
+ " }",
+ "",
+ " @Provides",
+ " Object fromModule() {",
+ " return object;",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.MultibindingsModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "interface MultibindingsModule {",
+ " @Binds",
+ " @IntoSet",
+ " String string(String string);",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = {LeafModule.class, MultibindingsModule.class})",
+ "interface Leaf {",
+ " int bindsInstance();",
+ " Object fromModule();",
+ " InducesDependenciesOnBuilderFields inducesDependenciesOnBuilderFields();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder bindsInstance(int boundInstance);",
+ " @BindsInstance Builder inducedInSubclass(String induced);",
+ " Builder module(LeafModule module);",
+ "",
+ " Leaf build();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private Integer bindsInstance;",
+ " private LeafModule leafModule;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization(",
+ " LeafModule leafModuleParam, Integer bindsInstanceParam) {",
+ " this.bindsInstance = bindsInstanceParam;",
+ " this.leafModule = leafModuleParam;",
+ " }",
+ "",
+ " @Override",
+ " public int bindsInstance() {",
+ " return bindsInstance;",
+ " }",
+ "",
+ " @Override",
+ " public Object fromModule() {",
+ " return LeafModule_FromModuleFactory.fromModule(leafModule());",
+ " }",
+ "",
+ " @Override",
+ " public abstract InducesDependenciesOnBuilderFields",
+ " inducesDependenciesOnBuilderFields();",
+ "",
+ " protected LeafModule leafModule() {",
+ " return leafModule;",
+ " }",
+ "",
+ " public abstract static class Builder implements Leaf.Builder {",
+ " protected Integer bindsInstance;",
+ " protected String inducedInSubclass;",
+ " protected LeafModule leafModule;",
+ "",
+ " @Override",
+ " public Builder bindsInstance(int boundInstance) {",
+ " this.bindsInstance = Preconditions.checkNotNull(boundInstance);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public Builder inducedInSubclass(String induced) {",
+ " this.inducedInSubclass = Preconditions.checkNotNull(induced);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public Builder module(LeafModule module) {",
+ " this.leafModule = Preconditions.checkNotNull(module);",
+ " return this;",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = MultibindingInducingModule.class)",
+ "interface Ancestor {",
+ " Leaf.Builder leaf();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.MultibindingInducingModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import dagger.Provides;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface MultibindingInducingModule {",
+ " @Provides",
+ " static InducesDependenciesOnBuilderFields induce(",
+ " Set<String> multibindingWithBuilderFieldDeps) { ",
+ " return new InducesDependenciesOnBuilderFields();",
+ " }",
+ "",
+ " @Multibinds",
+ " Set<String> multibinding();",
+ "}"));
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " @Override",
+ " public abstract Leaf.Builder leaf();",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " private String inducedInSubclass;",
+ "",
+ " protected LeafImpl() {}",
+ "",
+ " protected void configureInitialization(",
+ " LeafModule leafModule,",
+ " Integer bindsInstance,",
+ " String inducedInSubclassParam) {",
+ " this.inducedInSubclass = inducedInSubclassParam;",
+ " configureInitialization(leafModule, bindsInstance);",
+ " }",
+ "",
+ " protected Set<String> getSetOfString() {",
+ " return ImmutableSet.<String>of(inducedInSubclass);",
+ " }",
+ "",
+ " @Override",
+ " public final InducesDependenciesOnBuilderFields",
+ " inducesDependenciesOnBuilderFields() {",
+ " return MultibindingInducingModule_InduceFactory.induce(getSetOfString());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " Ancestor ancestor();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Ancestor ancestor() {",
+ " return new AncestorImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class AncestorImpl extends DaggerAncestor {",
+ " private AncestorImpl() {}",
+ "",
+ " @Override",
+ " public Leaf.Builder leaf() {",
+ " return new LeafBuilder();",
+ " }",
+ "",
+ " private final class LeafBuilder extends DaggerLeaf.Builder {",
+ " @Override",
+ " public Leaf build() {",
+ // TODO(b/117833324): Can we stick the validations into a method on the base class
+ // builder so that the contents of this method are just call to that and then new
+ // FooImpl? But repeated modules may make this more complicated, since those *should*
+ // be null
+ " Preconditions.checkBuilderRequirement(bindsInstance, Integer.class);",
+ " Preconditions.checkBuilderRequirement(inducedInSubclass, String.class);",
+ " Preconditions.checkBuilderRequirement(leafModule, LeafModule.class);",
+ " return new LeafImpl(leafModule, bindsInstance, inducedInSubclass);",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " private final LeafModule leafModule;",
+ "",
+ " private LeafImpl(",
+ " LeafModule leafModuleParam,",
+ " Integer bindsInstance,",
+ " String inducedInSubclass) {",
+ " this.leafModule = leafModuleParam;",
+ " configureInitialization(leafModuleParam, bindsInstance, inducedInSubclass);",
+ " }",
+ "",
+ " @Override",
+ " protected LeafModule leafModule() {",
+ " return leafModule;",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void subcomponentBuilders_moduleWithUnusedInstanceBindings() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Used", "Unused");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithUsedBinding",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ModuleWithUsedBinding {",
+ " @Provides",
+ " Used used() {",
+ " return new Used();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithUnusedBinding",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ModuleWithUnusedBinding {",
+ " @Provides",
+ " Unused unused() {",
+ " return new Unused();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = {ModuleWithUsedBinding.class, ModuleWithUnusedBinding.class})",
+ "interface Leaf {",
+ " Used used();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Builder setUsed(ModuleWithUsedBinding module);",
+ " Builder setUnused(ModuleWithUnusedBinding module);",
+ " Leaf build();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private ModuleWithUsedBinding moduleWithUsedBinding;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization(",
+ " ModuleWithUsedBinding moduleWithUsedBindingParam) {",
+ " this.moduleWithUsedBinding = moduleWithUsedBindingParam;",
+ " }",
+ "",
+ " @Override",
+ " public Used used() {",
+ " return ModuleWithUsedBinding_UsedFactory.used(",
+ " moduleWithUsedBinding());",
+ " }",
+ "",
+ " protected ModuleWithUsedBinding moduleWithUsedBinding() {",
+ " return moduleWithUsedBinding;",
+ " }",
+ "",
+ " public abstract static class Builder implements Leaf.Builder {",
+ " protected ModuleWithUsedBinding moduleWithUsedBinding;",
+ " protected ModuleWithUnusedBinding moduleWithUnusedBinding;",
+ "",
+ " @Override",
+ " public Builder setUsed(ModuleWithUsedBinding module) {",
+ " this.moduleWithUsedBinding = Preconditions.checkNotNull(module);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public Builder setUnused(ModuleWithUnusedBinding module) {",
+ " this.moduleWithUnusedBinding = Preconditions.checkNotNull(module);",
+ " return this;",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " Leaf.Builder leaf();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Leaf.Builder leaf() {",
+ " return new LeafBuilder();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " private final class LeafBuilder extends DaggerLeaf.Builder {",
+ " @Override",
+ " public Leaf build() {",
+ " if (moduleWithUsedBinding == null) {",
+ " this.moduleWithUsedBinding = new ModuleWithUsedBinding();",
+ " }",
+ // ModuleWithUnusedBinding is not verified since it's not used
+ " return new LeafImpl(moduleWithUsedBinding);",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " private final ModuleWithUsedBinding moduleWithUsedBinding;",
+ "",
+ " private LeafImpl(ModuleWithUsedBinding moduleWithUsedBindingParam) {",
+ " this.moduleWithUsedBinding = moduleWithUsedBindingParam;",
+ " configureInitialization(moduleWithUsedBindingParam);",
+ " }",
+ "",
+ " @Override",
+ " protected ModuleWithUsedBinding moduleWithUsedBinding() {",
+ " return moduleWithUsedBinding;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void subcomponentBuilders_repeatedModule() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.RepeatedModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class RepeatedModule {",
+ " @Provides",
+ " int i() {",
+ " return 1;",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = RepeatedModule.class)",
+ "interface Leaf {",
+ " int i();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Builder repeatedModule(RepeatedModule repeatedModule);",
+ " Leaf build();",
+ " }",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private RepeatedModule repeatedModule;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization(RepeatedModule repeatedModuleParam) {",
+ " this.repeatedModule = repeatedModuleParam;",
+ " }",
+ "",
+ " @Override",
+ " public int i() {",
+ " return repeatedModule().i();",
+ " }",
+ "",
+ " protected RepeatedModule repeatedModule() {",
+ " return repeatedModule;",
+ " }",
+ "",
+ " public abstract static class Builder implements Leaf.Builder {",
+ " protected RepeatedModule repeatedModule;",
+ "",
+ " @Override",
+ " public Builder repeatedModule(RepeatedModule repeatedModule) {",
+ " this.repeatedModule = Preconditions.checkNotNull(repeatedModule);",
+ " return this;",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RepeatedModule.class)",
+ "interface Root {",
+ " Leaf.Builder leaf();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private final RepeatedModule repeatedModule;",
+ "",
+ " private DaggerRoot(RepeatedModule repeatedModuleParam) {",
+ " this.repeatedModule = repeatedModuleParam;",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Leaf.Builder leaf() {",
+ " return new LeafBuilder();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private RepeatedModule repeatedModule;",
+ "",
+ " private Builder() {}",
+ "",
+ " public Builder repeatedModule(RepeatedModule repeatedModule) {",
+ " this.repeatedModule = Preconditions.checkNotNull(repeatedModule);",
+ " return this;",
+ " }",
+ "",
+ " public Root build() {",
+ " if (repeatedModule == null) {",
+ " this.repeatedModule = new RepeatedModule();",
+ " }",
+ " return new DaggerRoot(repeatedModule);",
+ " }",
+ " }",
+ "",
+ " private final class LeafBuilder extends DaggerLeaf.Builder {",
+ " @Override",
+ " public LeafBuilder repeatedModule(RepeatedModule repeatedModule) {",
+ " throw new UnsupportedOperationException(",
+ " String.format(",
+ " \"%s cannot be set because it is inherited from the enclosing component\",",
+ " RepeatedModule.class.getCanonicalName()));",
+ " }",
+ "",
+ " @Override",
+ " public Leaf build() {",
+ " return new LeafImpl();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " private LeafImpl() {",
+ " configureInitialization(null);",
+ " }",
+ "",
+ " @Override",
+ " protected RepeatedModule repeatedModule() {",
+ " return DaggerRoot.this.repeatedModule;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void bindsWithMissingDependency() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Binds;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Binds Object missingBindsDependency(MissingInLeaf missing);",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Object bindsWithMissingDependencyInLeaf();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract Object bindsWithMissingDependencyInLeaf();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.MissingInLeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface MissingInLeafModule {",
+ " @Provides",
+ " static MissingInLeaf boundInRoot() {",
+ " return new MissingInLeaf();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = MissingInLeafModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " private LeafImpl() {}",
+ "",
+ " @Override",
+ " public Object bindsWithMissingDependencyInLeaf() {",
+ " return MissingInLeafModule_BoundInRootFactory.boundInRoot();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void bindsWithMissingDependency_pruned() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Binds;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Binds Object missingBindsDependency(MissingInLeaf missing);",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.DependsOnBindsWithMissingDep",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class DependsOnBindsWithMissingDep {",
+ " @Inject DependsOnBindsWithMissingDep(Object bindsWithMissingDep) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " DependsOnBindsWithMissingDep DependsOnBindsWithMissingDep();",
+ "}"));
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public DependsOnBindsWithMissingDep DependsOnBindsWithMissingDep() {",
+ " return new DependsOnBindsWithMissingDep(getObject());",
+ " }",
+ "",
+ " protected abstract Object getObject();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.PrunesInjectConstructorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface PrunesInjectConstructorModule {",
+ " @Provides",
+ " static DependsOnBindsWithMissingDep pruneInjectConstructor() {",
+ " return new DependsOnBindsWithMissingDep(new Object());",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = PrunesInjectConstructorModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " private LeafImpl() {}",
+ "",
+ " @Override",
+ " protected Object getObject() {",
+ " " + PRUNED_METHOD_BODY,
+ " }",
+ "",
+ " @Override",
+ " public DependsOnBindsWithMissingDep DependsOnBindsWithMissingDep() {",
+ " return PrunesInjectConstructorModule_PruneInjectConstructorFactory",
+ " .pruneInjectConstructor();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void modifiedProducerFromProvider() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "DependsOnModifiedProducerFromProvider");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.Provides;",
+ "import java.util.Set;",
+ "",
+ "@ProducerModule",
+ "interface LeafModule {",
+ " @Produces",
+ " static DependsOnModifiedProducerFromProvider dependsOnModified(Set<String> set) {",
+ " return new DependsOnModifiedProducerFromProvider();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.ProductionSubcomponent;",
+ "import java.util.Set;",
+ "",
+ "@ProductionSubcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Producer<DependsOnModifiedProducerFromProvider>",
+ " dependsOnModifiedProducerFromProvider();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.internal.CancellationListener;",
+ "import dagger.producers.internal.Producers;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.Set;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf, CancellationListener {",
+ " private Producer<DependsOnModifiedProducerFromProvider>",
+ " dependsOnModifiedProducerFromProviderEntryPoint;",
+ " private Producer<DependsOnModifiedProducerFromProvider> dependsOnModifiedProducer;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.dependsOnModifiedProducer =",
+ " LeafModule_DependsOnModifiedFactory.create(",
+ " getProductionImplementationExecutorProvider(),",
+ " getProductionComponentMonitorProvider(),",
+ " getSetOfStringProducer());",
+ " this.dependsOnModifiedProducerFromProviderEntryPoint =",
+ " Producers.entryPointViewOf(dependsOnModifiedProducer, this);",
+ " }",
+ "",
+ " @Override",
+ " public Producer<DependsOnModifiedProducerFromProvider> ",
+ " dependsOnModifiedProducerFromProvider() {",
+ " return dependsOnModifiedProducerFromProviderEntryPoint;",
+ " }",
+ "",
+ " protected abstract Provider<Executor> ",
+ " getProductionImplementationExecutorProvider();",
+ "",
+ " protected abstract Provider<ProductionComponentMonitor>",
+ " getProductionComponentMonitorProvider();",
+ "",
+ " protected abstract Producer<Set<String>> getSetOfStringProducer();",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
+ " Producers.cancel(dependsOnModifiedProducer, mayInterruptIfRunning);",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.RootModule",
+ "package test;",
+ "",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.Production;",
+ "import java.util.Set;",
+ "import java.util.concurrent.Executor;",
+ "",
+ "@Module",
+ "interface RootModule {",
+ " @Provides",
+ " @IntoSet",
+ " static String induceModificationInLeaf() {",
+ " return new String();",
+ " }",
+ "",
+ " @Provides",
+ " @Production",
+ " static Executor productionExecutor() {",
+ " return null;",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RootModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"));
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ "import dagger.internal.DelegateFactory;",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.InstanceFactory;",
+ "import dagger.internal.SetFactory;",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.internal.CancellationListener;",
+ "import dagger.producers.internal.DelegateProducer;",
+ "import dagger.producers.internal.Producers;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.Set;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private DaggerRoot() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Root create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Leaf leaf() {",
+ " return new LeafImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public Root build() {",
+ " return new DaggerRoot();",
+ " }",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf implements CancellationListener {",
+ " private Provider<Executor> productionImplementationExecutorProvider =",
+ " new DelegateFactory<>();",
+ " private Provider<Leaf> leafProvider;",
+ " private Provider<ProductionComponentMonitor> monitorProvider =",
+ " new DelegateFactory<>();",
+ " private Provider<Set<String>> setOfStringProvider;",
+ " private Producer<Set<String>> setOfStringProducer = new DelegateProducer<>();",
+ "",
+ " private LeafImpl() {",
+ " configureInitialization();",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " DelegateFactory.setDelegate(",
+ " productionImplementationExecutorProvider,",
+ " DoubleCheck.provider(",
+ " (Provider) RootModule_ProductionExecutorFactory.create()));",
+ " this.leafProvider = InstanceFactory.create((Leaf) this);",
+ " DelegateFactory.setDelegate(",
+ " monitorProvider,",
+ " DoubleCheck.provider(",
+ " Leaf_MonitoringModule_MonitorFactory.create(",
+ " leafProvider,",
+ " getSetOfProductionComponentMonitorFactoryProvider())));",
+ " this.setOfStringProvider =",
+ " SetFactory.<String>builder(1, 0)",
+ " .addProvider(RootModule_InduceModificationInLeafFactory.create())",
+ " .build();",
+ " DelegateProducer.setDelegate(",
+ " setOfStringProducer,",
+ " Producers.producerFromProvider(getSetOfStringProvider()));",
+ " }",
+ "",
+ " @Override",
+ " protected Provider<Executor> getProductionImplementationExecutorProvider() {",
+ " return productionImplementationExecutorProvider;",
+ " }",
+ "",
+ " protected Provider<Set<ProductionComponentMonitor.Factory>> ",
+ " getSetOfProductionComponentMonitorFactoryProvider() {",
+ " return SetFactory.<ProductionComponentMonitor.Factory>empty();",
+ " }",
+ "",
+ " @Override",
+ " protected Provider<ProductionComponentMonitor> ",
+ " getProductionComponentMonitorProvider() {",
+ " return monitorProvider;",
+ " }",
+ "",
+ " protected Provider<Set<String>> getSetOfStringProvider() {",
+ " return setOfStringProvider;",
+ " }",
+ "",
+ " @Override",
+ " protected Producer<Set<String>> getSetOfStringProducer() {",
+ " return setOfStringProducer;",
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
+ " super.onProducerFutureCancelled(mayInterruptIfRunning);",
+ " Producers.cancel(getSetOfStringProducer(), mayInterruptIfRunning);",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .hasSourceEquivalentTo(generatedRoot);
+ }
+
+ @Test
+ public void modifiableBindingMethods_namesDedupedAcrossImplementations() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "foo.Thing",
+ "package foo;",
+ "", // force multi-line format
+ "public interface Thing extends CharSequence {}"),
+ JavaFileObjects.forSourceLines(
+ "bar.Thing",
+ "package bar;",
+ "", // force multi-line format
+ "public interface Thing extends Runnable {}"),
+ JavaFileObjects.forSourceLines(
+ "test.WillInduceSetOfRunnable",
+ "package test;",
+ "", // force multi-line format
+ "class WillInduceSetOfRunnable {}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Provides",
+ " static CharSequence depOnFooThing(foo.Thing thing) {",
+ " return thing.toString();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoSet",
+ " static Runnable depOnBarThing(bar.Thing thing) {",
+ " return () -> {};",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " CharSequence inducesFoo();",
+ " WillInduceSetOfRunnable willInduceSetOfRunnable();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ "import foo.Thing;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public CharSequence inducesFoo() {",
+ " return LeafModule_DepOnFooThingFactory.depOnFooThing(getThing());",
+ " }",
+ "",
+ " @Override",
+ " public abstract WillInduceSetOfRunnable willInduceSetOfRunnable();",
+ "",
+ " protected abstract Thing getThing();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface AncestorModule {",
+ " @Provides",
+ " static WillInduceSetOfRunnable induce(Set<Runnable> set) {",
+ " return null;",
+ " }",
+ "",
+ " @Multibinds Set<Runnable> runnables();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " Leaf leaf();",
+ "}"));
+
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import bar.Thing;",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class LeafImpl extends DaggerLeaf {",
+ " protected LeafImpl() {}",
+ "",
+ " private Runnable getDepOnBarThing() {",
+ " return LeafModule_DepOnBarThingFactory.depOnBarThing(getThing2());",
+ " }",
+ "",
+ " protected abstract Thing getThing2();",
+ "",
+ " protected Set<Runnable> getSetOfRunnable() {",
+ " return ImmutableSet.<Runnable>of(getDepOnBarThing());",
+ " }",
+ "",
+ " @Override",
+ " public final WillInduceSetOfRunnable willInduceSetOfRunnable() {",
+ " return AncestorModule_InduceFactory.induce(getSetOfRunnable());",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ /**
+ * This test verifies that Dagger can find the appropriate child subcomponent
+ * super-implementation, even if it is not enclosed in the current component's
+ * super-implementation. This can happen if a subcomponent is installed with a module's {@code
+ * subcomponents} attribute, but the binding is not accessed in a super-implementation. To exhibit
+ * this, we use multibindings that reference the pruned subcomponent, but make the multibinding
+ * also unresolved in the base implementation. An ancestor component defines a binding that
+ * depends on the multibinding, which induces the previously unresolved multibinding
+ * contributions, which itself induces the previously unresolved subcomponent.
+ */
+ @Test
+ public void subcomponentInducedFromAncestor() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Inducer");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.InducedSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface InducedSubcomponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " InducedSubcomponent build();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.MaybeLeaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = InducedSubcomponentModule.class)",
+ "interface MaybeLeaf {",
+ " Inducer inducer();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.MaybeLeaf",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module(subcomponents = InducedSubcomponent.class)",
+ "interface InducedSubcomponentModule {",
+ " @Provides",
+ " @IntoSet",
+ " static Object inducedSet(InducedSubcomponent.Builder builder) {",
+ " return new Object();",
+ " }",
+ "}"));
+
+ JavaFileObject generatedMaybeLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerMaybeLeaf implements MaybeLeaf {",
+ " protected DaggerMaybeLeaf() {}",
+ "",
+ " @Override",
+ " public abstract Inducer inducer();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerMaybeLeaf")
+ .hasSourceEquivalentTo(generatedMaybeLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface AncestorModule {",
+ " @Provides",
+ " static Inducer inducer(Set<Object> set) {",
+ " return null;",
+ " }",
+ "",
+ " @Multibinds Set<Object> set();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AncestorModule.class)",
+ "interface Ancestor {",
+ " MaybeLeaf noLongerLeaf();",
+ "}"));
+
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import dagger.internal.GenerationOptions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected DaggerAncestor() {}",
+ "",
+ " protected abstract class MaybeLeafImpl extends DaggerMaybeLeaf {",
+ " protected MaybeLeafImpl() {}",
+ "",
+ " private Object getInducedSet() {",
+ " return InducedSubcomponentModule_InducedSetFactory.inducedSet(",
+ // TODO(b/117833324): remove this unnecessary cast
+ " (InducedSubcomponent.Builder) getInducedSubcomponentBuilder());",
+ " }",
+ "",
+ " protected abstract Object getInducedSubcomponentBuilder();",
+ "",
+ " protected Set<Object> getSetOfObject() {",
+ " return ImmutableSet.<Object>of(getInducedSet());",
+ " }",
+ "",
+ " @Override",
+ " public final Inducer inducer() {",
+ " return AncestorModule_InducerFactory.inducer(getSetOfObject());",
+ " }",
+ "",
+ " protected abstract class InducedSubcomponentImpl extends",
+ " DaggerInducedSubcomponent {",
+ // ^ Note that this is DaggerInducedSubcomponent, not
+ // DaggerMaybeLeaf.InducedSubcomponentImpl
+ " protected InducedSubcomponentImpl() {}",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .hasSourceEquivalentTo(generatedAncestor);
+ }
+
+ @Test
+ public void rootScopedAtInjectConstructor_effectivelyMissingInSubcomponent() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "ProvidesMethodRootScoped");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.RootScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "public @interface RootScope {}"),
+ JavaFileObjects.forSourceLines(
+ "test.AtInjectRootScoped",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "@RootScope",
+ "class AtInjectRootScoped {",
+ " @Inject AtInjectRootScoped() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " AtInjectRootScoped shouldBeEffectivelyMissingInLeaf();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public abstract AtInjectRootScoped shouldBeEffectivelyMissingInLeaf();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@RootScope",
+ "@Component",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"));
+
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " @Override",
+ " public AtInjectRootScoped shouldBeEffectivelyMissingInLeaf() {",
+ " return DaggerRoot.this.atInjectRootScopedProvider.get();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .containsElementsIn(generatedRoot);
+ }
+
+ @Test
+ public void prunedModuleWithInstanceState() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Pruned");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Modified",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Modified {",
+ " @Inject Modified(Pruned pruned) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class LeafModule {",
+ " @Provides",
+ " Pruned pruned() {",
+ " return new Pruned();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Modified modified();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected DaggerLeaf() {}",
+ "",
+ " @Override",
+ " public Modified modified() {",
+ " return new Modified(LeafModule_PrunedFactory.pruned(leafModule()));",
+ " }",
+ "",
+ " protected abstract LeafModule leafModule();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.RootModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class RootModule {",
+ " @Provides",
+ " static Modified modified() {",
+ " return new Modified(null);",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RootModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"));
+
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " @Override",
+ " public Modified modified() {",
+ " return RootModule_ModifiedFactory.modified();",
+ " }",
+ "",
+ " @Override",
+ " protected LeafModule leafModule() {",
+ " return null;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .containsElementsIn(generatedRoot);
+ }
+
+ @Test
+ public void modifiableCycles() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class A {",
+ " @Inject A(B b) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class B {",
+ " @Inject B(Provider<A> a) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " Provider<A> frameworkInstanceCycle();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import dagger.internal.DelegateFactory;",
+ "import dagger.internal.GenerationOptions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private Provider<A> aProvider;",
+ " private Provider<B> bProvider;",
+ "",
+ " protected DaggerLeaf() {}",
+ "",
+ " protected void configureInitialization() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.aProvider = new DelegateFactory<>();",
+ " this.bProvider = B_Factory.create(frameworkInstanceCycle());",
+ " DelegateFactory.setDelegate(aProvider, A_Factory.create(getBProvider()));",
+ " }",
+ "",
+ " @Override",
+ " public Provider<A> frameworkInstanceCycle() {",
+ " return aProvider;",
+ " }",
+ "",
+ " protected Provider getBProvider() {",
+ " return bProvider;",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .hasSourceEquivalentTo(generatedLeaf);
+ }
+
+ /**
+ * This tests a regression case where the component builder in the base implementation used one
+ * set of disambiguated names from all of the {@link ComponentDescriptor#requirements()}, and the
+ * final implementation used a different set of disambiguated names from the resolved {@link
+ * BindingGraph#componentRequirements()}. This resulted in generated output that didn't compile,
+ * as the builder implementation attempted to use the new names in validation, which didn't line
+ * up with the old names.
+ */
+ @Test
+ public void componentBuilderFields_consistencyAcrossImplementations() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "a.Mod",
+ "package a;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Named;",
+ "",
+ "@Module",
+ "public class Mod {",
+ " @Provides",
+ " @Named(\"a\")",
+ " int i() { return 0; }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "b.Mod",
+ "package b;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Named;",
+ "",
+ "@Module",
+ "public class Mod {",
+ " @Provides",
+ " @Named(\"b\")",
+ " int i() { return 0; }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "c.Mod",
+ "package c;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Named;",
+ "",
+ "@Module",
+ "public class Mod {",
+ " @Provides",
+ " @Named(\"c\")",
+ " int i() { return 0; }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.HasUnusedModuleLeaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Named;",
+ "",
+ "@Subcomponent(modules = {a.Mod.class, b.Mod.class, c.Mod.class})",
+ "interface HasUnusedModuleLeaf {",
+ " @Named(\"a\") int a();",
+ // b omitted intentionally
+ " @Named(\"c\") int c();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Builder setAMod(a.Mod mod);",
+ " Builder setBMod(b.Mod mod);",
+ " Builder setCMod(c.Mod mod);",
+ " HasUnusedModuleLeaf build();",
+ " }",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import a.Mod;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerHasUnusedModuleLeaf implements HasUnusedModuleLeaf {",
+ " public abstract static class Builder implements HasUnusedModuleLeaf.Builder {",
+ " protected Mod mod;",
+ " protected b.Mod mod2;",
+ " protected c.Mod mod3;",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerHasUnusedModuleLeaf")
+ .containsElementsIn(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Root {",
+ " HasUnusedModuleLeaf.Builder leaf();",
+ "}"));
+
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ "import a.Mod;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " private final class HasUnusedModuleLeafBuilder",
+ " extends DaggerHasUnusedModuleLeaf.Builder {",
+ " @Override",
+ " public HasUnusedModuleLeaf build() {",
+ " if (mod == null) {",
+ " this.mod = new Mod();",
+ " }",
+ // Before this regression was fixed, `mod3` was instead `mod2`, since the `b.Mod` was
+ // pruned from the graph and did not need validation.
+ " if (mod3 == null) {",
+ " this.mod3 = new c.Mod();",
+ " }",
+ " return new HasUnusedModuleLeafImpl(mod, mod3);",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .containsElementsIn(generatedRoot);
+ }
+
+ @Test
+ public void dependencyExpressionCasting() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.PublicType",
+ "package test;",
+ "", //
+ "public class PublicType {}"),
+ JavaFileObjects.forSourceLines(
+ "test.ModifiableNonPublicSubclass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class ModifiableNonPublicSubclass extends PublicType {",
+ " @Inject ModifiableNonPublicSubclass() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Parameterized",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Parameterized<T extends PublicType> {",
+ " @Inject Parameterized(T t) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " Parameterized<ModifiableNonPublicSubclass> parameterizedWithNonPublicSubtype();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " @Override",
+ " public Parameterized<ModifiableNonPublicSubclass> ",
+ " parameterizedWithNonPublicSubtype() {",
+ " return Parameterized_Factory.newInstance(",
+ " (ModifiableNonPublicSubclass) getModifiableNonPublicSubclass());",
+ " }",
+ "",
+ " protected Object getModifiableNonPublicSubclass() {",
+ " return new ModifiableNonPublicSubclass();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .containsElementsIn(generatedLeaf);
+ }
+
+ @Test
+ public void multipleComponentMethodsForSameBindingRequest() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " String string1();",
+ " String string2();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " @Override",
+ " public final String string2() {",
+ " return string1();",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .containsElementsIn(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.RootModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface RootModule {",
+ " @Provides",
+ " static String string() {",
+ " return new String();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RootModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"));
+
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " protected final class LeafImpl extends DaggerLeaf {",
+ " private LeafImpl() {}",
+ "",
+ " @Override",
+ " public String string1() {",
+ " return RootModule_StringFactory.string();",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .containsElementsIn(generatedRoot);
+ }
+
+ @Test
+ public void boundInstanceUsedOnlyInInitialize() {
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent",
+ "interface Sub {",
+ " Provider<String> stringProvider();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " @BindsInstance",
+ " Builder string(String string);",
+ " Sub build();",
+ " }",
+ "}");
+
+ JavaFileObject generated =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.internal.InstanceFactory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerSub implements Sub {",
+ " private Provider<String> stringProvider;",
+ "",
+ " protected DaggerSub() {}",
+ "",
+ " protected void configureInitialization(String stringParam) {",
+ " initialize(stringParam);",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final String stringParam) {",
+ " this.stringProvider = InstanceFactory.create(stringParam);",
+ " }",
+ "",
+ " @Override",
+ " public Provider<String> stringProvider() {",
+ " return stringProvider;",
+ " }",
+ "}");
+
+ Compilation compilation = compile(ImmutableList.of(subcomponent));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSub")
+ .containsElementsIn(generated);
+ }
+
+ @Test
+ public void packagePrivate_derivedFromFrameworkInstance_ComponentMethod() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.PackagePrivate",
+ "package test;",
+ "",
+ "import dagger.Reusable;",
+ "import javax.inject.Inject;",
+ "",
+ "@Reusable", // Use @Reusable to force a framework field
+ "class PackagePrivate {",
+ " @Inject PackagePrivate() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Leaf {",
+ " PackagePrivate packagePrivate();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " private Provider<PackagePrivate> packagePrivateProvider;",
+ "",
+ " @Override",
+ " public PackagePrivate packagePrivate() {",
+ " return (PackagePrivate) getPackagePrivateProvider().get();",
+ " }",
+ "",
+ " protected Provider getPackagePrivateProvider() {",
+ " return packagePrivateProvider;",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .containsElementsIn(generatedLeaf);
+ }
+
+ @Test
+ public void castModifiableMethodAccessedInFinalImplementation() {
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "PackagePrivate");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.PublicBaseType",
+ "package test;",
+ "", //
+ "public class PublicBaseType {}"),
+ JavaFileObjects.forSourceLines(
+ "test.PackagePrivateSubtype",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ // Force this to be a modifiable binding resolved in the ancestor even though the
+ // binding is requested in the leaf.
+ "@AncestorScope",
+ "class PackagePrivateSubtype extends PublicBaseType {",
+ " @Inject PackagePrivateSubtype() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface AncestorScope {}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface LeafModule {",
+ " @Binds PublicBaseType publicBaseType(PackagePrivateSubtype subtype);",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.InjectsOptionalOfModifiable",
+ "package test;",
+ "",
+ "import java.util.Optional;",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectsOptionalOfModifiable {",
+ " @Inject InjectsOptionalOfModifiable(",
+ " Optional<PublicBaseType> optionalOfModifiable) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " InjectsOptionalOfModifiable injectsOptionalOfModifiable();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf {",
+ " protected abstract Optional<PublicBaseType> getOptionalOfPublicBaseType();",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .containsElementsIn(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.InjectsPackagePrivateSubtype",
+ "package test;",
+ "",
+ "import java.util.Optional;",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectsPackagePrivateSubtype {",
+ " @Inject InjectsPackagePrivateSubtype(",
+ // Force a modifiable binding method for PackagePrivateSubtype in Ancestor. The
+ // final Leaf implementation will refer to this method, but will need to cast it
+ // since the PackagePrivateSubtype is accessible from the current package, but the
+ // method returns Object
+ " PackagePrivateSubtype packagePrivateSubtype) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.AncestorModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface AncestorModule {",
+ " @Provides",
+ " static PackagePrivateSubtype packagePrivateSubtype() {",
+ " return new PackagePrivateSubtype();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Ancestor",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@AncestorScope",
+ "@Subcomponent",
+ "interface Ancestor {",
+ " InjectsPackagePrivateSubtype injectsPackagePrivateSubtype();",
+ " Leaf leaf();",
+ "}"));
+
+ JavaFileObject generatedAncestor =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerAncestor",
+ "package test;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerAncestor implements Ancestor {",
+ " protected Object getPackagePrivateSubtype() {",
+ " return getPackagePrivateSubtypeProvider().get();",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerAncestor")
+ .containsElementsIn(generatedAncestor);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.RootModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface RootModule {",
+ " @BindsOptionalOf",
+ " PublicBaseType optionalPublicBaseType();",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RootModule.class)",
+ "interface Root {",
+ " Ancestor ancestor();",
+ "}"));
+
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root {",
+ " protected final class AncestorImpl extends DaggerAncestor {",
+ " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
+ " @Override",
+ " protected Optional<PublicBaseType> getOptionalOfPublicBaseType() {",
+ " return Optional.of(",
+ " (PublicBaseType) AncestorImpl.this.getPackagePrivateSubtype());",
+ " }",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .containsElementsIn(generatedRoot);
+ }
+
+ @Test
+ public void injectInLeaf_ProductionInRoot() {
+ // most of this is also covered in ProducesMethodShadowsInjectConstructorTest, but this test
+ // asserts that the correct PrunedConcreteBindingExpression is used
+ ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
+ createSimplePackagePrivateClasses(filesToCompile, "Dependency", "Missing");
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.Injected",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Injected {",
+ " @Inject Injected(Dependency dependency, Missing missing) {}",
+ "",
+ " Injected(Dependency dependency) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.LeafModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "interface LeafModule {",
+ " @Produces",
+ " static Object dependsOnInjectReplacedWithProduces(Injected injected) {",
+ " return new Object();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Leaf",
+ "package test;",
+ "",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.ProductionSubcomponent;",
+ "",
+ "@ProductionSubcomponent(modules = LeafModule.class)",
+ "interface Leaf {",
+ " Producer<Object> objectProducer();",
+ "}"));
+
+ JavaFileObject generatedLeaf =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerLeaf",
+ "package test;",
+ "",
+ GENERATION_OPTIONS_ANNOTATION,
+ GENERATED_ANNOTATION,
+ "public abstract class DaggerLeaf implements Leaf, CancellationListener {",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.injectedProvider = Injected_Factory.create(",
+ " getDependencyProvider(), getMissingProvider());",
+ " this.injectedProducer = Producers.producerFromProvider(getInjectedProvider());",
+ " this.dependsOnInjectReplacedWithProducesProducer =",
+ " LeafModule_DependsOnInjectReplacedWithProducesFactory.create(",
+ " getProductionImplementationExecutorProvider(),",
+ " getProductionComponentMonitorProvider(),",
+ " getInjectedProducer());",
+ " this.objectProducerEntryPoint =",
+ " Producers.entryPointViewOf(",
+ " dependsOnInjectReplacedWithProducesProducer, this);",
+ " }",
+ "",
+ " protected abstract Provider getDependencyProvider();",
+ " protected abstract Provider getMissingProvider();",
+ "",
+ " protected Provider getInjectedProvider() {",
+ " return injectedProvider;",
+ " }",
+ "",
+ " protected Producer getInjectedProducer() {",
+ " return injectedProducer;",
+ " }",
+ "}");
+ Compilation compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerLeaf")
+ .containsElementsIn(generatedLeaf);
+
+ filesToCompile.add(
+ JavaFileObjects.forSourceLines(
+ "test.RootModule",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.MoreExecutors;",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.Production;",
+ "import java.util.concurrent.Executor;",
+ "",
+ "@ProducerModule",
+ "interface RootModule {",
+ " @Produces",
+ " static Injected replaceInjectWithProduces(Dependency dependency) {",
+ " return new Injected(dependency);",
+ " }",
+ "",
+ " @Produces",
+ " static Dependency dependency() {",
+ " return new Dependency();",
+ " }",
+ "",
+ " @Provides",
+ " @Production",
+ " static Executor executor() {",
+ " return MoreExecutors.directExecutor();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Root",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(modules = RootModule.class)",
+ "interface Root {",
+ " Leaf leaf();",
+ "}"));
+
+ JavaFileObject generatedRoot =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRoot",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRoot implements Root, CancellationListener {",
+ " private Producer<Dependency> dependencyProducer;",
+ " private Producer<Injected> replaceInjectWithProducesProducer;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.productionImplementationExecutorProvider =",
+ " DoubleCheck.provider((Provider) RootModule_ExecutorFactory.create());",
+ " this.rootProvider = InstanceFactory.create((Root) this);",
+ " this.monitorProvider =",
+ " DoubleCheck.provider(",
+ " Root_MonitoringModule_MonitorFactory.create(",
+ " rootProvider,",
+ " SetFactory.<ProductionComponentMonitor.Factory>empty()));",
+ " this.dependencyProducer =",
+ " RootModule_DependencyFactory.create(",
+ " productionImplementationExecutorProvider, monitorProvider);",
+ " this.replaceInjectWithProducesProducer =",
+ " RootModule_ReplaceInjectWithProducesFactory.create(",
+ " productionImplementationExecutorProvider,",
+ " monitorProvider,",
+ " dependencyProducer);",
+ " }",
+ "",
+ " protected final class LeafImpl extends DaggerLeaf",
+ " implements CancellationListener {",
+ " @Override",
+ " protected Provider getDependencyProvider() {",
+ " return MissingBindingFactory.create();",
+ " }",
+ "",
+ " @Override",
+ " protected Provider getMissingProvider() {",
+ " return MissingBindingFactory.create();",
+ " }",
+ "",
+ " @Override",
+ " protected Producer getInjectedProducer() {",
+ " return DaggerRoot.this.replaceInjectWithProducesProducer;",
+ " }",
+ " }",
+ "}");
+ compilation = compile(filesToCompile.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRoot")
+ .containsElementsIn(generatedRoot);
+ }
+
+ // TODO(ronshapiro): remove copies from AheadOfTimeSubcomponents*Test classes
+ private void createSimplePackagePrivateClasses(
+ ImmutableList.Builder<JavaFileObject> filesBuilder, String... ancillaryClasses) {
+ for (String className : ancillaryClasses) {
+ filesBuilder.add(
+ JavaFileObjects.forSourceLines(
+ String.format("test.%s", className),
+ "package test;",
+ "",
+ String.format("class %s { }", className)));
+ }
+ }
+
+ private static Compilation compile(Iterable<JavaFileObject> files, CompilerMode... modes) {
+ return compilerWithOptions(
+ ObjectArrays.concat(
+ new CompilerMode[] {AHEAD_OF_TIME_SUBCOMPONENTS_MODE}, modes, CompilerMode.class))
+ .compile(files);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java b/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java
new file mode 100644
index 0000000..fda5859
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.testing.compile.CompilationRule;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.lang.model.element.AnnotationMirror;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests {@link TypeProtoConverter}. */
+@RunWith(JUnit4.class)
+public class AnnotationProtoConverterTest {
+ @Rule public CompilationRule compilationRule = new CompilationRule();
+
+ private DaggerElements elements;
+ private DaggerTypes types;
+ private AnnotationProtoConverter annotationProtoConverter;
+
+ @Before
+ public void setUp() {
+ this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+ this.types = new DaggerTypes(compilationRule.getTypes(), elements);
+ this.annotationProtoConverter =
+ new AnnotationProtoConverter(new TypeProtoConverter(types, elements));
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface TestAnnotation {
+ byte b();
+ boolean bool();
+ short s();
+ char c();
+ int i();
+ long l();
+ double d();
+ float f();
+
+ String string();
+ RetentionPolicy enumValue();
+ Class<?> classValue();
+ HasDefaults[] nestedAnnotations();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface HasDefaults {
+ int value() default 2;
+ }
+
+ @TestAnnotation(
+ b = 1,
+ bool = true,
+ s = 2,
+ c = 'c',
+ i = 4,
+ l = 5,
+ d = 6.0d,
+ f = 7.0f,
+ string = "hello, world",
+ enumValue = RetentionPolicy.CLASS,
+ classValue = AnnotationProtoConverter.class,
+ nestedAnnotations = {@HasDefaults, @HasDefaults(8)})
+ static class TestSubject {}
+
+ @Test
+ public void conversion() {
+ AnnotationMirror actual =
+ getOnlyElement(elements.getTypeElement(TestSubject.class).getAnnotationMirrors());
+ AnnotationMirror translated =
+ annotationProtoConverter.fromProto(AnnotationProtoConverter.toProto(actual));
+ assertThat(AnnotationMirrors.equivalence().equivalent(actual, translated)).isTrue();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/BUILD b/javatests/dagger/internal/codegen/BUILD
new file mode 100644
index 0000000..ad214ff
--- /dev/null
+++ b/javatests/dagger/internal/codegen/BUILD
@@ -0,0 +1,56 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tests for the Dagger compiler/codegen
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "compiler_tests",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen:base",
+ "//java/dagger/internal/codegen:binding",
+ "//java/dagger/internal/codegen:binding_graph_validation",
+ "//java/dagger/internal/codegen:processor",
+ "//java/dagger/internal/codegen:validation",
+ "//java/dagger/internal/codegen:writing",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/serialization",
+ "//java/dagger/model",
+ "//java/dagger/model/testing",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@com_google_auto_value_auto_value//jar", # For AutoAnnotationProcessor
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/mockito",
+ "@google_bazel_common//third_party/java/truth",
+ "@google_bazel_common//third_party/java/truth:truth8",
+ ],
+)
diff --git a/javatests/dagger/internal/codegen/BindingGraphCapturer.java b/javatests/dagger/internal/codegen/BindingGraphCapturer.java
new file mode 100644
index 0000000..503fd47
--- /dev/null
+++ b/javatests/dagger/internal/codegen/BindingGraphCapturer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.collect.ImmutableMap;
+import dagger.model.BindingGraph;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+
+/**
+ * A testing plugin that captures {@link dagger.model.BindingGraph}s for tests to make assertions
+ * about.
+ */
+// TODO(dpb): Move to dagger.spi.testing?
+final class BindingGraphCapturer implements BindingGraphPlugin {
+
+ private final ImmutableMap.Builder<String, BindingGraph> bindingGraphs = ImmutableMap.builder();
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ bindingGraphs.put(
+ bindingGraph
+ .rootComponentNode()
+ .componentPath()
+ .currentComponent()
+ .getQualifiedName()
+ .toString(),
+ bindingGraph);
+ }
+
+ /** Returns a map of binding graphs, indexed by the canonical name of the root component type. */
+ public ImmutableMap<String, BindingGraph> bindingGraphs() {
+ return bindingGraphs.build();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java b/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
new file mode 100644
index 0000000..2496d8b
--- /dev/null
+++ b/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class BindsInstanceValidationTest {
+ @Test
+ public void bindsInstanceInModule() {
+ JavaFileObject testModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @BindsInstance abstract void str(String string);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(testModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@BindsInstance methods should not be included in @Modules. Did you mean @Binds");
+ }
+
+ @Test
+ public void bindsInstanceInComponent() {
+ JavaFileObject testComponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " @BindsInstance String s(String s);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(testComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@BindsInstance methods should not be included in @Components. "
+ + "Did you mean to put it in a @Component.Builder?");
+ }
+
+ @Test
+ public void bindsInstanceNotAbstract() {
+ JavaFileObject notAbstract =
+ JavaFileObjects.forSourceLines(
+ "test.BindsInstanceNotAbstract",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "",
+ "class BindsInstanceNotAbstract {",
+ " @BindsInstance BindsInstanceNotAbstract bind(int unused) { return this; }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(notAbstract);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@BindsInstance methods must be abstract")
+ .inFile(notAbstract)
+ .onLine(7);
+ }
+
+ @Test
+ public void bindsInstanceNoParameters() {
+ JavaFileObject notAbstract =
+ JavaFileObjects.forSourceLines(
+ "test.BindsInstanceNoParameters",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "",
+ "interface BindsInstanceNoParameters {",
+ " @BindsInstance void noParams();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(notAbstract);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@BindsInstance methods should have exactly one parameter for the bound type")
+ .inFile(notAbstract)
+ .onLine(6);
+ }
+
+ @Test
+ public void bindsInstanceManyParameters() {
+ JavaFileObject notAbstract =
+ JavaFileObjects.forSourceLines(
+ "test.BindsInstanceNoParameter",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "",
+ "interface BindsInstanceManyParameters {",
+ " @BindsInstance void manyParams(int i, long l);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(notAbstract);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@BindsInstance methods should have exactly one parameter for the bound type")
+ .inFile(notAbstract)
+ .onLine(6);
+ }
+
+ @Test
+ public void bindsInstanceFrameworkType() {
+ JavaFileObject bindsFrameworkType =
+ JavaFileObjects.forSourceLines(
+ "test.BindsInstanceFrameworkType",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.producers.Producer;",
+ "import javax.inject.Provider;",
+ "",
+ "interface BindsInstanceFrameworkType {",
+ " @BindsInstance void bindsProvider(Provider<Object> objectProvider);",
+ " @BindsInstance void bindsProducer(Producer<Object> objectProducer);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(bindsFrameworkType);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@BindsInstance parameters must not be framework types")
+ .inFile(bindsFrameworkType)
+ .onLine(8);
+
+ assertThat(compilation)
+ .hadErrorContaining("@BindsInstance parameters must not be framework types")
+ .inFile(bindsFrameworkType)
+ .onLine(9);
+ }
+
+}
diff --git a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
new file mode 100644
index 0000000..6ba9e3e
--- /dev/null
+++ b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.collect.ImmutableList;
+import dagger.Module;
+import dagger.multibindings.IntKey;
+import dagger.multibindings.LongKey;
+import dagger.producers.ProducerModule;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.util.Collection;
+import javax.inject.Qualifier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class BindsMethodValidationTest {
+ @Parameters
+ public static Collection<Object[]> data() {
+ return ImmutableList.copyOf(new Object[][] {{Module.class}, {ProducerModule.class}});
+ }
+
+ private final String moduleDeclaration;
+
+ public BindsMethodValidationTest(Class<? extends Annotation> moduleAnnotation) {
+ moduleDeclaration = "@" + moduleAnnotation.getCanonicalName() + " abstract class %s { %s }";
+ }
+
+ @Test
+ public void nonAbstract() {
+ assertThatMethod("@Binds Object concrete(String impl) { return null; }")
+ .hasError("must be abstract");
+ }
+
+ @Test
+ public void notAssignable() {
+ assertThatMethod("@Binds abstract String notAssignable(Object impl);").hasError("assignable");
+ }
+
+ @Test
+ public void moreThanOneParameter() {
+ assertThatMethod("@Binds abstract Object tooManyParameters(String s1, String s2);")
+ .hasError("one parameter");
+ }
+
+ @Test
+ public void typeParameters() {
+ assertThatMethod("@Binds abstract <S, T extends S> S generic(T t);")
+ .hasError("type parameters");
+ }
+
+ @Test
+ public void notInModule() {
+ assertThatMethodInUnannotatedClass("@Binds abstract Object bindObject(String s);")
+ .hasError("within a @Module or @ProducerModule");
+ }
+
+ @Test
+ public void throwsException() {
+ assertThatMethod("@Binds abstract Object throwsException(String s1) throws IOException;")
+ .importing(IOException.class)
+ .hasError("only throw unchecked");
+ }
+
+ @Test
+ public void returnsVoid() {
+ assertThatMethod("@Binds abstract void returnsVoid(Object impl);").hasError("void");
+ }
+
+ @Test
+ public void tooManyQualifiersOnMethod() {
+ assertThatMethod(
+ "@Binds @Qualifier1 @Qualifier2 abstract String tooManyQualifiers(String impl);")
+ .importing(Qualifier1.class, Qualifier2.class)
+ .hasError("more than one @Qualifier");
+ }
+
+ @Test
+ public void tooManyQualifiersOnParameter() {
+ assertThatMethod(
+ "@Binds abstract String tooManyQualifiers(@Qualifier1 @Qualifier2 String impl);")
+ .importing(Qualifier1.class, Qualifier2.class)
+ .hasError("more than one @Qualifier");
+ }
+
+ @Test
+ public void noParameters() {
+ assertThatMethod("@Binds abstract Object noParameters();").hasError("one parameter");
+ }
+
+ @Test
+ public void setElementsNotAssignable() {
+ assertThatMethod(
+ "@Binds @ElementsIntoSet abstract Set<String> bindSetOfIntegers(Set<Integer> ints);")
+ .hasError("assignable");
+ }
+
+ @Test
+ public void setElements_primitiveArgument() {
+ assertThatMethod("@Binds @ElementsIntoSet abstract Set<Number> bindInt(int integer);")
+ .hasError("assignable");
+ }
+
+ @Test
+ public void elementsIntoSet_withRawSets() {
+ assertThatMethod("@Binds @ElementsIntoSet abstract Set bindRawSet(HashSet hashSet);")
+ .hasError("cannot return a raw Set");
+ }
+
+ @Test
+ public void intoMap_noMapKey() {
+ assertThatMethod("@Binds @IntoMap abstract Object bindNoMapKey(String string);")
+ .hasError("methods of type map must declare a map key");
+ }
+
+ @Test
+ public void intoMap_multipleMapKeys() {
+ assertThatMethod(
+ "@Binds @IntoMap @IntKey(1) @LongKey(2L) abstract Object manyMapKeys(String string);")
+ .importing(IntKey.class, LongKey.class)
+ .hasError("may not have more than one map key");
+ }
+
+ private DaggerModuleMethodSubject assertThatMethod(String method) {
+ return assertThatModuleMethod(method).withDeclaration(moduleDeclaration);
+ }
+
+ @Qualifier
+ @Retention(RUNTIME)
+ public @interface Qualifier1 {}
+
+ @Qualifier
+ @Retention(RUNTIME)
+ public @interface Qualifier2 {}
+}
diff --git a/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java b/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java
new file mode 100644
index 0000000..28b75be
--- /dev/null
+++ b/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests that errors are reported correctly when a {@code @Binds} method's delegate (the type of its
+ * parameter) is missing.
+ */
+@RunWith(JUnit4.class)
+public class BindsMissingDelegateValidationTest {
+ @Test
+ public void bindsMissingDelegate() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "",
+ "@Component(modules = C.TestModule.class)",
+ "interface C {",
+ " Object object();",
+ "",
+ " static class NotBound {}",
+ "",
+ " @Module",
+ " abstract static class TestModule {",
+ " @Binds abstract Object bindObject(NotBound notBound);",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.C.NotBound cannot be provided")
+ .inFile(component)
+ .onLineContaining("interface C");
+ }
+
+ @Test
+ public void bindsMissingDelegate_duplicateBinding() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Component(modules = C.TestModule.class)",
+ "interface C {",
+ " Object object();",
+ "",
+ " static class NotBound {}",
+ "",
+ " @Module",
+ " abstract static class TestModule {",
+ " @Binds abstract Object bindObject(NotBound notBound);",
+ " @Provides static Object provideObject() { return new Object(); }",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ // Some javacs report only the first error for each source line.
+ // Assert that one of the expected errors is reported.
+ assertThat(compilation)
+ .hadErrorContainingMatch(
+ "\\Qtest.C.NotBound cannot be provided\\E|"
+ + message(
+ "\\Qjava.lang.Object is bound multiple times:",
+ " @Binds Object test.C.TestModule.bindObject(test.C.NotBound)",
+ " @Provides Object test.C.TestModule.provideObject()\\E"))
+ .inFile(component)
+ .onLineContaining("interface C");
+ }
+
+ @Test
+ public void bindsMissingDelegate_setBinding() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Component(modules = C.TestModule.class)",
+ "interface C {",
+ " Set<Object> objects();",
+ "",
+ " static class NotBound {}",
+ "",
+ " @Module",
+ " abstract static class TestModule {",
+ " @Binds @IntoSet abstract Object bindObject(NotBound notBound);",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.C.NotBound cannot be provided")
+ .inFile(component)
+ .onLineContaining("interface C");
+ }
+
+ @Test
+ public void bindsMissingDelegate_mapBinding() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Component(modules = C.TestModule.class)",
+ "interface C {",
+ " Map<String, Object> objects();",
+ "",
+ " static class NotBound {}",
+ "",
+ " @Module",
+ " abstract static class TestModule {",
+ " @Binds @IntoMap @StringKey(\"key\")",
+ " abstract Object bindObject(NotBound notBound);",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.C.NotBound cannot be provided")
+ .inFile(component)
+ .onLineContaining("interface C");
+ }
+
+ @Test
+ public void bindsMissingDelegate_mapBinding_sameKey() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@Component(modules = C.TestModule.class)",
+ "interface C {",
+ " Map<String, Object> objects();",
+ "",
+ " static class NotBound {}",
+ "",
+ " @Module",
+ " abstract static class TestModule {",
+ " @Binds @IntoMap @StringKey(\"key\")",
+ " abstract Object bindObject(NotBound notBound);",
+ "",
+ " @Provides @IntoMap @StringKey(\"key\")",
+ " static Object provideObject() { return new Object(); }",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ // Some javacs report only the first error for each source line.
+ assertThat(compilation)
+ .hadErrorContainingMatch(
+ "\\Qtest.C.NotBound cannot be provided\\E|"
+ + "\\Qsame map key is bound more than once\\E")
+ .inFile(component)
+ .onLineContaining("interface C");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java b/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java
new file mode 100644
index 0000000..fa44a6e
--- /dev/null
+++ b/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.Module;
+import dagger.producers.ProducerModule;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests {@link BindsOptionalOfMethodValidator}. */
+@RunWith(Parameterized.class)
+public class BindsOptionalOfMethodValidationTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> data() {
+ return ImmutableList.copyOf(new Object[][] {{Module.class}, {ProducerModule.class}});
+ }
+
+ private final String moduleDeclaration;
+
+ public BindsOptionalOfMethodValidationTest(Class<? extends Annotation> moduleAnnotation) {
+ moduleDeclaration = "@" + moduleAnnotation.getCanonicalName() + " abstract class %s { %s }";
+ }
+
+ @Test
+ public void nonAbstract() {
+ assertThatMethod("@BindsOptionalOf Object concrete() { return null; }")
+ .hasError("must be abstract");
+ }
+
+ @Test
+ public void hasParameters() {
+ assertThatMethod("@BindsOptionalOf abstract Object hasParameters(String s1);")
+ .hasError("parameters");
+ }
+
+ @Test
+ public void typeParameters() {
+ assertThatMethod("@BindsOptionalOf abstract <S> S generic();").hasError("type parameters");
+ }
+
+ @Test
+ public void notInModule() {
+ assertThatMethodInUnannotatedClass("@BindsOptionalOf abstract Object notInModule();")
+ .hasError("within a @Module or @ProducerModule");
+ }
+
+ @Test
+ public void throwsException() {
+ assertThatMethod("@BindsOptionalOf abstract Object throwsException() throws RuntimeException;")
+ .hasError("may not throw");
+ }
+
+ @Test
+ public void returnsVoid() {
+ assertThatMethod("@BindsOptionalOf abstract void returnsVoid();").hasError("void");
+ }
+
+ @Test
+ public void returnsMembersInjector() {
+ assertThatMethod("@BindsOptionalOf abstract MembersInjector<Object> returnsMembersInjector();")
+ .hasError("framework");
+ }
+
+ @Test
+ public void tooManyQualifiers() {
+ assertThatMethod(
+ "@BindsOptionalOf @Qualifier1 @Qualifier2 abstract String tooManyQualifiers();")
+ .importing(Qualifier1.class, Qualifier2.class)
+ .hasError("more than one @Qualifier");
+ }
+
+ @Test
+ public void intoSet() {
+ assertThatMethod("@BindsOptionalOf @IntoSet abstract String intoSet();")
+ .hasError("cannot have multibinding annotations");
+ }
+
+ @Test
+ public void elementsIntoSet() {
+ assertThatMethod("@BindsOptionalOf @ElementsIntoSet abstract Set<String> elementsIntoSet();")
+ .hasError("cannot have multibinding annotations");
+ }
+
+ @Test
+ public void intoMap() {
+ assertThatMethod("@BindsOptionalOf @IntoMap abstract String intoMap();")
+ .hasError("cannot have multibinding annotations");
+ }
+
+ /**
+ * Tests that @BindsOptionalOf @IntoMap actually causes module validation to fail.
+ *
+ * @see <a href="http://b/118434447">bug 118434447</a>
+ */
+ @Test
+ public void intoMapWithComponent() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @BindsOptionalOf @IntoMap Object object();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {}");
+
+ Compilation compilation = daggerCompiler().compile(module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("cannot have multibinding annotations")
+ .inFile(module)
+ .onLineContaining("object();");
+ }
+
+ /** An injectable value object. */
+ public static final class Thing {
+ @Inject
+ Thing() {}
+ }
+
+ @Test
+ public void implicitlyProvidedType() {
+ assertThatMethod("@BindsOptionalOf abstract Thing thing();")
+ .importing(Thing.class)
+ .hasError("return unqualified types that have an @Inject-annotated constructor");
+ }
+
+ @Test
+ public void hasScope() {
+ assertThatMethod("@BindsOptionalOf @Singleton abstract String scoped();")
+ .importing(Singleton.class)
+ .hasError("cannot be scoped");
+ }
+
+ private DaggerModuleMethodSubject assertThatMethod(String method) {
+ return assertThatModuleMethod(method).withDeclaration(moduleDeclaration);
+ }
+
+ /** A qualifier. */
+ @Qualifier
+ public @interface Qualifier1 {}
+
+ /** A qualifier. */
+ @Qualifier
+ public @interface Qualifier2 {}
+}
diff --git a/javatests/dagger/internal/codegen/CompilerMode.java b/javatests/dagger/internal/codegen/CompilerMode.java
new file mode 100644
index 0000000..23aa312
--- /dev/null
+++ b/javatests/dagger/internal/codegen/CompilerMode.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
+/** The configuration options for compiler modes. */
+enum CompilerMode {
+ DEFAULT_MODE,
+ FAST_INIT_MODE("-Adagger.fastInit=enabled"),
+ AHEAD_OF_TIME_SUBCOMPONENTS_MODE(
+ "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
+ "-Adagger.emitModifiableMetadataAnnotations=disabled"),
+ JAVA7("-source", "7", "-target", "7"),
+ ;
+
+ /** Returns the compiler modes as a list of parameters for parameterized tests */
+ static final ImmutableList<Object[]> TEST_PARAMETERS =
+ ImmutableList.copyOf(
+ new Object[][] {{CompilerMode.DEFAULT_MODE}, {CompilerMode.FAST_INIT_MODE}});
+
+ private final ImmutableList<String> javacopts;
+
+ private CompilerMode(String... javacopts) {
+ this.javacopts = ImmutableList.copyOf(javacopts);
+ }
+
+ /** Returns the javacopts for this compiler mode. */
+ FluentIterable<String> javacopts() {
+ return FluentIterable.from(javacopts);
+ }
+
+ /**
+ * Returns a {@link JavaFileBuilder} that builds {@link javax.tools.JavaFileObject}s for this
+ * mode.
+ */
+ JavaFileBuilder javaFileBuilder(String qualifiedName) {
+ return new JavaFileBuilder(this, qualifiedName);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/Compilers.java b/javatests/dagger/internal/codegen/Compilers.java
new file mode 100644
index 0000000..3e08f63
--- /dev/null
+++ b/javatests/dagger/internal/codegen/Compilers.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH;
+import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR;
+import static com.google.testing.compile.Compiler.javac;
+import static java.util.stream.Collectors.joining;
+
+import com.google.auto.value.processor.AutoAnnotationProcessor;
+import com.google.common.base.Splitter;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compiler;
+import javax.annotation.processing.Processor;
+
+/** {@link Compiler} instances for testing Dagger. */
+final class Compilers {
+ private static final String GUAVA = "guava";
+
+ static final ImmutableList<String> CLASS_PATH_WITHOUT_GUAVA_OPTION =
+ ImmutableList.of(
+ "-classpath",
+ Splitter.on(PATH_SEPARATOR.value()).splitToList(JAVA_CLASS_PATH.value()).stream()
+ .filter(jar -> !jar.contains(GUAVA))
+ .collect(joining(PATH_SEPARATOR.value())));
+
+ /**
+ * Returns a compiler that runs the Dagger and {@code @AutoAnnotation} processors, along with
+ * extras.
+ */
+ static Compiler daggerCompiler(Processor... extraProcessors) {
+ ImmutableList.Builder<Processor> processors = ImmutableList.builder();
+ processors.add(new ComponentProcessor(), new AutoAnnotationProcessor());
+ processors.add(extraProcessors);
+ return javac().withProcessors(processors.build());
+ }
+
+ static Compiler compilerWithOptions(CompilerMode... compilerModes) {
+ FluentIterable<String> options = FluentIterable.of();
+ for (CompilerMode compilerMode : compilerModes) {
+ options = options.append(compilerMode.javacopts());
+ }
+ return daggerCompiler().withOptions(options);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentBuilderTest.java b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
new file mode 100644
index 0000000..f280bdd
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_BUILDER;
+import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for {@link dagger.Component.Builder} */
+@RunWith(Parameterized.class)
+public class ComponentBuilderTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public ComponentBuilderTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ private static final ErrorMessages.ComponentCreatorMessages MSGS =
+ creatorMessagesFor(COMPONENT_BUILDER);
+
+ @Test
+ public void testUsesBuildAndSetterNames() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String string() { return null; }",
+ "}");
+
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " String string();",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " Builder setTestModule(TestModule testModule);",
+ " TestComponent create();",
+ " }",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private static final class Builder implements TestComponent.Builder {",
+ " private TestModule testModule;",
+ "",
+ " @Override",
+ " public Builder setTestModule(TestModule testModule) {",
+ " this.testModule = Preconditions.checkNotNull(testModule);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public TestComponent create() {",
+ " if (testModule == null) {",
+ " this.testModule = new TestModule();",
+ " }",
+ " return new DaggerTestComponent(testModule);",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(moduleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void testSetterMethodWithMoreThanOneArgFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder {",
+ " SimpleComponent build();",
+ " Builder set(String s, Integer i);",
+ " Builder set(Number n, Double d);",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.setterMethodsMustTakeOneArg())
+ .inFile(componentFile)
+ .onLineContaining("Builder set(String s, Integer i);");
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.setterMethodsMustTakeOneArg())
+ .inFile(componentFile)
+ .onLineContaining("Builder set(Number n, Double d);");
+ }
+
+ @Test
+ public void testInheritedSetterMethodWithMoreThanOneArgFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " interface Parent {",
+ " SimpleComponent build();",
+ " Builder set1(String s, Integer i);",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ MSGS.inheritedSetterMethodsMustTakeOneArg(),
+ "set1(java.lang.String,java.lang.Integer)"))
+ .inFile(componentFile)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void testSetterReturningNonVoidOrBuilderFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder {",
+ " SimpleComponent build();",
+ " String set(Integer i);",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.setterMethodsMustReturnVoidOrBuilder())
+ .inFile(componentFile)
+ .onLineContaining("String set(Integer i);");
+ }
+
+ @Test
+ public void testInheritedSetterReturningNonVoidOrBuilderFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " interface Parent {",
+ " SimpleComponent build();",
+ " String set(Integer i);",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ MSGS.inheritedSetterMethodsMustReturnVoidOrBuilder(), "set(java.lang.Integer)"))
+ .inFile(componentFile)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void testGenericsOnSetterMethodFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder {",
+ " SimpleComponent build();",
+ " <T> Builder set(T t);",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.methodsMayNotHaveTypeParameters())
+ .inFile(componentFile)
+ .onLineContaining("<T> Builder set(T t);");
+ }
+
+ @Test
+ public void testGenericsOnInheritedSetterMethodFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " interface Parent {",
+ " SimpleComponent build();",
+ " <T> Builder set(T t);",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(MSGS.inheritedMethodsMayNotHaveTypeParameters(), "<T>set(T)"))
+ .inFile(componentFile)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void testBindsInstanceNotAllowedOnBothSetterAndParameter() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " abstract String s();",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance",
+ " Builder s(@BindsInstance String s);",
+ "",
+ " SimpleComponent build();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.bindsInstanceNotAllowedOnBothSetterMethodAndParameter())
+ .inFile(componentFile)
+ .onLineContaining("Builder s(");
+ }
+
+ @Test
+ public void testBindsInstanceNotAllowedOnBothSetterAndParameter_inherited() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " abstract String s();",
+ "",
+ " interface BuilderParent<B extends BuilderParent> {",
+ " @BindsInstance",
+ " B s(@BindsInstance String s);",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends BuilderParent<Builder> {",
+ " SimpleComponent build();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ MSGS.inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter(),
+ "s(java.lang.String)"))
+ .inFile(componentFile)
+ .onLineContaining("Builder extends BuilderParent<Builder>");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentCreatorTest.java b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
new file mode 100644
index 0000000..6f4ea8d
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
@@ -0,0 +1,1253 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_BUILDER;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_FACTORY;
+import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.ComponentKind.COMPONENT;
+import static dagger.internal.codegen.ErrorMessages.componentMessagesFor;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for properties of component creators shared by both builders and factories. */
+@RunWith(Parameterized.class)
+public class ComponentCreatorTest extends ComponentCreatorTestHelper {
+ @Parameters(name = "compilerMode={0}, creatorKind={1}")
+ public static Collection<Object[]> parameters() {
+ Set<List<Object>> params =
+ Sets.<Object>cartesianProduct(
+ immutableEnumSet(DEFAULT_MODE, FAST_INIT_MODE),
+ immutableEnumSet(COMPONENT_BUILDER, COMPONENT_FACTORY));
+ return ImmutableList.copyOf(Iterables.transform(params, Collection::toArray));
+ }
+
+ public ComponentCreatorTest(
+ CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation) {
+ super(compilerMode, componentCreatorAnnotation);
+ }
+
+ @Test
+ public void testEmptyCreator() {
+ JavaFileObject injectableTypeFile =
+ JavaFileObjects.forSourceLines(
+ "test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType() {}",
+ "}");
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " SomeInjectableType someInjectableType();",
+ "",
+ " @Component.Builder",
+ " static interface Builder {",
+ " SimpleComponent build();",
+ " }",
+ "}");
+ JavaFileObject generatedComponent =
+ preprocessedJavaFile(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private static final class Builder implements SimpleComponent.Builder {",
+ " @Override",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = compile(injectableTypeFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void testCanInstantiateModulesUserCannotSet() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String string() { return null; }",
+ "}");
+
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " String string();",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " TestComponent build();",
+ " }",
+ "}");
+ JavaFileObject generatedComponent =
+ preprocessedJavaFile(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final TestModule testModule;",
+ "",
+ " private DaggerTestComponent(TestModule testModuleParam) {",
+ " this.testModule = testModuleParam;",
+ " }",
+ "",
+ " public static TestComponent.Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public String string() {",
+ " return TestModule_StringFactory.string(testModule);",
+ " }",
+ "",
+ " private static final class Builder implements TestComponent.Builder {",
+ " @Override",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(new TestModule());",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = compile(module, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ @Test
+ public void testMoreThanOneCreatorOfSameTypeFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " @Component.Builder",
+ " static interface Builder {",
+ " SimpleComponent build();",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder2 {",
+ " SimpleComponent build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ componentMessagesFor(COMPONENT).moreThanOne(),
+ process("[test.SimpleComponent.Builder, test.SimpleComponent.Builder2]")))
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testBothBuilderAndFactoryFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " @Component.Builder",
+ " static interface Builder {",
+ " SimpleComponent build();",
+ " }",
+ "",
+ " @Component.Factory",
+ " interface Factory {",
+ " SimpleComponent create();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ componentMessagesFor(COMPONENT).moreThanOne(),
+ "[test.SimpleComponent.Builder, test.SimpleComponent.Factory]"))
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testGenericCreatorTypeFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder<T> {",
+ " SimpleComponent build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.generics()).inFile(componentFile);
+ }
+
+ @Test
+ public void testCreatorNotInComponentFails() {
+ JavaFileObject builder =
+ preprocessedJavaFile(
+ "test.Builder",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component.Builder",
+ "interface Builder {}");
+ Compilation compilation = compile(builder);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.mustBeInComponent()).inFile(builder);
+ }
+
+ @Test
+ public void testCreatorMissingFactoryMethodFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.missingFactoryMethod())
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testCreatorWithBindsInstanceNoStaticCreateGenerated() {
+ JavaFileObject componentFile =
+ javaFileBuilder("test.SimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " Object object();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder object(Object object);",
+ " SimpleComponent build();",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Component.Factory",
+ " interface Factory {",
+ " SimpleComponent create(@BindsInstance Object object);",
+ " }")
+ .addLines("}")
+ .build();
+
+ JavaFileObject generatedComponent =
+ javaFileBuilder("test.DaggerSimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private final Object object;",
+ "",
+ " private DaggerSimpleComponent(Object objectParam) {",
+ " this.object = objectParam;",
+ " }",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " public static SimpleComponent.Builder builder() {",
+ " return new Builder();",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " public static SimpleComponent.Factory factory() {",
+ " return new Factory();",
+ " }")
+ .addLines(
+ "", //
+ " @Override",
+ " public Object object() {",
+ " return object;",
+ " }",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " private static final class Builder implements SimpleComponent.Builder {",
+ " private Object object;",
+ "",
+ " @Override",
+ " public Builder object(Object object) {",
+ " this.object = Preconditions.checkNotNull(object);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public SimpleComponent build() {",
+ " Preconditions.checkBuilderRequirement(object, Object.class);",
+ " return new DaggerSimpleComponent(object);",
+ " }",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " private static final class Factory implements SimpleComponent.Factory {",
+ " @Override",
+ " public SimpleComponent create(Object object) {",
+ " Preconditions.checkNotNull(object);",
+ " return new DaggerSimpleComponent(object);",
+ " }",
+ " }")
+ .addLines("}")
+ .build();
+
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ @Test
+ public void testCreatorWithPrimitiveBindsInstance() {
+ JavaFileObject componentFile =
+ javaFileBuilder("test.SimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " int anInt();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder i(int i);",
+ " SimpleComponent build();",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Component.Factory",
+ " interface Factory {",
+ " SimpleComponent create(@BindsInstance int i);",
+ " }")
+ .addLines(
+ "}")
+ .build();
+
+ JavaFileObject generatedComponent =
+ javaFileBuilder("test.DaggerSimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private final Integer i;",
+ "",
+ " private DaggerSimpleComponent(Integer iParam) {",
+ " this.i = iParam;",
+ " }",
+ "",
+ " @Override",
+ " public int anInt() {",
+ " return i;",
+ " }",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " private static final class Builder implements SimpleComponent.Builder {",
+ " private Integer i;",
+ "",
+ " @Override",
+ " public Builder i(int i) {",
+ " this.i = Preconditions.checkNotNull(i);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public SimpleComponent build() {",
+ " Preconditions.checkBuilderRequirement(i, Integer.class);",
+ " return new DaggerSimpleComponent(i);",
+ " }",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " private static final class Factory implements SimpleComponent.Factory {",
+ " @Override",
+ " public SimpleComponent create(int i) {",
+ " Preconditions.checkNotNull(i);",
+ " return new DaggerSimpleComponent(i);",
+ " }",
+ " }")
+ .addLines(
+ "}")
+ .build();
+
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void testPrivateCreatorFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " private interface Builder {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.isPrivate()).inFile(componentFile);
+ }
+
+ @Test
+ public void testNonStaticCreatorFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " abstract class Builder {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.mustBeStatic()).inFile(componentFile);
+ }
+
+ @Test
+ public void testNonAbstractCreatorFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " static class Builder {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.mustBeAbstract()).inFile(componentFile);
+ }
+
+ @Test
+ public void testCreatorOneConstructorWithArgsFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " static abstract class Builder {",
+ " Builder(String unused) {}",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.invalidConstructor())
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testCreatorMoreThanOneConstructorFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " static abstract class Builder {",
+ " Builder() {}",
+ " Builder(String unused) {}",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.invalidConstructor())
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testCreatorEnumFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " enum Builder {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.mustBeClassOrInterface())
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testCreatorFactoryMethodReturnsWrongTypeFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder {",
+ " String build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.factoryMethodMustReturnComponentType())
+ .inFile(componentFile)
+ .onLineContaining(process("String build();"));
+ }
+
+ @Test
+ public void testCreatorSetterForNonBindsInstancePrimitiveFails() {
+ JavaFileObject component =
+ javaFileBuilder("test.TestComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " Object object();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " @Component.Builder",
+ " interface Builder {",
+ " Builder primitive(long l);",
+ " TestComponent build();",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Component.Factory",
+ " interface Factory {",
+ " TestComponent create(long l);",
+ " }")
+ .addLines( //
+ "}")
+ .build();
+ Compilation compilation = compile(component);
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContaining(messages.nonBindsInstanceParametersMayNotBePrimitives())
+ .inFile(component)
+ .onLineContaining("(long l)");
+ }
+
+ @Test
+ public void testInheritedBuilderBuildReturnsWrongTypeFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " interface Parent {",
+ " String build();",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.inheritedFactoryMethodMustReturnComponentType(), process("build")))
+ .inFile(componentFile)
+ .onLineContaining(process("interface Builder"));
+ }
+
+ @Test
+ public void testTwoFactoryMethodsFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder {",
+ " SimpleComponent build();",
+ " SimpleComponent newSimpleComponent();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(String.format(messages.twoFactoryMethods(), process("build")))
+ .inFile(componentFile)
+ .onLineContaining("SimpleComponent newSimpleComponent();");
+ }
+
+ @Test
+ public void testInheritedTwoFactoryMethodsFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " interface Parent {",
+ " SimpleComponent build();",
+ " SimpleComponent newSimpleComponent();",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.inheritedTwoFactoryMethods(), process("build()"), "newSimpleComponent()"))
+ .inFile(componentFile)
+ .onLineContaining(process("interface Builder"));
+ }
+
+ @Test
+ public void testMultipleSettersPerTypeFails() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String s() { return \"\"; }",
+ "}");
+ JavaFileObject componentFile =
+ javaFileBuilder("test.SimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "abstract class SimpleComponent {",
+ " abstract String s();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " @Component.Builder",
+ " interface Builder {",
+ " SimpleComponent build();",
+ " void set1(TestModule s);",
+ " void set2(TestModule s);",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Component.Factory",
+ " interface Factory {",
+ " SimpleComponent create(TestModule m1, TestModule m2);",
+ " }")
+ .addLines( //
+ "}")
+ .build();
+ Compilation compilation = compile(moduleFile, componentFile);
+ assertThat(compilation).failed();
+ String elements =
+ creatorKind.equals(BUILDER)
+ ? "[void test.SimpleComponent.Builder.set1(test.TestModule), "
+ + "void test.SimpleComponent.Builder.set2(test.TestModule)]"
+ : "[test.TestModule m1, test.TestModule m2]";
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements))
+ .inFile(componentFile)
+ .onLineContaining(process("interface Builder"));
+ }
+
+ @Test
+ public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String s() { return \"\"; }",
+ "}");
+ JavaFileObject componentFile =
+ javaFileBuilder("test.SimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "abstract class SimpleComponent {",
+ " abstract String s();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " interface Parent<T> {",
+ " void set1(T t);",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends Parent<TestModule> {",
+ " SimpleComponent build();",
+ " void set2(TestModule s);",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " interface Parent<C, T> {",
+ " C create(TestModule m1, T t);",
+ " }",
+ "",
+ " @Component.Factory",
+ " interface Factory extends Parent<SimpleComponent, TestModule> {}")
+ .addLines( //
+ "}")
+ .build();
+ Compilation compilation = compile(moduleFile, componentFile);
+ assertThat(compilation).failed();
+ String elements =
+ creatorKind.equals(BUILDER)
+ ? "[void test.SimpleComponent.Builder.set1(test.TestModule), "
+ + "void test.SimpleComponent.Builder.set2(test.TestModule)]"
+ : "[test.TestModule m1, test.TestModule t]";
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements))
+ .inFile(componentFile)
+ .onLineContaining(process("interface Builder"));
+ }
+
+ @Test
+ public void testExtraSettersFails() {
+ JavaFileObject componentFile =
+ javaFileBuilder("test.SimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = AbstractModule.class)",
+ "abstract class SimpleComponent {")
+ .addLinesIf(
+ BUILDER,
+ " @Component.Builder",
+ " interface Builder {",
+ " SimpleComponent build();",
+ " void abstractModule(AbstractModule abstractModule);",
+ " void other(String s);",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Component.Factory",
+ " interface Factory {",
+ " SimpleComponent create(AbstractModule abstractModule, String s);",
+ " }")
+ .addLines("}")
+ .build();
+ JavaFileObject abstractModule =
+ JavaFileObjects.forSourceLines(
+ "test.AbstractModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class AbstractModule {}");
+ Compilation compilation = compile(componentFile, abstractModule);
+ assertThat(compilation).failed();
+ String elements =
+ creatorKind.equals(BUILDER)
+ ? "[void test.SimpleComponent.Builder.abstractModule(test.AbstractModule), "
+ + "void test.SimpleComponent.Builder.other(String)]"
+ : "[test.AbstractModule abstractModule, String s]";
+ assertThat(compilation)
+ .hadErrorContaining(String.format(messages.extraSetters(), elements))
+ .inFile(componentFile)
+ .onLineContaining(process("interface Builder"));
+ }
+
+ @Test
+ public void testMissingSettersFail() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " TestModule(String unused) {}",
+ " @Provides String s() { return null; }",
+ "}");
+ JavaFileObject module2File =
+ JavaFileObjects.forSourceLines(
+ "test.Test2Module",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class Test2Module {",
+ " @Provides Integer i() { return null; }",
+ "}");
+ JavaFileObject module3File =
+ JavaFileObjects.forSourceLines(
+ "test.Test3Module",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class Test3Module {",
+ " Test3Module(String unused) {}",
+ " @Provides Double d() { return null; }",
+ "}");
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = {TestModule.class, Test2Module.class, Test3Module.class},",
+ " dependencies = OtherComponent.class)",
+ "interface TestComponent {",
+ " String string();",
+ " Integer integer();",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " TestComponent create();",
+ " }",
+ "}");
+ JavaFileObject otherComponent =
+ JavaFileObjects.forSourceLines(
+ "test.OtherComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface OtherComponent {}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(moduleFile, module2File, module3File, componentFile, otherComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ // Ignores Test2Module because we can construct it ourselves.
+ // TODO(sameb): Ignore Test3Module because it's not used within transitive dependencies.
+ String.format(
+ messages.missingSetters(),
+ "[test.TestModule, test.Test3Module, test.OtherComponent]"))
+ .inFile(componentFile)
+ .onLineContaining(process("interface Builder"));
+ }
+
+ @Test
+ public void covariantFactoryMethodReturnType() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "test.Supertype",
+ "package test;",
+ "",
+ "interface Supertype {",
+ " Foo foo();",
+ "}");
+
+ JavaFileObject component =
+ preprocessedJavaFile(
+ "test.HasSupertype",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface HasSupertype extends Supertype {",
+ " @Component.Builder",
+ " interface Builder {",
+ " Supertype build();",
+ " }",
+ "}");
+
+ Compilation compilation = compile(foo, supertype, component);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void covariantFactoryMethodReturnType_hasNewMethod() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar() {}",
+ "}");
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "test.Supertype",
+ "package test;",
+ "",
+ "interface Supertype {",
+ " Foo foo();",
+ "}");
+
+ JavaFileObject component =
+ preprocessedJavaFile(
+ "test.HasSupertype",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface HasSupertype extends Supertype {",
+ " Bar bar();",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " Supertype build();",
+ " }",
+ "}");
+
+ Compilation compilation = compile(foo, bar, supertype, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining(
+ process(
+ "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype "
+ + "declares additional component method(s): bar(). In order to provide "
+ + "type-safe access to these methods, override build() to return "
+ + "test.HasSupertype"))
+ .inFile(component)
+ .onLine(11);
+ }
+
+ @Test
+ public void covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar() {}",
+ "}");
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "test.Supertype",
+ "package test;",
+ "",
+ "interface Supertype {",
+ " Foo foo();",
+ "}");
+
+ JavaFileObject creatorSupertype =
+ preprocessedJavaFile(
+ "test.CreatorSupertype",
+ "package test;",
+ "",
+ "interface CreatorSupertype {",
+ " Supertype build();",
+ "}");
+
+ JavaFileObject component =
+ preprocessedJavaFile(
+ "test.HasSupertype",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface HasSupertype extends Supertype {",
+ " Bar bar();",
+ "",
+ " @Component.Builder",
+ " interface Builder extends CreatorSupertype {}",
+ "}");
+
+ Compilation compilation = compile(foo, bar, supertype, creatorSupertype, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining(
+ process(
+ "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype "
+ + "declares additional component method(s): bar(). In order to provide "
+ + "type-safe access to these methods, override build() to return "
+ + "test.HasSupertype"));
+ }
+
+ @Test
+ public void testGenericsOnFactoryMethodFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Builder",
+ " interface Builder {",
+ " <T> SimpleComponent build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.methodsMayNotHaveTypeParameters())
+ .inFile(componentFile)
+ .onLineContaining(process("<T> SimpleComponent build();"));
+ }
+
+ @Test
+ public void testGenericsOnInheritedFactoryMethodFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " interface Parent {",
+ " <T> SimpleComponent build();",
+ " }",
+ "",
+ " @Component.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.inheritedMethodsMayNotHaveTypeParameters(), process("<T>build()")))
+ .inFile(componentFile)
+ .onLineContaining(process("interface Builder"));
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java b/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
new file mode 100644
index 0000000..2ee120e
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
+import static java.util.stream.Collectors.joining;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Arrays;
+import java.util.stream.Stream;
+import javax.tools.JavaFileObject;
+
+/**
+ * Base class for component creator codegen tests that are written in terms of builders and
+ * transformed, either by automatic string processing or using a {@code JavaFileBuilder}, to test
+ * factories as well.
+ */
+abstract class ComponentCreatorTestHelper {
+
+ private final CompilerMode compilerMode;
+
+ protected final ComponentCreatorKind creatorKind;
+ protected final ErrorMessages.ComponentCreatorMessages messages;
+
+ ComponentCreatorTestHelper(
+ CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation) {
+ this.compilerMode = compilerMode;
+ this.creatorKind = componentCreatorAnnotation.creatorKind();
+ this.messages = creatorMessagesFor(componentCreatorAnnotation);
+ }
+
+ // For tests where code for both builders and factories can be largely equivalent, i.e. when there
+ // is nothing to set, just preprocess the lines to change code written for a builder to code for a
+ // factory.
+ // For more complicated code, use a JavaFileBuilder to add different code depending on the creator
+ // kind.
+
+ /**
+ * Processes the given lines, replacing builder-related names with factory-related names if the
+ * creator kind is {@code FACTORY}.
+ */
+ String process(String... lines) {
+ Stream<String> stream = Arrays.stream(lines);
+ if (creatorKind.equals(FACTORY)) {
+ stream =
+ stream.map(
+ line ->
+ line.replace("Builder", "Factory")
+ .replace("builder", "factory")
+ .replace("build", "create"));
+ }
+ return stream.collect(joining("\n"));
+ }
+
+ /**
+ * Returns a Java file with the {@linkplain #process(String...)} processed} versions of the given
+ * lines.
+ */
+ JavaFileObject preprocessedJavaFile(String fullyQualifiedName, String... lines) {
+ return JavaFileObjects.forSourceString(fullyQualifiedName, process(lines));
+ }
+
+ /** Returns a file builder for the current creator kind. */
+ JavaFileBuilder javaFileBuilder(String qualifiedName) {
+ return new JavaFileBuilder(qualifiedName).withSettings(compilerMode, creatorKind);
+ }
+
+ /** Compiles the given files with the set compiler mode's javacopts. */
+ Compilation compile(JavaFileObject... files) {
+ return daggerCompiler().withOptions(compilerMode.javacopts()).compile(files);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentFactoryTest.java b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
new file mode 100644
index 0000000..403498b
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_FACTORY;
+import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for {@link dagger.Component.Factory} */
+@RunWith(Parameterized.class)
+public class ComponentFactoryTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public ComponentFactoryTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ private static final ErrorMessages.ComponentCreatorMessages MSGS =
+ creatorMessagesFor(COMPONENT_FACTORY);
+
+ @Test
+ public void testUsesParameterNames() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String string() { return null; }",
+ "}");
+
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " String string();",
+ "",
+ " @Component.Factory",
+ " interface Factory {",
+ " TestComponent newTestComponent(TestModule mod);",
+ " }",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private static final class Factory implements TestComponent.Factory {",
+ " @Override",
+ " public TestComponent newTestComponent(TestModule mod) {",
+ " Preconditions.checkNotNull(mod);",
+ " return new DaggerTestComponent(mod);",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(moduleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void testSetterMethodFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " @Component.Factory",
+ " interface Factory {",
+ " SimpleComponent create();",
+ " Factory set(String s);",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(String.format(MSGS.twoFactoryMethods(), "create()"))
+ .inFile(componentFile)
+ .onLineContaining("Factory set(String s);");
+ }
+
+ @Test
+ public void testInheritedSetterMethodFails() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "abstract class SimpleComponent {",
+ " interface Parent {",
+ " SimpleComponent create();",
+ " Parent set(String s);",
+ " }",
+ "",
+ " @Component.Factory",
+ " interface Factory extends Parent {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(String.format(MSGS.twoFactoryMethods(), "create()"))
+ .inFile(componentFile)
+ .onLineContaining("interface Factory");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java b/javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java
new file mode 100644
index 0000000..19eabac
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {ComponentHierarchyValidator}. */
+@RunWith(JUnit4.class)
+public class ComponentHierarchyValidationTest {
+ @Test
+ public void singletonSubcomponent() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Subcomponent",
+ "interface Child {}");
+
+ Compilation compilation = daggerCompiler().compile(component, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("conflicting scopes");
+ assertThat(compilation).hadErrorContaining("test.Parent also has @Singleton");
+
+ Compilation withoutScopeValidation =
+ daggerCompiler()
+ .withOptions("-Adagger.disableInterComponentScopeValidation=none")
+ .compile(component, subcomponent);
+ assertThat(withoutScopeValidation).succeeded();
+ }
+
+ @Test
+ public void productionComponents_productionScopeImplicitOnBoth() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ " Object productionScopedObject();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.ProductionScope;",
+ "",
+ "@ProducerModule",
+ "class ParentModule {",
+ " @Provides @ProductionScope Object parentScopedObject() { return new Object(); }",
+ "}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionSubcomponent;",
+ "",
+ "@ProductionSubcomponent(modules = ChildModule.class)",
+ "interface Child {",
+ " String productionScopedString();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.ProductionScope;",
+ "",
+ "@ProducerModule",
+ "class ChildModule {",
+ " @Provides @ProductionScope String childScopedString() { return new String(); }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().compile(component, subcomponent, parentModule, childModule);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void producerModuleRepeated() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(modules = RepeatedProducerModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject repeatedModule =
+ JavaFileObjects.forSourceLines(
+ "test.RepeatedProducerModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "",
+ "@ProducerModule",
+ "interface RepeatedProducerModule {}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionSubcomponent;",
+ "",
+ "@ProductionSubcomponent(modules = RepeatedProducerModule.class)",
+ "interface Child {}");
+ Compilation compilation = daggerCompiler().compile(component, subcomponent, repeatedModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Child repeats @ProducerModules:",
+ " test.Parent also installs: test.RepeatedProducerModule"))
+ .inFile(component)
+ .onLineContaining("interface Parent");
+ }
+
+ @Test
+ public void factoryMethodForSubcomponentWithBuilder_isNotAllowed() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module(subcomponents = Sub.class)",
+ "class TestModule {",
+ "}");
+
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Sub {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Sub build();",
+ " }",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface C {",
+ " Sub newSub();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(module, component, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Components may not have factory methods for subcomponents that define a builder.");
+ }
+
+ @Test
+ public void repeatedModulesWithScopes() {
+ JavaFileObject testScope =
+ JavaFileObjects.forSourceLines(
+ "test.TestScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface TestScope {}");
+ JavaFileObject moduleWithScopedProvides =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithScopedProvides",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ModuleWithScopedProvides {",
+ " @Provides",
+ " @TestScope",
+ " static Object o() { return new Object(); }",
+ "}");
+ JavaFileObject moduleWithScopedBinds =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithScopedBinds",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface ModuleWithScopedBinds {",
+ " @Binds",
+ " @TestScope",
+ " Object o(String s);",
+ "}");
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(",
+ " modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})",
+ "interface Child {}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(testScope, moduleWithScopedProvides, moduleWithScopedBinds, parent, child);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Child repeats modules with scoped bindings or declarations:",
+ " - test.Parent also includes:",
+ " - test.ModuleWithScopedProvides with scopes: @test.TestScope",
+ " - test.ModuleWithScopedBinds with scopes: @test.TestScope"));
+ }
+
+ @Test
+ public void repeatedModulesWithReusableScope() {
+ JavaFileObject moduleWithScopedProvides =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithScopedProvides",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Reusable;",
+ "",
+ "@Module",
+ "class ModuleWithScopedProvides {",
+ " @Provides",
+ " @Reusable",
+ " static Object o() { return new Object(); }",
+ "}");
+ JavaFileObject moduleWithScopedBinds =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithScopedBinds",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Reusable;",
+ "",
+ "@Module",
+ "interface ModuleWithScopedBinds {",
+ " @Binds",
+ " @Reusable",
+ " Object o(String s);",
+ "}");
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(",
+ " modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})",
+ "interface Child {}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(moduleWithScopedProvides, moduleWithScopedBinds, parent, child);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentProcessorTest.java b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
new file mode 100644
index 0000000..2b79b6f
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
@@ -0,0 +1,2769 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.NPE_FROM_COMPONENT_METHOD;
+import static dagger.internal.codegen.GeneratedLines.NPE_FROM_PROVIDES_METHOD;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Sets;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.MembersInjector;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ComponentProcessorTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public ComponentProcessorTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test public void doubleBindingFromResolvedModules() {
+ JavaFileObject parent = JavaFileObjects.forSourceLines("test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.List;",
+ "",
+ "@Module",
+ "abstract class ParentModule<A> {",
+ " @Provides List<A> provideListB(A a) { return null; }",
+ "}");
+ JavaFileObject child = JavaFileObjects.forSourceLines("test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ChildNumberModule extends ParentModule<Integer> {",
+ " @Provides Integer provideInteger() { return null; }",
+ "}");
+ JavaFileObject another = JavaFileObjects.forSourceLines("test.AnotherModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.List;",
+ "",
+ "@Module",
+ "class AnotherModule {",
+ " @Provides List<Integer> provideListOfInteger() { return null; }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.List;",
+ "",
+ "@Component(modules = {ChildNumberModule.class, AnotherModule.class})",
+ "interface BadComponent {",
+ " List<Integer> listOfInteger();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(parent, child, another, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("java.util.List<java.lang.Integer> is bound multiple times");
+ assertThat(compilation)
+ .hadErrorContaining("@Provides List<Integer> test.ChildNumberModule.provideListB(Integer)");
+ assertThat(compilation)
+ .hadErrorContaining("@Provides List<Integer> test.AnotherModule.provideListOfInteger()");
+ }
+
+ @Test public void privateNestedClassWithWarningThatIsAnErrorInComponent() {
+ JavaFileObject outerClass = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " @Inject OuterClass(InnerClass innerClass) {}",
+ "",
+ " private static final class InnerClass {",
+ " @Inject InnerClass() {}",
+ " }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface BadComponent {",
+ " OuterClass outerClass();",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(
+ compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
+ .compile(outerClass, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into private classes");
+ }
+
+ @Test public void simpleComponent() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " SomeInjectableType someInjectableType();",
+ " Lazy<SomeInjectableType> lazySomeInjectableType();",
+ " Provider<SomeInjectableType> someInjectableTypeProvider();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerSimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import dagger.internal.DoubleCheck;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
+ .addLines(
+ " private DaggerSimpleComponent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public SomeInjectableType someInjectableType() {",
+ " return new SomeInjectableType();",
+ " }",
+ "",
+ " @Override",
+ " public Lazy<SomeInjectableType> lazySomeInjectableType() {")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " return DoubleCheck.lazy(SomeInjectableType_Factory.create());")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " return DoubleCheck.lazy(someInjectableTypeProvider());")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Provider<SomeInjectableType> someInjectableTypeProvider() {")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " return SomeInjectableType_Factory.create();")
+ .addLinesIn(
+ FAST_INIT_MODE, //
+ " Object local = someInjectableTypeProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
+ " }",
+ " return (Provider<SomeInjectableType>) local;")
+ .addLines(
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) new SomeInjectableType();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectableTypeFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ @Test public void componentWithScope() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface SimpleComponent {",
+ " SomeInjectableType someInjectableType();",
+ " Lazy<SomeInjectableType> lazySomeInjectableType();",
+ " Provider<SomeInjectableType> someInjectableTypeProvider();",
+ "}");
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerSimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Object someInjectableType = new MemoizedSentinel();",
+ " private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " private Provider<SomeInjectableType> someInjectableTypeProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.someInjectableTypeProvider =",
+ " DoubleCheck.provider(SomeInjectableType_Factory.create());",
+ " }",
+ "")
+ .addLines(
+ " @Override", //
+ " public SomeInjectableType someInjectableType() {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " Object local = someInjectableType;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = someInjectableType;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new SomeInjectableType();",
+ " someInjectableType =",
+ " DoubleCheck.reentrantCheck(someInjectableType, local);",
+ " }",
+ " }",
+ " }",
+ " return (SomeInjectableType) local;")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " return someInjectableTypeProvider.get();")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Lazy<SomeInjectableType> lazySomeInjectableType() {")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " return DoubleCheck.lazy(someInjectableTypeProvider);")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " return DoubleCheck.lazy(someInjectableTypeProvider());")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Provider<SomeInjectableType> someInjectableTypeProvider() {")
+ .addLinesIn(
+ FAST_INIT_MODE, //
+ " Object local = someInjectableTypeProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
+ " }",
+ " return (Provider<SomeInjectableType>) local;")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " return someInjectableTypeProvider;")
+ .addLines( //
+ " }")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) DaggerSimpleComponent.this.someInjectableType();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }")
+ .build();
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectableTypeFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void simpleComponentWithNesting() {
+ JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines("test.OuterType",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterType {",
+ " static class A {",
+ " @Inject A() {}",
+ " }",
+ " static class B {",
+ " @Inject A a;",
+ " }",
+ " @Component interface SimpleComponent {",
+ " A a();",
+ " void inject(B b);",
+ " }",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerOuterType_SimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerOuterType_SimpleComponent",
+ " implements OuterType.SimpleComponent {",
+ " private DaggerOuterType_SimpleComponent() {}",
+ "",
+ " @Override",
+ " public OuterType.A a() {",
+ " return new OuterType.A();",
+ " }",
+ "",
+ " @Override",
+ " public void inject(OuterType.B b) {",
+ " injectB(b);",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private OuterType.B injectB(OuterType.B instance) {",
+ " OuterType_B_MembersInjector.injectA(instance, new OuterType.A());",
+ " return instance;",
+ " }",
+ "}")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(nestedTypesFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerOuterType_SimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void componentWithModule() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(B b) {}",
+ "}");
+ JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
+ "package test;",
+ "",
+ "interface B {}");
+ JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class C {",
+ " @Inject C() {}",
+ "}");
+
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides B b(C c) { return null; }",
+ "}");
+
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final TestModule testModule;",
+ "",
+ " private DaggerTestComponent(TestModule testModuleParam) {",
+ " this.testModule = testModuleParam;",
+ " }",
+ "",
+ " private B getB() {",
+ " return TestModule_BFactory.b(testModule, new C());",
+ " }",
+ "",
+ " @Override",
+ " public A a() {",
+ " return new A(getB());",
+ " }",
+ "",
+ " static final class Builder {",
+ " private TestModule testModule;",
+ "",
+ " public Builder testModule(TestModule testModule) {",
+ " this.testModule = Preconditions.checkNotNull(testModule);",
+ " return this;",
+ " }",
+ "",
+ " public TestComponent build() {",
+ " if (testModule == null) {",
+ " this.testModule = new TestModule();",
+ " }",
+ " return new DaggerTestComponent(testModule);",
+ " }",
+ " }",
+ "}")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(aFile, bFile, cFile, moduleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void componentWithAbstractModule() {
+ JavaFileObject aFile =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(B b) {}",
+ "}");
+ JavaFileObject bFile =
+ JavaFileObjects.forSourceLines("test.B",
+ "package test;",
+ "",
+ "interface B {}");
+ JavaFileObject cFile =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class C {",
+ " @Inject C() {}",
+ "}");
+
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Provides static B b(C c) { return null; }",
+ "}");
+
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private B getB() {",
+ " return TestModule_BFactory.b(new C());",
+ " }",
+ "",
+ " @Override",
+ " public A a() {",
+ " return new A(getB());",
+ " }",
+ "}")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(aFile, bFile, cFile, moduleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void transitiveModuleDeps() {
+ JavaFileObject always = JavaFileObjects.forSourceLines("test.AlwaysIncluded",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "final class AlwaysIncluded {}");
+ JavaFileObject testModule = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = {DepModule.class, AlwaysIncluded.class})",
+ "final class TestModule extends ParentTestModule {}");
+ JavaFileObject parentTest = JavaFileObjects.forSourceLines("test.ParentTestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = {ParentTestIncluded.class, AlwaysIncluded.class})",
+ "class ParentTestModule {}");
+ JavaFileObject parentTestIncluded = JavaFileObjects.forSourceLines("test.ParentTestIncluded",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = AlwaysIncluded.class)",
+ "final class ParentTestIncluded {}");
+ JavaFileObject depModule = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = {RefByDep.class, AlwaysIncluded.class})",
+ "final class DepModule extends ParentDepModule {}");
+ JavaFileObject refByDep = JavaFileObjects.forSourceLines("test.RefByDep",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = AlwaysIncluded.class)",
+ "final class RefByDep extends ParentDepModule {}");
+ JavaFileObject parentDep = JavaFileObjects.forSourceLines("test.ParentDepModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = {ParentDepIncluded.class, AlwaysIncluded.class})",
+ "class ParentDepModule {}");
+ JavaFileObject parentDepIncluded = JavaFileObjects.forSourceLines("test.ParentDepIncluded",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = AlwaysIncluded.class)",
+ "final class ParentDepIncluded {}");
+
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ "}");
+ // Generated code includes all includes, but excludes the parent modules.
+ // The "always" module should only be listed once.
+ JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " static final class Builder {",
+ "",
+ " @Deprecated",
+ " public Builder testModule(TestModule testModule) {",
+ " Preconditions.checkNotNull(testModule)",
+ " return this;",
+ " }",
+ "",
+ " @Deprecated",
+ " public Builder parentTestIncluded(ParentTestIncluded parentTestIncluded) {",
+ " Preconditions.checkNotNull(parentTestIncluded)",
+ " return this;",
+ " }",
+ "",
+ " @Deprecated",
+ " public Builder alwaysIncluded(AlwaysIncluded alwaysIncluded) {",
+ " Preconditions.checkNotNull(alwaysIncluded)",
+ " return this;",
+ " }",
+ "",
+ " @Deprecated",
+ " public Builder depModule(DepModule depModule) {",
+ " Preconditions.checkNotNull(depModule)",
+ " return this;",
+ " }",
+ "",
+ " @Deprecated",
+ " public Builder parentDepIncluded(ParentDepIncluded parentDepIncluded) {",
+ " Preconditions.checkNotNull(parentDepIncluded)",
+ " return this;",
+ " }",
+ "",
+ " @Deprecated",
+ " public Builder refByDep(RefByDep refByDep) {",
+ " Preconditions.checkNotNull(refByDep)",
+ " return this;",
+ " }",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent();",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ always,
+ testModule,
+ parentTest,
+ parentTestIncluded,
+ depModule,
+ refByDep,
+ parentDep,
+ parentDepIncluded,
+ componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void generatedTransitiveModule() {
+ JavaFileObject rootModule = JavaFileObjects.forSourceLines("test.RootModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = GeneratedModule.class)",
+ "final class RootModule {}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = RootModule.class)",
+ "interface TestComponent {}");
+ assertThat(
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(rootModule, component))
+ .failed();
+ assertThat(
+ daggerCompiler(
+ new GeneratingProcessor(
+ "test.GeneratedModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "final class GeneratedModule {}"))
+ .compile(rootModule, component))
+ .succeeded();
+ }
+
+ @Test
+ public void generatedModuleInSubcomponent() {
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = GeneratedModule.class)",
+ "interface ChildComponent {}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " ChildComponent childComponent();",
+ "}");
+ assertThat(
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(subcomponent, component))
+ .failed();
+ assertThat(
+ daggerCompiler(
+ new GeneratingProcessor(
+ "test.GeneratedModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "final class GeneratedModule {}"))
+ .compile(subcomponent, component))
+ .succeeded();
+ }
+
+ @Test
+ public void subcomponentNotGeneratedIfNotUsedInGraph() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " String notSubcomponent();",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module(subcomponents = Child.class)",
+ "class ParentModule {",
+ " @Provides static String notSubcomponent() { return new String(); }",
+ "}");
+
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Child build();",
+ " }",
+ "}");
+
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ "",
+ " private DaggerParent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Parent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public String notSubcomponent() {",
+ " return ParentModule_NotSubcomponentFactory.notSubcomponent();",
+ " }",
+ "",
+ " static final class Builder {",
+ "",
+ " private Builder() {}",
+ "",
+ " @Deprecated",
+ " public Builder parentModule(ParentModule parentModule) {",
+ " Preconditions.checkNotNull(parentModule);",
+ " return this;",
+ " }",
+ "",
+ " public Parent build() {",
+ " return new DaggerParent();",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(component, module, subcomponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ @Test
+ public void testDefaultPackage() {
+ JavaFileObject aClass = JavaFileObjects.forSourceLines("AClass", "class AClass {}");
+ JavaFileObject bClass = JavaFileObjects.forSourceLines("BClass",
+ "import javax.inject.Inject;",
+ "",
+ "class BClass {",
+ " @Inject BClass(AClass a) {}",
+ "}");
+ JavaFileObject aModule = JavaFileObjects.forSourceLines("AModule",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module class AModule {",
+ " @Provides AClass aClass() {",
+ " return new AClass();",
+ " }",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("SomeComponent",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = AModule.class)",
+ "interface SomeComponent {",
+ " BClass bClass();",
+ "}");
+ assertThat(
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(aModule, aClass, bClass, component))
+ .succeeded();
+ }
+
+ @Test public void membersInjection() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType() {}",
+ "}");
+ JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectedType {",
+ " @Inject SomeInjectableType injectedField;",
+ " SomeInjectedType() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " void inject(SomeInjectedType instance);",
+ " SomeInjectedType injectAndReturn(SomeInjectedType instance);",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerSimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " @Override",
+ " public void inject(SomeInjectedType instance) {",
+ " injectSomeInjectedType(instance);",
+ " }",
+ "",
+ " @Override",
+ " public SomeInjectedType injectAndReturn(SomeInjectedType instance) {",
+ " return injectSomeInjectedType(instance);",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {",
+ " SomeInjectedType_MembersInjector.injectInjectedField(",
+ " instance, new SomeInjectableType());",
+ " return instance;",
+ " }",
+ "}")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectableTypeFile, injectedTypeFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void componentInjection() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType(SimpleComponent component) {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " SomeInjectableType someInjectableType();",
+ " Provider<SimpleComponent> selfProvider();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private Provider<SimpleComponent> simpleComponentProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.simpleComponentProvider = InstanceFactory.create((SimpleComponent) this);",
+ " }",
+ "",
+ " @Override",
+ " public SomeInjectableType someInjectableType() {",
+ " return new SomeInjectableType(this)",
+ " }",
+ "",
+ " @Override",
+ " public Provider<SimpleComponent> selfProvider() {",
+ " return simpleComponentProvider;",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectableTypeFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void membersInjectionInsideProvision() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType() {}",
+ "}");
+ JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectedType {",
+ " @Inject SomeInjectableType injectedField;",
+ " @Inject SomeInjectedType() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " SomeInjectedType createAndInject();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerSimpleComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " @Override",
+ " public SomeInjectedType createAndInject() {",
+ " return injectSomeInjectedType(",
+ " SomeInjectedType_Factory.newInstance());",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {",
+ " SomeInjectedType_MembersInjector.injectInjectedField(",
+ " instance, new SomeInjectableType());",
+ " return instance;",
+ " }",
+ "}")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectableTypeFile, injectedTypeFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void componentDependency() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A() {}",
+ "}");
+ JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class B {",
+ " @Inject B(Provider<A> a) {}",
+ "}");
+ JavaFileObject aComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface AComponent {",
+ " A a();",
+ "}");
+ JavaFileObject bComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = AComponent.class)",
+ "interface BComponent {",
+ " B b();",
+ "}");
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerBComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerBComponent implements BComponent {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " private Provider<A> aProvider;")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private final AComponent aComponent;",
+ " private volatile Provider<A> aProvider;",
+ "",
+ " private DaggerBComponent(AComponent aComponentParam) {",
+ " this.aComponent = aComponentParam;",
+ " }",
+ "",
+ " private Provider<A> getAProvider() {",
+ " Object local = aProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " aProvider = (Provider<A>) local;",
+ " }",
+ " return (Provider<A>) local;",
+ " }")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final AComponent aComponentParam) {",
+ " this.aProvider = new test_AComponent_a(aComponentParam);",
+ " }")
+ .addLines(
+ "",
+ " @Override",
+ " public B b() {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " return new B(aProvider);")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " return new B(getAProvider());")
+ .addLines(
+ " }",
+ "",
+ " static final class Builder {",
+ " private AComponent aComponent;",
+ "",
+ " public Builder aComponent(AComponent aComponent) {",
+ " this.aComponent = Preconditions.checkNotNull(aComponent);",
+ " return this;",
+ " }",
+ "",
+ " public BComponent build() {",
+ " Preconditions.checkBuilderRequirement(aComponent, AComponent.class);",
+ " return new DaggerBComponent(aComponent);",
+ " }",
+ " }")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " private static class test_AComponent_a implements Provider<A> {",
+ " private final AComponent aComponent;",
+ " ",
+ " test_AComponent_a(AComponent aComponent) {",
+ " this.aComponent = aComponent;",
+ " }",
+ " ",
+ " @Override()",
+ " public A get() {",
+ " return Preconditions.checkNotNull(",
+ " aComponent.a(), " + NPE_FROM_COMPONENT_METHOD + ");",
+ " }",
+ " }",
+ "}")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T)",
+ " Preconditions.checkNotNull(",
+ " DaggerBComponent.this.aComponent.a(),",
+ " " + NPE_FROM_COMPONENT_METHOD + ");",
+ " default:",
+ " throw new AssertionError(id);",
+ " }",
+ " }",
+ " }")
+ .build();
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(aFile, bFile, aComponentFile, bComponentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerBComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void moduleNameCollision() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "public final class A {}");
+ JavaFileObject otherAFile = JavaFileObjects.forSourceLines("other.test.A",
+ "package other.test;",
+ "",
+ "public final class A {}");
+
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "public final class TestModule {",
+ " @Provides A a() { return null; }",
+ "}");
+ JavaFileObject otherModuleFile = JavaFileObjects.forSourceLines("other.test.TestModule",
+ "package other.test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "public final class TestModule {",
+ " @Provides A a() { return null; }",
+ "}");
+
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {TestModule.class, other.test.TestModule.class})",
+ "interface TestComponent {",
+ " A a();",
+ " other.test.A otherA();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final TestModule testModule;",
+ " private final other.test.TestModule testModule2;",
+ "",
+ " private DaggerTestComponent(",
+ " TestModule testModuleParam,",
+ " other.test.TestModule testModule2Param) {",
+ " this.testModule = testModuleParam;",
+ " this.testModule2 = testModule2Param;",
+ " }",
+ "",
+ " @Override",
+ " public A a() {",
+ " return TestModule_AFactory.a(testModule);",
+ " }",
+ "",
+ " @Override",
+ " public other.test.A otherA() {",
+ " return other.test.TestModule_AFactory.a(testModule2);",
+ " }",
+ "",
+ " static final class Builder {",
+ " private TestModule testModule;",
+ " private other.test.TestModule testModule2;",
+ "",
+ " public Builder testModule(TestModule testModule) {",
+ " this.testModule = Preconditions.checkNotNull(testModule);",
+ " return this;",
+ " }",
+ "",
+ " public Builder testModule(other.test.TestModule testModule) {",
+ " this.testModule2 = Preconditions.checkNotNull(testModule);",
+ " return this;",
+ " }",
+ "",
+ " public TestComponent build() {",
+ " if (testModule == null) {",
+ " this.testModule = new TestModule();",
+ " }",
+ " if (testModule2 == null) {",
+ " this.testModule2 = new other.test.TestModule();",
+ " }",
+ " return new DaggerTestComponent(testModule, testModule2);",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(aFile, otherAFile, moduleFile, otherModuleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void ignoresDependencyMethodsFromObject() {
+ JavaFileObject injectedTypeFile =
+ JavaFileObjects.forSourceLines(
+ "test.InjectedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class InjectedType {",
+ " @Inject InjectedType(",
+ " String stringInjection,",
+ " int intInjection,",
+ " AComponent aComponent,",
+ " Class<AComponent> aClass) {}",
+ "}");
+ JavaFileObject aComponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.AComponent",
+ "package test;",
+ "",
+ "class AComponent {",
+ " String someStringInjection() {",
+ " return \"injectedString\";",
+ " }",
+ "",
+ " int someIntInjection() {",
+ " return 123;",
+ " }",
+ "",
+ " Class<AComponent> someClassInjection() {",
+ " return AComponent.class;",
+ " }",
+ "",
+ " @Override",
+ " public String toString() {",
+ " return null;",
+ " }",
+ "",
+ " @Override",
+ " public int hashCode() {",
+ " return 456;",
+ " }",
+ "",
+ " @Override",
+ " public AComponent clone() {",
+ " return null;",
+ " }",
+ "}");
+ JavaFileObject bComponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.AComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = AComponent.class)",
+ "interface BComponent {",
+ " InjectedType injectedType();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerBComponent",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerBComponent implements BComponent {",
+ " private final AComponent aComponent;",
+ "",
+ " private DaggerBComponent(AComponent aComponentParam) {",
+ " this.aComponent = aComponentParam;",
+ " }",
+ "",
+ " @Override",
+ " public InjectedType injectedType() {",
+ " return new InjectedType(",
+ " Preconditions.checkNotNull(",
+ " aComponent.someStringInjection(),",
+ " \"Cannot return null from a non-@Nullable component method\"),",
+ " aComponent.someIntInjection(),",
+ " aComponent,",
+ " Preconditions.checkNotNull(",
+ " aComponent.someClassInjection(),",
+ " \"Cannot return null from a non-@Nullable component method\"));",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectedTypeFile, aComponentFile, bComponentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerBComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void resolutionOrder() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(B b) {}",
+ "}");
+ JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class B {",
+ " @Inject B(C c) {}",
+ "}");
+ JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class C {",
+ " @Inject C() {}",
+ "}");
+ JavaFileObject xFile = JavaFileObjects.forSourceLines("test.X",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class X {",
+ " @Inject X(C c) {}",
+ "}");
+
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " A a();",
+ " C c();",
+ " X x();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private B getB() {",
+ " return new B(new C());",
+ " }",
+ "",
+ " @Override",
+ " public A a() {",
+ " return new A(getB());",
+ " }",
+ "",
+ " @Override",
+ " public C c() {",
+ " return new C();",
+ " }",
+ "",
+ " @Override",
+ " public X x() {",
+ " return new X(new C());",
+ " }",
+ "}")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(aFile, bFile, cFile, xFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void simpleComponent_redundantComponentMethod() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType() {}",
+ "}");
+ JavaFileObject componentSupertypeAFile = JavaFileObjects.forSourceLines("test.SupertypeA",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SupertypeA {",
+ " SomeInjectableType someInjectableType();",
+ "}");
+ JavaFileObject componentSupertypeBFile = JavaFileObjects.forSourceLines("test.SupertypeB",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SupertypeB {",
+ " SomeInjectableType someInjectableType();",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent extends SupertypeA, SupertypeB {",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private DaggerSimpleComponent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public SomeInjectableType someInjectableType() {",
+ " return new SomeInjectableType();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ injectableTypeFile,
+ componentSupertypeAFile,
+ componentSupertypeBFile,
+ componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ @Test public void simpleComponent_inheritedComponentMethodDep() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType() {}",
+ "}");
+ JavaFileObject componentSupertype = JavaFileObjects.forSourceLines("test.Supertype",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Supertype {",
+ " SomeInjectableType someInjectableType();",
+ "}");
+ JavaFileObject depComponentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SimpleComponent extends Supertype {",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " @Override",
+ " public SomeInjectableType someInjectableType() {",
+ " return new SomeInjectableType();",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectableTypeFile, componentSupertype, depComponentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void wildcardGenericsRequiresAtProvides() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A() {}",
+ "}");
+ JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class B<T> {",
+ " @Inject B(T t) {}",
+ "}");
+ JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class C {",
+ " @Inject C(B<? extends A> bA) {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " C c();",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(aFile, bFile, cFile, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.B<? extends test.A> cannot be provided without an @Provides-annotated method");
+ }
+
+ // https://github.com/google/dagger/issues/630
+ @Test
+ public void arrayKeyRequiresAtProvides() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " String[] array();",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("String[] cannot be provided without an @Provides-annotated method");
+ }
+
+ @Test
+ public void componentImplicitlyDependsOnGeneratedType() {
+ JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SomeInjectableType {",
+ " @Inject SomeInjectableType(GeneratedType generatedType) {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " SomeInjectableType someInjectableType();",
+ "}");
+ Compilation compilation =
+ daggerCompiler(
+ new GeneratingProcessor(
+ "test.GeneratedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class GeneratedType {",
+ " @Inject GeneratedType() {}",
+ "}"))
+ .withOptions(compilerMode.javacopts())
+ .compile(injectableTypeFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent");
+ }
+
+ @Test
+ public void componentSupertypeDependsOnGeneratedType() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SimpleComponent extends SimpleComponentInterface {}");
+ JavaFileObject interfaceFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponentInterface",
+ "package test;",
+ "",
+ "interface SimpleComponentInterface {",
+ " GeneratedType generatedType();",
+ "}");
+ Compilation compilation =
+ daggerCompiler(
+ new GeneratingProcessor(
+ "test.GeneratedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class GeneratedType {",
+ " @Inject GeneratedType() {}",
+ "}"))
+ .withOptions(compilerMode.javacopts())
+ .compile(componentFile, interfaceFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent");
+ }
+
+ /**
+ * We warn when generating a {@link MembersInjector} for a type post-hoc (i.e., if Dagger wasn't
+ * invoked when compiling the type). But Dagger only generates {@link MembersInjector}s for types
+ * with {@link Inject @Inject} constructors if they have any injection sites, and it only
+ * generates them for types without {@link Inject @Inject} constructors if they have local
+ * (non-inherited) injection sites. So make sure we warn in only those cases where running the
+ * Dagger processor actually generates a {@link MembersInjector}.
+ */
+ @Test
+ public void unprocessedMembersInjectorNotes() {
+ Compilation compilation =
+ javac()
+ .withOptions(
+ compilerMode
+ .javacopts()
+ .append(
+ "-Xlint:-processing",
+ "-Adagger.warnIfInjectionFactoryNotGeneratedUpstream=enabled"))
+ .withProcessors(
+ new ElementFilteringComponentProcessor(
+ Predicates.not(
+ element ->
+ MoreElements.getPackage(element)
+ .getQualifiedName()
+ .contentEquals("test.inject"))))
+ .compile(
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(test.inject.NoInjectMemberNoConstructor object);",
+ " void inject(test.inject.NoInjectMemberWithConstructor object);",
+ " void inject(test.inject.LocalInjectMemberNoConstructor object);",
+ " void inject(test.inject.LocalInjectMemberWithConstructor object);",
+ " void inject(test.inject.ParentInjectMemberNoConstructor object);",
+ " void inject(test.inject.ParentInjectMemberWithConstructor object);",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class TestModule {",
+ " @Provides static Object object() {",
+ " return \"object\";",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.inject.NoInjectMemberNoConstructor",
+ "package test.inject;",
+ "",
+ "public class NoInjectMemberNoConstructor {",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.inject.NoInjectMemberWithConstructor",
+ "package test.inject;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class NoInjectMemberWithConstructor {",
+ " @Inject NoInjectMemberWithConstructor() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.inject.LocalInjectMemberNoConstructor",
+ "package test.inject;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class LocalInjectMemberNoConstructor {",
+ " @Inject Object object;",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.inject.LocalInjectMemberWithConstructor",
+ "package test.inject;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class LocalInjectMemberWithConstructor {",
+ " @Inject LocalInjectMemberWithConstructor() {}",
+ " @Inject Object object;",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.inject.ParentInjectMemberNoConstructor",
+ "package test.inject;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class ParentInjectMemberNoConstructor",
+ " extends LocalInjectMemberNoConstructor {}"),
+ JavaFileObjects.forSourceLines(
+ "test.inject.ParentInjectMemberWithConstructor",
+ "package test.inject;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class ParentInjectMemberWithConstructor",
+ " extends LocalInjectMemberNoConstructor {",
+ " @Inject ParentInjectMemberWithConstructor() {}",
+ "}"));
+
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .hadNoteContaining(
+ "Generating a MembersInjector for "
+ + "test.inject.LocalInjectMemberNoConstructor. "
+ + "Prefer to run the dagger processor over that class instead.");
+ assertThat(compilation)
+ .hadNoteContaining(
+ "Generating a MembersInjector for "
+ + "test.inject.LocalInjectMemberWithConstructor. "
+ + "Prefer to run the dagger processor over that class instead.");
+ assertThat(compilation)
+ .hadNoteContaining(
+ "Generating a MembersInjector for "
+ + "test.inject.ParentInjectMemberWithConstructor. "
+ + "Prefer to run the dagger processor over that class instead.");
+ assertThat(compilation).hadNoteCount(3);
+ }
+
+ @Test
+ public void scopeAnnotationOnInjectConstructorNotValid() {
+ JavaFileObject aScope =
+ JavaFileObjects.forSourceLines(
+ "test.AScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface AScope {}");
+ JavaFileObject aClass =
+ JavaFileObjects.forSourceLines(
+ "test.AClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class AClass {",
+ " @Inject @AScope AClass() {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(aScope, aClass);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Scope annotations are not allowed on @Inject constructors")
+ .inFile(aClass)
+ .onLine(6);
+ }
+
+ @Test
+ public void unusedSubcomponents_dontResolveExtraBindingsInParentComponents() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(subcomponents = Pruned.class)",
+ "class TestModule {}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = TestModule.class)",
+ "interface Parent {}");
+
+ JavaFileObject prunedSubcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Pruned",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Pruned {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Pruned build();",
+ " }",
+ "",
+ " Foo foo();",
+ "}");
+ JavaFileObject generated =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ " private DaggerParent() {",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Parent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " @Deprecated",
+ " public Builder testModule(TestModule testModule) {",
+ " Preconditions.checkNotNull(testModule);",
+ " return this;",
+ " }",
+ "",
+ " public Parent build() {",
+ " return new DaggerParent();",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(foo, module, component, prunedSubcomponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .hasSourceEquivalentTo(generated);
+ }
+
+ @Test
+ public void bindsToDuplicateBinding_bindsKeyIsNotDuplicated() {
+ JavaFileObject firstModule =
+ JavaFileObjects.forSourceLines(
+ "test.FirstModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class FirstModule {",
+ " @Provides static String first() { return \"first\"; }",
+ "}");
+ JavaFileObject secondModule =
+ JavaFileObjects.forSourceLines(
+ "test.SecondModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class SecondModule {",
+ " @Provides static String second() { return \"second\"; }",
+ "}");
+ JavaFileObject bindsModule =
+ JavaFileObjects.forSourceLines(
+ "test.BindsModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class BindsModule {",
+ " @Binds abstract Object bindToDuplicateBinding(String duplicate);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = {FirstModule.class, SecondModule.class, BindsModule.class})",
+ "interface TestComponent {",
+ " Object notDuplicated();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(firstModule, secondModule, bindsModule, component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("java.lang.String is bound multiple times")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void nullIncorrectlyReturnedFromNonNullableInlinedProvider() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "public abstract class TestModule {",
+ " @Provides static String nonNullableString() { return \"string\"; }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.InjectsMember",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class InjectsMember {",
+ " @Inject String member;",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " String nonNullableString();",
+ " void inject(InjectsMember member);",
+ "}"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.TestModule_NonNullableStringFactory")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.TestModule_NonNullableStringFactory",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_NonNullableStringFactory",
+ " implements Factory<String> {",
+ " @Override",
+ " public String get() {",
+ " return nonNullableString();",
+ " }",
+ "",
+ " public static String nonNullableString() {",
+ " return Preconditions.checkNotNull(",
+ " TestModule.nonNullableString(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}"));
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public String nonNullableString() {",
+ " return TestModule_NonNullableStringFactory.nonNullableString());",
+ " }",
+ "",
+ " @Override",
+ " public void inject(InjectsMember member) {",
+ " injectInjectsMember(member);",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private InjectsMember injectInjectsMember(InjectsMember instance) {",
+ " InjectsMember_MembersInjector.injectMember(instance,",
+ " TestModule_NonNullableStringFactory.nonNullableString());",
+ " return instance;",
+ " }",
+ "}")
+ .build();
+
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void nullCheckingIgnoredWhenProviderReturnsPrimitive() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "public abstract class TestModule {",
+ " @Provides static int primitiveInteger() { return 1; }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.InjectsMember",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class InjectsMember {",
+ " @Inject Integer member;",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Integer nonNullableInteger();",
+ " void inject(InjectsMember member);",
+ "}"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.TestModule_PrimitiveIntegerFactory")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.TestModule_PrimitiveIntegerFactory",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_PrimitiveIntegerFactory",
+ " implements Factory<Integer> {",
+ "",
+ " @Override",
+ " public Integer get() {",
+ " return primitiveInteger();",
+ " }",
+ "",
+ " public static int primitiveInteger() {",
+ " return TestModule.primitiveInteger();",
+ " }",
+ "}"));
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public Integer nonNullableInteger() {",
+ " return TestModule.primitiveInteger();",
+ " }",
+ "",
+ " @Override",
+ " public void inject(InjectsMember member) {",
+ " injectInjectsMember(member);",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private InjectsMember injectInjectsMember(InjectsMember instance) {",
+ " InjectsMember_MembersInjector.injectMember(",
+ " instance, TestModule.primitiveInteger());",
+ " return instance;",
+ " }",
+ "}")
+ .build();
+
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void privateMethodUsedOnlyInChildDoesNotUseQualifiedThis() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules=TestModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject testModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Provides @Singleton static Number number() {",
+ " return 3;",
+ " }",
+ "",
+ " @Provides static String string(Number number) {",
+ " return number.toString();",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " String string();",
+ "}");
+
+ JavaFileObject expectedPattern =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ " private String getString() {",
+ " return TestModule_StringFactory.string(numberProvider.get());",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(parent, testModule, child);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .containsElementsIn(expectedPattern);
+ }
+
+ @Test
+ public void componentMethodInChildCallsComponentMethodInParent() {
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "test.Supertype",
+ "package test;",
+ "",
+ "interface Supertype {",
+ " String string();",
+ "}");
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules=TestModule.class)",
+ "interface Parent extends Supertype {",
+ " Child child();",
+ "}");
+ JavaFileObject testModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Provides @Singleton static Number number() {",
+ " return 3;",
+ " }",
+ "",
+ " @Provides static String string(Number number) {",
+ " return number.toString();",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child extends Supertype {}");
+
+ JavaFileObject expectedPattern =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ " private final class ChildImpl implements Child {",
+ " @Override",
+ " public String string() {",
+ " return DaggerParent.this.string();",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(supertype, parent, testModule, child);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .containsElementsIn(expectedPattern);
+ }
+
+ @Test
+ public void justInTimeAtInjectConstructor_hasGeneratedQualifier() {
+ JavaFileObject injected =
+ JavaFileObjects.forSourceLines(
+ "test.Injected",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Injected {",
+ " @Inject Injected(@GeneratedQualifier String string) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static String unqualified() {",
+ " return new String();",
+ " }",
+ "",
+ " @Provides",
+ " @GeneratedQualifier",
+ " static String qualified() {",
+ " return new String();",
+ " }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Injected injected();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public Injected injected() {",
+ // Ensure that the qualified @Provides method is used. It's also probably more likely
+ // that if the qualifier type hasn't been generated, a duplicate binding error will be
+ // reported, since the annotation won't be recognized as a qualifier and instead as an
+ // ordinary annotation.
+ " return new Injected(TestModule_QualifiedFactory.qualified());",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler(
+ new GeneratingProcessor(
+ "test.GeneratedQualifier",
+ "package test;",
+ "",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "",
+ "import java.lang.annotation.Retention;",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Retention(RUNTIME)",
+ "@Qualifier",
+ "@interface GeneratedQualifier {}"))
+ .compile(injected, module, component);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void moduleHasGeneratedQualifier() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static String unqualified() {",
+ " return new String();",
+ " }",
+ "",
+ " @Provides",
+ " @GeneratedQualifier",
+ " static String qualified() {",
+ " return new String();",
+ " }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " String unqualified();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public String unqualified() {",
+ // Ensure that the unqualified @Provides method is used. It's also probably more likely
+ // if the qualifier hasn't been generated, a duplicate binding exception will be thrown
+ // since the annotation won't be considered a qualifier
+ " return TestModule_UnqualifiedFactory.unqualified();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler(
+ new GeneratingProcessor(
+ "test.GeneratedQualifier",
+ "package test;",
+ "",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "",
+ "import java.lang.annotation.Retention;",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Retention(RUNTIME)",
+ "@Qualifier",
+ "@interface GeneratedQualifier {}"))
+ .compile(module, component);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void publicComponentType() {
+ JavaFileObject publicComponent =
+ JavaFileObjects.forSourceLines(
+ "test.PublicComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "public interface PublicComponent {}");
+ Compilation compilation = daggerCompiler().compile(publicComponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerPublicComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerPublicComponent",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerPublicComponent implements PublicComponent {",
+ " private DaggerPublicComponent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static PublicComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public PublicComponent build() {",
+ " return new DaggerPublicComponent();",
+ " }",
+ " }",
+ "}"));
+ }
+
+ /**
+ * A {@link ComponentProcessor} that excludes elements using a {@link Predicate}.
+ */
+ private static final class ElementFilteringComponentProcessor extends AbstractProcessor {
+ private final ComponentProcessor componentProcessor = new ComponentProcessor();
+ private final Predicate<? super Element> filter;
+
+ /**
+ * Creates a {@link ComponentProcessor} that only processes elements that match {@code filter}.
+ */
+ public ElementFilteringComponentProcessor(Predicate<? super Element> filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ componentProcessor.init(processingEnv);
+ }
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return componentProcessor.getSupportedAnnotationTypes();
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return componentProcessor.getSupportedSourceVersion();
+ }
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ return componentProcessor.getSupportedOptions();
+ }
+
+ @Override
+ public boolean process(
+ Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
+ return componentProcessor.process(
+ annotations,
+ new RoundEnvironment() {
+ @Override
+ public boolean processingOver() {
+ return roundEnv.processingOver();
+ }
+
+ @Override
+ public Set<? extends Element> getRootElements() {
+ return Sets.filter(roundEnv.getRootElements(), filter);
+ }
+
+ @Override
+ public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) {
+ return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter);
+ }
+
+ @Override
+ public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
+ return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter);
+ }
+
+ @Override
+ public boolean errorRaised() {
+ return roundEnv.errorRaised();
+ }
+ });
+ }
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
new file mode 100644
index 0000000..85e2d7b
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.NPE_FROM_COMPONENT_METHOD;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ComponentRequirementFieldTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public ComponentRequirementFieldTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void bindsInstance() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "import java.util.List;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " int i();",
+ " List<String> list();",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder i(int i);",
+ " @BindsInstance Builder list(List<String> list);",
+ " TestComponent build();",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final Integer i;",
+ " private final List<String> list;",
+ "",
+ " private DaggerTestComponent(Integer iParam, List<String> listParam) {",
+ " this.i = iParam;",
+ " this.list = listParam;",
+ " }",
+ "",
+ " @Override",
+ " public int i() {",
+ " return i;",
+ " }",
+ "",
+ " @Override",
+ " public List<String> list() {",
+ " return list;",
+ " }",
+ "",
+ " private static final class Builder implements TestComponent.Builder {",
+ " private Integer i;",
+ " private List<String> list;",
+ "",
+ " @Override",
+ " public Builder i(int i) {",
+ " this.i = Preconditions.checkNotNull(i);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public Builder list(List<String> list) {",
+ " this.list = Preconditions.checkNotNull(list);",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public TestComponent build() {",
+ " Preconditions.checkBuilderRequirement(i, Integer.class);",
+ " Preconditions.checkBuilderRequirement(list, List.class);",
+ " return new DaggerTestComponent(i, list);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void instanceModuleMethod() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides int i() { return 0; }",
+ "}");
+ JavaFileObject otherPackageModule =
+ JavaFileObjects.forSourceLines(
+ "other.OtherPackageModule",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "public class OtherPackageModule {",
+ " @Provides long l() { return 0L; }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import other.OtherPackageModule;",
+ "",
+ "@Component(modules = {ParentModule.class, OtherPackageModule.class})",
+ "interface TestComponent {",
+ " int i();",
+ " long l();",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(module, otherPackageModule, component);
+ assertThat(compilation).succeeded();
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import other.OtherPackageModule;",
+ "import other.OtherPackageModule_LFactory;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final ParentModule parentModule;",
+ " private final OtherPackageModule otherPackageModule;",
+ "",
+ " @Override",
+ " public int i() {",
+ " return parentModule.i();",
+ " }",
+ "",
+ " @Override",
+ " public long l() {",
+ " return OtherPackageModule_LFactory.l(otherPackageModule);",
+ " }",
+ "}");
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void componentInstances() {
+ JavaFileObject dependency =
+ JavaFileObjects.forSourceLines(
+ "test.Dep",
+ "package test;",
+ "",
+ "interface Dep {",
+ " String string();",
+ " Object object();",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = Dep.class)",
+ "interface TestComponent {",
+ " TestComponent self();",
+ " TestSubcomponent subcomponent();",
+ "",
+ " Dep dep();",
+ " String methodOnDep();",
+ " Object otherMethodOnDep();",
+ "}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface TestSubcomponent {",
+ " TestComponent parent();",
+ " Dep depFromSubcomponent();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(dependency, component, subcomponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final Dep dep;",
+ "",
+ " private DaggerTestComponent(Dep depParam) {",
+ " this.dep = depParam;",
+ " }",
+ "",
+ " @Override",
+ " public TestComponent self() {",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public Dep dep() {",
+ " return dep;",
+ " }",
+ "",
+ " @Override",
+ " public String methodOnDep() {",
+ " return Preconditions.checkNotNull(",
+ " dep.string(), " + NPE_FROM_COMPONENT_METHOD + " );",
+ " }",
+ "",
+ " @Override",
+ " public Object otherMethodOnDep() {",
+ " return Preconditions.checkNotNull(",
+ " dep.object(), " + NPE_FROM_COMPONENT_METHOD + " );",
+ " }",
+ "",
+ " private final class TestSubcomponentImpl implements TestSubcomponent {",
+ " @Override",
+ " public TestComponent parent() {",
+ " return DaggerTestComponent.this;",
+ " }",
+ "",
+ " @Override",
+ " public Dep depFromSubcomponent() {",
+ " return DaggerTestComponent.this.dep;",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void componentRequirementNeededInFactoryCreationOfSubcomponent() {
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.Provides;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides",
+ // intentionally non-static. this needs to require the module when the subcompnent
+ // adds to the Set binding
+ " Object reliesOnMultibinding(Set<Object> set) { return set; }",
+ "",
+ " @Provides @IntoSet static Object contribution() { return new Object(); }",
+ "}");
+
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ChildModule {",
+ " @Provides @IntoSet static Object contribution() { return new Object(); }",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface TestComponent {",
+ " Provider<Object> dependsOnMultibinding();",
+ " TestSubcomponent subcomponent();",
+ "}");
+
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface TestSubcomponent {",
+ " Provider<Object> dependsOnMultibinding();",
+ "}");
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final ParentModule parentModule;",
+ "",
+ " private DaggerTestComponent(ParentModule parentModuleParam) {",
+ " this.parentModule = parentModuleParam;",
+ " }",
+ "",
+ " private final class TestSubcomponentImpl implements TestSubcomponent {",
+ " private Set<Object> getSetOfObject() {",
+ " return ImmutableSet.<Object>of(",
+ " ParentModule_ContributionFactory.contribution(),",
+ " ChildModule_ContributionFactory.contribution());",
+ " }",
+ "",
+ " private Object getObject() {",
+ " return ParentModule_ReliesOnMultibindingFactory.reliesOnMultibinding(",
+ " DaggerTestComponent.this.parentModule, getSetOfObject());",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final ParentModule parentModule;",
+ "",
+ " private DaggerTestComponent(ParentModule parentModuleParam) {",
+ " this.parentModule = parentModuleParam;",
+ " initialize(parentModuleParam);",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final ParentModule parentModuleParam) {",
+ " this.setOfObjectProvider =",
+ " SetFactory.<Object>builder(1, 0)",
+ " .addProvider(ParentModule_ContributionFactory.create())",
+ " .build();",
+ " this.reliesOnMultibindingProvider =",
+ " ParentModule_ReliesOnMultibindingFactory.create(",
+ " parentModuleParam, setOfObjectProvider);",
+ " }",
+ "",
+ " private final class TestSubcomponentImpl implements TestSubcomponent {",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.setOfObjectProvider =",
+ " SetFactory.<Object>builder(2, 0)",
+ " .addProvider(ParentModule_ContributionFactory.create())",
+ " .addProvider(ChildModule_ContributionFactory.create())",
+ " .build();",
+ " this.reliesOnMultibindingProvider =",
+ " ParentModule_ReliesOnMultibindingFactory.create(",
+ " DaggerTestComponent.this.parentModule, setOfObjectProvider);",
+ " }",
+ " }",
+ "}");
+ }
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(parentModule, childModule, component, subcomponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentValidationTest.java b/javatests/dagger/internal/codegen/ComponentValidationTest.java
new file mode 100644
index 0000000..169a318
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentValidationTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ComponentValidationTest {
+ @Test
+ public void componentOnConcreteClass() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "final class NotAComponent {}");
+ Compilation compilation = daggerCompiler().compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("interface");
+ }
+
+ @Test public void componentOnEnum() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "enum NotAComponent {",
+ " INSTANCE",
+ "}");
+ Compilation compilation = daggerCompiler().compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("interface");
+ }
+
+ @Test public void componentOnAnnotation() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "@interface NotAComponent {}");
+ Compilation compilation = daggerCompiler().compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("interface");
+ }
+
+ @Test public void nonModuleModule() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = Object.class)",
+ "interface NotAComponent {}");
+ Compilation compilation = daggerCompiler().compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("is not annotated with @Module");
+ }
+
+ @Test
+ public void componentWithInvalidModule() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.BadModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class BadModule {",
+ " @Binds abstract Object noParameters();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.BadComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = BadModule.class)",
+ "interface BadComponent {",
+ " Object object();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, component);
+ assertThat(compilation)
+ .hadErrorContaining("test.BadModule has errors")
+ .inFile(component)
+ .onLine(5);
+ }
+
+ @Test
+ public void attemptToInjectWildcardGenerics() {
+ JavaFileObject testComponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " Lazy<? extends Number> wildcardNumberLazy();",
+ " Provider<? super Number> wildcardNumberProvider();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(testComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("wildcard type").inFile(testComponent).onLine(9);
+ assertThat(compilation).hadErrorContaining("wildcard type").inFile(testComponent).onLine(10);
+ }
+
+ @Test
+ public void invalidComponentDependencies() {
+ JavaFileObject testComponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = int.class)",
+ "interface TestComponent {}");
+ Compilation compilation = daggerCompiler().compile(testComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("int is not a valid component dependency type");
+ }
+
+ @Test
+ public void invalidComponentModules() {
+ JavaFileObject testComponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = int.class)",
+ "interface TestComponent {}");
+ Compilation compilation = daggerCompiler().compile(testComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("int is not a valid module type");
+ }
+
+ @Test
+ public void moduleInDependencies() {
+ JavaFileObject testModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String s() { return null; }",
+ "}");
+ JavaFileObject testComponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = TestModule.class)",
+ "interface TestComponent {}");
+ Compilation compilation = daggerCompiler().compile(testModule, testComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.TestModule is a module, which cannot be a component dependency");
+ }
+
+ @Test
+ public void componentDependencyMustNotCycle_Direct() {
+ JavaFileObject shortLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentShort",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = ComponentShort.class)",
+ "interface ComponentShort {",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(shortLifetime);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ComponentShort contains a cycle in its component dependencies:",
+ " test.ComponentShort"));
+ }
+
+ @Test
+ public void componentDependencyMustNotCycle_Indirect() {
+ JavaFileObject longLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentLong",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = ComponentMedium.class)",
+ "interface ComponentLong {",
+ "}");
+ JavaFileObject mediumLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentMedium",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = ComponentLong.class)",
+ "interface ComponentMedium {",
+ "}");
+ JavaFileObject shortLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentShort",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = ComponentMedium.class)",
+ "interface ComponentShort {",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(longLifetime, mediumLifetime, shortLifetime);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ComponentLong contains a cycle in its component dependencies:",
+ " test.ComponentLong",
+ " test.ComponentMedium",
+ " test.ComponentLong"))
+ .inFile(longLifetime);
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ComponentMedium contains a cycle in its component dependencies:",
+ " test.ComponentMedium",
+ " test.ComponentLong",
+ " test.ComponentMedium"))
+ .inFile(mediumLifetime);
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ComponentShort contains a cycle in its component dependencies:",
+ " test.ComponentMedium",
+ " test.ComponentLong",
+ " test.ComponentMedium",
+ " test.ComponentShort"))
+ .inFile(shortLifetime);
+ }
+
+ @Test
+ public void abstractModuleWithInstanceMethod() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Provides int i() { return 1; }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " int i();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("TestModule is abstract and has instance @Provides methods")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void abstractModuleWithInstanceMethod_subclassedIsAllowed() {
+ JavaFileObject abstractModule =
+ JavaFileObjects.forSourceLines(
+ "test.AbstractModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class AbstractModule {",
+ " @Provides int i() { return 1; }",
+ "}");
+ JavaFileObject subclassedModule =
+ JavaFileObjects.forSourceLines(
+ "test.SubclassedModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "class SubclassedModule extends AbstractModule {}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = SubclassedModule.class)",
+ "interface TestComponent {",
+ " int i();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(abstractModule, subclassedModule, component);
+ assertThat(compilation).succeeded();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ConflictingEntryPointsTest.java b/javatests/dagger/internal/codegen/ConflictingEntryPointsTest.java
new file mode 100644
index 0000000..7ffd922
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ConflictingEntryPointsTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ConflictingEntryPointsTest {
+
+ @Test
+ public void covariantType() {
+ JavaFileObject base1 =
+ JavaFileObjects.forSourceLines(
+ "test.Base1", //
+ "package test;",
+ "",
+ "interface Base1 {",
+ " Long foo();",
+ "}");
+ JavaFileObject base2 =
+ JavaFileObjects.forSourceLines(
+ "test.Base2", //
+ "package test;",
+ "",
+ "interface Base2 {",
+ " Number foo();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent extends Base1, Base2 {",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder foo(Long foo);",
+ " @BindsInstance Builder foo(Number foo);",
+ " TestComponent build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(base1, base2, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "conflicting entry point declarations:",
+ " Long test.Base1.foo()",
+ " Number test.Base2.foo()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent ");
+ }
+
+ @Test
+ public void covariantTypeFromGenericSupertypes() {
+ JavaFileObject base1 =
+ JavaFileObjects.forSourceLines(
+ "test.Base1", //
+ "package test;",
+ "",
+ "interface Base1<T> {",
+ " T foo();",
+ "}");
+ JavaFileObject base2 =
+ JavaFileObjects.forSourceLines(
+ "test.Base2", //
+ "package test;",
+ "",
+ "interface Base2<T> {",
+ " T foo();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent extends Base1<Long>, Base2<Number> {",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder foo(Long foo);",
+ " @BindsInstance Builder foo(Number foo);",
+ " TestComponent build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(base1, base2, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "conflicting entry point declarations:",
+ " Long test.Base1.foo()",
+ " Number test.Base2.foo()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent ");
+ }
+
+ @Test
+ public void differentQualifier() {
+ JavaFileObject base1 =
+ JavaFileObjects.forSourceLines(
+ "test.Base1", //
+ "package test;",
+ "",
+ "interface Base1 {",
+ " Object foo();",
+ "}");
+ JavaFileObject base2 =
+ JavaFileObjects.forSourceLines(
+ "test.Base2", //
+ "package test;",
+ "",
+ "import javax.inject.Named;",
+ "",
+ "interface Base2 {",
+ " @Named(\"foo\") Object foo();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "import javax.inject.Named;",
+ "",
+ "@Component",
+ "interface TestComponent extends Base1, Base2 {",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder foo(Object foo);",
+ " @BindsInstance Builder namedFoo(@Named(\"foo\") Object foo);",
+ " TestComponent build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(base1, base2, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "conflicting entry point declarations:",
+ " Object test.Base1.foo()",
+ " @Named(\"foo\") Object test.Base2.foo()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent ");
+ }
+
+ @Test
+ public void sameKey() {
+ JavaFileObject base1 =
+ JavaFileObjects.forSourceLines(
+ "test.Base1", //
+ "package test;",
+ "",
+ "interface Base1 {",
+ " Object foo();",
+ "}");
+ JavaFileObject base2 =
+ JavaFileObjects.forSourceLines(
+ "test.Base2", //
+ "package test;",
+ "",
+ "interface Base2 {",
+ " Object foo();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent extends Base1, Base2 {",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder foo(Object foo);",
+ " TestComponent build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(base1, base2, component);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void sameQualifiedKey() {
+ JavaFileObject base1 =
+ JavaFileObjects.forSourceLines(
+ "test.Base1", //
+ "package test;",
+ "",
+ "import javax.inject.Named;",
+ "",
+ "interface Base1 {",
+ " @Named(\"foo\") Object foo();",
+ "}");
+ JavaFileObject base2 =
+ JavaFileObjects.forSourceLines(
+ "test.Base2", //
+ "package test;",
+ "",
+ "import javax.inject.Named;",
+ "",
+ "interface Base2 {",
+ " @Named(\"foo\") Object foo();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Component;",
+ "import javax.inject.Named;",
+ "",
+ "@Component",
+ "interface TestComponent extends Base1, Base2 {",
+ "",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder foo(@Named(\"foo\") Object foo);",
+ " TestComponent build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(base1, base2, component);
+ assertThat(compilation).succeeded();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java b/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
new file mode 100644
index 0000000..1fcf7bc
--- /dev/null
+++ b/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.Module;
+import dagger.producers.ProducerModule;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+import javax.tools.JavaFileObject;
+
+/** A {@link Truth} subject for testing Dagger module methods. */
+final class DaggerModuleMethodSubject extends Subject<DaggerModuleMethodSubject, String> {
+
+ /** A {@link Truth} subject factory for testing Dagger module methods. */
+ static final class Factory implements Subject.Factory<DaggerModuleMethodSubject, String> {
+
+ /** Starts a clause testing a Dagger {@link Module @Module} method. */
+ static DaggerModuleMethodSubject assertThatModuleMethod(String method) {
+ return assertAbout(daggerModuleMethod())
+ .that(method)
+ .withDeclaration("@Module abstract class %s { %s }");
+ }
+
+ /** Starts a clause testing a Dagger {@link ProducerModule @ProducerModule} method. */
+ static DaggerModuleMethodSubject assertThatProductionModuleMethod(String method) {
+ return assertAbout(daggerModuleMethod())
+ .that(method)
+ .withDeclaration("@ProducerModule abstract class %s { %s }");
+ }
+
+ /** Starts a clause testing a method in an unannotated class. */
+ static DaggerModuleMethodSubject assertThatMethodInUnannotatedClass(String method) {
+ return assertAbout(daggerModuleMethod())
+ .that(method)
+ .withDeclaration("abstract class %s { %s }");
+ }
+
+ static Factory daggerModuleMethod() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public DaggerModuleMethodSubject createSubject(FailureMetadata failureMetadata, String that) {
+ return new DaggerModuleMethodSubject(failureMetadata, that);
+ }
+ }
+
+ private final String actual;
+ private final ImmutableList.Builder<String> imports =
+ new ImmutableList.Builder<String>()
+ .add(
+ // explicitly import Module so it's not ambiguous with java.lang.Module
+ "import dagger.Module;",
+ "import dagger.*;",
+ "import dagger.multibindings.*;",
+ "import dagger.producers.*;",
+ "import java.util.*;",
+ "import javax.inject.*;");
+ private String declaration;
+ private ImmutableList<JavaFileObject> additionalSources = ImmutableList.of();
+
+ private DaggerModuleMethodSubject(FailureMetadata failureMetadata, String subject) {
+ super(failureMetadata, subject);
+ this.actual = subject;
+ }
+
+ /**
+ * Imports classes and interfaces. Note that all types in the following packages are already
+ * imported:<ul>
+ * <li>{@code dagger.*}
+ * <li>{@code dagger.multibindings.*}
+ * <li>(@code dagger.producers.*}
+ * <li>{@code java.util.*}
+ * <li>{@code javax.inject.*}
+ * </ul>
+ */
+ DaggerModuleMethodSubject importing(Class<?>... imports) {
+ return importing(Arrays.asList(imports));
+ }
+
+ /**
+ * Imports classes and interfaces. Note that all types in the following packages are already
+ * imported:<ul>
+ * <li>{@code dagger.*}
+ * <li>{@code dagger.multibindings.*}
+ * <li>(@code dagger.producers.*}
+ * <li>{@code java.util.*}
+ * <li>{@code javax.inject.*}
+ * </ul>
+ */
+ DaggerModuleMethodSubject importing(List<? extends Class<?>> imports) {
+ imports.stream()
+ .map(clazz -> String.format("import %s;", clazz.getCanonicalName()))
+ .forEachOrdered(this.imports::add);
+ return this;
+ }
+
+ /**
+ * Sets the declaration of the module. Must be a string with two {@code %s} parameters. The first
+ * will be replaced with the name of the type, and the second with the method declaration, which
+ * must be within paired braces.
+ */
+ DaggerModuleMethodSubject withDeclaration(String declaration) {
+ this.declaration = declaration;
+ return this;
+ }
+
+ /** Additional source files that must be compiled with the module. */
+ DaggerModuleMethodSubject withAdditionalSources(JavaFileObject... sources) {
+ this.additionalSources = ImmutableList.copyOf(sources);
+ return this;
+ }
+
+ /**
+ * Fails if compiling the module with the method doesn't report an error at the method
+ * declaration whose message contains {@code errorSubstring}.
+ */
+ void hasError(String errorSubstring) {
+ String source = moduleSource();
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule", source);
+ Compilation compilation =
+ daggerCompiler().compile(FluentIterable.from(additionalSources).append(module));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(errorSubstring)
+ .inFile(module)
+ .onLine(methodLine(source));
+ }
+
+ private int methodLine(String source) {
+ String beforeMethod = source.substring(0, source.indexOf(actual));
+ int methodLine = 1;
+ for (int nextNewlineIndex = beforeMethod.indexOf('\n');
+ nextNewlineIndex >= 0;
+ nextNewlineIndex = beforeMethod.indexOf('\n', nextNewlineIndex + 1)) {
+ methodLine++;
+ }
+ return methodLine;
+ }
+
+ private String moduleSource() {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ writer.println("package test;");
+ writer.println();
+ for (String importLine : imports.build()) {
+ writer.println(importLine);
+ }
+ writer.println();
+ writer.printf(declaration, "TestModule", "\n" + actual + "\n");
+ writer.println();
+ return stringWriter.toString();
+ }
+
+}
diff --git a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
new file mode 100644
index 0000000..2f4aecf
--- /dev/null
+++ b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.CompilationSubject;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DelegateBindingExpressionTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public DelegateBindingExpressionTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ private static final JavaFileObject REGULAR_SCOPED =
+ JavaFileObjects.forSourceLines(
+ "test.RegularScoped",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "import javax.inject.Inject;",
+ "",
+ "@RegularScoped.CustomScope",
+ "class RegularScoped {",
+ " @Inject RegularScoped() {}",
+ "",
+ " @Scope @interface CustomScope {}",
+ "}");
+
+ private static final JavaFileObject REUSABLE_SCOPED =
+ JavaFileObjects.forSourceLines(
+ "test.ReusableScoped",
+ "package test;",
+ "",
+ "import dagger.Reusable;",
+ "import javax.inject.Inject;",
+ "",
+ "@Reusable",
+ "class ReusableScoped {",
+ " @Inject ReusableScoped() {}",
+ "}");
+
+ private static final JavaFileObject UNSCOPED =
+ JavaFileObjects.forSourceLines(
+ "test.Unscoped",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Unscoped {",
+ " @Inject Unscoped() {}",
+ "}");
+
+ private static final JavaFileObject COMPONENT =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "@RegularScoped.CustomScope",
+ "interface TestComponent {",
+ " @Qualifier(RegularScoped.class)",
+ " Object regular();",
+ "",
+ " @Qualifier(ReusableScoped.class)",
+ " Object reusable();",
+ "",
+ " @Qualifier(Unscoped.class)",
+ " Object unscoped();",
+ "}");
+
+ private static final JavaFileObject QUALIFIER =
+ JavaFileObjects.forSourceLines(
+ "test.Qualifier",
+ "package test;",
+ "",
+ "@javax.inject.Qualifier",
+ "@interface Qualifier {",
+ " Class<?> value();",
+ "}");
+
+ @Test
+ public void toDoubleCheck() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds @RegularScoped.CustomScope @Qualifier(RegularScoped.class)",
+ " Object regular(RegularScoped delegate);",
+ "",
+ " @Binds @RegularScoped.CustomScope @Qualifier(ReusableScoped.class)",
+ " Object reusable(ReusableScoped delegate);",
+ "",
+ " @Binds @RegularScoped.CustomScope @Qualifier(Unscoped.class)",
+ " Object unscoped(Unscoped delegate);",
+ "}");
+
+ assertThatCompilationWithModule(module)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Object regularScoped = new MemoizedSentinel();",
+ " private volatile ReusableScoped reusableScoped;",
+ "",
+ " private RegularScoped getRegularScoped() {",
+ " Object local = regularScoped;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = regularScoped;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new RegularScoped();",
+ " regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
+ " }",
+ " }",
+ " }",
+ " return (RegularScoped) local;",
+ " }",
+ "",
+ " private ReusableScoped getReusableScoped() {",
+ " Object local = reusableScoped;",
+ " if (local == null) {",
+ " local = new ReusableScoped();",
+ " reusableScoped = (ReusableScoped) local;",
+ " }",
+ " return (ReusableScoped) local;",
+ " }",
+ "")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.regularScopedProvider = ",
+ " DoubleCheck.provider(RegularScoped_Factory.create());",
+ " this.reusableScopedProvider = ",
+ " SingleCheck.provider(ReusableScoped_Factory.create());",
+ " this.reusableProvider = DoubleCheck.provider(",
+ " (Provider) reusableScopedProvider);",
+ " this.unscopedProvider = DoubleCheck.provider(",
+ " (Provider) Unscoped_Factory.create());",
+ " }")
+ .addLines( //
+ "}")
+ .build());
+ }
+
+ @Test
+ public void toSingleCheck() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Reusable;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds @Reusable @Qualifier(RegularScoped.class)",
+ " Object regular(RegularScoped delegate);",
+ "",
+ " @Binds @Reusable @Qualifier(ReusableScoped.class)",
+ " Object reusable(ReusableScoped delegate);",
+ "",
+ " @Binds @Reusable @Qualifier(Unscoped.class)",
+ " Object unscoped(Unscoped delegate);",
+ "}");
+
+ assertThatCompilationWithModule(module)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Object regularScoped = new MemoizedSentinel();",
+ " private volatile ReusableScoped reusableScoped;",
+ "",
+ " private RegularScoped getRegularScoped() {",
+ " Object local = regularScoped;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = regularScoped;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new RegularScoped();",
+ " regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
+ " }",
+ " }",
+ " }",
+ " return (RegularScoped) local;",
+ " }",
+ "",
+ " private ReusableScoped getReusableScoped() {",
+ " Object local = reusableScoped;",
+ " if (local == null) {",
+ " local = new ReusableScoped();",
+ " reusableScoped = (ReusableScoped) local;",
+ " }",
+ " return (ReusableScoped) local;",
+ " }",
+ "")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.regularScopedProvider = ",
+ " DoubleCheck.provider(RegularScoped_Factory.create());",
+ " this.reusableScopedProvider = ",
+ " SingleCheck.provider(ReusableScoped_Factory.create());",
+ " this.unscopedProvider = SingleCheck.provider(",
+ " (Provider) Unscoped_Factory.create());",
+ " }")
+ .addLines( //
+ "}")
+ .build());
+ }
+
+ @Test
+ public void toUnscoped() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds @Qualifier(RegularScoped.class)",
+ " Object regular(RegularScoped delegate);",
+ "",
+ " @Binds @Qualifier(ReusableScoped.class)",
+ " Object reusable(ReusableScoped delegate);",
+ "",
+ " @Binds @Qualifier(Unscoped.class)",
+ " Object unscoped(Unscoped delegate);",
+ "}");
+
+ assertThatCompilationWithModule(module)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Object regularScoped = new MemoizedSentinel();",
+ " private volatile ReusableScoped reusableScoped;",
+ "",
+ " private RegularScoped getRegularScoped() {",
+ " Object local = regularScoped;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = regularScoped;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new RegularScoped();",
+ " regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
+ " }",
+ " }",
+ " }",
+ " return (RegularScoped) local;",
+ " }",
+ "",
+ " private ReusableScoped getReusableScoped() {",
+ " Object local = reusableScoped;",
+ " if (local == null) {",
+ " local = new ReusableScoped();",
+ " reusableScoped = (ReusableScoped) local;",
+ " }",
+ " return (ReusableScoped) local;",
+ " }",
+ "")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.regularScopedProvider = ",
+ " DoubleCheck.provider(RegularScoped_Factory.create());",
+ " this.reusableScopedProvider = ",
+ " SingleCheck.provider(ReusableScoped_Factory.create());",
+ " }")
+ .addLines( //
+ "}")
+ .build());
+ }
+
+ @Test
+ public void castNeeded_rawTypes_Provider_get() {
+ JavaFileObject accessibleSupertype =
+ JavaFileObjects.forSourceLines(
+ "other.Supertype",
+ "package other;",
+ "",
+ // accessible from the component, but the subtype is not
+ "public interface Supertype {}");
+ JavaFileObject inaccessibleSubtype =
+ JavaFileObjects.forSourceLines(
+ "other.Subtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "class Subtype implements Supertype {",
+ " @Inject Subtype() {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.SupertypeModule",
+ "package other;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "public interface SupertypeModule {",
+ " @Binds Supertype to(Subtype subtype);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = other.SupertypeModule.class)",
+ "interface TestComponent {",
+ " other.Supertype supertype();",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(accessibleSupertype, inaccessibleSubtype, module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider subtypeProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.subtypeProvider = DoubleCheck.provider(Subtype_Factory.create());",
+ " }",
+ "",
+ " @Override",
+ " public Supertype supertype() {",
+ " return (Supertype) subtypeProvider.get();",
+ " }")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Object subtype = new MemoizedSentinel();",
+ "",
+ " private Object getSubtype() {",
+ " Object local = subtype;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = subtype;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = Subtype_Factory.newInstance();",
+ " subtype = DoubleCheck.reentrantCheck(subtype, local);",
+ " }",
+ " }",
+ " }",
+ " return (Object) local;",
+ " }",
+ "",
+ " @Override",
+ " public Supertype supertype() {",
+ " return (Supertype) getSubtype();",
+ " }")
+ .build());
+ }
+
+ @Test
+ public void noCast_rawTypes_Provider_get_toInaccessibleType() {
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "other.Supertype",
+ "package other;",
+ "",
+ "interface Supertype {}");
+ JavaFileObject subtype =
+ JavaFileObjects.forSourceLines(
+ "other.Subtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "class Subtype implements Supertype {",
+ " @Inject Subtype() {}",
+ "}");
+ JavaFileObject usesSupertype =
+ JavaFileObjects.forSourceLines(
+ "other.UsesSupertype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesSupertype {",
+ " @Inject UsesSupertype(Supertype supertype) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.SupertypeModule",
+ "package other;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "public interface SupertypeModule {",
+ " @Binds Supertype to(Subtype subtype);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = other.SupertypeModule.class)",
+ "interface TestComponent {",
+ " other.UsesSupertype usesSupertype();",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(supertype, subtype, usesSupertype, module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider subtypeProvider;",
+ "",
+ " @Override",
+ " public UsesSupertype usesSupertype() {",
+ // can't cast the provider.get() to a type that's not accessible
+ " return UsesSupertype_Factory.newInstance(subtypeProvider.get());",
+ " }",
+ "}")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Object subtype = new MemoizedSentinel();",
+ "",
+ " private Object getSubtype() {",
+ " Object local = subtype;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = subtype;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = Subtype_Factory.newInstance();",
+ " subtype = DoubleCheck.reentrantCheck(subtype, local);",
+ " }",
+ " }",
+ " }",
+ " return (Object) local;",
+ " }",
+ "",
+ " @Override",
+ " public UsesSupertype usesSupertype() {",
+ " return UsesSupertype_Factory.newInstance(getSubtype());",
+ " }")
+ .build());
+ }
+
+ @Test
+ public void castedToRawType() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Named;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static String provideString() { return new String(); }",
+ "",
+ " @Binds",
+ " CharSequence charSequence(String string);",
+ "",
+ " @Binds",
+ " @Named(\"named\")",
+ " String namedString(String string);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Named;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<CharSequence> charSequence();",
+ "",
+ " @Named(\"named\") Provider<String> namedString();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @Override",
+ " public Provider<CharSequence> charSequence() {",
+ " return (Provider) TestModule_ProvideStringFactory.create();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<String> namedString() {",
+ " return TestModule_ProvideStringFactory.create();",
+ " }",
+ "}")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider<String> provideStringProvider;",
+ "",
+ " private Provider<String> getStringProvider() {",
+ " Object local = provideStringProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " provideStringProvider = (Provider<String>) local;",
+ " }",
+ " return (Provider<String>) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<CharSequence> charSequence() {",
+ " return (Provider) getStringProvider();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<String> namedString() {",
+ " return getStringProvider();",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) TestModule_ProvideStringFactory.provideString();",
+ " default:",
+ " throw new AssertionError(id);",
+ " }",
+ " }",
+ " }")
+ .build());
+ }
+
+ @Test
+ public void doubleBinds() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static String provideString() { return new String(); }",
+ "",
+ " @Binds",
+ " CharSequence charSequence(String string);",
+ "",
+ " @Binds",
+ " Object object(CharSequence charSequence);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Named;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<CharSequence> charSequence();",
+ " Provider<Object> object();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @Override",
+ " public Provider<CharSequence> charSequence() {",
+ " return (Provider) TestModule_ProvideStringFactory.create();",
+ " }",
+ " @Override",
+ " public Provider<Object> object() {",
+ " return (Provider) TestModule_ProvideStringFactory.create();",
+ " }",
+ "}")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider<String> provideStringProvider;",
+ "",
+ " private Provider<String> getStringProvider() {",
+ " Object local = provideStringProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " provideStringProvider = (Provider<String>) local;",
+ " }",
+ " return (Provider<String>) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<CharSequence> charSequence() {",
+ " return (Provider) getStringProvider();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Object> object() {",
+ " return (Provider) getStringProvider();",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) TestModule_ProvideStringFactory.provideString();",
+ " default:",
+ " throw new AssertionError(id);",
+ " }",
+ " }",
+ " }")
+ .build());
+ }
+
+ @Test
+ public void inlineFactoryOfInacessibleType() {
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "other.Supertype", "package other;", "", "public interface Supertype {}");
+ JavaFileObject injectableSubtype =
+ JavaFileObjects.forSourceLines(
+ "other.Subtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class Subtype implements Supertype {",
+ // important: this doesn't have any dependencies and therefore the factory will be able
+ // to be referenced with an inline Subtype_Factory.create()
+ " @Inject Subtype() {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.TestModule",
+ "package other;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "public interface TestModule {",
+ " @Binds Supertype to(Subtype subtype);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.RequestsSubtypeAsProvider",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = other.TestModule.class)",
+ "interface RequestsSubtypeAsProvider {",
+ " Provider<other.Supertype> supertypeProvider();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(supertype, injectableSubtype, module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRequestsSubtypeAsProvider")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerRequestsSubtypeAsProvider")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerRequestsSubtypeAsProvider",
+ " implements RequestsSubtypeAsProvider {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @Override",
+ " public Provider<Supertype> supertypeProvider() {",
+ " return (Provider) Subtype_Factory.create();",
+ " }",
+ "}")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider subtypeProvider;",
+ "",
+ " private Provider getSubtypeProvider() {",
+ " Object local = subtypeProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " subtypeProvider = (Provider) local;",
+ " }",
+ " return (Provider) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Supertype> supertypeProvider() {",
+ " return getSubtypeProvider();",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) Subtype_Factory.newInstance();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }")
+ .build());
+ }
+
+ @Test
+ public void providerWhenBindsScopeGreaterThanDependencyScope() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Reusable;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "public abstract class TestModule {",
+ " @Reusable",
+ " @Provides",
+ " static String provideString() {",
+ " return \"\";",
+ " }",
+ "",
+ " @Binds",
+ " @Singleton",
+ " abstract Object bindString(String str);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "import javax.inject.Provider;",
+ "",
+ "@Singleton",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<Object> getObject();",
+ "}");
+
+ Compilation compilation = daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " private Provider<String> provideStringProvider;",
+ " private Provider<Object> bindStringProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.provideStringProvider =",
+ " SingleCheck.provider(TestModule_ProvideStringFactory.create());",
+ " this.bindStringProvider =",
+ " DoubleCheck.provider((Provider) provideStringProvider);",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Object> getObject() {",
+ " return bindStringProvider;",
+ " }",
+ "}")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile String string;",
+ " private volatile Object object = new MemoizedSentinel();",
+ " private volatile Provider<Object> bindStringProvider;",
+ "",
+ " private String getString() {",
+ " Object local = string;",
+ " if (local == null) {",
+ " local = TestModule_ProvideStringFactory.provideString();",
+ " string = (String) local;",
+ " }",
+ " return (String) local;",
+ " }",
+ "",
+ " private Object getObject2() {",
+ " Object local = object;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = object;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = getString();",
+ " object = DoubleCheck.reentrantCheck(object, local);",
+ " }",
+ " }",
+ " }",
+ " return (Object) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Object> getObject() {",
+ " Object local = bindStringProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " bindStringProvider = (Provider<Object>) local;",
+ " }",
+ " return (Provider<Object>) local;",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) DaggerTestComponent.this.getObject2();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }")
+ .build());
+ }
+
+ private CompilationSubject assertThatCompilationWithModule(JavaFileObject module) {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ module,
+ COMPONENT,
+ QUALIFIER,
+ REGULAR_SCOPED,
+ REUSABLE_SCOPED,
+ UNSCOPED);
+ assertThat(compilation).succeeded();
+ return assertThat(compilation);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
new file mode 100644
index 0000000..4a4e0a5
--- /dev/null
+++ b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
@@ -0,0 +1,699 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.endsWithMessage;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.regex.Pattern;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DependencyCycleValidationTest {
+ private static final JavaFileObject SIMPLE_CYCLIC_DEPENDENCY =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Outer {",
+ " static class A {",
+ " @Inject A(C cParam) {}",
+ " }",
+ "",
+ " static class B {",
+ " @Inject B(A aParam) {}",
+ " }",
+ "",
+ " static class C {",
+ " @Inject C(B bParam) {}",
+ " }",
+ "",
+ " @Module",
+ " interface MModule {",
+ " @Binds Object object(C c);",
+ " }",
+ "",
+ " @Component",
+ " interface CComponent {",
+ " C getC();",
+ " }",
+ "}");
+
+ @Test
+ public void cyclicDependency() {
+ Compilation compilation = daggerCompiler().compile(SIMPLE_CYCLIC_DEPENDENCY);
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " test.Outer.C is injected at",
+ " test.Outer.A(cParam)",
+ " test.Outer.A is injected at",
+ " test.Outer.B(aParam)",
+ " test.Outer.B is injected at",
+ " test.Outer.C(bParam)",
+ " test.Outer.C is provided at",
+ " test.Outer.CComponent.getC()"))
+ .inFile(SIMPLE_CYCLIC_DEPENDENCY)
+ .onLineContaining("interface CComponent");
+
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void cyclicDependencyWithModuleBindingValidation() {
+ // Cycle errors should not show a dependency trace to an entry point when doing full binding
+ // graph validation. So ensure that the message doesn't end with "test.Outer.C is provided at
+ // test.Outer.CComponent.getC()", as the previous test's message does.
+ Pattern moduleBindingValidationError =
+ endsWithMessage(
+ "Found a dependency cycle:",
+ " test.Outer.C is injected at",
+ " test.Outer.A(cParam)",
+ " test.Outer.A is injected at",
+ " test.Outer.B(aParam)",
+ " test.Outer.B is injected at",
+ " test.Outer.C(bParam)");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(SIMPLE_CYCLIC_DEPENDENCY);
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(moduleBindingValidationError)
+ .inFile(SIMPLE_CYCLIC_DEPENDENCY)
+ .onLineContaining("interface MModule");
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(moduleBindingValidationError)
+ .inFile(SIMPLE_CYCLIC_DEPENDENCY)
+ .onLineContaining("interface CComponent");
+
+ assertThat(compilation).hadErrorCount(2);
+ }
+
+ @Test public void cyclicDependencyNotIncludingEntryPoint() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Outer {",
+ " static class A {",
+ " @Inject A(C cParam) {}",
+ " }",
+ "",
+ " static class B {",
+ " @Inject B(A aParam) {}",
+ " }",
+ "",
+ " static class C {",
+ " @Inject C(B bParam) {}",
+ " }",
+ "",
+ " static class D {",
+ " @Inject D(C cParam) {}",
+ " }",
+ "",
+ " @Component",
+ " interface DComponent {",
+ " D getD();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " test.Outer.C is injected at",
+ " test.Outer.A(cParam)",
+ " test.Outer.A is injected at",
+ " test.Outer.B(aParam)",
+ " test.Outer.B is injected at",
+ " test.Outer.C(bParam)",
+ " test.Outer.C is injected at",
+ " test.Outer.D(cParam)",
+ " test.Outer.D is provided at",
+ " test.Outer.DComponent.getD()"))
+ .inFile(component)
+ .onLineContaining("interface DComponent");
+ }
+
+ @Test
+ public void cyclicDependencyNotBrokenByMapBinding() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Outer {",
+ " static class A {",
+ " @Inject A(Map<String, C> cMap) {}",
+ " }",
+ "",
+ " static class B {",
+ " @Inject B(A aParam) {}",
+ " }",
+ "",
+ " static class C {",
+ " @Inject C(B bParam) {}",
+ " }",
+ "",
+ " @Component(modules = CModule.class)",
+ " interface CComponent {",
+ " C getC();",
+ " }",
+ "",
+ " @Module",
+ " static class CModule {",
+ " @Provides @IntoMap",
+ " @StringKey(\"C\")",
+ " static C c(C c) {",
+ " return c;",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " test.Outer.C is injected at",
+ " test.Outer.CModule.c(c)",
+ " java.util.Map<java.lang.String,test.Outer.C> is injected at",
+ " test.Outer.A(cMap)",
+ " test.Outer.A is injected at",
+ " test.Outer.B(aParam)",
+ " test.Outer.B is injected at",
+ " test.Outer.C(bParam)",
+ " test.Outer.C is provided at",
+ " test.Outer.CComponent.getC()"))
+ .inFile(component)
+ .onLineContaining("interface CComponent");
+ }
+
+ @Test
+ public void cyclicDependencyWithSetBinding() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Outer {",
+ " static class A {",
+ " @Inject A(Set<C> cSet) {}",
+ " }",
+ "",
+ " static class B {",
+ " @Inject B(A aParam) {}",
+ " }",
+ "",
+ " static class C {",
+ " @Inject C(B bParam) {}",
+ " }",
+ "",
+ " @Component(modules = CModule.class)",
+ " interface CComponent {",
+ " C getC();",
+ " }",
+ "",
+ " @Module",
+ " static class CModule {",
+ " @Provides @IntoSet",
+ " static C c(C c) {",
+ " return c;",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " test.Outer.C is injected at",
+ " test.Outer.CModule.c(c)",
+ " java.util.Set<test.Outer.C> is injected at",
+ " test.Outer.A(cSet)",
+ " test.Outer.A is injected at",
+ " test.Outer.B(aParam)",
+ " test.Outer.B is injected at",
+ " test.Outer.C(bParam)",
+ " test.Outer.C is provided at",
+ " test.Outer.CComponent.getC()"))
+ .inFile(component)
+ .onLineContaining("interface CComponent");
+ }
+
+ @Test
+ public void falsePositiveCyclicDependencyIndirectionDetected() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class Outer {",
+ " static class A {",
+ " @Inject A(C cParam) {}",
+ " }",
+ "",
+ " static class B {",
+ " @Inject B(A aParam) {}",
+ " }",
+ "",
+ " static class C {",
+ " @Inject C(B bParam) {}",
+ " }",
+ "",
+ " static class D {",
+ " @Inject D(Provider<C> cParam) {}",
+ " }",
+ "",
+ " @Component",
+ " interface DComponent {",
+ " D getD();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " test.Outer.C is injected at",
+ " test.Outer.A(cParam)",
+ " test.Outer.A is injected at",
+ " test.Outer.B(aParam)",
+ " test.Outer.B is injected at",
+ " test.Outer.C(bParam)",
+ " javax.inject.Provider<test.Outer.C> is injected at",
+ " test.Outer.D(cParam)",
+ " test.Outer.D is provided at",
+ " test.Outer.DComponent.getD()"))
+ .inFile(component)
+ .onLineContaining("interface DComponent");
+ }
+
+ @Test
+ public void cyclicDependencyInSubcomponents() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Parent {",
+ " Child.Builder child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = CycleModule.class)",
+ "interface Child {",
+ " Grandchild.Builder grandchild();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Child build();",
+ " }",
+ "}");
+ JavaFileObject grandchild =
+ JavaFileObjects.forSourceLines(
+ "test.Grandchild",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Grandchild {",
+ " String entry();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Grandchild build();",
+ " }",
+ "}");
+ JavaFileObject cycleModule =
+ JavaFileObjects.forSourceLines(
+ "test.CycleModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class CycleModule {",
+ " @Provides static Object object(String string) {",
+ " return string;",
+ " }",
+ "",
+ " @Provides static String string(Object object) {",
+ " return object.toString();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(parent, child, grandchild, cycleModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " java.lang.String is injected at",
+ " test.CycleModule.object(string)",
+ " java.lang.Object is injected at",
+ " test.CycleModule.string(object)",
+ " java.lang.String is provided at",
+ " test.Grandchild.entry()"))
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+
+ @Test
+ public void cyclicDependencyInSubcomponentsWithChildren() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Parent {",
+ " Child.Builder child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = CycleModule.class)",
+ "interface Child {",
+ " String entry();",
+ "",
+ " Grandchild.Builder grandchild();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Child build();",
+ " }",
+ "}");
+ // Grandchild has no entry point that depends on the cycle. http://b/111317986
+ JavaFileObject grandchild =
+ JavaFileObjects.forSourceLines(
+ "test.Grandchild",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Grandchild {",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Grandchild build();",
+ " }",
+ "}");
+ JavaFileObject cycleModule =
+ JavaFileObjects.forSourceLines(
+ "test.CycleModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class CycleModule {",
+ " @Provides static Object object(String string) {",
+ " return string;",
+ " }",
+ "",
+ " @Provides static String string(Object object) {",
+ " return object.toString();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(parent, child, grandchild, cycleModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " java.lang.String is injected at",
+ " test.CycleModule.object(string)",
+ " java.lang.Object is injected at",
+ " test.CycleModule.string(object)",
+ " java.lang.String is provided at",
+ " test.Child.entry() [test.Parent → test.Child]"))
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+
+ @Test
+ public void circularBindsMethods() {
+ JavaFileObject qualifier =
+ JavaFileObjects.forSourceLines(
+ "test.SomeQualifier",
+ "package test;",
+ "",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Qualifier @interface SomeQualifier {}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Binds abstract Object bindUnqualified(@SomeQualifier Object qualified);",
+ " @Binds @SomeQualifier abstract Object bindQualified(Object unqualified);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Object unqualified();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(qualifier, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " java.lang.Object is injected at",
+ " test.TestModule.bindQualified(unqualified)",
+ " @test.SomeQualifier java.lang.Object is injected at",
+ " test.TestModule.bindUnqualified(qualified)",
+ " java.lang.Object is provided at",
+ " test.TestComponent.unqualified()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void selfReferentialBinds() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Binds abstract Object bindToSelf(Object sameKey);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Object selfReferential();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " java.lang.Object is injected at",
+ " test.TestModule.bindToSelf(sameKey)",
+ " java.lang.Object is provided at",
+ " test.TestComponent.selfReferential()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void cycleFromMembersInjectionMethod_WithSameKeyAsMembersInjectionMethod() {
+ JavaFileObject a =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class A {",
+ " @Inject A() {}",
+ " @Inject B b;",
+ "}");
+ JavaFileObject b =
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class B {",
+ " @Inject B() {}",
+ " @Inject A a;",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.CycleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface CycleComponent {",
+ " void inject(A a);",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(a, b, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "Found a dependency cycle:",
+ " test.B is injected at",
+ " test.A.b",
+ " test.A is injected at",
+ " test.B.a",
+ " test.B is injected at",
+ " test.A.b",
+ " test.A is injected at",
+ " test.CycleComponent.inject(test.A)"))
+ .inFile(component)
+ .onLineContaining("interface CycleComponent");
+ }
+
+ @Test
+ public void longCycleMaskedByShortBrokenCycles() {
+ JavaFileObject cycles =
+ JavaFileObjects.forSourceLines(
+ "test.Cycles",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "import dagger.Component;",
+ "",
+ "final class Cycles {",
+ " static class A {",
+ " @Inject A(Provider<A> aProvider, B b) {}",
+ " }",
+ "",
+ " static class B {",
+ " @Inject B(Provider<B> bProvider, A a) {}",
+ " }",
+ "",
+ " @Component",
+ " interface C {",
+ " A a();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(cycles);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Found a dependency cycle:")
+ .inFile(cycles)
+ .onLineContaining("interface C");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java b/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java
new file mode 100644
index 0000000..a2da92f
--- /dev/null
+++ b/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DiagnosticFormattingTest {
+ @Test public void stripCommonTypePrefixes() {
+ String typeName = "com.google.common.collect.ImmutableList<java.lang.Boolean>";
+ assertThat(DiagnosticFormatting.stripCommonTypePrefixes(typeName))
+ .isEqualTo("ImmutableList<Boolean>");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
new file mode 100644
index 0000000..14a6fb9
--- /dev/null
+++ b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
@@ -0,0 +1,1083 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
+import static org.junit.Assume.assumeFalse;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DuplicateBindingsValidationTest {
+
+ @Parameters(name = "fullBindingGraphValidation={0}")
+ public static ImmutableList<Object[]> parameters() {
+ return ImmutableList.copyOf(new Object[][] {{false}, {true}});
+ }
+
+ private final boolean fullBindingGraphValidation;
+
+ public DuplicateBindingsValidationTest(boolean fullBindingGraphValidation) {
+ this.fullBindingGraphValidation = fullBindingGraphValidation;
+ }
+
+ @Test public void duplicateExplicitBindings_ProvidesAndComponentProvision() {
+ assumeFalse(fullBindingGraphValidation);
+
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "final class Outer {",
+ " interface A {}",
+ "",
+ " interface B {}",
+ "",
+ " @Module",
+ " static class AModule {",
+ " @Provides String provideString() { return \"\"; }",
+ " @Provides A provideA(String s) { return new A() {}; }",
+ " }",
+ "",
+ " @Component(modules = AModule.class)",
+ " interface Parent {",
+ " A getA();",
+ " }",
+ "",
+ " @Module",
+ " static class BModule {",
+ " @Provides B provideB(A a) { return new B() {}; }",
+ " }",
+ "",
+ " @Component(dependencies = Parent.class, modules = { BModule.class, AModule.class})",
+ " interface Child {",
+ " B getB();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Outer.A is bound multiple times:",
+ " @Provides test.Outer.A test.Outer.AModule.provideA(String)",
+ " test.Outer.A test.Outer.Parent.getA()"))
+ .inFile(component)
+ .onLineContaining("interface Child");
+ }
+
+ @Test public void duplicateExplicitBindings_TwoProvidesMethods() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Outer {",
+ " interface A {}",
+ "",
+ " static class B {",
+ " @Inject B(A a) {}",
+ " }",
+ "",
+ " @Module",
+ " static class Module1 {",
+ " @Provides A provideA1() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module2 {",
+ " @Provides String provideString() { return \"\"; }",
+ " @Provides A provideA2(String s) { return new A() {}; }",
+ " }",
+ "",
+ " @Module(includes = { Module1.class, Module2.class})",
+ " abstract static class Module3 {}",
+ "",
+ " @Component(modules = { Module1.class, Module2.class})",
+ " interface TestComponent {",
+ " A getA();",
+ " B getB();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Outer.A is bound multiple times:",
+ " @Provides test.Outer.A test.Outer.Module1.provideA1()",
+ " @Provides test.Outer.A test.Outer.Module2.provideA2(String)"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+
+ if (fullBindingGraphValidation) {
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Outer.A is bound multiple times:",
+ " @Provides test.Outer.A test.Outer.Module1.provideA1()",
+ " @Provides test.Outer.A test.Outer.Module2.provideA2(String)"))
+ .inFile(component)
+ .onLineContaining("class Module3");
+ }
+
+ // The duplicate bindngs are also requested from B, but we don't want to report them again.
+ assertThat(compilation).hadErrorCount(fullBindingGraphValidation ? 2 : 1);
+ }
+
+ @Test
+ public void duplicateExplicitBindings_ProvidesVsBinds() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Outer {",
+ " interface A {}",
+ "",
+ " static final class B implements A {",
+ " @Inject B() {}",
+ " }",
+ "",
+ " @Module",
+ " static class Module1 {",
+ " @Provides A provideA1() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static abstract class Module2 {",
+ " @Binds abstract A bindA2(B b);",
+ " }",
+ "",
+ " @Module(includes = { Module1.class, Module2.class})",
+ " abstract static class Module3 {}",
+ "",
+ " @Component(modules = { Module1.class, Module2.class})",
+ " interface TestComponent {",
+ " A getA();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Outer.A is bound multiple times:",
+ " @Provides test.Outer.A test.Outer.Module1.provideA1()",
+ " @Binds test.Outer.A test.Outer.Module2.bindA2(test.Outer.B)"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+
+ if (fullBindingGraphValidation) {
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Outer.A is bound multiple times:",
+ " @Provides test.Outer.A test.Outer.Module1.provideA1()",
+ " @Binds test.Outer.A test.Outer.Module2.bindA2(test.Outer.B)"))
+ .inFile(component)
+ .onLineContaining("class Module3");
+ }
+ }
+
+ @Test
+ public void duplicateExplicitBindings_multibindingsAndExplicitSets() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.HashSet;",
+ "import java.util.Set;",
+ "import javax.inject.Qualifier;",
+ "",
+ "final class Outer {",
+ " @Qualifier @interface SomeQualifier {}",
+ "",
+ " @Module",
+ " abstract static class TestModule1 {",
+ " @Provides @IntoSet static String stringSetElement() { return \"\"; }",
+ "",
+ " @Binds",
+ " @IntoSet abstract String bindStringSetElement(@SomeQualifier String value);",
+ "",
+ " @Provides @SomeQualifier",
+ " static String provideSomeQualifiedString() { return \"\"; }",
+ " }",
+ "",
+ " @Module",
+ " static class TestModule2 {",
+ " @Provides Set<String> stringSet() { return new HashSet<String>(); }",
+ " }",
+ "",
+ " @Module(includes = { TestModule1.class, TestModule2.class})",
+ " abstract static class TestModule3 {}",
+ "",
+ " @Component(modules = { TestModule1.class, TestModule2.class })",
+ " interface TestComponent {",
+ " Set<String> getStringSet();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.util.Set<java.lang.String> has incompatible bindings or declarations:",
+ " Set bindings and declarations:",
+ " @Binds @dagger.multibindings.IntoSet String "
+ + "test.Outer.TestModule1.bindStringSetElement(@test.Outer.SomeQualifier "
+ + "String)",
+ " @Provides @dagger.multibindings.IntoSet String "
+ + "test.Outer.TestModule1.stringSetElement()",
+ " Unique bindings and declarations:",
+ " @Provides Set<String> test.Outer.TestModule2.stringSet()"))
+ .inFile(component)
+ .onLineContaining(
+ fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
+ }
+
+ @Test
+ public void duplicateExplicitBindings_multibindingsAndExplicitMaps() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.HashMap;",
+ "import java.util.Map;",
+ "import javax.inject.Qualifier;",
+ "",
+ "final class Outer {",
+ " @Qualifier @interface SomeQualifier {}",
+ "",
+ " @Module",
+ " abstract static class TestModule1 {",
+ " @Provides @IntoMap",
+ " @StringKey(\"foo\")",
+ " static String stringMapEntry() { return \"\"; }",
+ "",
+ " @Binds @IntoMap @StringKey(\"bar\")",
+ " abstract String bindStringMapEntry(@SomeQualifier String value);",
+ "",
+ " @Provides @SomeQualifier",
+ " static String provideSomeQualifiedString() { return \"\"; }",
+ " }",
+ "",
+ " @Module",
+ " static class TestModule2 {",
+ " @Provides Map<String, String> stringMap() {",
+ " return new HashMap<String, String>();",
+ " }",
+ " }",
+ "",
+ " @Module(includes = { TestModule1.class, TestModule2.class})",
+ " abstract static class TestModule3 {}",
+ "",
+ " @Component(modules = { TestModule1.class, TestModule2.class })",
+ " interface TestComponent {",
+ " Map<String, String> getStringMap();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.util.Map<java.lang.String,java.lang.String> has incompatible bindings "
+ + "or declarations:",
+ " Map bindings and declarations:",
+ " @Binds @dagger.multibindings.IntoMap "
+ + "@dagger.multibindings.StringKey(\"bar\") String"
+ + " test.Outer.TestModule1.bindStringMapEntry(@test.Outer.SomeQualifier "
+ + "String)",
+ " @Provides @dagger.multibindings.IntoMap "
+ + "@dagger.multibindings.StringKey(\"foo\") String"
+ + " test.Outer.TestModule1.stringMapEntry()",
+ " Unique bindings and declarations:",
+ " @Provides Map<String,String> test.Outer.TestModule2.stringMap()"))
+ .inFile(component)
+ .onLineContaining(
+ fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
+ }
+
+ @Test
+ public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Set() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.HashSet;",
+ "import java.util.Set;",
+ "",
+ "final class Outer {",
+ " @Module",
+ " abstract static class TestModule1 {",
+ " @Multibinds abstract Set<String> stringSet();",
+ " }",
+ "",
+ " @Module",
+ " static class TestModule2 {",
+ " @Provides Set<String> stringSet() { return new HashSet<String>(); }",
+ " }",
+ "",
+ " @Module(includes = { TestModule1.class, TestModule2.class})",
+ " abstract static class TestModule3 {}",
+ "",
+ " @Component(modules = { TestModule1.class, TestModule2.class })",
+ " interface TestComponent {",
+ " Set<String> getStringSet();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.util.Set<java.lang.String> has incompatible bindings or declarations:",
+ " Set bindings and declarations:",
+ " @dagger.multibindings.Multibinds Set<String> "
+ + "test.Outer.TestModule1.stringSet()",
+ " Unique bindings and declarations:",
+ " @Provides Set<String> test.Outer.TestModule2.stringSet()"))
+ .inFile(component)
+ .onLineContaining(
+ fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
+ }
+
+ @Test
+ public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Map() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.HashMap;",
+ "import java.util.Map;",
+ "",
+ "final class Outer {",
+ " @Module",
+ " abstract static class TestModule1 {",
+ " @Multibinds abstract Map<String, String> stringMap();",
+ " }",
+ "",
+ " @Module",
+ " static class TestModule2 {",
+ " @Provides Map<String, String> stringMap() {",
+ " return new HashMap<String, String>();",
+ " }",
+ " }",
+ "",
+ " @Module(includes = { TestModule1.class, TestModule2.class})",
+ " abstract static class TestModule3 {}",
+ "",
+ " @Component(modules = { TestModule1.class, TestModule2.class })",
+ " interface TestComponent {",
+ " Map<String, String> getStringMap();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.util.Map<java.lang.String,java.lang.String> has incompatible bindings "
+ + "or declarations:",
+ " Map bindings and declarations:",
+ " @dagger.multibindings.Multibinds Map<String,String> "
+ + "test.Outer.TestModule1.stringMap()",
+ " Unique bindings and declarations:",
+ " @Provides Map<String,String> test.Outer.TestModule2.stringMap()"))
+ .inFile(component)
+ .onLineContaining(
+ fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
+ }
+
+ @Test public void duplicateBindings_TruncateAfterLimit() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Outer {",
+ " interface A {}",
+ "",
+ " @Module",
+ " static class Module01 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module02 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module03 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module04 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module05 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module06 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module07 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module08 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module09 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module10 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module11 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module",
+ " static class Module12 {",
+ " @Provides A provideA() { return new A() {}; }",
+ " }",
+ "",
+ " @Module(includes = {",
+ " Module01.class,",
+ " Module02.class,",
+ " Module03.class,",
+ " Module04.class,",
+ " Module05.class,",
+ " Module06.class,",
+ " Module07.class,",
+ " Module08.class,",
+ " Module09.class,",
+ " Module10.class,",
+ " Module11.class,",
+ " Module12.class",
+ " })",
+ " abstract static class Modules {}",
+ "",
+ " @Component(modules = {",
+ " Module01.class,",
+ " Module02.class,",
+ " Module03.class,",
+ " Module04.class,",
+ " Module05.class,",
+ " Module06.class,",
+ " Module07.class,",
+ " Module08.class,",
+ " Module09.class,",
+ " Module10.class,",
+ " Module11.class,",
+ " Module12.class",
+ " })",
+ " interface TestComponent {",
+ " A getA();",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Outer.A is bound multiple times:",
+ " @Provides test.Outer.A test.Outer.Module01.provideA()",
+ " @Provides test.Outer.A test.Outer.Module02.provideA()",
+ " @Provides test.Outer.A test.Outer.Module03.provideA()",
+ " @Provides test.Outer.A test.Outer.Module04.provideA()",
+ " @Provides test.Outer.A test.Outer.Module05.provideA()",
+ " @Provides test.Outer.A test.Outer.Module06.provideA()",
+ " @Provides test.Outer.A test.Outer.Module07.provideA()",
+ " @Provides test.Outer.A test.Outer.Module08.provideA()",
+ " @Provides test.Outer.A test.Outer.Module09.provideA()",
+ " @Provides test.Outer.A test.Outer.Module10.provideA()",
+ " and 2 others"))
+ .inFile(component)
+ .onLineContaining(fullBindingGraphValidation ? "class Modules" : "interface TestComponent");
+ }
+
+ @Test
+ public void childBindingConflictsWithParent() {
+ JavaFileObject aComponent =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Component(modules = A.AModule.class)",
+ "interface A {",
+ " Object conflict();",
+ "",
+ " B.Builder b();",
+ "",
+ " @Module(subcomponents = B.class)",
+ " static class AModule {",
+ " @Provides static Object abConflict() {",
+ " return \"a\";",
+ " }",
+ " }",
+ "}");
+ JavaFileObject bComponent =
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = B.BModule.class)",
+ "interface B {",
+ " Object conflict();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " B build();",
+ " }",
+ "",
+ " @Module",
+ " static class BModule {",
+ " @Provides static Object abConflict() {",
+ " return \"b\";",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(fullBindingGraphValidationOption())
+ .compile(aComponent, bComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.lang.Object is bound multiple times:",
+ " @Provides Object test.A.AModule.abConflict()",
+ " @Provides Object test.B.BModule.abConflict()"))
+ .inFile(aComponent)
+ .onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
+ }
+
+ @Test
+ public void grandchildBindingConflictsWithGrandparent() {
+ JavaFileObject aComponent =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Component(modules = A.AModule.class)",
+ "interface A {",
+ " Object conflict();",
+ "",
+ " B.Builder b();",
+ "",
+ " @Module(subcomponents = B.class)",
+ " static class AModule {",
+ " @Provides static Object acConflict() {",
+ " return \"a\";",
+ " }",
+ " }",
+ "}");
+ JavaFileObject bComponent =
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface B {",
+ " C.Builder c();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " B build();",
+ " }",
+ "}");
+ JavaFileObject cComponent =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = C.CModule.class)",
+ "interface C {",
+ " Object conflict();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " C build();",
+ " }",
+ "",
+ " @Module",
+ " static class CModule {",
+ " @Provides static Object acConflict() {",
+ " return \"c\";",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(fullBindingGraphValidationOption())
+ .compile(aComponent, bComponent, cComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.lang.Object is bound multiple times:",
+ " @Provides Object test.A.AModule.acConflict()",
+ " @Provides Object test.C.CModule.acConflict()"))
+ .inFile(aComponent)
+ .onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
+ }
+
+ @Test
+ public void grandchildBindingConflictsWithChild() {
+ JavaFileObject aComponent =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface A {",
+ " B b();",
+ "}");
+ JavaFileObject bComponent =
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = B.BModule.class)",
+ "interface B {",
+ " Object conflict();",
+ "",
+ " C.Builder c();",
+ "",
+ " @Module(subcomponents = C.class)",
+ " static class BModule {",
+ " @Provides static Object bcConflict() {",
+ " return \"b\";",
+ " }",
+ " }",
+ "}");
+ JavaFileObject cComponent =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = C.CModule.class)",
+ "interface C {",
+ " Object conflict();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " C build();",
+ " }",
+ "",
+ " @Module",
+ " static class CModule {",
+ " @Provides static Object bcConflict() {",
+ " return \"c\";",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(fullBindingGraphValidationOption())
+ .compile(aComponent, bComponent, cComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.lang.Object is bound multiple times:",
+ " @Provides Object test.B.BModule.bcConflict()",
+ " @Provides Object test.C.CModule.bcConflict()"))
+ .inFile(fullBindingGraphValidation ? bComponent : aComponent)
+ .onLineContaining(fullBindingGraphValidation ? "class BModule" : "interface A {");
+ }
+
+ @Test
+ public void childProvidesConflictsWithParentInjects() {
+ assumeFalse(fullBindingGraphValidation);
+
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import java.util.Set;",
+ "import javax.inject.Inject;",
+ "",
+ "final class Foo {",
+ " @Inject Foo(Set<String> strings) {}",
+ "}");
+ JavaFileObject injected1 =
+ JavaFileObjects.forSourceLines(
+ "test.Injected1",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Component(modules = Injected1.Injected1Module.class)",
+ "interface Injected1 {",
+ " Foo foo();",
+ " Injected2 injected2();",
+ "",
+ " @Module",
+ " interface Injected1Module {",
+ " @Provides @IntoSet static String string() {",
+ " return \"injected1\";",
+ " }",
+ " }",
+ "}");
+ JavaFileObject injected2 =
+ JavaFileObjects.forSourceLines(
+ "test.Injected2",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = Injected2.Injected2Module.class)",
+ "interface Injected2 {",
+ " Foo foo();",
+ " Provided1 provided1();",
+ "",
+ " @Module",
+ " interface Injected2Module {",
+ " @Provides @IntoSet static String string() {",
+ " return \"injected2\";",
+ " }",
+ " }",
+ "}");
+ JavaFileObject provided1 =
+ JavaFileObjects.forSourceLines(
+ "test.Provided1",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = Provided1.Provided1Module.class)",
+ "interface Provided1 {",
+ " Foo foo();",
+ " Provided2 provided2();",
+ "",
+ " @Module",
+ " static class Provided1Module {",
+ " @Provides static Foo provideFoo(Set<String> strings) {",
+ " return new Foo(strings);",
+ " }",
+ "",
+ " @Provides @IntoSet static String string() {",
+ " return \"provided1\";",
+ " }",
+ " }",
+ "}");
+ JavaFileObject provided2 =
+ JavaFileObjects.forSourceLines(
+ "test.Provided2",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Subcomponent(modules = Provided2.Provided2Module.class)",
+ "interface Provided2 {",
+ " Foo foo();",
+ "",
+ " @Module",
+ " static class Provided2Module {",
+ " @Provides @IntoSet static String string() {",
+ " return \"provided2\";",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(foo, injected1, injected2, provided1, provided2);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining(
+ message(
+ "test.Foo is bound multiple times:",
+ " @Inject test.Foo(Set<String>) [test.Injected1]",
+ " @Provides test.Foo test.Provided1.Provided1Module.provideFoo(Set<String>) "
+ + "[test.Injected1 → test.Injected2 → test.Provided1]"))
+ .inFile(injected1)
+ .onLineContaining("interface Injected1 {");
+ }
+
+ @Test
+ public void grandchildBindingConflictsWithParentWithNullableViolationAsWarning() {
+ JavaFileObject parentConflictsWithChild =
+ JavaFileObjects.forSourceLines(
+ "test.ParentConflictsWithChild",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.annotation.Nullable;",
+ "",
+ "@Component(modules = ParentConflictsWithChild.ParentModule.class)",
+ "interface ParentConflictsWithChild {",
+ " Child.Builder child();",
+ "",
+ " @Module(subcomponents = Child.class)",
+ " static class ParentModule {",
+ " @Provides @Nullable static Object nullableParentChildConflict() {",
+ " return \"parent\";",
+ " }",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = Child.ChildModule.class)",
+ "interface Child {",
+ " Object parentChildConflictThatViolatesNullability();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Child build();",
+ " }",
+ "",
+ " @Module",
+ " static class ChildModule {",
+ " @Provides static Object nonNullableParentChildConflict() {",
+ " return \"child\";",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.nullableValidation=WARNING", fullBindingGraphValidationOption())
+ .compile(parentConflictsWithChild, child);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.lang.Object is bound multiple times:",
+ " @Provides Object test.Child.ChildModule.nonNullableParentChildConflict()",
+ " @Provides @javax.annotation.Nullable Object"
+ + " test.ParentConflictsWithChild.ParentModule.nullableParentChildConflict()"))
+ .inFile(parentConflictsWithChild)
+ .onLineContaining(
+ fullBindingGraphValidation
+ ? "class ParentModule"
+ : "interface ParentConflictsWithChild");
+ }
+
+ private String fullBindingGraphValidationOption() {
+ return "-Adagger.fullBindingGraphValidation=" + (fullBindingGraphValidation ? "ERROR" : "NONE");
+ }
+
+ @Test
+ public void reportedInParentAndChild() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child.Builder childBuilder();",
+ " String duplicated();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.Optional;",
+ "",
+ "@Module",
+ "interface ParentModule {",
+ " @Provides static String one(Optional<Object> optional) { return \"one\"; }",
+ " @Provides static String two() { return \"two\"; }",
+ " @BindsOptionalOf Object optional();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface Child {",
+ " String duplicated();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Child build();",
+ " }",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.Optional;",
+ "",
+ "@Module",
+ "interface ChildModule {",
+ " @Provides static Object object() { return \"object\"; }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("java.lang.String is bound multiple times")
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ assertThat(compilation).hadErrorCount(1);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ElidedFactoriesTest.java b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
new file mode 100644
index 0000000..58ddb82
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ElidedFactoriesTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public ElidedFactoriesTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void simpleComponent() {
+ JavaFileObject injectedType =
+ JavaFileObjects.forSourceLines(
+ "test.InjectedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class InjectedType {",
+ " @Inject InjectedType() {}",
+ "}");
+
+ JavaFileObject dependsOnInjected =
+ JavaFileObjects.forSourceLines(
+ "test.InjectedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class DependsOnInjected {",
+ " @Inject DependsOnInjected(InjectedType injected) {}",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " DependsOnInjected dependsOnInjected();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private DaggerSimpleComponent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public DependsOnInjected dependsOnInjected() {",
+ " return new DependsOnInjected(new InjectedType());",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {",
+ " }",
+ "",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(injectedType, dependsOnInjected, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ @Test
+ public void simpleComponent_injectsProviderOf_dependsOnScoped() {
+ JavaFileObject scopedType =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "final class ScopedType {",
+ " @Inject ScopedType() {}",
+ "}");
+
+ JavaFileObject dependsOnScoped =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class DependsOnScoped {",
+ " @Inject DependsOnScoped(ScopedType scoped) {}",
+ "}");
+
+ JavaFileObject needsProvider =
+ JavaFileObjects.forSourceLines(
+ "test.NeedsProvider",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class NeedsProvider {",
+ " @Inject NeedsProvider(Provider<DependsOnScoped> provider) {}",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface SimpleComponent {",
+ " NeedsProvider needsProvider();",
+ "}");
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.MemoizedSentinel;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private volatile Object scopedType = new MemoizedSentinel();",
+ " private volatile Provider<DependsOnScoped> dependsOnScopedProvider;",
+ "",
+ " private DaggerSimpleComponent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " private ScopedType getScopedType() {",
+ " Object local = scopedType;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = scopedType;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new ScopedType();",
+ " scopedType = DoubleCheck.reentrantCheck(scopedType, local);",
+ " }",
+ " }",
+ " }",
+ " return (ScopedType) local;",
+ " }",
+ "",
+ " private DependsOnScoped getDependsOnScoped() {",
+ " return new DependsOnScoped(getScopedType());",
+ " }",
+ "",
+ " private Provider<DependsOnScoped> getDependsOnScopedProvider() {",
+ " Object local = dependsOnScopedProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " dependsOnScopedProvider = (Provider<DependsOnScoped>) local;",
+ " }",
+ " return (Provider<DependsOnScoped>) local;",
+ " }",
+ "",
+ " @Override",
+ " public NeedsProvider needsProvider() {",
+ " return new NeedsProvider(getDependsOnScopedProvider());",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) DaggerSimpleComponent.this.getDependsOnScoped();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private Provider<ScopedType> scopedTypeProvider;",
+ " private Provider<DependsOnScoped> dependsOnScopedProvider;",
+ "",
+ " private DaggerSimpleComponent() {",
+ " initialize();",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.scopedTypeProvider = DoubleCheck.provider(ScopedType_Factory.create());",
+ " this.dependsOnScopedProvider = ",
+ " DependsOnScoped_Factory.create(scopedTypeProvider);",
+ " }",
+ "",
+ " @Override",
+ " public NeedsProvider needsProvider() {",
+ " return new NeedsProvider(dependsOnScopedProvider);",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {",
+ " }",
+ "",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }",
+ "}");
+ }
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(scopedType, dependsOnScoped, componentFile, needsProvider);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ @Test
+ public void scopedBinding_onlyUsedInSubcomponent() {
+ JavaFileObject scopedType =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "final class ScopedType {",
+ " @Inject ScopedType() {}",
+ "}");
+
+ JavaFileObject dependsOnScoped =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class DependsOnScoped {",
+ " @Inject DependsOnScoped(ScopedType scoped) {}",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface SimpleComponent {",
+ " Sub sub();",
+ "}");
+ JavaFileObject subcomponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Sub {",
+ " DependsOnScoped dependsOnScoped();",
+ "}");
+
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.MemoizedSentinel;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private volatile Object scopedType = new MemoizedSentinel();",
+ "",
+ " private DaggerSimpleComponent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " private ScopedType getScopedType() {",
+ " Object local = scopedType;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = scopedType;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new ScopedType();",
+ " scopedType = DoubleCheck.reentrantCheck(scopedType, local);",
+ " }",
+ " }",
+ " }",
+ " return (ScopedType) local;",
+ " }",
+ "",
+ " @Override",
+ " public Sub sub() {",
+ " return new SubImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }",
+ "",
+ " private final class SubImpl implements Sub {",
+ " private SubImpl() {}",
+ "",
+ " @Override",
+ " public DependsOnScoped dependsOnScoped() {",
+ " return new DependsOnScoped(DaggerSimpleComponent.this.getScopedType());",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerSimpleComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerSimpleComponent implements SimpleComponent {",
+ " private Provider<ScopedType> scopedTypeProvider;",
+ "",
+ " private DaggerSimpleComponent() {",
+ " initialize();",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.scopedTypeProvider = DoubleCheck.provider(ScopedType_Factory.create());",
+ " }",
+ "",
+ " @Override",
+ " public Sub sub() {",
+ " return new SubImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public SimpleComponent build() {",
+ " return new DaggerSimpleComponent();",
+ " }",
+ " }",
+ "",
+ " private final class SubImpl implements Sub {",
+ " private SubImpl() {}",
+ "",
+ " @Override",
+ " public DependsOnScoped dependsOnScoped() {",
+ " return new DependsOnScoped(",
+ " DaggerSimpleComponent.this.scopedTypeProvider.get());",
+ " }",
+ " }",
+ "}");
+ }
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(scopedType, dependsOnScoped, componentFile, subcomponentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerSimpleComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/FrameworkFieldTest.java b/javatests/dagger/internal/codegen/FrameworkFieldTest.java
new file mode 100644
index 0000000..cbc8c03
--- /dev/null
+++ b/javatests/dagger/internal/codegen/FrameworkFieldTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.internal.codegen.javapoet.TypeNames.MEMBERS_INJECTOR;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
+import static dagger.internal.codegen.javapoet.TypeNames.membersInjectorOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+
+import com.google.testing.compile.CompilationRule;
+import com.squareup.javapoet.ClassName;
+import javax.inject.Inject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test case for {@link FrameworkField}.
+ */
+@RunWith(JUnit4.class)
+public class FrameworkFieldTest {
+ @Rule public CompilationRule compilationRule = new CompilationRule();
+
+ private ClassName xTypeName;
+
+ @Before public void setUp() {
+ xTypeName =
+ ClassName.get(compilationRule.getElements().getTypeElement(X.class.getCanonicalName()));
+ }
+
+ @Test public void frameworkType() {
+ assertThat(FrameworkField.create(PROVIDER, xTypeName, "test").type())
+ .isEqualTo(providerOf(xTypeName));
+ assertThat(FrameworkField.create(MEMBERS_INJECTOR, xTypeName, "test").type())
+ .isEqualTo(membersInjectorOf(xTypeName));
+ }
+
+ @Test public void nameSuffix() {
+ assertThat(FrameworkField.create(PROVIDER, xTypeName, "foo").name())
+ .isEqualTo("fooProvider");
+ assertThat(FrameworkField.create(PROVIDER, xTypeName, "fooProvider").name())
+ .isEqualTo("fooProvider");
+ }
+
+ static final class X {
+ @Inject X() {}
+ }
+}
diff --git a/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java b/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
new file mode 100644
index 0000000..3e6e3ad
--- /dev/null
+++ b/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.model.RequestKind.INSTANCE;
+import static dagger.model.RequestKind.LAZY;
+import static dagger.model.RequestKind.PRODUCED;
+import static dagger.model.RequestKind.PRODUCER;
+import static dagger.model.RequestKind.PROVIDER;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test case for {@link FrameworkTypeMapper}. */
+@RunWith(JUnit4.class)
+public class FrameworkTypeMapperTest {
+ @Test public void forProvider() {
+ FrameworkTypeMapper mapper = FrameworkTypeMapper.FOR_PROVIDER;
+ assertThat(mapper.getFrameworkType(INSTANCE)).isEqualTo(FrameworkType.PROVIDER);
+ assertThat(mapper.getFrameworkType(LAZY)).isEqualTo(FrameworkType.PROVIDER);
+ assertThat(mapper.getFrameworkType(PROVIDER)).isEqualTo(FrameworkType.PROVIDER);
+ }
+
+ @Test public void forProducer() {
+ FrameworkTypeMapper mapper = FrameworkTypeMapper.FOR_PRODUCER;
+ assertThat(mapper.getFrameworkType(INSTANCE)).isEqualTo(FrameworkType.PRODUCER_NODE);
+ assertThat(mapper.getFrameworkType(LAZY)).isEqualTo(FrameworkType.PROVIDER);
+ assertThat(mapper.getFrameworkType(PROVIDER)).isEqualTo(FrameworkType.PROVIDER);
+ assertThat(mapper.getFrameworkType(PRODUCER)).isEqualTo(FrameworkType.PRODUCER_NODE);
+ assertThat(mapper.getFrameworkType(PRODUCED)).isEqualTo(FrameworkType.PRODUCER_NODE);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java b/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java
new file mode 100644
index 0000000..944e307
--- /dev/null
+++ b/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.endsWithMessage;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.regex.Pattern;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class FullBindingGraphValidationTest {
+ private static final JavaFileObject MODULE_WITH_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithErrors",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface ModuleWithErrors {",
+ " @Binds Object object1(String string);",
+ " @Binds Object object2(Long l);",
+ " @Binds Number missingDependency(Integer i);",
+ "}");
+
+ // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+ private static final Pattern MODULE_WITH_ERRORS_MESSAGE =
+ endsWithMessage(
+ "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+ " @Binds Object test.ModuleWithErrors.object1(String)",
+ " @Binds Object test.ModuleWithErrors.object2(Long)");
+
+ @Test
+ public void moduleWithErrors_validationTypeNone() {
+ Compilation compilation = daggerCompiler().compile(MODULE_WITH_ERRORS);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void moduleWithErrors_validationTypeError() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(MODULE_WITH_ERRORS);
+
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+ .inFile(MODULE_WITH_ERRORS)
+ .onLineContaining("interface ModuleWithErrors");
+
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void moduleWithErrors_validationTypeWarning() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ .compile(MODULE_WITH_ERRORS);
+
+ assertThat(compilation).succeeded();
+
+ assertThat(compilation)
+ .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+ .inFile(MODULE_WITH_ERRORS)
+ .onLineContaining("interface ModuleWithErrors");
+
+ assertThat(compilation).hadWarningCount(1);
+ }
+
+ private static final JavaFileObject INCLUDES_MODULE_WITH_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.IncludesModuleWithErrors",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = ModuleWithErrors.class)",
+ "interface IncludesModuleWithErrors {}");
+
+ @Test
+ public void includesModuleWithErrors_validationTypeNone() {
+ Compilation compilation =
+ daggerCompiler().compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void includesModuleWithErrors_validationTypeError() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
+
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+ .inFile(MODULE_WITH_ERRORS)
+ .onLineContaining("interface ModuleWithErrors");
+
+ assertThat(compilation)
+ .hadErrorContainingMatch("test.ModuleWithErrors has errors")
+ .inFile(INCLUDES_MODULE_WITH_ERRORS)
+ .onLineContaining("ModuleWithErrors.class");
+
+ assertThat(compilation).hadErrorCount(2);
+ }
+
+ @Test
+ public void includesModuleWithErrors_validationTypeWarning() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ .compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
+
+ assertThat(compilation).succeeded();
+
+ assertThat(compilation)
+ .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+ .inFile(MODULE_WITH_ERRORS)
+ .onLineContaining("interface ModuleWithErrors");
+
+ // TODO(b/130284666)
+ assertThat(compilation)
+ .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+ .inFile(INCLUDES_MODULE_WITH_ERRORS)
+ .onLineContaining("interface IncludesModuleWithErrors");
+
+ assertThat(compilation).hadWarningCount(2);
+ }
+
+ private static final JavaFileObject A_MODULE =
+ JavaFileObjects.forSourceLines(
+ "test.AModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface AModule {",
+ " @Binds Object object(String string);",
+ "}");
+
+ private static final JavaFileObject COMBINED_WITH_A_MODULE_HAS_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.CombinedWithAModuleHasErrors",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = AModule.class)",
+ "interface CombinedWithAModuleHasErrors {",
+ " @Binds Object object(Long l);",
+ "}");
+
+ // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+ private static final Pattern COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE =
+ endsWithMessage(
+ "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+ " @Binds Object test.AModule.object(String)",
+ " @Binds Object test.CombinedWithAModuleHasErrors.object(Long)");
+
+ @Test
+ public void moduleIncludingModuleWithCombinedErrors_validationTypeNone() {
+ Compilation compilation = daggerCompiler().compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
+
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void moduleIncludingModuleWithCombinedErrors_validationTypeError() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
+
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE)
+ .inFile(COMBINED_WITH_A_MODULE_HAS_ERRORS)
+ .onLineContaining("interface CombinedWithAModuleHasErrors");
+
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void moduleIncludingModuleWithCombinedErrors_validationTypeWarning() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ .compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
+
+ assertThat(compilation).succeeded();
+
+ assertThat(compilation)
+ .hadWarningContainingMatch(COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE)
+ .inFile(COMBINED_WITH_A_MODULE_HAS_ERRORS)
+ .onLineContaining("interface CombinedWithAModuleHasErrors");
+
+ assertThat(compilation).hadWarningCount(1);
+ }
+
+ private static final JavaFileObject SUBCOMPONENT_WITH_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.SubcomponentWithErrors",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AModule.class)",
+ "interface SubcomponentWithErrors {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder object(Object object);",
+ " SubcomponentWithErrors build();",
+ " }",
+ "}");
+
+ // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+ private static final Pattern SUBCOMPONENT_WITH_ERRORS_MESSAGE =
+ endsWithMessage(
+ "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+ " @Binds Object test.AModule.object(String)",
+ " @BindsInstance test.SubcomponentWithErrors.Builder"
+ + " test.SubcomponentWithErrors.Builder.object(Object)");
+
+ @Test
+ public void subcomponentWithErrors_validationTypeNone() {
+ Compilation compilation = daggerCompiler().compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void subcomponentWithErrors_validationTypeError() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .inFile(SUBCOMPONENT_WITH_ERRORS)
+ .onLineContaining("interface SubcomponentWithErrors");
+
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void subcomponentWithErrors_validationTypeWarning() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ .compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+ assertThat(compilation).succeeded();
+
+ assertThat(compilation)
+ .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .inFile(SUBCOMPONENT_WITH_ERRORS)
+ .onLineContaining("interface SubcomponentWithErrors");
+
+ assertThat(compilation).hadWarningCount(1);
+ }
+
+ private static final JavaFileObject MODULE_WITH_SUBCOMPONENT_WITH_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithSubcomponentWithErrors",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module(subcomponents = SubcomponentWithErrors.class)",
+ "interface ModuleWithSubcomponentWithErrors {}");
+
+ @Test
+ public void moduleWithSubcomponentWithErrors_validationTypeNone() {
+ Compilation compilation =
+ daggerCompiler()
+ .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void moduleWithSubcomponentWithErrors_validationTypeError() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
+ .onLineContaining("interface ModuleWithSubcomponentWithErrors");
+
+ // TODO(b/130283677)
+ assertThat(compilation)
+ .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .inFile(SUBCOMPONENT_WITH_ERRORS)
+ .onLineContaining("interface SubcomponentWithErrors");
+
+ assertThat(compilation).hadErrorCount(2);
+ }
+
+ @Test
+ public void moduleWithSubcomponentWithErrors_validationTypeWarning() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+ assertThat(compilation).succeeded();
+
+ assertThat(compilation)
+ .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
+ .onLineContaining("interface ModuleWithSubcomponentWithErrors");
+
+ // TODO(b/130283677)
+ assertThat(compilation)
+ .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .inFile(SUBCOMPONENT_WITH_ERRORS)
+ .onLineContaining("interface SubcomponentWithErrors");
+
+ assertThat(compilation).hadWarningCount(2);
+ }
+
+ private static final JavaFileObject A_SUBCOMPONENT =
+ JavaFileObjects.forSourceLines(
+ "test.ASubcomponent",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = AModule.class)",
+ "interface ASubcomponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ASubcomponent build();",
+ " }",
+ "}");
+
+ private static final JavaFileObject COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.CombinedWithASubcomponentHasErrors",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module(subcomponents = ASubcomponent.class)",
+ "interface CombinedWithASubcomponentHasErrors {",
+ " @Binds Object object(Number number);",
+ "}");
+
+ // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+ private static final Pattern COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE =
+ endsWithMessage(
+ "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+ " @Binds Object test.AModule.object(String)",
+ " @Binds Object test.CombinedWithASubcomponentHasErrors.object(Number)");
+
+ @Test
+ public void moduleWithSubcomponentWithCombinedErrors_validationTypeNone() {
+ Compilation compilation =
+ daggerCompiler().compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
+
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void moduleWithSubcomponentWithCombinedErrors_validationTypeError() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
+
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContainingMatch(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE)
+ .inFile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS)
+ .onLineContaining("interface CombinedWithASubcomponentHasErrors");
+
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void moduleWithSubcomponentWithCombinedErrors_validationTypeWarning() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ .compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
+
+ assertThat(compilation).succeeded();
+
+ assertThat(compilation)
+ .hadWarningContainingMatch(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE)
+ .inFile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS)
+ .onLineContaining("interface CombinedWithASubcomponentHasErrors");
+
+ assertThat(compilation).hadWarningCount(1);
+ }
+
+ @Test
+ public void bothAliasesDifferentValues() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(
+ "-Adagger.moduleBindingValidation=NONE",
+ "-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(MODULE_WITH_ERRORS);
+
+ assertThat(compilation).failed();
+
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Only one of the equivalent options "
+ + "(-Adagger.fullBindingGraphValidation, -Adagger.moduleBindingValidation)"
+ + " should be used; prefer -Adagger.fullBindingGraphValidation");
+
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void bothAliasesSameValue() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(
+ "-Adagger.moduleBindingValidation=NONE", "-Adagger.fullBindingGraphValidation=NONE")
+ .compile(MODULE_WITH_ERRORS);
+
+ assertThat(compilation).succeeded();
+
+ assertThat(compilation)
+ .hadWarningContaining(
+ "Only one of the equivalent options "
+ + "(-Adagger.fullBindingGraphValidation, -Adagger.moduleBindingValidation)"
+ + " should be used; prefer -Adagger.fullBindingGraphValidation");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/GeneratedLines.java b/javatests/dagger/internal/codegen/GeneratedLines.java
new file mode 100644
index 0000000..2aa17ac
--- /dev/null
+++ b/javatests/dagger/internal/codegen/GeneratedLines.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.javapoet.CodeBlocks.stringLiteral;
+
+import com.squareup.javapoet.CodeBlock;
+
+/**
+ * Common lines outputted during code generation.
+ */
+public final class GeneratedLines {
+ public static final String GENERATED_ANNOTATION =
+ "@Generated("
+ + "value = \"dagger.internal.codegen.ComponentProcessor\", "
+ + "comments = \"https://dagger.dev\")";
+
+ public static final String IMPORT_GENERATED_ANNOTATION =
+ isBeforeJava9()
+ ? "import javax.annotation.Generated;"
+ : "import javax.annotation.processing.Generated;";
+
+ static final String GENERATION_OPTIONS_ANNOTATION = "@GenerationOptions(fastInit = false)";
+
+ private static boolean isBeforeJava9() {
+ try {
+ Class.forName("java.lang.Module");
+ return false;
+ } catch (ClassNotFoundException e) {
+ return true;
+ }
+ }
+
+ public static final CodeBlock NPE_FROM_PROVIDES_METHOD =
+ stringLiteral("Cannot return null from a non-@Nullable @Provides method");
+
+ public static final CodeBlock NPE_FROM_COMPONENT_METHOD =
+ stringLiteral("Cannot return null from a non-@Nullable component method");
+}
diff --git a/javatests/dagger/internal/codegen/GeneratingProcessor.java b/javatests/dagger/internal/codegen/GeneratingProcessor.java
new file mode 100644
index 0000000..8e4e7b5
--- /dev/null
+++ b/javatests/dagger/internal/codegen/GeneratingProcessor.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+
+/** A simple {@link Processor} that generates one source file. */
+final class GeneratingProcessor extends AbstractProcessor {
+ private final String generatedClassName;
+ private final String generatedSource;
+ private boolean processed;
+
+ GeneratingProcessor(String generatedClassName, String... source) {
+ this.generatedClassName = generatedClassName;
+ this.generatedSource = Joiner.on("\n").join(source);
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of("*");
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (!processed) {
+ processed = true;
+ try (Writer writer =
+ processingEnv.getFiler().createSourceFile(generatedClassName).openWriter()) {
+ writer.append(generatedSource);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return false;
+ }
+}
diff --git a/javatests/dagger/internal/codegen/GenericMethodsTest.java b/javatests/dagger/internal/codegen/GenericMethodsTest.java
new file mode 100644
index 0000000..cd4595e
--- /dev/null
+++ b/javatests/dagger/internal/codegen/GenericMethodsTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class GenericMethodsTest {
+ @Test
+ public void parameterizedComponentMethods() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.MembersInjector;",
+ "import java.util.Set;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " <T1> void injectTypeVariable(T1 type);",
+ " <T2> MembersInjector<T2> membersInjector();",
+ " <T3> Set<T3> setOfT();",
+ " <UNUSED> TestComponent unused();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("cannot have type variables")
+ .inFile(component)
+ .onLineContaining("<T1>");
+ assertThat(compilation)
+ .hadErrorContaining("cannot have type variables")
+ .inFile(component)
+ .onLineContaining("<T2>");
+ assertThat(compilation)
+ .hadErrorContaining("cannot have type variables")
+ .inFile(component)
+ .onLineContaining("<T3>");
+ assertThat(compilation)
+ .hadErrorContaining("cannot have type variables")
+ .inFile(component)
+ .onLineContaining("<UNUSED>");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
new file mode 100644
index 0000000..e6e9706
--- /dev/null
+++ b/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
@@ -0,0 +1,1410 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+// TODO(gak): add tests for generation in the default package.
+public final class InjectConstructorFactoryGeneratorTest {
+ private static final JavaFileObject QUALIFIER_A =
+ JavaFileObjects.forSourceLines("test.QualifierA",
+ "package test;",
+ "",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Qualifier @interface QualifierA {}");
+ private static final JavaFileObject QUALIFIER_B =
+ JavaFileObjects.forSourceLines("test.QualifierB",
+ "package test;",
+ "",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Qualifier @interface QualifierB {}");
+ private static final JavaFileObject SCOPE_A =
+ JavaFileObjects.forSourceLines("test.ScopeA",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeA {}");
+ private static final JavaFileObject SCOPE_B =
+ JavaFileObjects.forSourceLines("test.ScopeB",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeB {}");
+
+ @Test public void injectOnPrivateConstructor() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateConstructor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class PrivateConstructor {",
+ " @Inject private PrivateConstructor() {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into private constructors")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void injectConstructorOnInnerClass() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class OuterClass {",
+ " class InnerClass {",
+ " @Inject InnerClass() {}",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@Inject constructors are invalid on inner classes. "
+ + "Did you mean to make the class static?")
+ .inFile(file)
+ .onLine(7);
+ }
+
+ @Test public void injectConstructorOnAbstractClass() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.AbstractClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "abstract class AbstractClass {",
+ " @Inject AbstractClass() {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Inject is nonsense on the constructor of an abstract class")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void injectConstructorOnGenericClass() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class GenericClass<T> {",
+ " @Inject GenericClass(T t) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.GenericClass_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class GenericClass_Factory<T> implements Factory<GenericClass<T>> {",
+ " private final Provider<T> tProvider;",
+ "",
+ " public GenericClass_Factory(Provider<T> tProvider) {",
+ " this.tProvider = tProvider;",
+ " }",
+ "",
+ " @Override",
+ " public GenericClass<T> get() {",
+ " return new GenericClass<T>(tProvider.get());",
+ " }",
+ "",
+ " public static <T> GenericClass_Factory<T> create(Provider<T> tProvider) {",
+ " return new GenericClass_Factory<T>(tProvider);",
+ " }",
+ "",
+ " public static <T> GenericClass<T> newInstance(T t) {",
+ " return new GenericClass<T>(t);",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test public void fieldAndMethodGenerics() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class GenericClass<A, B> {",
+ " @Inject A a;",
+ "",
+ " @Inject GenericClass() {}",
+ "",
+ " @Inject void register(B b) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.GenericClass_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class GenericClass_Factory<A, B> implements Factory<GenericClass<A, B>> {",
+ " private final Provider<A> aProvider;",
+ " private final Provider<B> bProvider;",
+ "",
+ " public GenericClass_Factory(",
+ " Provider<A> aProvider, Provider<B> bProvider) {",
+ " this.aProvider = aProvider;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " @Override",
+ " public GenericClass<A, B> get() {",
+ " GenericClass<A, B> instance = new GenericClass<A, B>();",
+ " GenericClass_MembersInjector.injectA(instance, aProvider.get());",
+ " GenericClass_MembersInjector.injectRegister(instance, bProvider.get());",
+ " return instance;",
+ " }",
+ "",
+ " public static <A, B> GenericClass_Factory<A, B> create(",
+ " Provider<A> aProvider, Provider<B> bProvider) {",
+ " return new GenericClass_Factory<A, B>(aProvider, bProvider);",
+ " }",
+ "",
+ " public static <A, B> GenericClass<A, B> newInstance() {",
+ " return new GenericClass<A, B>();",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test public void genericClassWithNoDependencies() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class GenericClass<T> {",
+ " @Inject GenericClass() {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.GenericClass_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class GenericClass_Factory<T> implements Factory<GenericClass<T>> {",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private static final GenericClass_Factory INSTANCE = new GenericClass_Factory();",
+ "",
+ " @Override",
+ " public GenericClass<T> get() {",
+ " return new GenericClass<T>();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " public static <T> GenericClass_Factory<T> create() {",
+ " return INSTANCE;",
+ " }",
+ "",
+ " public static <T> GenericClass<T> newInstance() {",
+ " return new GenericClass<T>();",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test public void twoGenericTypes() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class GenericClass<A, B> {",
+ " @Inject GenericClass(A a, B b) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.GenericClass_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class GenericClass_Factory<A, B> implements Factory<GenericClass<A, B>> {",
+ " private final Provider<A> aProvider;",
+ " private final Provider<B> bProvider;",
+ "",
+ " public GenericClass_Factory(Provider<A> aProvider, Provider<B> bProvider) {",
+ " this.aProvider = aProvider;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " @Override",
+ " public GenericClass<A, B> get() {",
+ " return new GenericClass<A, B>(aProvider.get(), bProvider.get());",
+ " }",
+ "",
+ " public static <A, B> GenericClass_Factory<A, B> create(",
+ " Provider<A> aProvider, Provider<B> bProvider) {",
+ " return new GenericClass_Factory<A, B>(aProvider, bProvider);",
+ " }",
+ "",
+ " public static <A, B> GenericClass<A, B> newInstance(A a, B b) {",
+ " return new GenericClass<A, B>(a, b);",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test public void boundedGenerics() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import java.util.List;",
+ "",
+ "class GenericClass<A extends Number & Comparable<A>,",
+ " B extends List<? extends String>,",
+ " C extends List<? super String>> {",
+ " @Inject GenericClass(A a, B b, C c) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.GenericClass_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import java.util.List;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class GenericClass_Factory<A extends Number & Comparable<A>,",
+ " B extends List<? extends String>,",
+ " C extends List<? super String>>",
+ " implements Factory<GenericClass<A, B, C>> {",
+ " private final Provider<A> aProvider;",
+ " private final Provider<B> bProvider;",
+ " private final Provider<C> cProvider;",
+ "",
+ " public GenericClass_Factory(Provider<A> aProvider,",
+ " Provider<B> bProvider,",
+ " Provider<C> cProvider) {",
+ " this.aProvider = aProvider;",
+ " this.bProvider = bProvider;",
+ " this.cProvider = cProvider;",
+ " }",
+ "",
+ " @Override",
+ " public GenericClass<A, B, C> get() {",
+ " return new GenericClass<A, B, C>(",
+ " aProvider.get(), bProvider.get(), cProvider.get());",
+ " }",
+ "",
+ " public static <A extends Number & Comparable<A>,",
+ " B extends List<? extends String>,",
+ " C extends List<? super String>> GenericClass_Factory<A, B, C> create(",
+ " Provider<A> aProvider, Provider<B> bProvider, Provider<C> cProvider) {",
+ " return new GenericClass_Factory<A, B, C>(aProvider, bProvider, cProvider);",
+ " }",
+ "",
+ " public static <",
+ " A extends Number & Comparable<A>,",
+ " B extends List<? extends String>,",
+ " C extends List<? super String>>",
+ " GenericClass<A, B, C> newInstance(A a, B b, C c) {",
+ " return new GenericClass<A, B, C>(a, b, c);",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test public void multipleSameTypesWithGenericsAndQualifiersAndLazies() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "import dagger.Lazy;",
+ "",
+ "class GenericClass<A, B> {",
+ " @Inject GenericClass(A a, A a2, Provider<A> pa, @QualifierA A qa, Lazy<A> la, ",
+ " String s, String s2, Provider<String> ps, ",
+ " @QualifierA String qs, Lazy<String> ls,",
+ " B b, B b2, Provider<B> pb, @QualifierA B qb, Lazy<B> lb) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.GenericClass_Factory",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class GenericClass_Factory<A, B>",
+ " implements Factory<GenericClass<A, B>> {",
+ " private final Provider<A> aAndA2AndPaAndLaProvider;",
+ " private final Provider<A> qaProvider;",
+ " private final Provider<String> sAndS2AndPsAndLsProvider;",
+ " private final Provider<String> qsProvider;",
+ " private final Provider<B> bAndB2AndPbAndLbProvider;",
+ " private final Provider<B> qbProvider;",
+ "",
+ " public GenericClass_Factory(Provider<A> aAndA2AndPaAndLaProvider,",
+ " Provider<A> qaProvider,",
+ " Provider<String> sAndS2AndPsAndLsProvider,",
+ " Provider<String> qsProvider,",
+ " Provider<B> bAndB2AndPbAndLbProvider,",
+ " Provider<B> qbProvider) {",
+ " this.aAndA2AndPaAndLaProvider = aAndA2AndPaAndLaProvider;",
+ " this.qaProvider = qaProvider;",
+ " this.sAndS2AndPsAndLsProvider = sAndS2AndPsAndLsProvider;",
+ " this.qsProvider = qsProvider;",
+ " this.bAndB2AndPbAndLbProvider = bAndB2AndPbAndLbProvider;",
+ " this.qbProvider = qbProvider;",
+ " }",
+ "",
+ " @Override",
+ " public GenericClass<A, B> get() {",
+ " return new GenericClass<A, B>(",
+ " aAndA2AndPaAndLaProvider.get(),",
+ " aAndA2AndPaAndLaProvider.get(),",
+ " aAndA2AndPaAndLaProvider,",
+ " qaProvider.get(),",
+ " DoubleCheck.lazy(aAndA2AndPaAndLaProvider),",
+ " sAndS2AndPsAndLsProvider.get(),",
+ " sAndS2AndPsAndLsProvider.get(),",
+ " sAndS2AndPsAndLsProvider,",
+ " qsProvider.get(),",
+ " DoubleCheck.lazy(sAndS2AndPsAndLsProvider),",
+ " bAndB2AndPbAndLbProvider.get(),",
+ " bAndB2AndPbAndLbProvider.get(),",
+ " bAndB2AndPbAndLbProvider,",
+ " qbProvider.get(),",
+ " DoubleCheck.lazy(bAndB2AndPbAndLbProvider));",
+ " }",
+ "",
+ " public static <A, B> GenericClass_Factory<A, B> create(",
+ " Provider<A> aAndA2AndPaAndLaProvider,",
+ " Provider<A> qaProvider,",
+ " Provider<String> sAndS2AndPsAndLsProvider,",
+ " Provider<String> qsProvider,",
+ " Provider<B> bAndB2AndPbAndLbProvider,",
+ " Provider<B> qbProvider) {",
+ " return new GenericClass_Factory<A, B>(",
+ " aAndA2AndPaAndLaProvider,",
+ " qaProvider,",
+ " sAndS2AndPsAndLsProvider,",
+ " qsProvider,",
+ " bAndB2AndPbAndLbProvider,",
+ " qbProvider);",
+ " }",
+ "",
+ " public static <A, B> GenericClass<A, B> newInstance(",
+ " A a,",
+ " A a2,",
+ " Provider<A> pa,",
+ " A qa,",
+ " Lazy<A> la,",
+ " String s,",
+ " String s2,",
+ " Provider<String> ps,",
+ " String qs,",
+ " Lazy<String> ls,",
+ " B b,",
+ " B b2,",
+ " Provider<B> pb,",
+ " B qb,",
+ " Lazy<B> lb) {",
+ " return new GenericClass<A, B>(",
+ " a, a2, pa, qa, la, s, s2, ps, qs, ls, b, b2, pb, qb, lb);",
+ " }",
+ "}");
+ assertAbout(javaSources()).that(ImmutableList.of(file, QUALIFIER_A))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test public void multipleInjectConstructors() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.TooManyInjectConstructors",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class TooManyInjectConstructors {",
+ " @Inject TooManyInjectConstructors() {}",
+ " TooManyInjectConstructors(int i) {}",
+ " @Inject TooManyInjectConstructors(String s) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Types may only contain one @Inject constructor")
+ .inFile(file)
+ .onLine(6);
+ assertThat(compilation)
+ .hadErrorContaining("Types may only contain one @Inject constructor")
+ .inFile(file)
+ .onLine(8);
+ }
+
+ @Test public void multipleQualifiersOnInjectConstructorParameter() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleQualifierConstructorParam",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class MultipleQualifierConstructorParam {",
+ " @Inject MultipleQualifierConstructorParam(@QualifierA @QualifierB String s) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file, QUALIFIER_A, QUALIFIER_B);
+ assertThat(compilation).failed();
+ // for whatever reason, javac only reports the error once on the constructor
+ assertThat(compilation)
+ .hadErrorContaining("A single dependency request may not use more than one @Qualifier")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void injectConstructorOnClassWithMultipleScopes() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleScopeClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "@ScopeA @ScopeB class MultipleScopeClass {",
+ " @Inject MultipleScopeClass() {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file, SCOPE_A, SCOPE_B);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("A single binding may not declare more than one @Scope")
+ .inFile(file)
+ .onLine(5)
+ .atColumn(1);
+ assertThat(compilation)
+ .hadErrorContaining("A single binding may not declare more than one @Scope")
+ .inFile(file)
+ .onLine(5)
+ .atColumn(9);
+ }
+
+ @Test public void injectConstructorWithQualifier() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleScopeClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class MultipleScopeClass {",
+ " @Inject",
+ " @QualifierA",
+ " @QualifierB",
+ " MultipleScopeClass() {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file, QUALIFIER_A, QUALIFIER_B);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Qualifier annotations are not allowed on @Inject constructors")
+ .inFile(file)
+ .onLine(7);
+ assertThat(compilation)
+ .hadErrorContaining("@Qualifier annotations are not allowed on @Inject constructors")
+ .inFile(file)
+ .onLine(8);
+ }
+
+ @Test public void injectConstructorWithCheckedExceptionsError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.CheckedExceptionClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class CheckedExceptionClass {",
+ " @Inject CheckedExceptionClass() throws Exception {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support checked exceptions on @Inject constructors")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void injectConstructorWithCheckedExceptionsWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.CheckedExceptionClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class CheckedExceptionClass {",
+ " @Inject CheckedExceptionClass() throws Exception {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining("Dagger does not support checked exceptions on @Inject constructors")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void privateInjectClassError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " private static final class InnerClass {",
+ " @Inject InnerClass() {}",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into private classes")
+ .inFile(file)
+ .onLine(7);
+ }
+
+ @Test public void privateInjectClassWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " private static final class InnerClass {",
+ " @Inject InnerClass() {}",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining("Dagger does not support injection into private classes")
+ .inFile(file)
+ .onLine(7);
+ }
+
+ @Test public void nestedInPrivateInjectClassError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " private static final class MiddleClass {",
+ " static final class InnerClass {",
+ " @Inject InnerClass() {}",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into private classes")
+ .inFile(file)
+ .onLine(8);
+ }
+
+ @Test public void nestedInPrivateInjectClassWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " private static final class MiddleClass {",
+ " static final class InnerClass {",
+ " @Inject InnerClass() {}",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining("Dagger does not support injection into private classes")
+ .inFile(file)
+ .onLine(8);
+ }
+
+ @Test public void finalInjectField() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.FinalInjectField",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class FinalInjectField {",
+ " @Inject final String s;",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Inject fields may not be final")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void privateInjectFieldError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectField",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class PrivateInjectField {",
+ " @Inject private String s;",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into private fields")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void privateInjectFieldWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectField",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class PrivateInjectField {",
+ " @Inject private String s;",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
+ }
+
+ @Test public void staticInjectFieldError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectField",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class StaticInjectField {",
+ " @Inject static String s;",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into static fields")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void staticInjectFieldWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectField",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class StaticInjectField {",
+ " @Inject static String s;",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions("-Adagger.staticMemberValidation=WARNING").compile(file);
+ assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
+ }
+
+ @Test public void multipleQualifiersOnField() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleQualifierInjectField",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class MultipleQualifierInjectField {",
+ " @Inject @QualifierA @QualifierB String s;",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file, QUALIFIER_A, QUALIFIER_B);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("A single dependency request may not use more than one @Qualifier")
+ .inFile(file)
+ .onLine(6)
+ .atColumn(11);
+ assertThat(compilation)
+ .hadErrorContaining("A single dependency request may not use more than one @Qualifier")
+ .inFile(file)
+ .onLine(6)
+ .atColumn(23);
+ }
+
+ @Test public void abstractInjectMethod() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.AbstractInjectMethod",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "abstract class AbstractInjectMethod {",
+ " @Inject abstract void method();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Methods with @Inject may not be abstract")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void privateInjectMethodError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectMethod",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class PrivateInjectMethod {",
+ " @Inject private void method(){}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into private methods")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void privateInjectMethodWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.PrivateInjectMethod",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class PrivateInjectMethod {",
+ " @Inject private void method(){}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
+ }
+
+ @Test public void staticInjectMethodError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectMethod",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class StaticInjectMethod {",
+ " @Inject static void method(){}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into static methods")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void staticInjectMethodWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.StaticInjectMethod",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class StaticInjectMethod {",
+ " @Inject static void method(){}",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions("-Adagger.staticMemberValidation=WARNING").compile(file);
+ assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
+ }
+
+ @Test public void genericInjectMethod() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericInjectMethod",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class AbstractInjectMethod {",
+ " @Inject <T> void method();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Methods with @Inject may not declare type parameters")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void multipleQualifiersOnInjectMethodParameter() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.MultipleQualifierMethodParam",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class MultipleQualifierMethodParam {",
+ " @Inject void method(@QualifierA @QualifierB String s) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(file, QUALIFIER_A, QUALIFIER_B);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("A single dependency request may not use more than one @Qualifier")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void injectConstructorDependsOnProduced() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import dagger.producers.Produced;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(Produced<String> str) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(aFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Produced may only be injected in @Produces methods");
+ }
+
+ @Test public void injectConstructorDependsOnProducer() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import dagger.producers.Producer;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(Producer<String> str) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(aFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Producer may only be injected in @Produces methods");
+ }
+
+ @Test public void injectFieldDependsOnProduced() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import dagger.producers.Produced;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject Produced<String> str;",
+ "}");
+ Compilation compilation = daggerCompiler().compile(aFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Produced may only be injected in @Produces methods");
+ }
+
+ @Test public void injectFieldDependsOnProducer() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import dagger.producers.Producer;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject Producer<String> str;",
+ "}");
+ Compilation compilation = daggerCompiler().compile(aFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Producer may only be injected in @Produces methods");
+ }
+
+ @Test public void injectMethodDependsOnProduced() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import dagger.producers.Produced;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject void inject(Produced<String> str) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(aFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Produced may only be injected in @Produces methods");
+ }
+
+ @Test public void injectMethodDependsOnProducer() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import dagger.producers.Producer;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject void inject(Producer<String> str) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(aFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Producer may only be injected in @Produces methods");
+ }
+
+ @Test public void injectConstructor() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectConstructor {",
+ " @Inject InjectConstructor(String s) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.InjectConstructor_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class InjectConstructor_Factory ",
+ " implements Factory<InjectConstructor> {",
+ "",
+ " private final Provider<String> sProvider;",
+ "",
+ " public InjectConstructor_Factory(Provider<String> sProvider) {",
+ " this.sProvider = sProvider;",
+ " }",
+ "",
+ " @Override public InjectConstructor get() {",
+ " return new InjectConstructor(sProvider.get());",
+ " }",
+ "",
+ " public static InjectConstructor_Factory create(Provider<String> sProvider) {",
+ " return new InjectConstructor_Factory(sProvider);",
+ " }",
+ "",
+ " public static InjectConstructor newInstance(String s) {",
+ " return new InjectConstructor(s);",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file).processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test public void injectConstructorAndMembersInjection() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.AllInjections",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class AllInjections {",
+ " @Inject String s;",
+ " @Inject AllInjections(String s) {}",
+ " @Inject void s(String s) {}",
+ "}");
+ JavaFileObject expectedFactory =
+ JavaFileObjects.forSourceLines(
+ "test.AllInjections_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class AllInjections_Factory implements Factory<AllInjections> {",
+ " private final Provider<String> sProvider;",
+ "",
+ " public AllInjections_Factory(Provider<String> sProvider) {",
+ " this.sProvider = sProvider;",
+ " }",
+ "",
+ " @Override public AllInjections get() {",
+ " AllInjections instance = new AllInjections(sProvider.get());",
+ " AllInjections_MembersInjector.injectS(instance, sProvider.get());",
+ " AllInjections_MembersInjector.injectS2(instance, sProvider.get());",
+ " return instance;",
+ " }",
+ "",
+ " public static AllInjections_Factory create(Provider<String> sProvider) {",
+ " return new AllInjections_Factory(sProvider);",
+ " }",
+ "",
+ " public static AllInjections newInstance(String s) {",
+ " return new AllInjections(s);",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file).processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expectedFactory);
+ }
+
+ @Test
+ public void wildcardDependency() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
+ "package test;",
+ "",
+ "import java.util.List;",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectConstructor {",
+ " @Inject InjectConstructor(List<?> objects) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.InjectConstructor_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import java.util.List;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class InjectConstructor_Factory ",
+ " implements Factory<InjectConstructor> {",
+ "",
+ " private final Provider<List<?>> objectsProvider;",
+ "",
+ " public InjectConstructor_Factory(Provider<List<?>> objectsProvider) {",
+ " this.objectsProvider = objectsProvider;",
+ " }",
+ "",
+ " @Override public InjectConstructor get() {",
+ " return new InjectConstructor(objectsProvider.get());",
+ " }",
+ "",
+ " public static InjectConstructor_Factory create(",
+ " Provider<List<?>> objectsProvider) {",
+ " return new InjectConstructor_Factory(objectsProvider);",
+ " }",
+ "",
+ " public static InjectConstructor newInstance(List<?> objects) {",
+ " return new InjectConstructor(objects);",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(file).processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test
+ public void basicNameCollision() {
+ JavaFileObject factoryFile = JavaFileObjects.forSourceLines("other.pkg.Factory",
+ "package other.pkg;",
+ "",
+ "public class Factory {}");
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import other.pkg.Factory;",
+ "",
+ "class InjectConstructor {",
+ " @Inject InjectConstructor(Factory factory) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.InjectConstructor_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class InjectConstructor_Factory ",
+ " implements Factory<InjectConstructor> {",
+ "",
+ " private final Provider<other.pkg.Factory> factoryProvider;",
+ "",
+ " public InjectConstructor_Factory(Provider<other.pkg.Factory> factoryProvider) {",
+ " this.factoryProvider = factoryProvider;",
+ " }",
+ "",
+ " @Override public InjectConstructor get() {",
+ " return new InjectConstructor(factoryProvider.get());",
+ " }",
+ "",
+ " public static InjectConstructor_Factory create(",
+ " Provider<other.pkg.Factory> factoryProvider) {",
+ " return new InjectConstructor_Factory(factoryProvider);",
+ " }",
+ "",
+ " public static InjectConstructor newInstance(",
+ " other.pkg.Factory factory) {",
+ " return new InjectConstructor(factory);",
+ " }",
+ "}");
+ assertAbout(javaSources()).that(ImmutableList.of(factoryFile, file))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test
+ public void nestedNameCollision() {
+ JavaFileObject factoryFile = JavaFileObjects.forSourceLines("other.pkg.Outer",
+ "package other.pkg;",
+ "",
+ "public class Outer {",
+ " public class Factory {}",
+ "}");
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import other.pkg.Outer;",
+ "",
+ "class InjectConstructor {",
+ " @Inject InjectConstructor(Outer.Factory factory) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.InjectConstructor_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "import other.pkg.Outer;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class InjectConstructor_Factory ",
+ " implements Factory<InjectConstructor> {",
+ "",
+ " private final Provider<Outer.Factory> factoryProvider;",
+ "",
+ " public InjectConstructor_Factory(Provider<Outer.Factory> factoryProvider) {",
+ " this.factoryProvider = factoryProvider;",
+ " }",
+ "",
+ " @Override public InjectConstructor get() {",
+ " return new InjectConstructor(factoryProvider.get());",
+ " }",
+ "",
+ " public static InjectConstructor_Factory create(",
+ " Provider<Outer.Factory> factoryProvider) {",
+ " return new InjectConstructor_Factory(factoryProvider);",
+ " }",
+ "",
+ " public static InjectConstructor newInstance(",
+ " Outer.Factory factory) {",
+ " return new InjectConstructor(factory);",
+ " }",
+ "}");
+ assertAbout(javaSources()).that(ImmutableList.of(factoryFile, file))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test
+ public void samePackageNameCollision() {
+ JavaFileObject samePackageInterface = JavaFileObjects.forSourceLines("test.CommonName",
+ "package test;",
+ "",
+ "public interface CommonName {}");
+ JavaFileObject differentPackageInterface = JavaFileObjects.forSourceLines(
+ "other.pkg.CommonName",
+ "package other.pkg;",
+ "",
+ "public interface CommonName {}");
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectConstructor implements CommonName {",
+ " @Inject InjectConstructor(other.pkg.CommonName otherPackage, CommonName samePackage) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.InjectConstructor_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class InjectConstructor_Factory ",
+ " implements Factory<InjectConstructor> {",
+ "",
+ " private final Provider<other.pkg.CommonName> otherPackageProvider;",
+ " private final Provider<CommonName> samePackageProvider;",
+ "",
+ " public InjectConstructor_Factory(",
+ " Provider<other.pkg.CommonName> otherPackageProvider,",
+ " Provider<CommonName> samePackageProvider) {",
+ " this.otherPackageProvider = otherPackageProvider;",
+ " this.samePackageProvider = samePackageProvider;",
+ " }",
+ "",
+ " @Override public InjectConstructor get() {",
+ " return new InjectConstructor(",
+ " otherPackageProvider.get(), samePackageProvider.get());",
+ " }",
+ "",
+ " public static InjectConstructor_Factory create(",
+ " Provider<other.pkg.CommonName> otherPackageProvider,",
+ " Provider<CommonName> samePackageProvider) {",
+ " return new InjectConstructor_Factory(otherPackageProvider, samePackageProvider);",
+ " }",
+ "",
+ " public static InjectConstructor newInstance(",
+ " other.pkg.CommonName otherPackage, CommonName samePackage) {",
+ " return new InjectConstructor(otherPackage, samePackage);",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(samePackageInterface, differentPackageInterface, file))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(expected);
+ }
+
+ @Test
+ public void noDeps() {
+ JavaFileObject simpleType = JavaFileObjects.forSourceLines("test.SimpleType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class SimpleType {",
+ " @Inject SimpleType() {}",
+ "}");
+ JavaFileObject factory =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleType_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class SimpleType_Factory implements Factory<SimpleType> {",
+ " private static final SimpleType_Factory INSTANCE = new SimpleType_Factory();",
+ "",
+ " @Override public SimpleType get() {",
+ " return new SimpleType();",
+ " }",
+ "",
+ " public static SimpleType_Factory create() {",
+ " return INSTANCE;",
+ " }",
+ "",
+ " public static SimpleType newInstance() {",
+ " return new SimpleType();",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(simpleType)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(factory);
+ }
+
+ @Test public void simpleComponentWithNesting() {
+ JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines("test.OuterType",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterType {",
+ " static class A {",
+ " @Inject A() {}",
+ " }",
+ " static class B {",
+ " @Inject A a;",
+ " }",
+ "}");
+ JavaFileObject aFactory =
+ JavaFileObjects.forSourceLines(
+ "test.OuterType_A_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class OuterType_A_Factory implements Factory<OuterType.A> {",
+ " private static final OuterType_A_Factory INSTANCE = new OuterType_A_Factory();",
+ "",
+ " @Override public OuterType.A get() {",
+ " return new OuterType.A();",
+ " }",
+ "",
+ " public static OuterType_A_Factory create() {",
+ " return INSTANCE;",
+ " }",
+ "",
+ " public static OuterType.A newInstance() {",
+ " return new OuterType.A();",
+ " }",
+ "}");
+ assertAbout(javaSources()).that(ImmutableList.of(nestedTypesFile))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(aFactory);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/JavaFileBuilder.java b/javatests/dagger/internal/codegen/JavaFileBuilder.java
new file mode 100644
index 0000000..f682b0c
--- /dev/null
+++ b/javatests/dagger/internal/codegen/JavaFileBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import javax.tools.JavaFileObject;
+
+/**
+ * A fluent API to build a {@link JavaFileObject} appropriate for a current set of settings, such as
+ * compiler mode.
+ *
+ * <p>After creating a builder, you can add lines to the file. Call {@link #addLines(String...)} to
+ * add lines irrespective of the settings. If you want to add different lines for different possible
+ * settings, call {@link #addLinesIf(Object, String...)} to add those lines only if the given
+ * setting has been added via {@link #withSetting(Object)} or {@link #withSettings(Object...)}.
+ */
+final class JavaFileBuilder {
+ private final String qualifiedName;
+ private final Set<Object> settings = new HashSet<>();
+
+ private final ImmutableList.Builder<String> sourceLines = ImmutableList.builder();
+
+ /** Creates a builder for a file whose top level type has a given qualified name. */
+ JavaFileBuilder(String qualifiedName) {
+ checkArgument(!qualifiedName.isEmpty());
+ this.qualifiedName = qualifiedName;
+ }
+
+ // TODO(cgdecker): Get rid of the special constructor/method for CompilerMode
+
+ /** Creates a builder for a file whose top level type has a given qualified name. */
+ JavaFileBuilder(CompilerMode compilerMode, String qualifiedName) {
+ this(qualifiedName);
+ settings.add(compilerMode);
+ }
+
+ /** Adds the given setting as one that code should be generated for. */
+ JavaFileBuilder withSetting(Object setting) {
+ this.settings.add(setting);
+ return this;
+ }
+
+ /** Adds the given settings as one that code should be generated for. */
+ JavaFileBuilder withSettings(Object s1, Object s2, Object... more) {
+ settings.add(s1);
+ settings.add(s2);
+ Collections.addAll(settings, more);
+ return this;
+ }
+
+ /** Adds lines no matter what the {@link CompilerMode} is. */
+ JavaFileBuilder addLines(String... lines) {
+ sourceLines.add(lines);
+ return this;
+ }
+
+ /** Adds lines if in the given mode. */
+ JavaFileBuilder addLinesIn(CompilerMode mode, String... lines) {
+ return addLinesIf(mode, lines);
+ }
+
+ /** Adds lines if in the given setting is set. */
+ JavaFileBuilder addLinesIf(Object setting, String... lines) {
+ if (settings.contains(setting)) {
+ sourceLines.add(lines);
+ }
+ return this;
+ }
+
+ /** Builds the {@link JavaFileObject}. */
+ JavaFileObject build() {
+ return JavaFileObjects.forSourceLines(qualifiedName, sourceLines.build());
+ }
+}
diff --git a/javatests/dagger/internal/codegen/KeyFactoryTest.java b/javatests/dagger/internal/codegen/KeyFactoryTest.java
new file mode 100644
index 0000000..d526ec9
--- /dev/null
+++ b/javatests/dagger/internal/codegen/KeyFactoryTest.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.testing.compile.CompilationRule;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.model.Key.MultibindingContributionIdentifier;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.lang.annotation.Retention;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link Key}.
+ */
+@RunWith(JUnit4.class)
+public class KeyFactoryTest {
+ @Rule public CompilationRule compilationRule = new CompilationRule();
+
+ private DaggerElements elements;
+ private DaggerTypes types;
+ private KeyFactory keyFactory;
+
+ @Before public void setUp() {
+ this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+ this.types = new DaggerTypes(compilationRule.getTypes(), elements);
+ TypeProtoConverter typeProtoConverter = new TypeProtoConverter(types, elements);
+ this.keyFactory = new KeyFactory(
+ types, elements, typeProtoConverter, new AnnotationProtoConverter(typeProtoConverter));
+ }
+
+ @Test public void forInjectConstructorWithResolvedType() {
+ TypeElement typeElement =
+ compilationRule.getElements().getTypeElement(InjectedClass.class.getCanonicalName());
+ ExecutableElement constructor =
+ Iterables.getOnlyElement(ElementFilter.constructorsIn(typeElement.getEnclosedElements()));
+ Key key =
+ keyFactory.forInjectConstructorWithResolvedType(constructor.getEnclosingElement().asType());
+ assertThat(key).isEqualTo(Key.builder(typeElement.asType()).build());
+ assertThat(key.toString()).isEqualTo("dagger.internal.codegen.KeyFactoryTest.InjectedClass");
+ }
+
+ static final class InjectedClass {
+ @SuppressWarnings("unused")
+ @Inject InjectedClass(String s, int i) {}
+ }
+
+ @Test public void forProvidesMethod() {
+ TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
+ TypeElement moduleElement =
+ elements.getTypeElement(ProvidesMethodModule.class.getCanonicalName());
+ ExecutableElement providesMethod =
+ Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
+ Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
+ assertThat(key).isEqualTo(Key.builder(stringType).build());
+ assertThat(key.toString()).isEqualTo("java.lang.String");
+ }
+
+ @Module
+ static final class ProvidesMethodModule {
+ @Provides String provideString() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Test public void forProvidesMethod_qualified() {
+ TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
+ TypeElement qualifierElement =
+ elements.getTypeElement(TestQualifier.class.getCanonicalName());
+ TypeElement moduleElement =
+ elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
+ ExecutableElement providesMethod =
+ Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
+ Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
+ assertThat(MoreTypes.equivalence().wrap(key.qualifier().get().getAnnotationType()))
+ .isEqualTo(MoreTypes.equivalence().wrap(qualifierElement.asType()));
+ assertThat(MoreTypes.equivalence().wrap(key.type()))
+ .isEqualTo(MoreTypes.equivalence().wrap(stringType));
+ assertThat(key.toString())
+ .isEqualTo(
+ "@dagger.internal.codegen.KeyFactoryTest.TestQualifier({"
+ + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
+ + "param1=1, value=\"value a\"), "
+ + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
+ + "param1=2, value=\"value b\"), "
+ + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
+ + "param1=3145, value=\"default\")"
+ + "}) java.lang.String");
+ }
+
+ @Test public void qualifiedKeyEquivalents() {
+ TypeElement moduleElement =
+ elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
+ ExecutableElement providesMethod =
+ Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
+ Key provisionKey = keyFactory.forProvidesMethod(providesMethod, moduleElement);
+
+ TypeMirror type = elements.getTypeElement(String.class.getCanonicalName()).asType();
+ TypeElement injectableElement =
+ elements.getTypeElement(QualifiedFieldHolder.class.getCanonicalName());
+ Element injectionField =
+ Iterables.getOnlyElement(ElementFilter.fieldsIn(injectableElement.getEnclosedElements()));
+ AnnotationMirror qualifier = Iterables.getOnlyElement(injectionField.getAnnotationMirrors());
+ Key injectionKey = Key.builder(type).qualifier(qualifier).build();
+
+ assertThat(provisionKey).isEqualTo(injectionKey);
+ assertThat(injectionKey.toString())
+ .isEqualTo(
+ "@dagger.internal.codegen.KeyFactoryTest.TestQualifier({"
+ + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
+ + "param1=1, value=\"value a\"), "
+ + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
+ + "param1=2, value=\"value b\"), "
+ + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
+ + "param1=3145, value=\"default\")"
+ + "}) java.lang.String");
+ }
+
+ @Module
+ static final class QualifiedProvidesMethodModule {
+ @Provides
+ @TestQualifier({
+ @InnerAnnotation(value = "value a", param1 = 1),
+ // please note the order of 'param' and 'value' is inverse
+ @InnerAnnotation(param1 = 2, value = "value b"),
+ @InnerAnnotation()
+ })
+ static String provideQualifiedString() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static final class QualifiedFieldHolder {
+ @TestQualifier({
+ @InnerAnnotation(value = "value a", param1 = 1),
+ // please note the order of 'param' and 'value' is inverse
+ @InnerAnnotation(param1 = 2, value = "value b"),
+ @InnerAnnotation()
+ })
+ String aString;
+ }
+
+ @Retention(RUNTIME)
+ @Qualifier
+ @interface TestQualifier {
+ InnerAnnotation[] value();
+ }
+
+ @interface InnerAnnotation {
+ int param1() default 3145;
+
+ String value() default "default";
+ }
+
+ @Test public void forProvidesMethod_sets() {
+ TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName());
+ TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
+ TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType);
+ TypeElement moduleElement =
+ elements.getTypeElement(SetProvidesMethodsModule.class.getCanonicalName());
+ for (ExecutableElement providesMethod
+ : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
+ Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
+ assertThat(key)
+ .isEqualTo(
+ Key.builder(setOfStringsType)
+ .multibindingContributionIdentifier(
+ new MultibindingContributionIdentifier(providesMethod, moduleElement))
+ .build());
+ assertThat(key.toString())
+ .isEqualTo(
+ String.format(
+ "java.util.Set<java.lang.String> "
+ + "dagger.internal.codegen.KeyFactoryTest.SetProvidesMethodsModule#%s",
+ providesMethod.getSimpleName()));
+ }
+ }
+
+ @Module
+ static final class SetProvidesMethodsModule {
+ @Provides @IntoSet String provideString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Provides @ElementsIntoSet Set<String> provideStrings() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Module
+ static final class PrimitiveTypes {
+ @Provides int foo() {
+ return 0;
+ }
+ }
+
+ @Module
+ static final class BoxedPrimitiveTypes {
+ @Provides Integer foo() {
+ return 0;
+ }
+ }
+
+ @Test public void primitiveKeysMatchBoxedKeys() {
+ TypeElement primitiveHolder = elements.getTypeElement(PrimitiveTypes.class.getCanonicalName());
+ ExecutableElement intMethod =
+ Iterables.getOnlyElement(ElementFilter.methodsIn(primitiveHolder.getEnclosedElements()));
+ TypeElement boxedPrimitiveHolder =
+ elements.getTypeElement(BoxedPrimitiveTypes.class.getCanonicalName());
+ ExecutableElement integerMethod = Iterables.getOnlyElement(
+ ElementFilter.methodsIn(boxedPrimitiveHolder.getEnclosedElements()));
+
+ // TODO(cgruber): Truth subject for TypeMirror and TypeElement
+ TypeMirror intType = intMethod.getReturnType();
+ assertThat(intType.getKind().isPrimitive()).isTrue();
+ TypeMirror integerType = integerMethod.getReturnType();
+ assertThat(integerType.getKind().isPrimitive()).isFalse();
+ assertWithMessage("type equality").that(types.isSameType(intType, integerType)).isFalse();
+ Key intKey = keyFactory.forProvidesMethod(intMethod, primitiveHolder);
+ Key integerKey = keyFactory.forProvidesMethod(integerMethod, boxedPrimitiveHolder);
+ assertThat(intKey).isEqualTo(integerKey);
+ assertThat(intKey.toString()).isEqualTo("java.lang.Integer");
+ assertThat(integerKey.toString()).isEqualTo("java.lang.Integer");
+ }
+
+ @Test public void forProducesMethod() {
+ TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
+ TypeElement moduleElement =
+ elements.getTypeElement(ProducesMethodsModule.class.getCanonicalName());
+ for (ExecutableElement producesMethod
+ : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
+ Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
+ assertThat(key).isEqualTo(Key.builder(stringType).build());
+ assertThat(key.toString()).isEqualTo("java.lang.String");
+ }
+ }
+
+ @ProducerModule
+ static final class ProducesMethodsModule {
+ @Produces String produceString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Produces ListenableFuture<String> produceFutureString() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Test public void forProducesMethod_sets() {
+ TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName());
+ TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
+ TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType);
+ TypeElement moduleElement =
+ elements.getTypeElement(SetProducesMethodsModule.class.getCanonicalName());
+ for (ExecutableElement producesMethod
+ : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
+ Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
+ assertThat(key)
+ .isEqualTo(
+ Key.builder(setOfStringsType)
+ .multibindingContributionIdentifier(
+ new MultibindingContributionIdentifier(producesMethod, moduleElement))
+ .build());
+ assertThat(key.toString())
+ .isEqualTo(
+ String.format(
+ "java.util.Set<java.lang.String> "
+ + "dagger.internal.codegen.KeyFactoryTest.SetProducesMethodsModule#%s",
+ producesMethod.getSimpleName()));
+ }
+ }
+
+ @ProducerModule
+ static final class SetProducesMethodsModule {
+ @Produces @IntoSet String produceString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Produces @IntoSet ListenableFuture<String> produceFutureString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Produces @ElementsIntoSet Set<String> produceStrings() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Produces @ElementsIntoSet
+ ListenableFuture<Set<String>> produceFutureStrings() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
new file mode 100644
index 0000000..ad48712
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
@@ -0,0 +1,1101 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MapBindingComponentProcessorTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public MapBindingComponentProcessorTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void mapBindingsWithEnumKey() {
+ JavaFileObject mapModuleOneFile =
+ JavaFileObjects
+ .forSourceLines("test.MapModuleOne",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModuleOne {",
+ " @Provides @IntoMap @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {",
+ " return new AdminHandler();",
+ " }",
+ "}");
+ JavaFileObject mapModuleTwoFile =
+ JavaFileObjects
+ .forSourceLines("test.MapModuleTwo",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModuleTwo {",
+ " @Provides @IntoMap @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {",
+ " return new LoginHandler();",
+ " }",
+ "}");
+ JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
+ "package test;",
+ "import dagger.MapKey;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "",
+ "@MapKey(unwrapValue = true)",
+ "@Retention(RUNTIME)",
+ "public @interface PathKey {",
+ " PathEnum value();",
+ "}");
+ JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
+ "package test;",
+ "",
+ "public enum PathEnum {",
+ " ADMIN,",
+ " LOGIN;",
+ "}");
+
+ JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
+ "package test;",
+ "",
+ "interface Handler {}");
+ JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
+ "package test;",
+ "",
+ "class LoginHandler implements Handler {",
+ " public LoginHandler() {}",
+ "}");
+ JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
+ "package test;",
+ "",
+ "class AdminHandler implements Handler {",
+ " public AdminHandler() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
+ "interface TestComponent {",
+ " Provider<Map<PathEnum, Provider<Handler>>> dispatcher();",
+ "}");
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final MapModuleOne mapModuleOne;",
+ " private final MapModuleTwo mapModuleTwo;",
+ " private volatile Provider<Handler> provideAdminHandlerProvider;",
+ " private volatile Provider<Handler> provideLoginHandlerProvider;",
+ " private volatile Provider<Map<PathEnum, Provider<Handler>>>",
+ " mapOfPathEnumAndProviderOfHandlerProvider;",
+ "",
+ " private Provider<Handler> getProvideAdminHandlerProvider() {",
+ " Object local = provideAdminHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(1);",
+ " provideAdminHandlerProvider = (Provider<Handler>) local;",
+ " }",
+ " return (Provider<Handler>) local;",
+ " }",
+ "",
+ " private Provider<Handler> getProvideLoginHandlerProvider() {",
+ " Object local = provideLoginHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(2);",
+ " provideLoginHandlerProvider = (Provider<Handler>) local;",
+ " }",
+ " return (Provider<Handler>) local;",
+ " }",
+ "",
+ " private Map<PathEnum, Provider<Handler>>",
+ " getMapOfPathEnumAndProviderOfHandler() {",
+ " return ImmutableMap.<PathEnum, Provider<Handler>>of(",
+ " PathEnum.ADMIN, getProvideAdminHandlerProvider(),",
+ " PathEnum.LOGIN, getProvideLoginHandlerProvider());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {",
+ " Object local = mapOfPathEnumAndProviderOfHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " mapOfPathEnumAndProviderOfHandlerProvider =",
+ " (Provider<Map<PathEnum, Provider<Handler>>>) local;",
+ " }",
+ " return (Provider<Map<PathEnum, Provider<Handler>>>) local;",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) DaggerTestComponent.this",
+ " .getMapOfPathEnumAndProviderOfHandler();",
+ " case 1:",
+ " return (T) MapModuleOne_ProvideAdminHandlerFactory",
+ " .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
+ " case 2:",
+ " return (T) MapModuleTwo_ProvideLoginHandlerFactory",
+ " .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Provider<Handler> provideAdminHandlerProvider;",
+ " private Provider<Handler> provideLoginHandlerProvider;",
+ " private Provider<Map<PathEnum, Provider<Handler>>>",
+ " mapOfPathEnumAndProviderOfHandlerProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(",
+ " final MapModuleOne mapModuleOneParam,",
+ " final MapModuleTwo mapModuleTwoParam) {",
+ " this.provideAdminHandlerProvider =",
+ " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+ " this.provideLoginHandlerProvider =",
+ " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+ " this.mapOfPathEnumAndProviderOfHandlerProvider =",
+ " MapProviderFactory.<PathEnum, Handler>builder(2)",
+ " .put(PathEnum.ADMIN, provideAdminHandlerProvider)",
+ " .put(PathEnum.LOGIN, provideLoginHandlerProvider)",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {",
+ " return mapOfPathEnumAndProviderOfHandlerProvider;",
+ " }",
+ "}");
+ }
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ mapModuleOneFile,
+ mapModuleTwoFile,
+ enumKeyFile,
+ pathEnumFile,
+ HandlerFile,
+ LoginHandlerFile,
+ AdminHandlerFile,
+ componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void mapBindingsWithInaccessibleKeys() {
+ JavaFileObject mapKeys =
+ JavaFileObjects.forSourceLines(
+ "mapkeys.MapKeys",
+ "package mapkeys;",
+ "",
+ "import dagger.MapKey;",
+ "import dagger.multibindings.ClassKey;",
+ "",
+ "public class MapKeys {",
+ " @MapKey(unwrapValue = false)",
+ " public @interface ComplexKey {",
+ " Class<?>[] manyClasses();",
+ " Class<?> oneClass();",
+ " ClassKey annotation();",
+ " }",
+ "",
+ " @MapKey",
+ " @interface EnumKey {",
+ " PackagePrivateEnum value();",
+ " }",
+ "",
+ " enum PackagePrivateEnum { INACCESSIBLE }",
+ "",
+ " interface Inaccessible {}",
+ "}");
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "mapkeys.MapModule",
+ "package mapkeys;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ClassKey;",
+ "import dagger.multibindings.IntoMap;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "public interface MapModule {",
+ " @Provides @IntoMap @ClassKey(MapKeys.Inaccessible.class)",
+ " static int classKey() { return 1; }",
+ "",
+ " @Provides @IntoMap @MapKeys.EnumKey(MapKeys.PackagePrivateEnum.INACCESSIBLE)",
+ " static int enumKey() { return 1; }",
+ "",
+ " @Binds Object bindInaccessibleEnumMapToAccessibleTypeForComponent(",
+ " Map<MapKeys.PackagePrivateEnum, Integer> map);",
+ "",
+ " @Provides @IntoMap",
+ " @MapKeys.ComplexKey(",
+ " manyClasses = {java.lang.Object.class, java.lang.String.class},",
+ " oneClass = MapKeys.Inaccessible.class,",
+ " annotation = @ClassKey(java.lang.Object.class)",
+ " )",
+ " static int complexKeyWithInaccessibleValue() { return 1; }",
+ "",
+ " @Provides @IntoMap",
+ " @MapKeys.ComplexKey(",
+ " manyClasses = {MapKeys.Inaccessible.class, java.lang.String.class},",
+ " oneClass = java.lang.String.class,",
+ " annotation = @ClassKey(java.lang.Object.class)",
+ " )",
+ " static int complexKeyWithInaccessibleArrayValue() { return 1; }",
+ "",
+ " @Provides @IntoMap",
+ " @MapKeys.ComplexKey(",
+ " manyClasses = {java.lang.String.class},",
+ " oneClass = java.lang.String.class,",
+ " annotation = @ClassKey(MapKeys.Inaccessible.class)",
+ " )",
+ " static int complexKeyWithInaccessibleAnnotationValue() { return 1; }",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "import mapkeys.MapKeys;",
+ "import mapkeys.MapModule;",
+ "",
+ "@Component(modules = MapModule.class)",
+ "interface TestComponent {",
+ " Map<Class<?>, Integer> classKey();",
+ " Provider<Map<Class<?>, Integer>> classKeyProvider();",
+ "",
+ " Object inaccessibleEnum();",
+ " Provider<Object> inaccessibleEnumProvider();",
+ "",
+ " Map<MapKeys.ComplexKey, Integer> complexKey();",
+ " Provider<Map<MapKeys.ComplexKey, Integer>> complexKeyProvider();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(mapKeys, moduleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Provider<Map<Class<?>, Integer>> mapOfClassOfAndIntegerProvider;",
+ "",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider mapOfPackagePrivateEnumAndIntegerProvider;",
+ "",
+ " private Provider<Map<MapKeys.ComplexKey, Integer>>",
+ " mapOfComplexKeyAndIntegerProvider;",
+ "",
+ " private Map getMapOfPackagePrivateEnumAndInteger() {",
+ " return ImmutableMap.of(",
+ " MapModule_EnumKeyMapKey.create(), MapModule.enumKey());",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.mapOfClassOfAndIntegerProvider =",
+ " MapFactory.<Class<?>, Integer>builder(1)",
+ " .put(MapModule_ClassKeyMapKey.create(),",
+ " MapModule_ClassKeyFactory.create())",
+ " .build();",
+ " this.mapOfPackagePrivateEnumAndIntegerProvider =",
+ " MapFactory.builder(1)",
+ " .put(MapModule_EnumKeyMapKey.create(), ",
+ " (Provider) MapModule_EnumKeyFactory.create())",
+ " .build();",
+ " this.mapOfComplexKeyAndIntegerProvider =",
+ " MapFactory.<MapKeys.ComplexKey, Integer>builder(3)",
+ " .put(",
+ " MapModule_ComplexKeyWithInaccessibleValueMapKey.create(),",
+ " MapModule_ComplexKeyWithInaccessibleValueFactory.create())",
+ " .put(",
+ " MapModule_ComplexKeyWithInaccessibleArrayValueMapKey.create(),",
+ " MapModule_ComplexKeyWithInaccessibleArrayValueFactory.create())",
+ " .put(",
+ " MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey.create(),",
+ " MapModule_ComplexKeyWithInaccessibleAnnotationValueFactory.create())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Map<Class<?>, Integer> classKey() {",
+ " return ImmutableMap.<Class<?>, Integer>of(",
+ " MapModule_ClassKeyMapKey.create(), MapModule.classKey());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<Class<?>, Integer>> classKeyProvider() {",
+ " return mapOfClassOfAndIntegerProvider;",
+ " }",
+ "",
+ " @Override",
+ " public Object inaccessibleEnum() {",
+ " return getMapOfPackagePrivateEnumAndInteger();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Object> inaccessibleEnumProvider() {",
+ " return mapOfPackagePrivateEnumAndIntegerProvider;",
+ " }",
+ "",
+ " @Override",
+ " public Map<MapKeys.ComplexKey, Integer> complexKey() {",
+ " return ImmutableMap.<MapKeys.ComplexKey, Integer>of(",
+ " MapModule_ComplexKeyWithInaccessibleValueMapKey.create(),",
+ " MapModule.complexKeyWithInaccessibleValue(),",
+ " MapModule_ComplexKeyWithInaccessibleArrayValueMapKey.create(),",
+ " MapModule.complexKeyWithInaccessibleArrayValue(),",
+ " MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey.create(),",
+ " MapModule.complexKeyWithInaccessibleAnnotationValue());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<MapKeys.ComplexKey, Integer>> complexKeyProvider() {",
+ " return mapOfComplexKeyAndIntegerProvider;",
+ " }",
+ "}"));
+ assertThat(compilation)
+ .generatedSourceFile(
+ "mapkeys.MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "mapkeys.MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey",
+ "package mapkeys;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey {",
+ " public static MapKeys.ComplexKey create() {",
+ " return MapKeys_ComplexKeyCreator.createComplexKey(",
+ " new Class[] {String.class},",
+ " String.class,",
+ " MapKeys_ComplexKeyCreator.createClassKey(MapKeys.Inaccessible.class));",
+ " }",
+ "}"));
+ assertThat(compilation)
+ .generatedSourceFile("mapkeys.MapModule_ClassKeyMapKey")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "mapkeys.MapModule_ClassKeyMapKey",
+ "package mapkeys;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class MapModule_ClassKeyMapKey {",
+ " public static Class<?> create() {",
+ " return MapKeys.Inaccessible.class;",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void mapBindingsWithStringKey() {
+ JavaFileObject mapModuleOneFile =
+ JavaFileObjects
+ .forSourceLines("test.MapModuleOne",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.StringKey;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModuleOne {",
+ " @Provides @IntoMap @StringKey(\"Admin\") Handler provideAdminHandler() {",
+ " return new AdminHandler();",
+ " }",
+ "}");
+ JavaFileObject mapModuleTwoFile =
+ JavaFileObjects
+ .forSourceLines("test.MapModuleTwo",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "",
+ "@Module",
+ "final class MapModuleTwo {",
+ " @Provides @IntoMap @StringKey(\"Login\") Handler provideLoginHandler() {",
+ " return new LoginHandler();",
+ " }",
+ "}");
+ JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
+ "package test;",
+ "",
+ "interface Handler {}");
+ JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
+ "package test;",
+ "",
+ "class LoginHandler implements Handler {",
+ " public LoginHandler() {}",
+ "}");
+ JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
+ "package test;",
+ "",
+ "class AdminHandler implements Handler {",
+ " public AdminHandler() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
+ "interface TestComponent {",
+ " Provider<Map<String, Provider<Handler>>> dispatcher();",
+ "}");
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final MapModuleOne mapModuleOne;",
+ " private final MapModuleTwo mapModuleTwo;",
+ " private volatile Provider<Handler> provideAdminHandlerProvider;",
+ " private volatile Provider<Handler> provideLoginHandlerProvider;",
+ " private volatile Provider<Map<String, Provider<Handler>>>",
+ " mapOfStringAndProviderOfHandlerProvider;",
+ "",
+ " private Provider<Handler> getProvideAdminHandlerProvider() {",
+ " Object local = provideAdminHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(1);",
+ " provideAdminHandlerProvider = (Provider<Handler>) local;",
+ " }",
+ " return (Provider<Handler>) local;",
+ " }",
+ "",
+ " private Provider<Handler> getProvideLoginHandlerProvider() {",
+ " Object local = provideLoginHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(2);",
+ " provideLoginHandlerProvider = (Provider<Handler>) local;",
+ " }",
+ " return (Provider<Handler>) local;",
+ " }",
+ "",
+ " private Map<String, Provider<Handler>>",
+ " getMapOfStringAndProviderOfHandler() {",
+ " return ImmutableMap.<String, Provider<Handler>>of(",
+ " \"Admin\", getProvideAdminHandlerProvider(),",
+ " \"Login\", getProvideLoginHandlerProvider());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<String, Provider<Handler>>> dispatcher() {",
+ " Object local = mapOfStringAndProviderOfHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " mapOfStringAndProviderOfHandlerProvider =",
+ " (Provider<Map<String, Provider<Handler>>>) local;",
+ " }",
+ " return (Provider<Map<String, Provider<Handler>>>) local;",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) DaggerTestComponent.this",
+ " .getMapOfStringAndProviderOfHandler();",
+ " case 1:",
+ " return (T) MapModuleOne_ProvideAdminHandlerFactory",
+ " .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
+ " case 2:",
+ " return (T) MapModuleTwo_ProvideLoginHandlerFactory",
+ " .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Provider<Handler> provideAdminHandlerProvider;",
+ " private Provider<Handler> provideLoginHandlerProvider;",
+ " private Provider<Map<String, Provider<Handler>>>",
+ " mapOfStringAndProviderOfHandlerProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(",
+ " final MapModuleOne mapModuleOneParam,",
+ " final MapModuleTwo mapModuleTwoParam) {",
+ " this.provideAdminHandlerProvider =",
+ " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+ " this.provideLoginHandlerProvider =",
+ " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+ " this.mapOfStringAndProviderOfHandlerProvider =",
+ " MapProviderFactory.<String, Handler>builder(2)",
+ " .put(\"Admin\", provideAdminHandlerProvider)",
+ " .put(\"Login\", provideLoginHandlerProvider)",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<String, Provider<Handler>>> dispatcher() {",
+ " return mapOfStringAndProviderOfHandlerProvider;",
+ " }",
+ "}");
+ }
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ mapModuleOneFile,
+ mapModuleTwoFile,
+ HandlerFile,
+ LoginHandlerFile,
+ AdminHandlerFile,
+ componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void mapBindingsWithWrappedKey() {
+ JavaFileObject mapModuleOneFile =
+ JavaFileObjects
+ .forSourceLines("test.MapModuleOne",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModuleOne {",
+ " @Provides @IntoMap",
+ " @WrappedClassKey(Integer.class) Handler provideAdminHandler() {",
+ " return new AdminHandler();",
+ " }",
+ "}");
+ JavaFileObject mapModuleTwoFile =
+ JavaFileObjects
+ .forSourceLines("test.MapModuleTwo",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModuleTwo {",
+ " @Provides @IntoMap",
+ " @WrappedClassKey(Long.class) Handler provideLoginHandler() {",
+ " return new LoginHandler();",
+ " }",
+ "}");
+ JavaFileObject wrappedClassKeyFile = JavaFileObjects.forSourceLines("test.WrappedClassKey",
+ "package test;",
+ "import dagger.MapKey;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "",
+ "@MapKey(unwrapValue = false)",
+ "@Retention(RUNTIME)",
+ "public @interface WrappedClassKey {",
+ " Class<?> value();",
+ "}");
+ JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
+ "package test;",
+ "",
+ "interface Handler {}");
+ JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
+ "package test;",
+ "",
+ "class LoginHandler implements Handler {",
+ " public LoginHandler() {}",
+ "}");
+ JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
+ "package test;",
+ "",
+ "class AdminHandler implements Handler {",
+ " public AdminHandler() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
+ "interface TestComponent {",
+ " Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher();",
+ "}");
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final MapModuleOne mapModuleOne;",
+ " private final MapModuleTwo mapModuleTwo;",
+ " private volatile Provider<Handler> provideAdminHandlerProvider;",
+ " private volatile Provider<Handler> provideLoginHandlerProvider;",
+ " private volatile Provider<Map<WrappedClassKey, Provider<Handler>>>",
+ " mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
+ "",
+ " private DaggerTestComponent(",
+ " MapModuleOne mapModuleOneParam,",
+ " MapModuleTwo mapModuleTwoParam) {",
+ " this.mapModuleOne = mapModuleOneParam;",
+ " this.mapModuleTwo = mapModuleTwoParam;",
+ " }",
+ "",
+ " private Provider<Handler> getProvideAdminHandlerProvider() {",
+ " Object local = provideAdminHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(1);",
+ " provideAdminHandlerProvider = (Provider<Handler>) local;",
+ " }",
+ " return (Provider<Handler>) local;",
+ " }",
+ "",
+ " private Provider<Handler> getProvideLoginHandlerProvider() {",
+ " Object local = provideLoginHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(2);",
+ " provideLoginHandlerProvider = (Provider<Handler>) local;",
+ " }",
+ " return (Provider<Handler>) local;",
+ " }",
+ "",
+ " private Map<WrappedClassKey, Provider<Handler>>",
+ " getMapOfWrappedClassKeyAndProviderOfHandler() {",
+ " return ImmutableMap.<WrappedClassKey, Provider<Handler>>of(",
+ " WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
+ " getProvideAdminHandlerProvider(),",
+ " WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
+ " getProvideLoginHandlerProvider());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {",
+ " Object local = mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " mapOfWrappedClassKeyAndProviderOfHandlerProvider =",
+ " (Provider<Map<WrappedClassKey, Provider<Handler>>>) local;",
+ " }",
+ " return (Provider<Map<WrappedClassKey, Provider<Handler>>>) local;",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) DaggerTestComponent.this",
+ " .getMapOfWrappedClassKeyAndProviderOfHandler();",
+ " case 1:",
+ " return (T) MapModuleOne_ProvideAdminHandlerFactory",
+ " .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
+ " case 2:",
+ " return (T) MapModuleTwo_ProvideLoginHandlerFactory",
+ " .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Provider<Handler> provideAdminHandlerProvider;",
+ " private Provider<Handler> provideLoginHandlerProvider;",
+ " private Provider<Map<WrappedClassKey, Provider<Handler>>>",
+ " mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(",
+ " final MapModuleOne mapModuleOneParam,",
+ " final MapModuleTwo mapModuleTwoParam) {",
+ " this.provideAdminHandlerProvider =",
+ " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+ " this.provideLoginHandlerProvider =",
+ " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+ " this.mapOfWrappedClassKeyAndProviderOfHandlerProvider =",
+ " MapProviderFactory.<WrappedClassKey, Handler>builder(2)",
+ " .put(WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
+ " provideAdminHandlerProvider)",
+ " .put(WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
+ " provideLoginHandlerProvider)",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {",
+ " return mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
+ " }",
+ "}");
+ }
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ mapModuleOneFile,
+ mapModuleTwoFile,
+ wrappedClassKeyFile,
+ HandlerFile,
+ LoginHandlerFile,
+ AdminHandlerFile,
+ componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void mapBindingsWithNonProviderValue() {
+ JavaFileObject mapModuleOneFile = JavaFileObjects.forSourceLines("test.MapModuleOne",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModuleOne {",
+ " @Provides @IntoMap @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {",
+ " return new AdminHandler();",
+ " }",
+ "}");
+ JavaFileObject mapModuleTwoFile = JavaFileObjects.forSourceLines("test.MapModuleTwo",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModuleTwo {",
+ " @Provides @IntoMap @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {",
+ " return new LoginHandler();",
+ " }",
+ "}");
+ JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
+ "package test;",
+ "import dagger.MapKey;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "",
+ "@MapKey(unwrapValue = true)",
+ "@Retention(RUNTIME)",
+ "public @interface PathKey {",
+ " PathEnum value();",
+ "}");
+ JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
+ "package test;",
+ "",
+ "public enum PathEnum {",
+ " ADMIN,",
+ " LOGIN;",
+ "}");
+ JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
+ "package test;",
+ "",
+ "interface Handler {}");
+ JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
+ "package test;",
+ "",
+ "class LoginHandler implements Handler {",
+ " public LoginHandler() {}",
+ "}");
+ JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
+ "package test;",
+ "",
+ "class AdminHandler implements Handler {",
+ " public AdminHandler() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
+ "interface TestComponent {",
+ " Provider<Map<PathEnum, Handler>> dispatcher();",
+ "}");
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final MapModuleOne mapModuleOne;",
+ " private final MapModuleTwo mapModuleTwo;",
+ " private volatile Provider<Map<PathEnum, Handler>>",
+ " mapOfPathEnumAndHandlerProvider;",
+ "",
+ " private Map<PathEnum, Handler> getMapOfPathEnumAndHandler() {",
+ " return ImmutableMap.<PathEnum, Handler>of(",
+ " PathEnum.ADMIN,",
+ " MapModuleOne_ProvideAdminHandlerFactory.provideAdminHandler(",
+ " mapModuleOne),",
+ " PathEnum.LOGIN,",
+ " MapModuleTwo_ProvideLoginHandlerFactory.provideLoginHandler(",
+ " mapModuleTwo));",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<PathEnum, Handler>> dispatcher() {",
+ " Object local = mapOfPathEnumAndHandlerProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " mapOfPathEnumAndHandlerProvider = (Provider<Map<PathEnum, Handler>>) local;",
+ " }",
+ " return (Provider<Map<PathEnum, Handler>>) local;",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) DaggerTestComponent.this.getMapOfPathEnumAndHandler();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Provider<Handler> provideAdminHandlerProvider;",
+ " private Provider<Handler> provideLoginHandlerProvider;",
+ " private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(",
+ " final MapModuleOne mapModuleOneParam,",
+ " final MapModuleTwo mapModuleTwoParam) {",
+ " this.provideAdminHandlerProvider =",
+ " MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+ " this.provideLoginHandlerProvider =",
+ " MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+ " this.mapOfPathEnumAndHandlerProvider =",
+ " MapFactory.<PathEnum, Handler>builder(2)",
+ " .put(PathEnum.ADMIN, provideAdminHandlerProvider)",
+ " .put(PathEnum.LOGIN, provideLoginHandlerProvider)",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<PathEnum, Handler>> dispatcher() {",
+ " return mapOfPathEnumAndHandlerProvider;",
+ " }",
+ "}");
+ }
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ mapModuleOneFile,
+ mapModuleTwoFile,
+ enumKeyFile,
+ pathEnumFile,
+ HandlerFile,
+ LoginHandlerFile,
+ AdminHandlerFile,
+ componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void injectMapWithoutMapBinding() {
+ JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("test.MapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.HashMap;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "final class MapModule {",
+ " @Provides Map<String, String> provideAMap() {",
+ " Map<String, String> map = new HashMap<String, String>();",
+ " map.put(\"Hello\", \"World\");",
+ " return map;",
+ " }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "",
+ "@Component(modules = {MapModule.class})",
+ "interface TestComponent {",
+ " Map<String, String> dispatcher();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final MapModule mapModule;",
+ "",
+ " @Override",
+ " public Map<String, String> dispatcher() {",
+ " return MapModule_ProvideAMapFactory.provideAMap(mapModule);",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(mapModuleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
new file mode 100644
index 0000000..11f6bc1
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MapBindingExpressionTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public MapBindingExpressionTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void mapBindings() {
+ JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("test.MapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntKey;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.LongKey;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "interface MapModule {",
+ " @Multibinds Map<String, String> stringMap();",
+ " @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }",
+ " @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }",
+ " @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }",
+ " @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = MapModule.class)",
+ "interface TestComponent {",
+ " Map<String, String> strings();",
+ " Map<String, Provider<String>> providerStrings();",
+ "",
+ " Map<Integer, Integer> ints();",
+ " Map<Integer, Provider<Integer>> providerInts();",
+ " Map<Long, Long> longs();",
+ " Map<Long, Provider<Long>> providerLongs();",
+ "}");
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.internal.MapBuilder;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider<Integer> provideIntProvider;",
+ " private volatile Provider<Long> provideLong0Provider;",
+ " private volatile Provider<Long> provideLong1Provider;",
+ " private volatile Provider<Long> provideLong2Provider;",
+ "",
+ " private Provider<Integer> getProvideIntProvider() {",
+ " Object local = provideIntProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " provideIntProvider = (Provider<Integer>) local;",
+ " }",
+ " return (Provider<Integer>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong0Provider() {",
+ " Object local = provideLong0Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(1);",
+ " provideLong0Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong1Provider() {",
+ " Object local = provideLong1Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(2);",
+ " provideLong1Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong2Provider() {",
+ " Object local = provideLong2Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(3);",
+ " provideLong2Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }")
+ .addLines(
+ " @Override",
+ " public Map<String, String> strings() {",
+ " return Collections.<String, String>emptyMap();",
+ " }",
+ "",
+ " @Override",
+ " public Map<String, Provider<String>> providerStrings() {",
+ " return Collections.<String, Provider<String>>emptyMap();",
+ " }",
+ "",
+ " @Override",
+ " public Map<Integer, Integer> ints() {",
+ " return Collections.<Integer, Integer>singletonMap(0, MapModule.provideInt());",
+ " }",
+ "",
+ " @Override",
+ " public Map<Integer, Provider<Integer>> providerInts() {",
+ " return Collections.<Integer, Provider<Integer>>singletonMap(")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " 0, MapModule_ProvideIntFactory.create());")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " 0, getProvideIntProvider());")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Map<Long, Long> longs() {",
+ " return MapBuilder.<Long, Long>newMapBuilder(3)",
+ " .put(0L, MapModule.provideLong0())",
+ " .put(1L, MapModule.provideLong1())",
+ " .put(2L, MapModule.provideLong2())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Map<Long, Provider<Long>> providerLongs() {",
+ " return MapBuilder.<Long, Provider<Long>>newMapBuilder(3)")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " .put(0L, MapModule_ProvideLong0Factory.create())",
+ " .put(1L, MapModule_ProvideLong1Factory.create())",
+ " .put(2L, MapModule_ProvideLong2Factory.create())")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " .put(0L, getProvideLong0Provider())",
+ " .put(1L, getProvideLong1Provider())",
+ " .put(2L, getProvideLong2Provider())")
+ .addLines( //
+ " .build();", " }")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) (Integer) MapModule.provideInt();",
+ " case 1: return (T) (Long) MapModule.provideLong0();",
+ " case 2: return (T) (Long) MapModule.provideLong1();",
+ " case 3: return (T) (Long) MapModule.provideLong2();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}")
+ .build();
+ Compilation compilation = daggerCompilerWithoutGuava().compile(mapModuleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void inaccessible() {
+ JavaFileObject inaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible",
+ "package other;",
+ "",
+ "class Inaccessible {}");
+ JavaFileObject usesInaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.UsesInaccessible",
+ "package other;",
+ "",
+ "import java.util.Map;",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesInaccessible {",
+ " @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}",
+ "}");
+
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.TestModule",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "public abstract class TestModule {",
+ " @Multibinds abstract Map<Integer, Inaccessible> ints();",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "import other.TestModule;",
+ "import other.UsesInaccessible;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " UsesInaccessible usesInaccessible();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import other.UsesInaccessible;",
+ "import other.UsesInaccessible_Factory;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public UsesInaccessible usesInaccessible() {",
+ " return UsesInaccessible_Factory.newInstance(",
+ " (Map) Collections.emptyMap());",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompilerWithoutGuava().compile(module, inaccessible, usesInaccessible, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void subcomponentOmitsInheritedBindings() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
+ " return \"parent value\";",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " Map<String, Object> objectMap();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ " private final ParentModule parentModule;",
+ "",
+ " private final class ChildImpl implements Child {",
+ " @Override",
+ " public Map<String, Object> objectMap() {",
+ " return Collections.<String, Object>singletonMap(",
+ " \"parent key\",",
+ " ParentModule_ParentKeyObjectFactory.parentKeyObject(",
+ " DaggerParent.this.parentModule));",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompilerWithoutGuava().compile(parent, parentModule, child);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ private Compiler daggerCompilerWithoutGuava() {
+ return daggerCompiler()
+ .withOptions(compilerMode.javacopts().append(CLASS_PATH_WITHOUT_GUAVA_OPTION));
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
new file mode 100644
index 0000000..aa1a715
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MapBindingExpressionWithGuavaTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public MapBindingExpressionWithGuavaTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void mapBindings() {
+ JavaFileObject mapModuleFile =
+ JavaFileObjects.forSourceLines(
+ "test.MapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntKey;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.LongKey;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "interface MapModule {",
+ " @Multibinds Map<String, String> stringMap();",
+ " @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }",
+ " @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }",
+ " @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }",
+ " @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }",
+ "}");
+ JavaFileObject subcomponentModuleFile =
+ JavaFileObjects.forSourceLines(
+ "test.SubcomponentMapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntKey;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.LongKey;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "interface SubcomponentMapModule {",
+ " @Provides @IntoMap @LongKey(3) static long provideLong3() { return 3; }",
+ " @Provides @IntoMap @LongKey(4) static long provideLong4() { return 4; }",
+ " @Provides @IntoMap @LongKey(5) static long provideLong5() { return 5; }",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = MapModule.class)",
+ "interface TestComponent {",
+ " Map<String, String> strings();",
+ " Map<String, Provider<String>> providerStrings();",
+ "",
+ " Map<Integer, Integer> ints();",
+ " Map<Integer, Provider<Integer>> providerInts();",
+ " Map<Long, Long> longs();",
+ " Map<Long, Provider<Long>> providerLongs();",
+ "",
+ " Sub sub();",
+ "}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = SubcomponentMapModule.class)",
+ "interface Sub {",
+ " Map<Long, Long> longs();",
+ " Map<Long, Provider<Long>> providerLongs();",
+ "}");
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider<Integer> provideIntProvider;",
+ " private volatile Provider<Long> provideLong0Provider;",
+ " private volatile Provider<Long> provideLong1Provider;",
+ " private volatile Provider<Long> provideLong2Provider;",
+ "",
+ " private Provider<Integer> getProvideIntProvider() {",
+ " Object local = provideIntProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " provideIntProvider = (Provider<Integer>) local;",
+ " }",
+ " return (Provider<Integer>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong0Provider() {",
+ " Object local = provideLong0Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(1);",
+ " provideLong0Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong1Provider() {",
+ " Object local = provideLong1Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(2);",
+ " provideLong1Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong2Provider() {",
+ " Object local = provideLong2Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(3);",
+ " provideLong2Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }")
+ .addLines(
+ " @Override",
+ " public Map<String, String> strings() {",
+ " return ImmutableMap.<String, String>of();",
+ " }",
+ "",
+ " @Override",
+ " public Map<String, Provider<String>> providerStrings() {",
+ " return ImmutableMap.<String, Provider<String>>of();",
+ " }",
+ "",
+ " @Override",
+ " public Map<Integer, Integer> ints() {",
+ " return ImmutableMap.<Integer, Integer>of(0, MapModule.provideInt());",
+ " }",
+ "",
+ " @Override",
+ " public Map<Integer, Provider<Integer>> providerInts() {",
+ " return ImmutableMap.<Integer, Provider<Integer>>of(")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " 0, MapModule_ProvideIntFactory.create());")
+ .addLinesIn(
+ FAST_INIT_MODE, //
+ " 0, getProvideIntProvider());")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Map<Long, Long> longs() {",
+ " return ImmutableMap.<Long, Long>of(",
+ " 0L, MapModule.provideLong0(),",
+ " 1L, MapModule.provideLong1(),",
+ " 2L, MapModule.provideLong2());",
+ " }",
+ "",
+ " @Override",
+ " public Map<Long, Provider<Long>> providerLongs() {",
+ " return ImmutableMap.<Long, Provider<Long>>of(")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " 0L, MapModule_ProvideLong0Factory.create(),",
+ " 1L, MapModule_ProvideLong1Factory.create(),",
+ " 2L, MapModule_ProvideLong2Factory.create());")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " 0L, getProvideLong0Provider(),",
+ " 1L, getProvideLong1Provider(),",
+ " 2L, getProvideLong2Provider());")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Sub sub() {",
+ " return new SubImpl();",
+ " }",
+ "",
+ " private final class SubImpl implements Sub {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider<Long> provideLong3Provider;",
+ " private volatile Provider<Long> provideLong4Provider;",
+ " private volatile Provider<Long> provideLong5Provider;",
+ " private SubImpl() {}",
+ "",
+ " private Provider<Long> getProvideLong3Provider() {",
+ " Object local = provideLong3Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " provideLong3Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong4Provider() {",
+ " Object local = provideLong4Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(1);",
+ " provideLong4Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }",
+ "",
+ " private Provider<Long> getProvideLong5Provider() {",
+ " Object local = provideLong5Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(2);",
+ " provideLong5Provider = (Provider<Long>) local;",
+ " }",
+ " return (Provider<Long>) local;",
+ " }")
+ .addLines(
+ " @Override",
+ " public Map<Long, Long> longs() {",
+ " return ImmutableMap.<Long, Long>builderWithExpectedSize(6)",
+ " .put(0L, MapModule.provideLong0())",
+ " .put(1L, MapModule.provideLong1())",
+ " .put(2L, MapModule.provideLong2())",
+ " .put(3L, SubcomponentMapModule.provideLong3())",
+ " .put(4L, SubcomponentMapModule.provideLong4())",
+ " .put(5L, SubcomponentMapModule.provideLong5())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Map<Long, Provider<Long>> providerLongs() {",
+ " return ImmutableMap.<Long, Provider<Long>>builderWithExpectedSize(6)")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " .put(0L, MapModule_ProvideLong0Factory.create())",
+ " .put(1L, MapModule_ProvideLong1Factory.create())",
+ " .put(2L, MapModule_ProvideLong2Factory.create())",
+ " .put(3L, SubcomponentMapModule_ProvideLong3Factory.create())",
+ " .put(4L, SubcomponentMapModule_ProvideLong4Factory.create())",
+ " .put(5L, SubcomponentMapModule_ProvideLong5Factory.create())")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " .put(0L, DaggerTestComponent.this.getProvideLong0Provider())",
+ " .put(1L, DaggerTestComponent.this.getProvideLong1Provider())",
+ " .put(2L, DaggerTestComponent.this.getProvideLong2Provider())",
+ " .put(3L, getProvideLong3Provider())",
+ " .put(4L, getProvideLong4Provider())",
+ " .put(5L, getProvideLong5Provider())")
+ .addLines( //
+ " .build();", " }")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) (Long) SubcomponentMapModule.provideLong3();",
+ " case 1: return (T) (Long) SubcomponentMapModule.provideLong4();",
+ " case 2: return (T) (Long) SubcomponentMapModule.provideLong5();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) (Integer) MapModule.provideInt();",
+ " case 1: return (T) (Long) MapModule.provideLong0();",
+ " case 2: return (T) (Long) MapModule.provideLong1();",
+ " case 3: return (T) (Long) MapModule.provideLong2();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}")
+ .build();
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(mapModuleFile, componentFile, subcomponentModuleFile, subcomponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void inaccessible() {
+ JavaFileObject inaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible", "package other;", "", "class Inaccessible {}");
+ JavaFileObject usesInaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.UsesInaccessible",
+ "package other;",
+ "",
+ "import java.util.Map;",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesInaccessible {",
+ " @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}",
+ "}");
+
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.TestModule",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "public abstract class TestModule {",
+ " @Multibinds abstract Map<Integer, Inaccessible> ints();",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "import other.TestModule;",
+ "import other.UsesInaccessible;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " UsesInaccessible usesInaccessible();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import other.UsesInaccessible;",
+ "import other.UsesInaccessible_Factory;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public UsesInaccessible usesInaccessible() {",
+ " return UsesInaccessible_Factory.newInstance((Map) ImmutableMap.of());",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(module, inaccessible, usesInaccessible, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void subcomponentOmitsInheritedBindings() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
+ " return \"parent value\";",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " Map<String, Object> objectMap();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ " private final ParentModule parentModule;",
+ "",
+ " private final class ChildImpl implements Child {",
+ " @Override",
+ " public Map<String, Object> objectMap() {",
+ " return ImmutableMap.<String, Object>of(",
+ " \"parent key\",",
+ " ParentModule_ParentKeyObjectFactory.parentKeyObject(",
+ " DaggerParent.this.parentModule));",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void productionComponents() {
+ JavaFileObject mapModuleFile =
+ JavaFileObjects.forSourceLines(
+ "test.MapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Map;",
+ "",
+ "@Module",
+ "interface MapModule {",
+ " @Multibinds Map<String, String> stringMap();",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "import java.util.Map;",
+ "",
+ "@ProductionComponent(modules = MapModule.class)",
+ "interface TestComponent {",
+ " ListenableFuture<Map<String, String>> stringMap();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.producers.internal.CancellationListener;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent, "
+ + "CancellationListener {",
+ " @Override",
+ " public ListenableFuture<Map<String, String>> stringMap() {",
+ " return Futures.immediateFuture(",
+ " (Map<String, String>) ImmutableMap.<String, String>of());",
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
+ "}");
+ Compilation compilation =
+ compilerWithOptions(
+ compilerMode
+ , CompilerMode.JAVA7
+ )
+ .compile(mapModuleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MapKeyProcessorTest.java b/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
new file mode 100644
index 0000000..746c60d
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.auto.value.processor.AutoAnnotationProcessor;
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MapKeyProcessorTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public MapKeyProcessorTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void mapKeyCreatorFile() {
+ JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.PathKey",
+ "package test;",
+ "import dagger.MapKey;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "",
+ "@MapKey(unwrapValue = false)",
+ "@Retention(RUNTIME)",
+ "public @interface PathKey {",
+ " PathEnum value();",
+ " String relativePath() default \"Defaultpath\";",
+ "}");
+ JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
+ "package test;",
+ "",
+ "public enum PathEnum {",
+ " ADMIN,",
+ " LOGIN;",
+ "}");
+ JavaFileObject generatedKeyCreator =
+ JavaFileObjects.forSourceLines(
+ "test.PathKeyCreator",
+ "package test;",
+ "",
+ "import com.google.auto.value.AutoAnnotation;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class PathKeyCreator {",
+ " private PathKeyCreator() {}",
+ "",
+ " @AutoAnnotation",
+ " public static PathKey createPathKey(PathEnum value, String relativePath) {",
+ " return new AutoAnnotation_PathKeyCreator_createPathKey(value, relativePath);",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(enumKeyFile, pathEnumFile))
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor(), new AutoAnnotationProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(generatedKeyCreator);
+ }
+
+ @Test
+ public void nestedMapKeyCreatorFile() {
+ JavaFileObject enumKeyFile = JavaFileObjects.forSourceLines("test.Container",
+ "package test;",
+ "import dagger.MapKey;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "",
+ "public interface Container {",
+ "@MapKey(unwrapValue = false)",
+ "@Retention(RUNTIME)",
+ "public @interface PathKey {",
+ " PathEnum value();",
+ " String relativePath() default \"Defaultpath\";",
+ "}",
+ "}");
+ JavaFileObject pathEnumFile = JavaFileObjects.forSourceLines("test.PathEnum",
+ "package test;",
+ "",
+ "public enum PathEnum {",
+ " ADMIN,",
+ " LOGIN;",
+ "}");
+ JavaFileObject generatedKeyCreator =
+ JavaFileObjects.forSourceLines(
+ "test.Container_PathKeyCreator",
+ "package test;",
+ "",
+ "import com.google.auto.value.AutoAnnotation;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class Container_PathKeyCreator {",
+ " private Container_PathKeyCreator() {}",
+ "",
+ " @AutoAnnotation",
+ " public static Container.PathKey createPathKey("
+ + "PathEnum value, String relativePath) {",
+ " return new AutoAnnotation_Container_PathKeyCreator_createPathKey(",
+ " value, relativePath);",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(enumKeyFile, pathEnumFile))
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor(), new AutoAnnotationProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(generatedKeyCreator);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java b/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
new file mode 100644
index 0000000..04b0986
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MapMultibindingValidationTest {
+ @Test
+ public void duplicateMapKeys() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.MapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.StringKey;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModule {",
+ " @Provides @IntoMap @StringKey(\"AKey\") Object provideObjectForAKey() {",
+ " return \"one\";",
+ " }",
+ "",
+ " @Provides @IntoMap @StringKey(\"AKey\") Object provideObjectForAKeyAgain() {",
+ " return \"one again\";",
+ " }",
+ "}");
+
+ // If they're all there, report only Map<K, V>.
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ component(
+ "Map<String, Object> objects();",
+ "Map<String, Provider<Object>> objectProviders();",
+ "Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,java.lang.Object>");
+ assertThat(compilation).hadErrorContaining("provideObjectForAKey()");
+ assertThat(compilation).hadErrorContaining("provideObjectForAKeyAgain()");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation =
+ daggerCompiler().withOptions("-Adagger.fullBindingGraphValidation=ERROR").compile(module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>")
+ .inFile(module)
+ .onLineContaining("class MapModule");
+ assertThat(compilation).hadErrorContaining("provideObjectForAKey()");
+ assertThat(compilation).hadErrorContaining("provideObjectForAKeyAgain()");
+ assertThat(compilation).hadErrorCount(1);
+
+ // If there's Map<K, V> and Map<K, Provider<V>>, report only Map<K, V>.
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ component(
+ "Map<String, Object> objects();",
+ "Map<String, Provider<Object>> objectProviders();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,java.lang.Object>");
+ assertThat(compilation).hadErrorCount(1);
+
+ // If there's Map<K, V> and Map<K, Producer<V>>, report only Map<K, V>.
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ component(
+ "Map<String, Object> objects();",
+ "Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,java.lang.Object>");
+ assertThat(compilation).hadErrorCount(1);
+
+ // If there's Map<K, Provider<V>> and Map<K, Producer<V>>, report only Map<K, Provider<V>>.
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ component(
+ "Map<String, Provider<Object>> objectProviders();",
+ "Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation = daggerCompiler().compile(module, component("Map<String, Object> objects();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,java.lang.Object>");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation =
+ daggerCompiler()
+ .compile(module, component("Map<String, Provider<Object>> objectProviders();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation =
+ daggerCompiler()
+ .compile(
+ module, component("Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The same map key is bound more than once for "
+ + "java.util.Map<java.lang.String,dagger.producers.Producer<java.lang.Object>>");
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void inconsistentMapKeyAnnotations() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.MapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.StringKey;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "final class MapModule {",
+ " @Provides @IntoMap @StringKey(\"AKey\") Object provideObjectForAKey() {",
+ " return \"one\";",
+ " }",
+ "",
+ " @Provides @IntoMap @StringKeyTwo(\"BKey\") Object provideObjectForBKey() {",
+ " return \"two\";",
+ " }",
+ "}");
+ JavaFileObject stringKeyTwoFile =
+ JavaFileObjects.forSourceLines(
+ "test.StringKeyTwo",
+ "package test;",
+ "",
+ "import dagger.MapKey;",
+ "",
+ "@MapKey(unwrapValue = true)",
+ "public @interface StringKeyTwo {",
+ " String value();",
+ "}");
+
+ // If they're all there, report only Map<K, V>.
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ stringKeyTwoFile,
+ component(
+ "Map<String, Object> objects();",
+ "Map<String, Provider<Object>> objectProviders();",
+ "Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,java.lang.Object>"
+ + " uses more than one @MapKey annotation type");
+ assertThat(compilation).hadErrorContaining("provideObjectForAKey()");
+ assertThat(compilation).hadErrorContaining("provideObjectForBKey()");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(module, stringKeyTwoFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>"
+ + " uses more than one @MapKey annotation type")
+ .inFile(module)
+ .onLineContaining("class MapModule");
+ assertThat(compilation).hadErrorContaining("provideObjectForAKey()");
+ assertThat(compilation).hadErrorContaining("provideObjectForBKey()");
+ assertThat(compilation).hadErrorCount(1);
+
+ // If there's Map<K, V> and Map<K, Provider<V>>, report only Map<K, V>.
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ stringKeyTwoFile,
+ component(
+ "Map<String, Object> objects();",
+ "Map<String, Provider<Object>> objectProviders();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,java.lang.Object>"
+ + " uses more than one @MapKey annotation type");
+ assertThat(compilation).hadErrorCount(1);
+
+ // If there's Map<K, V> and Map<K, Producer<V>>, report only Map<K, V>.
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ stringKeyTwoFile,
+ component(
+ "Map<String, Object> objects();",
+ "Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,java.lang.Object>"
+ + " uses more than one @MapKey annotation type");
+ assertThat(compilation).hadErrorCount(1);
+
+ // If there's Map<K, Provider<V>> and Map<K, Producer<V>>, report only Map<K, Provider<V>>.
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ stringKeyTwoFile,
+ component(
+ "Map<String, Provider<Object>> objectProviders();",
+ "Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>"
+ + " uses more than one @MapKey annotation type");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation =
+ daggerCompiler()
+ .compile(module, stringKeyTwoFile, component("Map<String, Object> objects();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,java.lang.Object>"
+ + " uses more than one @MapKey annotation type");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ stringKeyTwoFile,
+ component("Map<String, Provider<Object>> objectProviders();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>"
+ + " uses more than one @MapKey annotation type");
+ assertThat(compilation).hadErrorCount(1);
+
+ compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ stringKeyTwoFile,
+ component("Producer<Map<String, Producer<Object>>> objectProducers();"));
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,dagger.producers.Producer<java.lang.Object>>"
+ + " uses more than one @MapKey annotation type");
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ private static JavaFileObject component(String... entryPoints) {
+ return JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ ImmutableList.<String>builder()
+ .add(
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.producers.Producer;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {MapModule.class})",
+ "interface TestComponent {")
+ .add(entryPoints)
+ .add("}")
+ .build());
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MembersInjectionTest.java b/javatests/dagger/internal/codegen/MembersInjectionTest.java
new file mode 100644
index 0000000..edaedaf
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MembersInjectionTest.java
@@ -0,0 +1,1531 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MembersInjectionTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public MembersInjectionTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void parentClass_noInjectedMembers() {
+ JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public final class Child extends Parent {",
+ " @Inject Child() {}",
+ "}");
+ JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "public abstract class Parent {}");
+
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " Child child();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public Child child() {",
+ " return new Child();",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(childFile, parentFile, componentFile);
+
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void parentClass_injectedMembersInSupertype() {
+ JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public final class Child extends Parent {",
+ " @Inject Child() {}",
+ "}");
+ JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public abstract class Parent {",
+ " @Inject Dep dep;",
+ "}");
+ JavaFileObject depFile = JavaFileObjects.forSourceLines("test.Dep",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class Dep {",
+ " @Inject Dep() {}",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " Child child();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public Child child() {",
+ " return injectChild(Child_Factory.newInstance());",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private Child injectChild(Child instance) {",
+ " Parent_MembersInjector.injectDep(instance, new Dep());",
+ " return instance;",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(childFile, parentFile, depFile, componentFile);
+
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test public void fieldAndMethodGenerics() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class GenericClass<A, B> {",
+ " @Inject A a;",
+ "",
+ " @Inject GenericClass() {}",
+ "",
+ " @Inject void register(B b) {}",
+ "}");
+ JavaFileObject expected = JavaFileObjects.forSourceLines(
+ "test.GenericClass_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class GenericClass_MembersInjector<A, B>",
+ " implements MembersInjector<GenericClass<A, B>> {",
+ " private final Provider<A> aProvider;",
+ " private final Provider<B> bProvider;",
+ "",
+ " public GenericClass_MembersInjector(Provider<A> aProvider, Provider<B> bProvider) {",
+ " this.aProvider = aProvider;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " public static <A, B> MembersInjector<GenericClass<A, B>> create(",
+ " Provider<A> aProvider, Provider<B> bProvider) {",
+ " return new GenericClass_MembersInjector<A, B>(aProvider, bProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(GenericClass<A, B> instance) {",
+ " injectA(instance, aProvider.get());",
+ " injectRegister(instance, bProvider.get());",
+ " }",
+ "",
+ " public static <A, B> void injectA(Object instance, A a) {",
+ " ((GenericClass<A, B>) instance).a = a;",
+ " }",
+ "",
+ " public static <A, B> void injectRegister(Object instance, B b) {",
+ " ((GenericClass<A, B>) instance).register(b);",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(file)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expected);
+ }
+
+ @Test public void subclassedGenericMembersInjectors() {
+ JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A() {}",
+ "}");
+ JavaFileObject a2 = JavaFileObjects.forSourceLines("test.A2",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A2 {",
+ " @Inject A2() {}",
+ "}");
+ JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Parent<X, Y> {",
+ " @Inject X x;",
+ " @Inject Y y;",
+ " @Inject A2 a2;",
+ "",
+ " @Inject Parent() {}",
+ "}");
+ JavaFileObject child = JavaFileObjects.forSourceLines("test.Child",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Child<T> extends Parent<T, A> {",
+ " @Inject A a;",
+ " @Inject T t;",
+ "",
+ " @Inject Child() {}",
+ "}");
+ JavaFileObject expected = JavaFileObjects.forSourceLines(
+ "test.Child_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class Child_MembersInjector<T>",
+ " implements MembersInjector<Child<T>> {",
+ " private final Provider<T> tAndXProvider;",
+ " private final Provider<A> aAndYProvider;",
+ " private final Provider<A2> a2Provider;",
+ "",
+ " public Child_MembersInjector(",
+ " Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
+ " this.tAndXProvider = tAndXProvider;",
+ " this.aAndYProvider = aAndYProvider;",
+ " this.a2Provider = a2Provider;",
+ " }",
+ "",
+ " public static <T> MembersInjector<Child<T>> create(",
+ " Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
+ " return new Child_MembersInjector<T>(tAndXProvider, aAndYProvider, a2Provider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(Child<T> instance) {",
+ " Parent_MembersInjector.injectX(instance, tAndXProvider.get());",
+ " Parent_MembersInjector.injectY(instance, aAndYProvider.get());",
+ " Parent_MembersInjector.injectA2(instance, a2Provider.get());",
+ " injectA(instance, aAndYProvider.get());",
+ " injectT(instance, tAndXProvider.get());",
+ " }",
+ "",
+ " public static <T> void injectA(Object instance, Object a) {",
+ " ((Child<T>) instance).a = (A) a;",
+ " }",
+ "",
+ " public static <T> void injectT(Object instance, T t) {",
+ " ((Child<T>) instance).t = t;",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(a, a2, parent, child))
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expected);
+ }
+
+ @Test public void fieldInjection() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.FieldInjection",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class FieldInjection {",
+ " @Inject String string;",
+ " @Inject Lazy<String> lazyString;",
+ " @Inject Provider<String> stringProvider;",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.FieldInjection_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.DoubleCheck;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class FieldInjection_MembersInjector",
+ " implements MembersInjector<FieldInjection> {",
+ " private final Provider<String> stringProvider;",
+ "",
+ " public FieldInjection_MembersInjector(Provider<String> stringProvider) {",
+ " this.stringProvider = stringProvider;",
+ " }",
+ "",
+ " public static MembersInjector<FieldInjection> create(",
+ " Provider<String> stringProvider) {",
+ " return new FieldInjection_MembersInjector(stringProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(FieldInjection instance) {",
+ " injectString(instance, stringProvider.get());",
+ " injectLazyString(instance, DoubleCheck.lazy(stringProvider));",
+ " injectStringProvider(instance, stringProvider);",
+ " }",
+ "",
+ " public static void injectString(Object instance, String string) {",
+ " ((FieldInjection) instance).string = string;",
+ " }",
+ "",
+ " public static void injectLazyString(Object instance, Lazy<String> lazyString) {",
+ " ((FieldInjection) instance).lazyString = lazyString;",
+ " }",
+ "",
+ " public static void injectStringProvider(",
+ " Object instance, Provider<String> stringProvider) {",
+ " ((FieldInjection) instance).stringProvider = stringProvider;",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(file)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expected);
+ }
+
+ @Test public void methodInjection() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.MethodInjection",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class MethodInjection {",
+ " @Inject void noArgs() {}",
+ " @Inject void oneArg(String string) {}",
+ " @Inject void manyArgs(",
+ " String string, Lazy<String> lazyString, Provider<String> stringProvider) {}",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.MethodInjection_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.DoubleCheck;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class MethodInjection_MembersInjector",
+ " implements MembersInjector<MethodInjection> {",
+ "",
+ " private final Provider<String> stringProvider;",
+ "",
+ " public MethodInjection_MembersInjector(Provider<String> stringProvider) {",
+ " this.stringProvider = stringProvider;",
+ " }",
+ "",
+ " public static MembersInjector<MethodInjection> create(",
+ " Provider<String> stringProvider) {",
+ " return new MethodInjection_MembersInjector(stringProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(MethodInjection instance) {",
+ " injectNoArgs(instance);",
+ " injectOneArg(instance, stringProvider.get());",
+ " injectManyArgs(",
+ " instance,",
+ " stringProvider.get(),",
+ " DoubleCheck.lazy(stringProvider),",
+ " stringProvider);",
+ " }",
+ "",
+ " public static void injectNoArgs(Object instance) {",
+ " ((MethodInjection) instance).noArgs();",
+ " }",
+ "",
+ " public static void injectOneArg(Object instance, String string) {",
+ " ((MethodInjection) instance).oneArg(string);",
+ " }",
+ "",
+ " public static void injectManyArgs(",
+ " Object instance,",
+ " String string,",
+ " Lazy<String> lazyString,",
+ " Provider<String> stringProvider) {",
+ " ((MethodInjection) instance).manyArgs(string, lazyString, stringProvider);",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(file)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expected);
+ }
+
+ @Test
+ public void mixedMemberInjection() {
+ JavaFileObject file = JavaFileObjects.forSourceLines(
+ "test.MixedMemberInjection",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class MixedMemberInjection {",
+ " @Inject String string;",
+ " @Inject void setString(String s) {}",
+ " @Inject Object object;",
+ " @Inject void setObject(Object o) {}",
+ "}");
+ JavaFileObject expected = JavaFileObjects.forSourceLines(
+ "test.MixedMemberInjection_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class MixedMemberInjection_MembersInjector",
+ " implements MembersInjector<MixedMemberInjection> {",
+ "",
+ " private final Provider<String> stringAndSProvider;",
+ " private final Provider<Object> objectAndOProvider;",
+ "",
+ " public MixedMemberInjection_MembersInjector(",
+ " Provider<String> stringAndSProvider,",
+ " Provider<Object> objectAndOProvider) {",
+ " this.stringAndSProvider = stringAndSProvider;",
+ " this.objectAndOProvider = objectAndOProvider;",
+ " }",
+ "",
+ " public static MembersInjector<MixedMemberInjection> create(",
+ " Provider<String> stringAndSProvider,",
+ " Provider<Object> objectAndOProvider) {",
+ " return new MixedMemberInjection_MembersInjector(",
+ " stringAndSProvider, objectAndOProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(MixedMemberInjection instance) {",
+ " injectString(instance, stringAndSProvider.get());",
+ " injectObject(instance, objectAndOProvider.get());",
+ " injectSetString(instance, stringAndSProvider.get());",
+ " injectSetObject(instance, objectAndOProvider.get());",
+ " }",
+ "",
+ " public static void injectString(Object instance, String string) {",
+ " ((MixedMemberInjection) instance).string = string;",
+ " }",
+ "",
+ " public static void injectObject(Object instance, Object object) {",
+ " ((MixedMemberInjection) instance).object = object;",
+ " }",
+ "",
+ " public static void injectSetString(Object instance, String s) {",
+ " ((MixedMemberInjection) instance).setString(s);",
+ " }",
+ "",
+ " public static void injectSetObject(Object instance, Object o) {",
+ " ((MixedMemberInjection) instance).setObject(o);",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(file)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expected);
+ }
+
+ @Test public void injectConstructorAndMembersInjection() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.AllInjections",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class AllInjections {",
+ " @Inject String s;",
+ " @Inject AllInjections(String s) {}",
+ " @Inject void s(String s) {}",
+ "}");
+ JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
+ "test.AllInjections_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class AllInjections_MembersInjector ",
+ " implements MembersInjector<AllInjections> {",
+ "",
+ " private final Provider<String> sProvider;",
+ "",
+ " public AllInjections_MembersInjector(Provider<String> sProvider) {",
+ " this.sProvider = sProvider;",
+ " }",
+ "",
+ " public static MembersInjector<AllInjections> create(Provider<String> sProvider) {",
+ " return new AllInjections_MembersInjector(sProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(AllInjections instance) {",
+ " injectS(instance, sProvider.get());",
+ " injectS2(instance, sProvider.get());",
+ " }",
+ "",
+ // TODO(b/64477506): now that these all take "object", it would be nice to rename "instance"
+ // to the type name
+ " public static void injectS(Object instance, String s) {",
+ " ((AllInjections) instance).s = s;",
+ " }",
+ "",
+ " public static void injectS2(Object instance, String s) {",
+ " ((AllInjections) instance).s(s);",
+ " }",
+ "",
+ "}");
+ assertAbout(javaSource())
+ .that(file)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expectedMembersInjector);
+ }
+
+ @Test public void supertypeMembersInjection() {
+ JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "class A {}");
+ JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class B extends A {",
+ " @Inject String s;",
+ "}");
+ JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
+ "test.AllInjections_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class B_MembersInjector implements MembersInjector<B> {",
+ " private final Provider<String> sProvider;",
+ "",
+ " public B_MembersInjector(Provider<String> sProvider) {",
+ " this.sProvider = sProvider;",
+ " }",
+ "",
+ " public static MembersInjector<B> create(Provider<String> sProvider) {",
+ " return new B_MembersInjector(sProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(B instance) {",
+ " injectS(instance, sProvider.get());",
+ " }",
+ "",
+ " public static void injectS(Object instance, String s) {",
+ " ((B) instance).s = s;",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(aFile, bFile))
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expectedMembersInjector);
+ }
+
+ @Test
+ public void simpleComponentWithNesting() {
+ JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines(
+ "test.OuterType",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterType {",
+ " static class A {",
+ " @Inject A() {}",
+ " }",
+ " static class B {",
+ " @Inject A a;",
+ " }",
+ " @Component interface SimpleComponent {",
+ " A a();",
+ " void inject(B b);",
+ " }",
+ "}");
+ JavaFileObject bMembersInjector = JavaFileObjects.forSourceLines(
+ "test.OuterType_B_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class OuterType_B_MembersInjector",
+ " implements MembersInjector<OuterType.B> {",
+ " private final Provider<OuterType.A> aProvider;",
+ "",
+ " public OuterType_B_MembersInjector(Provider<OuterType.A> aProvider) {",
+ " this.aProvider = aProvider;",
+ " }",
+ "",
+ " public static MembersInjector<OuterType.B> create(Provider<OuterType.A> aProvider) {",
+ " return new OuterType_B_MembersInjector(aProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(OuterType.B instance) {",
+ " injectA(instance, aProvider.get());",
+ " }",
+ "",
+ " public static void injectA(Object instance, Object a) {",
+ " ((OuterType.B) instance).a = (OuterType.A) a;",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(nestedTypesFile))
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(bMembersInjector);
+ }
+
+ @Test
+ public void componentWithNestingAndGeneratedType() {
+ JavaFileObject nestedTypesFile =
+ JavaFileObjects.forSourceLines(
+ "test.OuterType",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterType {",
+ " @Inject GeneratedType generated;",
+ " static class A {",
+ " @Inject A() {}",
+ " }",
+ " static class B {",
+ " @Inject A a;",
+ " }",
+ " @Component interface SimpleComponent {",
+ " A a();",
+ " void inject(B b);",
+ " }",
+ "}");
+ JavaFileObject bMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.OuterType_B_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class OuterType_B_MembersInjector",
+ " implements MembersInjector<OuterType.B> {",
+ " private final Provider<OuterType.A> aProvider;",
+ "",
+ " public OuterType_B_MembersInjector(Provider<OuterType.A> aProvider) {",
+ " this.aProvider = aProvider;",
+ " }",
+ "",
+ " public static MembersInjector<OuterType.B> create(",
+ " Provider<OuterType.A> aProvider) {",
+ " return new OuterType_B_MembersInjector(aProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(OuterType.B instance) {",
+ " injectA(instance, aProvider.get());",
+ " }",
+ "",
+ " public static void injectA(Object instance, Object a) {",
+ " ((OuterType.B) instance).a = (OuterType.A) a;",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(nestedTypesFile)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(
+ new ComponentProcessor(),
+ new AbstractProcessor() {
+ private boolean done;
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of("*");
+ }
+
+ @Override
+ public boolean process(
+ Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (!done) {
+ done = true;
+ try (Writer writer =
+ processingEnv
+ .getFiler()
+ .createSourceFile("test.GeneratedType")
+ .openWriter()) {
+ writer.write(
+ Joiner.on('\n')
+ .join(
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class GeneratedType {",
+ " @Inject GeneratedType() {}",
+ "}"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return false;
+ }
+ })
+ .compilesWithoutError()
+ .and()
+ .generatesSources(bMembersInjector);
+ }
+
+ @Test
+ public void lowerCaseNamedMembersInjector_forLowerCaseType() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class foo {",
+ " @Inject String string;",
+ "}");
+ JavaFileObject fooModule =
+ JavaFileObjects.forSourceLines(
+ "test.fooModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class fooModule {",
+ " @Provides String string() { return \"foo\"; }",
+ "}");
+ JavaFileObject fooComponent =
+ JavaFileObjects.forSourceLines(
+ "test.fooComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = fooModule.class)",
+ "interface fooComponent {",
+ " void inject(foo target);",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(foo, fooModule, fooComponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation).generatedFile(CLASS_OUTPUT, "test", "foo_MembersInjector.class");
+ }
+
+ @Test
+ public void fieldInjectionForShadowedMember() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar() {}",
+ "}");
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Parent { ",
+ " @Inject Foo object;",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Child extends Parent { ",
+ " @Inject Bar object;",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface C { ",
+ " void inject(Child child);",
+ "}");
+
+ JavaFileObject expectedMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.Child_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class Child_MembersInjector implements MembersInjector<Child> {",
+ " private final Provider<Foo> objectProvider;",
+ " private final Provider<Bar> objectProvider2;",
+ "",
+ " public Child_MembersInjector(",
+ " Provider<Foo> objectProvider, Provider<Bar> objectProvider2) {",
+ " this.objectProvider = objectProvider;",
+ " this.objectProvider2 = objectProvider2;",
+ " }",
+ "",
+ " public static MembersInjector<Child> create(",
+ " Provider<Foo> objectProvider, Provider<Bar> objectProvider2) {",
+ " return new Child_MembersInjector(objectProvider, objectProvider2);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(Child instance) {",
+ " Parent_MembersInjector.injectObject(instance, objectProvider.get());",
+ " injectObject(instance, objectProvider2.get());",
+ " }",
+ "",
+ " public static void injectObject(Object instance, Object object) {",
+ " ((Child) instance).object = (Bar) object;",
+ " }",
+ "}");
+
+ assertAbout(javaSources())
+ .that(ImmutableList.of(foo, bar, parent, child, component))
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expectedMembersInjector);
+ }
+
+ @Test public void privateNestedClassError() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " private static final class InnerClass {",
+ " @Inject int field;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into private classes")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void privateNestedClassWarning() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " private static final class InnerClass {",
+ " @Inject int field;",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(
+ compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
+ .compile(file);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining("Dagger does not support injection into private classes")
+ .inFile(file)
+ .onLine(6);
+ }
+
+ @Test public void privateSuperclassIsOkIfNotInjectedInto() {
+ JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class OuterClass {",
+ " private static class BaseClass {}",
+ "",
+ " static final class DerivedClass extends BaseClass {",
+ " @Inject int field;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void rawFrameworkTypeField() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.RawFrameworkTypes",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class RawProviderField {",
+ " @Inject Provider fieldWithRawProvider;",
+ "}",
+ "",
+ "@Component",
+ "interface C {",
+ " void inject(RawProviderField rawProviderField);",
+ "}");
+
+ Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("javax.inject.Provider cannot be provided")
+ .inFile(file)
+ .onLineContaining("interface C");
+ }
+
+ @Test
+ public void rawFrameworkTypeParameter() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.RawFrameworkTypes",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class RawProviderParameter {",
+ " @Inject void methodInjection(Provider rawProviderParameter) {}",
+ "}",
+ "",
+ "@Component",
+ "interface C {",
+ " void inject(RawProviderParameter rawProviderParameter);",
+ "}");
+
+ Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("javax.inject.Provider cannot be provided")
+ .inFile(file)
+ .onLineContaining("interface C");
+ }
+
+ @Test
+ public void injectsPrimitive() {
+ JavaFileObject injectedType =
+ JavaFileObjects.forSourceLines(
+ "test.InjectedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectedType {",
+ " @Inject InjectedType() {}",
+ "",
+ " @Inject int primitiveInt;",
+ " @Inject Integer boxedInt;",
+ "}");
+ JavaFileObject membersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.InjectedType_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class InjectedType_MembersInjector ",
+ " implements MembersInjector<InjectedType> {",
+ " private final Provider<Integer> boxedIntAndPrimitiveIntProvider;",
+ "",
+ " public InjectedType_MembersInjector(",
+ " Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
+ " this.boxedIntAndPrimitiveIntProvider = boxedIntAndPrimitiveIntProvider;",
+ " }",
+ "",
+ " public static MembersInjector<InjectedType> create(",
+ " Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
+ " return new InjectedType_MembersInjector(boxedIntAndPrimitiveIntProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(InjectedType instance) {",
+ " injectPrimitiveInt(instance, boxedIntAndPrimitiveIntProvider.get());",
+ " injectBoxedInt(instance, boxedIntAndPrimitiveIntProvider.get());",
+ " }",
+ "",
+ " public static void injectPrimitiveInt(Object instance, int primitiveInt) {",
+ " ((InjectedType) instance).primitiveInt = primitiveInt;",
+ " }",
+ "",
+ " public static void injectBoxedInt(Object instance, Integer boxedInt) {",
+ " ((InjectedType) instance).boxedInt = boxedInt;",
+ " }",
+ "}");
+ JavaFileObject factory =
+ JavaFileObjects.forSourceLines(
+ "test.InjectedType_Factory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class InjectedType_Factory implements Factory<InjectedType> {",
+ " private final Provider<Integer> boxedIntAndPrimitiveIntProvider;",
+ "",
+ " public InjectedType_Factory(Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
+ " this.boxedIntAndPrimitiveIntProvider = boxedIntAndPrimitiveIntProvider;",
+ " }",
+ "",
+ " @Override",
+ " public InjectedType get() {",
+ " InjectedType instance = new InjectedType();",
+ " InjectedType_MembersInjector.injectPrimitiveInt(",
+ " instance, boxedIntAndPrimitiveIntProvider.get());",
+ " InjectedType_MembersInjector.injectBoxedInt(",
+ " instance, boxedIntAndPrimitiveIntProvider.get());",
+ " return instance;",
+ " }",
+ "",
+ " public static InjectedType_Factory create(",
+ " Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
+ " return new InjectedType_Factory(boxedIntAndPrimitiveIntProvider);",
+ " }",
+ "",
+ " public static InjectedType newInstance() {",
+ " return new InjectedType();",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(injectedType);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.InjectedType_MembersInjector")
+ .hasSourceEquivalentTo(membersInjector);
+ assertThat(compilation)
+ .generatedSourceFile("test.InjectedType_Factory")
+ .hasSourceEquivalentTo(factory);
+ }
+
+ @Test
+ public void accessibility() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "other.Foo",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject inaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Inaccessible {",
+ " @Inject Inaccessible() {}",
+ " @Inject Foo foo;",
+ " @Inject void method(Foo foo) {}",
+ "}");
+ JavaFileObject usesInaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.UsesInaccessible",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesInaccessible {",
+ " @Inject UsesInaccessible(Inaccessible inaccessible) {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import other.UsesInaccessible;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " UsesInaccessible usesInaccessible();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(foo, inaccessible, usesInaccessible, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("other.Inaccessible_MembersInjector")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible_MembersInjector",
+ "package other;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class Inaccessible_MembersInjector",
+ " implements MembersInjector<Inaccessible> {",
+ " private final Provider<Foo> fooProvider;",
+ "",
+ " public Inaccessible_MembersInjector(Provider<Foo> fooProvider) {",
+ " this.fooProvider = fooProvider;",
+ " }",
+ "",
+ " public static MembersInjector<Inaccessible> create(Provider<Foo> fooProvider) {",
+ " return new Inaccessible_MembersInjector(fooProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(Inaccessible instance) {",
+ " injectFoo(instance, fooProvider.get());",
+ " injectMethod(instance, fooProvider.get());",
+ " }",
+ "",
+ " public static void injectFoo(Object instance, Object foo) {",
+ " ((Inaccessible) instance).foo = (Foo) foo;",
+ " }",
+ "",
+ " public static void injectMethod(Object instance, Object foo) {",
+ " ((Inaccessible) instance).method((Foo) foo);",
+ " }",
+ "}"));
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import other.Foo_Factory;",
+ "import other.Inaccessible_Factory;",
+ "import other.Inaccessible_MembersInjector;",
+ "import other.UsesInaccessible;",
+ "import other.UsesInaccessible_Factory;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Object getInaccessible() {",
+ " return injectInaccessible(Inaccessible_Factory.newInstance());",
+ " }",
+ "",
+ " @Override",
+ " public UsesInaccessible usesInaccessible() {",
+ " return UsesInaccessible_Factory.newInstance(",
+ " getInaccessible());",
+ " }",
+ "",
+ // TODO(ronshapiro): if possible, it would be great to rename "instance", but we
+ // need to make sure that this doesn't conflict with any framework field in this or
+ // any parent component
+ " @CanIgnoreReturnValue",
+ " private Object injectInaccessible(Object instance) {",
+ " Inaccessible_MembersInjector.injectFoo(instance, Foo_Factory.newInstance());",
+ " Inaccessible_MembersInjector.injectMethod(instance, Foo_Factory.newInstance());",
+ " return instance;",
+ " }",
+ "}");
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void accessibleRawType_ofInaccessibleType() {
+ JavaFileObject inaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible",
+ "package other;",
+ "",
+ "class Inaccessible {}");
+ JavaFileObject inaccessiblesModule =
+ JavaFileObjects.forSourceLines(
+ "other.InaccessiblesModule",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.ArrayList;",
+ "import java.util.List;",
+ "import javax.inject.Provider;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "public class InaccessiblesModule {",
+ // force Provider initialization
+ " @Provides @Singleton static List<Inaccessible> inaccessibles() {",
+ " return new ArrayList<>();",
+ " }",
+ "}");
+ JavaFileObject usesInaccessibles =
+ JavaFileObjects.forSourceLines(
+ "other.UsesInaccessibles",
+ "package other;",
+ "",
+ "import java.util.List;",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesInaccessibles {",
+ " @Inject UsesInaccessibles() {}",
+ " @Inject List<Inaccessible> inaccessibles;",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "import other.UsesInaccessibles;",
+ "",
+ "@Singleton",
+ "@Component(modules = other.InaccessiblesModule.class)",
+ "interface TestComponent {",
+ " UsesInaccessibles usesInaccessibles();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(inaccessible, inaccessiblesModule, usesInaccessibles, component);
+ assertThat(compilation).succeeded();
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
+ "import other.InaccessiblesModule;",
+ "import other.InaccessiblesModule_InaccessiblesFactory;",
+ "import other.UsesInaccessibles;",
+ "import other.UsesInaccessibles_Factory;",
+ "import other.UsesInaccessibles_MembersInjector;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Object listOfInaccessible = new MemoizedSentinel();",
+ "",
+ " private List getListOfInaccessible() {",
+ " Object local = listOfInaccessible;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = listOfInaccessible;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = InaccessiblesModule_InaccessiblesFactory.inaccessibles();",
+ " listOfInaccessible =",
+ " DoubleCheck.reentrantCheck(listOfInaccessible, local);",
+ " }",
+ " }",
+ " }",
+ " return (List) local;",
+ " }")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider inaccessiblesProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.inaccessiblesProvider =",
+ " DoubleCheck.provider(InaccessiblesModule_InaccessiblesFactory.create());",
+ " }")
+ .addLines(
+ "",
+ " @Override",
+ " public UsesInaccessibles usesInaccessibles() {",
+ " return injectUsesInaccessibles(",
+ " UsesInaccessibles_Factory.newInstance());",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private UsesInaccessibles injectUsesInaccessibles(",
+ " UsesInaccessibles instance) {",
+ " UsesInaccessibles_MembersInjector.injectInaccessibles(")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " instance, (List) getListOfInaccessible());")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " instance, (List) inaccessiblesProvider.get());")
+ .addLines(
+ " return instance;",
+ " }",
+ "}")
+ .build();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void publicSupertypeHiddenSubtype() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "other.Foo",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "other.Supertype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class Supertype<T> {",
+ " @Inject T t;",
+ "}");
+ JavaFileObject subtype =
+ JavaFileObjects.forSourceLines(
+ "other.Subtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Subtype extends Supertype<Foo> {",
+ " @Inject Subtype() {}",
+ "}");
+ JavaFileObject injectsSubtype =
+ JavaFileObjects.forSourceLines(
+ "other.InjectsSubtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class InjectsSubtype {",
+ " @Inject InjectsSubtype(Subtype s) {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " other.InjectsSubtype injectsSubtype();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(foo, supertype, subtype, injectsSubtype, component);
+ assertThat(compilation).succeeded();
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
+ "import other.Foo_Factory;",
+ "import other.InjectsSubtype;",
+ "import other.InjectsSubtype_Factory;",
+ "import other.Subtype_Factory;",
+ "import other.Supertype;",
+ "import other.Supertype_MembersInjector;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Object getSubtype() {",
+ " return injectSubtype(Subtype_Factory.newInstance());",
+ " }",
+ "",
+ " @Override",
+ " public InjectsSubtype injectsSubtype() {",
+ " return InjectsSubtype_Factory.newInstance(getSubtype());",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private Object injectSubtype(Object instance) {",
+ " Supertype_MembersInjector.injectT(",
+ " (Supertype) instance, Foo_Factory.newInstance());",
+ " return instance;",
+ " }",
+ "}");
+
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java b/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java
new file mode 100644
index 0000000..68d5daa
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests that errors are reported for invalid members injection methods and {@link
+ * dagger.MembersInjector} dependency requests.
+ */
+@RunWith(JUnit4.class)
+public class MembersInjectionValidationTest {
+ @Test
+ public void membersInjectDependsOnUnboundedType() {
+ JavaFileObject injectsUnboundedType =
+ JavaFileObjects.forSourceLines(
+ "test.InjectsUnboundedType",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import java.util.ArrayList;",
+ "import javax.inject.Inject;",
+ "",
+ "class InjectsUnboundedType {",
+ " @Inject MembersInjector<ArrayList<?>> listInjector;",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(injectsUnboundedType);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Cannot inject members into types with unbounded type arguments: "
+ + "java.util.ArrayList<?>")
+ .inFile(injectsUnboundedType)
+ .onLineContaining("@Inject MembersInjector<ArrayList<?>> listInjector;");
+ }
+
+ @Test
+ public void membersInjectPrimitive() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " void inject(int primitive);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Cannot inject members into int")
+ .inFile(component)
+ .onLineContaining("void inject(int primitive);");
+ }
+
+ @Test
+ public void membersInjectArray() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " void inject(Object[] array);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Cannot inject members into java.lang.Object[]")
+ .inFile(component)
+ .onLineContaining("void inject(Object[] array);");
+ }
+
+ @Test
+ public void membersInjectorOfArray() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.MembersInjector;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " MembersInjector<Object[]> objectArrayInjector();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Cannot inject members into java.lang.Object[]")
+ .inFile(component)
+ .onLineContaining("objectArrayInjector();");
+ }
+
+ @Test
+ public void membersInjectRawType() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Set;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " void inject(Set rawSet);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("Cannot inject members into raw type java.util.Set");
+ }
+
+ @Test
+ public void qualifiedMembersInjector() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.MembersInjector;",
+ "import javax.inject.Named;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " @Named(\"foo\") MembersInjector<Object> objectInjector();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Cannot inject members into qualified types")
+ .inFile(component)
+ .onLineContaining("objectInjector();");
+ }
+
+ @Test
+ public void qualifiedMembersInjectionMethod() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.MembersInjector;",
+ "import javax.inject.Named;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " @Named(\"foo\") void injectObject(Object object);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Cannot inject members into qualified types")
+ .inFile(component)
+ .onLineContaining("injectObject(Object object);");
+ }
+
+ @Test
+ public void qualifiedMembersInjectionMethodParameter() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.MembersInjector;",
+ "import javax.inject.Named;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " void injectObject(@Named(\"foo\") Object object);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Cannot inject members into qualified types")
+ .inFile(component)
+ .onLineContaining("injectObject(@Named(\"foo\") Object object);");
+ }
+
+ @Test
+ public void staticFieldInjection() {
+ JavaFileObject injected =
+ JavaFileObjects.forSourceLines(
+ "test.Injected",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class Injected {",
+ " @Inject static Object object;",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " void inject(Injected injected);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(injected, component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("static fields").inFile(injected).onLine(6);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
new file mode 100644
index 0000000..687c29a
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.testing.compile.CompilationRule;
+import dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.InnerClass;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MethodSignatureFormatterTest {
+ @Rule public CompilationRule compilationRule = new CompilationRule();
+
+ static class OuterClass {
+ @interface Foo {
+ Class<?> bar();
+ }
+
+ static class InnerClass {
+ @Foo(bar = String.class)
+ @Singleton
+ String foo(
+ @SuppressWarnings("unused") int a,
+ @SuppressWarnings("unused") ImmutableList<Boolean> blah) {
+ return "foo";
+ }
+ }
+ }
+
+ @Test public void methodSignatureTest() {
+ DaggerElements elements =
+ new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+ DaggerTypes types = new DaggerTypes(compilationRule.getTypes(), elements);
+ TypeElement inner = elements.getTypeElement(InnerClass.class);
+ ExecutableElement method = Iterables.getOnlyElement(methodsIn(inner.getEnclosedElements()));
+ String formatted = new MethodSignatureFormatter(types).format(method);
+ // This is gross, but it turns out that annotation order is not guaranteed when getting
+ // all the AnnotationMirrors from an Element, so I have to test this chopped-up to make it
+ // less brittle.
+ assertThat(formatted).contains("@Singleton");
+ assertThat(formatted).doesNotContain("@javax.inject.Singleton"); // maybe more importantly
+ assertThat(formatted)
+ .contains("@dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.Foo"
+ + "(bar=String.class)");
+ assertThat(formatted).contains(" String "); // return type compressed
+ assertThat(formatted).contains("int, ImmutableList<Boolean>)"); // parameters compressed.
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MissingAndroidProcessorTest.java b/javatests/dagger/internal/codegen/MissingAndroidProcessorTest.java
new file mode 100644
index 0000000..0f91e0e
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MissingAndroidProcessorTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MissingAndroidProcessorTest {
+ @Test
+ public void missingProcessor() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.android.ContributesAndroidInjector;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @ContributesAndroidInjector",
+ " Object o();",
+ "}");
+ JavaFileObject contributesAndroidInjectorStub =
+ JavaFileObjects.forSourceLines(
+ "dagger.android.ContributesAndroidInjector",
+ "package dagger.android;",
+ "",
+ "public @interface ContributesAndroidInjector {}");
+ Compilation compilation = daggerCompiler().compile(module, contributesAndroidInjectorStub);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("dagger.android.processor.AndroidProcessor")
+ .inFile(module)
+ .onLine(9);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java b/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
new file mode 100644
index 0000000..e2f9634
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MissingBindingSuggestionsTest {
+ private static JavaFileObject injectable(String className, String constructorParams) {
+ return JavaFileObjects.forSourceLines("test." + className,
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class " + className +" {",
+ " @Inject " + className + "(" + constructorParams + ") {}",
+ "}");
+ }
+
+ private static JavaFileObject emptyInterface(String interfaceName) {
+ return JavaFileObjects.forSourceLines("test." + interfaceName,
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "interface " + interfaceName +" {}");
+ }
+
+ @Test public void suggestsBindingInSeparateComponent() {
+ JavaFileObject fooComponent = JavaFileObjects.forSourceLines("test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface FooComponent {",
+ " Foo getFoo();",
+ "}");
+ JavaFileObject barModule = JavaFileObjects.forSourceLines("test.BarModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "final class BarModule {",
+ " @Provides Bar provideBar() {return null;}",
+ "}");
+ JavaFileObject barComponent = JavaFileObjects.forSourceLines("test.BarComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = {BarModule.class})",
+ "interface BarComponent {",
+ " Bar getBar();",
+ "}");
+ JavaFileObject foo = injectable("Foo", "Bar bar");
+ JavaFileObject bar = emptyInterface("Bar");
+
+ JavaFileObject topComponent = JavaFileObjects.forSourceLines("test.TopComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TopComponent {",
+ " FooComponent getFoo();",
+ " BarComponent getBar(BarModule barModule);",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(fooComponent, barComponent, topComponent, foo, bar, barModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("A binding with matching key exists in component: test.BarComponent");
+ }
+
+ @Test public void suggestsBindingInNestedSubcomponent() {
+ JavaFileObject fooComponent = JavaFileObjects.forSourceLines("test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface FooComponent {",
+ " Foo getFoo();",
+ "}");
+ JavaFileObject barComponent = JavaFileObjects.forSourceLines("test.BarComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent()",
+ "interface BarComponent {",
+ " BazComponent getBaz();",
+ "}");
+ JavaFileObject bazModule = JavaFileObjects.forSourceLines("test.BazModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "final class BazModule {",
+ " @Provides Baz provideBaz() {return null;}",
+ "}");
+ JavaFileObject bazComponent = JavaFileObjects.forSourceLines("test.BazComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = {BazModule.class})",
+ "interface BazComponent {",
+ " Baz getBaz();",
+ "}");
+ JavaFileObject foo = injectable("Foo", "Baz baz");
+ JavaFileObject baz = emptyInterface("Baz");
+
+ JavaFileObject topComponent = JavaFileObjects.forSourceLines("test.TopComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TopComponent {",
+ " FooComponent getFoo();",
+ " BarComponent getBar();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .compile(fooComponent, barComponent, bazComponent, topComponent, foo, baz, bazModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("A binding with matching key exists in component: test.BazComponent");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MissingBindingValidationTest.java b/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
new file mode 100644
index 0000000..8eaa8d5
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MissingBindingValidationTest {
+ @Test
+ public void dependOnInterface() {
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.MyComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface MyComponent {",
+ " Foo getFoo();",
+ "}");
+ JavaFileObject injectable = JavaFileObjects.forSourceLines("test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo(Bar bar) {}",
+ "}");
+ JavaFileObject nonInjectable = JavaFileObjects.forSourceLines("test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "interface Bar {}");
+ Compilation compilation = daggerCompiler().compile(component, injectable, nonInjectable);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.Bar cannot be provided without an @Provides-annotated method.")
+ .inFile(component)
+ .onLineContaining("interface MyComponent");
+ }
+
+ @Test
+ public void entryPointDependsOnInterface() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " @Component()",
+ " interface AComponent {",
+ " A getA();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[Dagger/MissingBinding] test.TestClass.A cannot be provided "
+ + "without an @Provides-annotated method.")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test
+ public void entryPointDependsOnQualifiedInterface() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Qualifier;",
+ "",
+ "final class TestClass {",
+ " @Qualifier @interface Q {}",
+ " interface A {}",
+ "",
+ " @Component()",
+ " interface AComponent {",
+ " @Q A qualifiedA();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[Dagger/MissingBinding] @test.TestClass.Q test.TestClass.A cannot be provided "
+ + "without an @Provides-annotated method.")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test public void constructorInjectionWithoutAnnotation() {
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "final class TestClass {",
+ " static class A {",
+ " A() {}",
+ " }",
+ "",
+ " @Component()",
+ " interface AComponent {",
+ " A getA();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.TestClass.A cannot be provided without an @Inject constructor or an "
+ + "@Provides-annotated method.")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test public void membersInjectWithoutProvision() {
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "final class TestClass {",
+ " static class A {",
+ " @Inject A() {}",
+ " }",
+ "",
+ " static class B {",
+ " @Inject A a;",
+ " }",
+ "",
+ " @Component()",
+ " interface AComponent {",
+ " B getB();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.TestClass.B cannot be provided without an @Inject constructor or an "
+ + "@Provides-annotated method. This type supports members injection but cannot be "
+ + "implicitly provided.")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test
+ public void missingBindingWithSameKeyAsMembersInjectionMethod() {
+ JavaFileObject self =
+ JavaFileObjects.forSourceLines(
+ "test.Self",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class Self {",
+ " @Inject Provider<Self> selfProvider;",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.SelfComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SelfComponent {",
+ " void inject(Self target);",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(self, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.Self cannot be provided without an @Inject constructor")
+ .inFile(component)
+ .onLineContaining("interface SelfComponent");
+ }
+
+ @Test
+ public void genericInjectClassWithWildcardDependencies() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " Foo<? extends Number> foo();",
+ "}");
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class Foo<T> {",
+ " @Inject Foo(T t) {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, foo);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.Foo<? extends java.lang.Number> cannot be provided "
+ + "without an @Provides-annotated method");
+ }
+
+ @Test public void longChainOfDependencies() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Named;",
+ "import javax.inject.Provider;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " static class B {",
+ " @Inject B(A a) {}",
+ " }",
+ "",
+ " static class C {",
+ " @Inject B b;",
+ " @Inject C(X x) {}",
+ " }",
+ "",
+ " interface D { }",
+ "",
+ " static class DImpl implements D {",
+ " @Inject DImpl(C c, B b) {}",
+ " }",
+ "",
+ " static class X {",
+ " @Inject X() {}",
+ " }",
+ "",
+ " @Module",
+ " static class DModule {",
+ " @Provides @Named(\"slim shady\") D d(X x1, DImpl impl, X x2) { return impl; }",
+ " }",
+ "",
+ " @Component(modules = { DModule.class })",
+ " interface AComponent {",
+ " @Named(\"slim shady\") D getFoo();",
+ " C injectC(C c);",
+ " Provider<C> cProvider();",
+ " Lazy<C> lazyC();",
+ " Provider<Lazy<C>> lazyCProvider();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.TestClass.A cannot be provided without an @Provides-annotated method.",
+ " test.TestClass.A is injected at",
+ " test.TestClass.B(a)",
+ " test.TestClass.B is injected at",
+ " test.TestClass.C.b",
+ " test.TestClass.C is injected at",
+ " test.TestClass.AComponent.injectC(test.TestClass.C)",
+ "The following other entry points also depend on it:",
+ " test.TestClass.AComponent.getFoo()",
+ " test.TestClass.AComponent.cProvider()",
+ " test.TestClass.AComponent.lazyC()",
+ " test.TestClass.AComponent.lazyCProvider()"))
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test
+ public void bindsMethodAppearsInTrace() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "TestComponent",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " TestInterface testInterface();",
+ "}");
+ JavaFileObject interfaceFile =
+ JavaFileObjects.forSourceLines("TestInterface", "interface TestInterface {}");
+ JavaFileObject implementationFile =
+ JavaFileObjects.forSourceLines(
+ "TestImplementation",
+ "import javax.inject.Inject;",
+ "",
+ "final class TestImplementation implements TestInterface {",
+ " @Inject TestImplementation(String missingBinding) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "TestModule",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds abstract TestInterface bindTestInterface(TestImplementation implementation);",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(component, module, interfaceFile, implementationFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.lang.String cannot be provided without an @Inject constructor or an "
+ + "@Provides-annotated method.",
+ " java.lang.String is injected at",
+ " TestImplementation(missingBinding)",
+ " TestImplementation is injected at",
+ " TestModule.bindTestInterface(implementation)",
+ " TestInterface is provided at",
+ " TestComponent.testInterface()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test public void resolvedParametersInDependencyTrace() {
+ JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class Generic<T> {",
+ " @Inject Generic(T t) {}",
+ "}");
+ JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import java.util.List;",
+ "",
+ "final class TestClass {",
+ " @Inject TestClass(List list) {}",
+ "}");
+ JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class UsesTest {",
+ " @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " UsesTest usesTest();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.util.List cannot be provided without an @Provides-annotated method.",
+ " java.util.List is injected at",
+ " test.TestClass(list)",
+ " test.TestClass is injected at",
+ " test.Generic(t)",
+ " test.Generic<test.TestClass> is injected at",
+ " test.UsesTest(genericTestClass)",
+ " test.UsesTest is provided at",
+ " test.TestComponent.usesTest()"));
+ }
+
+ @Test public void resolvedVariablesInDependencyTrace() {
+ JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class Generic<T> {",
+ " @Inject T t;",
+ " @Inject Generic() {}",
+ "}");
+ JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import java.util.List;",
+ "",
+ "final class TestClass {",
+ " @Inject TestClass(List list) {}",
+ "}");
+ JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class UsesTest {",
+ " @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " UsesTest usesTest();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.util.List cannot be provided without an @Provides-annotated method.",
+ " java.util.List is injected at",
+ " test.TestClass(list)",
+ " test.TestClass is injected at",
+ " test.Generic.t",
+ " test.Generic<test.TestClass> is injected at",
+ " test.UsesTest(genericTestClass)",
+ " test.UsesTest is provided at",
+ " test.TestComponent.usesTest()"));
+ }
+
+ @Test
+ public void bindingUsedOnlyInSubcomponentDependsOnBindingOnlyInSubcomponent() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "Parent",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "ParentModule",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides static Object needsString(String string) {",
+ " return \"needs string: \" + string;",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "Child",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface Child {",
+ " String string();",
+ " Object needsString();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "ChildModule",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ChildModule {",
+ " @Provides static String string() {",
+ " return \"child string\";",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContainingMatch(
+ "(?s)\\Qjava.lang.String cannot be provided\\E.*\\QChild.needsString()\\E")
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+
+ @Test
+ public void multibindingContributionBetweenAncestorComponentAndEntrypointComponent() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "Parent",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "Child",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface Child {",
+ " Grandchild grandchild();",
+ "}");
+ JavaFileObject grandchild =
+ JavaFileObjects.forSourceLines(
+ "Grandchild",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Grandchild {",
+ " Object object();",
+ "}");
+
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "ParentModule",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides static Object dependsOnSet(Set<String> strings) {",
+ " return \"needs strings: \" + strings;",
+ " }",
+ "",
+ " @Provides @IntoSet static String contributesToSet() {",
+ " return \"parent string\";",
+ " }",
+ "",
+ " @Provides int missingDependency(double dub) {",
+ " return 4;",
+ " }",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "ChildModule",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "class ChildModule {",
+ " @Provides @IntoSet static String contributesToSet(int i) {",
+ " return \"\" + i;",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().compile(parent, parentModule, child, childModule, grandchild);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContainingMatch(
+ "(?s)\\Qjava.lang.Double cannot be provided\\E.*"
+ + "\\QGrandchild.object() [Parent → Child → Grandchild]\\E$")
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+
+ @Test
+ public void manyDependencies() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Object object();",
+ " String string();",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Binds abstract Object object(NotBound notBound);",
+ "",
+ " @Provides static String string(NotBound notBound, Object object) {",
+ " return notBound.toString();",
+ " }",
+ "}");
+ JavaFileObject notBound =
+ JavaFileObjects.forSourceLines(
+ "test.NotBound", //
+ "package test;",
+ "",
+ "interface NotBound {}");
+ Compilation compilation = daggerCompiler().compile(component, module, notBound);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "[Dagger/MissingBinding] "
+ + "test.NotBound cannot be provided without an @Provides-annotated method.",
+ " test.NotBound is injected at",
+ " test.TestModule.object(notBound)",
+ " java.lang.Object is provided at",
+ " test.TestComponent.object()",
+ "It is also requested at:",
+ " test.TestModule.string(notBound, …)",
+ "The following other entry points also depend on it:",
+ " test.TestComponent.string()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ @Test
+ public void tooManyRequests() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class Foo {",
+ " @Inject Foo(",
+ " String one,",
+ " String two,",
+ " String three,",
+ " String four,",
+ " String five,",
+ " String six,",
+ " String seven,",
+ " String eight,",
+ " String nine,",
+ " String ten,",
+ " String eleven,",
+ " String twelve,",
+ " String thirteen) {",
+ " }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " String string();",
+ " Foo foo();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(foo, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "[Dagger/MissingBinding] java.lang.String cannot be provided without an @Inject "
+ + "constructor or an @Provides-annotated method.",
+ " java.lang.String is provided at",
+ " test.TestComponent.string()",
+ "It is also requested at:",
+ " test.Foo(one, …)",
+ " test.Foo(…, two, …)",
+ " test.Foo(…, three, …)",
+ " test.Foo(…, four, …)",
+ " test.Foo(…, five, …)",
+ " test.Foo(…, six, …)",
+ " test.Foo(…, seven, …)",
+ " test.Foo(…, eight, …)",
+ " test.Foo(…, nine, …)",
+ " test.Foo(…, ten, …)",
+ " and 3 others"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void tooManyEntryPoints() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " String string1();",
+ " String string2();",
+ " String string3();",
+ " String string4();",
+ " String string5();",
+ " String string6();",
+ " String string7();",
+ " String string8();",
+ " String string9();",
+ " String string10();",
+ " String string11();",
+ " String string12();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "[Dagger/MissingBinding] java.lang.String cannot be provided without an @Inject "
+ + "constructor or an @Provides-annotated method.",
+ " java.lang.String is provided at",
+ " test.TestComponent.string1()",
+ "The following other entry points also depend on it:",
+ " test.TestComponent.string2()",
+ " test.TestComponent.string3()",
+ " test.TestComponent.string4()",
+ " test.TestComponent.string5()",
+ " test.TestComponent.string6()",
+ " test.TestComponent.string7()",
+ " test.TestComponent.string8()",
+ " test.TestComponent.string9()",
+ " test.TestComponent.string10()",
+ " test.TestComponent.string11()",
+ " and 1 other"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ModelTest.java b/javatests/dagger/internal/codegen/ModelTest.java
new file mode 100644
index 0000000..ee4cf47
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ModelTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static dagger.model.testing.BindingGraphSubject.assertThat;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.model.BindingGraph;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ModelTest {
+
+ @Test
+ public void cycleTest() {
+ JavaFileObject a =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(B b) {}",
+ "}");
+ JavaFileObject b =
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class B {",
+ " @Inject B(Provider<A> a) {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+
+ BindingGraphCapturer capturer = new BindingGraphCapturer();
+ Compilation compilation =
+ javac().withProcessors(ComponentProcessor.forTesting(capturer)).compile(a, b, component);
+ assertThat(compilation).succeeded();
+ BindingGraph bindingGraph = capturer.bindingGraphs().get("test.TestComponent");
+ assertThat(bindingGraph).bindingWithKey("test.A").dependsOnBindingWithKey("test.B");
+ assertThat(bindingGraph).bindingWithKey("test.B").dependsOnBindingWithKey("test.A");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
new file mode 100644
index 0000000..50c41ed
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
@@ -0,0 +1,1552 @@
+
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.NPE_FROM_PROVIDES_METHOD;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ModuleFactoryGeneratorTest {
+
+ private static final JavaFileObject NULLABLE =
+ JavaFileObjects.forSourceLines(
+ "test.Nullable", "package test;", "public @interface Nullable {}");
+
+ // TODO(gak): add tests for invalid combinations of scope and qualifier annotations like we have
+ // for @Inject
+
+ @Test public void providesMethodNotInModule() {
+ assertThatMethodInUnannotatedClass("@Provides String provideString() { return null; }")
+ .hasError("@Provides methods can only be present within a @Module or @ProducerModule");
+ }
+
+ @Test public void providesMethodAbstract() {
+ assertThatModuleMethod("@Provides abstract String abstractMethod();")
+ .hasError("@Provides methods cannot be abstract");
+ }
+
+ @Test public void providesMethodPrivate() {
+ assertThatModuleMethod("@Provides private String privateMethod() { return null; }")
+ .hasError("@Provides methods cannot be private");
+ }
+
+ @Test public void providesMethodReturnVoid() {
+ assertThatModuleMethod("@Provides void voidMethod() {}")
+ .hasError("@Provides methods must return a value (not void)");
+ }
+
+ @Test
+ public void providesMethodReturnsProvider() {
+ assertThatModuleMethod("@Provides Provider<String> provideProvider() {}")
+ .hasError("@Provides methods must not return framework types");
+ }
+
+ @Test
+ public void providesMethodReturnsLazy() {
+ assertThatModuleMethod("@Provides Lazy<String> provideLazy() {}")
+ .hasError("@Provides methods must not return framework types");
+ }
+
+ @Test
+ public void providesMethodReturnsMembersInjector() {
+ assertThatModuleMethod("@Provides MembersInjector<String> provideMembersInjector() {}")
+ .hasError("@Provides methods must not return framework types");
+ }
+
+ @Test
+ public void providesMethodReturnsProducer() {
+ assertThatModuleMethod("@Provides Producer<String> provideProducer() {}")
+ .hasError("@Provides methods must not return framework types");
+ }
+
+ @Test
+ public void providesMethodReturnsProduced() {
+ assertThatModuleMethod("@Provides Produced<String> provideProduced() {}")
+ .hasError("@Provides methods must not return framework types");
+ }
+
+ @Test public void providesMethodWithTypeParameter() {
+ assertThatModuleMethod("@Provides <T> String typeParameter() { return null; }")
+ .hasError("@Provides methods may not have type parameters");
+ }
+
+ @Test public void providesMethodSetValuesWildcard() {
+ assertThatModuleMethod("@Provides @ElementsIntoSet Set<?> provideWildcard() { return null; }")
+ .hasError(
+ "@Provides methods must return a primitive, an array, a type variable, "
+ + "or a declared type");
+ }
+
+ @Test public void providesMethodSetValuesRawSet() {
+ assertThatModuleMethod("@Provides @ElementsIntoSet Set provideSomething() { return null; }")
+ .hasError("@Provides methods annotated with @ElementsIntoSet cannot return a raw Set");
+ }
+
+ @Test public void providesMethodSetValuesNotASet() {
+ assertThatModuleMethod(
+ "@Provides @ElementsIntoSet List<String> provideStrings() { return null; }")
+ .hasError("@Provides methods annotated with @ElementsIntoSet must return a Set");
+ }
+
+ @Test public void modulesWithTypeParamsMustBeAbstract() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "final class TestModule<A> {}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Modules with type parameters must be abstract")
+ .inFile(moduleFile)
+ .onLine(6);
+ }
+
+ @Test public void provideOverriddenByNoProvide() {
+ JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class Parent {",
+ " @Provides String foo() { return null; }",
+ "}");
+ assertThatModuleMethod("String foo() { return null; }")
+ .withDeclaration("@Module class %s extends Parent { %s }")
+ .withAdditionalSources(parent)
+ .hasError(
+ "Binding methods may not be overridden in modules. Overrides: "
+ + "@Provides String test.Parent.foo()");
+ }
+
+ @Test public void provideOverriddenByProvide() {
+ JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class Parent {",
+ " @Provides String foo() { return null; }",
+ "}");
+ assertThatModuleMethod("@Provides String foo() { return null; }")
+ .withDeclaration("@Module class %s extends Parent { %s }")
+ .withAdditionalSources(parent)
+ .hasError(
+ "Binding methods may not override another method. Overrides: "
+ + "@Provides String test.Parent.foo()");
+ }
+
+ @Test public void providesOverridesNonProvides() {
+ JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "class Parent {",
+ " String foo() { return null; }",
+ "}");
+ assertThatModuleMethod("@Provides String foo() { return null; }")
+ .withDeclaration("@Module class %s extends Parent { %s }")
+ .withAdditionalSources(parent)
+ .hasError(
+ "Binding methods may not override another method. Overrides: "
+ + "String test.Parent.foo()");
+ }
+
+ @Test public void validatesIncludedModules() {
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = Void.class)",
+ "class TestModule {}");
+
+ Compilation compilation = daggerCompiler().compile(module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.lang.Void is listed as a module, but is not annotated with @Module");
+ }
+
+ @Test public void singleProvidesMethodNoArgs() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String provideString() {",
+ " return \"\";",
+ " }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProvideStringFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProvideStringFactory implements Factory<String> {",
+ " private final TestModule module;",
+ "",
+ " public TestModule_ProvideStringFactory(TestModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override public String get() {",
+ " return provideString(module);",
+ " }",
+ "",
+ " public static TestModule_ProvideStringFactory create(TestModule module) {",
+ " return new TestModule_ProvideStringFactory(module);",
+ " }",
+ "",
+ " public static String provideString(TestModule instance) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideString(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(moduleFile)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(factoryFile);
+ }
+
+ @Test public void singleProvidesMethodNoArgs_disableNullable() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String provideString() {",
+ " return \"\";",
+ " }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProvideStringFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProvideStringFactory implements Factory<String> {",
+ " private final TestModule module;",
+ "",
+ " public TestModule_ProvideStringFactory(TestModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override public String get() {",
+ " return provideString(module);",
+ " }",
+ "",
+ " public static TestModule_ProvideStringFactory create(TestModule module) {",
+ " return new TestModule_ProvideStringFactory(module);",
+ " }",
+ "",
+ " public static String provideString(TestModule instance) {",
+ " return instance.provideString();",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(moduleFile)
+ .withCompilerOptions("-Adagger.nullableValidation=WARNING")
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(factoryFile);
+ }
+
+ @Test public void nullableProvides() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides @Nullable String provideString() { return null; }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProvideStringFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProvideStringFactory implements Factory<String> {",
+ " private final TestModule module;",
+ "",
+ " public TestModule_ProvideStringFactory(TestModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override",
+ " @Nullable",
+ " public String get() {",
+ " return provideString(module);",
+ " }",
+ "",
+ " public static TestModule_ProvideStringFactory create(TestModule module) {",
+ " return new TestModule_ProvideStringFactory(module);",
+ " }",
+ "",
+ " @Nullable",
+ " public static String provideString(TestModule instance) {",
+ " return instance.provideString();",
+ " }",
+ "}");
+ assertAbout(javaSources()).that(ImmutableList.of(moduleFile, NULLABLE))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(factoryFile);
+ }
+
+ @Test public void multipleProvidesMethods() {
+ JavaFileObject classXFile = JavaFileObjects.forSourceLines("test.X",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class X {",
+ " @Inject public String s;",
+ "}");
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.Arrays;",
+ "import java.util.List;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides List<Object> provideObjects(",
+ " @QualifierA Object a, @QualifierB Object b, MembersInjector<X> xInjector) {",
+ " return Arrays.asList(a, b);",
+ " }",
+ "",
+ " @Provides @QualifierA Object provideAObject() {",
+ " return new Object();",
+ " }",
+ "",
+ " @Provides @QualifierB Object provideBObject() {",
+ " return new Object();",
+ " }",
+ "}");
+ JavaFileObject listFactoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProvideObjectsFactory",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ "import java.util.List;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProvideObjectsFactory",
+ " implements Factory<List<Object>> {",
+ " private final TestModule module;",
+ " private final Provider<Object> aProvider;",
+ " private final Provider<Object> bProvider;",
+ " private final Provider<MembersInjector<X>> xInjectorProvider;",
+ "",
+ " public TestModule_ProvideObjectsFactory(",
+ " TestModule module,",
+ " Provider<Object> aProvider,",
+ " Provider<Object> bProvider,",
+ " Provider<MembersInjector<X>> xInjectorProvider) {",
+ " this.module = module;",
+ " this.aProvider = aProvider;",
+ " this.bProvider = bProvider;",
+ " this.xInjectorProvider = xInjectorProvider;",
+ " }",
+ "",
+ " @Override public List<Object> get() {",
+ " return provideObjects(",
+ " module, aProvider.get(), bProvider.get(), xInjectorProvider.get());",
+ " }",
+ "",
+ " public static TestModule_ProvideObjectsFactory create(",
+ " TestModule module,",
+ " Provider<Object> aProvider,",
+ " Provider<Object> bProvider,",
+ " Provider<MembersInjector<X>> xInjectorProvider) {",
+ " return new TestModule_ProvideObjectsFactory(",
+ " module, aProvider, bProvider, xInjectorProvider);",
+ " }",
+ "",
+ " public static List<Object> provideObjects(",
+ " TestModule instance, Object a, Object b, MembersInjector<X> xInjector) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideObjects(a, b, xInjector), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ assertAbout(javaSources()).that(
+ ImmutableList.of(classXFile, moduleFile, QUALIFIER_A, QUALIFIER_B))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(listFactoryFile);
+ }
+
+ @Test public void providesSetElement() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import java.util.logging.Logger;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides @IntoSet String provideString() {",
+ " return \"\";",
+ " }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProvideStringFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProvideStringFactory implements Factory<String> {",
+ " private final TestModule module;",
+ "",
+ " public TestModule_ProvideStringFactory(TestModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override public String get() {",
+ " return provideString(module);",
+ " }",
+ "",
+ " public static TestModule_ProvideStringFactory create(TestModule module) {",
+ " return new TestModule_ProvideStringFactory(module);",
+ " }",
+ "",
+ " public static String provideString(TestModule instance) {",
+ " return Preconditions.checkNotNull(instance.provideString(), "
+ + NPE_FROM_PROVIDES_METHOD
+ + ");",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(moduleFile)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(factoryFile);
+ }
+
+ @Test public void providesSetElementWildcard() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import java.util.logging.Logger;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import java.util.ArrayList;",
+ "import java.util.List;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides @IntoSet List<List<?>> provideWildcardList() {",
+ " return new ArrayList<>();",
+ " }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProvideWildcardListFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ "import java.util.List;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProvideWildcardListFactory implements "
+ + "Factory<List<List<?>>> {",
+ " private final TestModule module;",
+ "",
+ " public TestModule_ProvideWildcardListFactory(TestModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override public List<List<?>> get() {",
+ " return provideWildcardList(module);",
+ " }",
+ "",
+ " public static TestModule_ProvideWildcardListFactory create(TestModule module) {",
+ " return new TestModule_ProvideWildcardListFactory(module);",
+ " }",
+ "",
+ " public static List<List<?>> provideWildcardList(TestModule instance) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideWildcardList(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(moduleFile)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(factoryFile);
+ }
+
+ @Test public void providesSetValues() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides @ElementsIntoSet Set<String> provideStrings() {",
+ " return null;",
+ " }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProvideStringsFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProvideStringsFactory implements Factory<Set<String>> {",
+ " private final TestModule module;",
+ "",
+ " public TestModule_ProvideStringsFactory(TestModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override public Set<String> get() {",
+ " return provideStrings(module);",
+ " }",
+ "",
+ " public static TestModule_ProvideStringsFactory create(TestModule module) {",
+ " return new TestModule_ProvideStringsFactory(module);",
+ " }",
+ "",
+ " public static Set<String> provideStrings(TestModule instance) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideStrings(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ assertAbout(javaSource()).that(moduleFile)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesSources(factoryFile);
+ }
+
+ @Test public void multipleProvidesMethodsWithSameName() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides Object provide(int i) {",
+ " return i;",
+ " }",
+ "",
+ " @Provides String provide() {",
+ " return \"\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Cannot have more than one binding method with the same name in a single module")
+ .inFile(moduleFile)
+ .onLine(8);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Cannot have more than one binding method with the same name in a single module")
+ .inFile(moduleFile)
+ .onLine(12);
+ }
+
+ @Test
+ public void providesMethodThrowsChecked() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides int i() throws Exception {",
+ " return 0;",
+ " }",
+ "",
+ " @Provides String s() throws Throwable {",
+ " return \"\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Provides methods may only throw unchecked exceptions")
+ .inFile(moduleFile)
+ .onLine(8);
+ assertThat(compilation)
+ .hadErrorContaining("@Provides methods may only throw unchecked exceptions")
+ .inFile(moduleFile)
+ .onLine(12);
+ }
+
+ @Test
+ public void providedTypes() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.io.Closeable;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String string() {",
+ " return null;",
+ " }",
+ "",
+ " @Provides Set<String> strings() {",
+ " return null;",
+ " }",
+ "",
+ " @Provides Set<? extends Closeable> closeables() {",
+ " return null;",
+ " }",
+ "",
+ " @Provides String[] stringArray() {",
+ " return null;",
+ " }",
+ "",
+ " @Provides int integer() {",
+ " return 0;",
+ " }",
+ "",
+ " @Provides int[] integers() {",
+ " return null;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void privateModule() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "final class Enclosing {",
+ " @Module private static final class PrivateModule {",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Modules cannot be private")
+ .inFile(moduleFile)
+ .onLine(6);
+ }
+
+ @Test
+ public void enclosedInPrivateModule() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "final class Enclosing {",
+ " private static final class PrivateEnclosing {",
+ " @Module static final class TestModule {",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Modules cannot be enclosed in private types")
+ .inFile(moduleFile)
+ .onLine(7);
+ }
+
+ @Test
+ public void publicModuleNonPublicIncludes() {
+ JavaFileObject publicModuleFile = JavaFileObjects.forSourceLines("test.PublicModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = {",
+ " BadNonPublicModule.class, OtherPublicModule.class, OkNonPublicModule.class",
+ "})",
+ "public final class PublicModule {",
+ "}");
+ JavaFileObject badNonPublicModuleFile =
+ JavaFileObjects.forSourceLines(
+ "test.BadNonPublicModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class BadNonPublicModule {",
+ " @Provides",
+ " int provideInt() {",
+ " return 42;",
+ " }",
+ "}");
+ JavaFileObject okNonPublicModuleFile = JavaFileObjects.forSourceLines("test.OkNonPublicModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class OkNonPublicModule {",
+ " @Provides",
+ " static String provideString() {",
+ " return \"foo\";",
+ " }",
+ "}");
+ JavaFileObject otherPublicModuleFile = JavaFileObjects.forSourceLines("test.OtherPublicModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "public final class OtherPublicModule {",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ publicModuleFile,
+ badNonPublicModuleFile,
+ okNonPublicModuleFile,
+ otherPublicModuleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "This module is public, but it includes non-public (or effectively non-public) modules "
+ + "(test.BadNonPublicModule) that have non-static, non-abstract binding methods. "
+ + "Either reduce the visibility of this module, make the included modules public, "
+ + "or make all of the binding methods on the included modules abstract or static.")
+ .inFile(publicModuleFile)
+ .onLine(8);
+ }
+
+ @Test
+ public void genericSubclassedModule() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.List;",
+ "import java.util.ArrayList;",
+ "",
+ "@Module",
+ "abstract class ParentModule<A extends CharSequence,",
+ " B,",
+ " C extends Number & Comparable<C>> {",
+ " @Provides List<B> provideListB(B b) {",
+ " List<B> list = new ArrayList<B>();",
+ " list.add(b);",
+ " return list;",
+ " }",
+ "",
+ " @Provides @IntoSet B provideBElement(B b) {",
+ " return b;",
+ " }",
+ "",
+ " @Provides @IntoMap @StringKey(\"b\") B provideBEntry(B b) {",
+ " return b;",
+ " }",
+ "}");
+ JavaFileObject numberChild = JavaFileObjects.forSourceLines("test.ChildNumberModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ChildNumberModule extends ParentModule<String, Number, Double> {",
+ " @Provides Number provideNumber() { return 1; }",
+ "}");
+ JavaFileObject integerChild = JavaFileObjects.forSourceLines("test.ChildIntegerModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ChildIntegerModule extends ParentModule<StringBuilder, Integer, Float> {",
+ " @Provides Integer provideInteger() { return 2; }",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.C",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.List;",
+ "",
+ "@Component(modules={ChildNumberModule.class, ChildIntegerModule.class})",
+ "interface C {",
+ " List<Number> numberList();",
+ " List<Integer> integerList();",
+ "}");
+ JavaFileObject listBFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule_ProvideListBFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ "import java.util.List;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ParentModule_ProvideListBFactory<A extends CharSequence,",
+ " B, C extends Number & Comparable<C>> implements Factory<List<B>> {",
+ " private final ParentModule<A, B, C> module;",
+ " private final Provider<B> bProvider;",
+ "",
+ " public ParentModule_ProvideListBFactory(",
+ " ParentModule<A, B, C> module, Provider<B> bProvider) {",
+ " this.module = module;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " @Override",
+ " public List<B> get() { ",
+ " return provideListB(module, bProvider.get());",
+ " }",
+ "",
+ " public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
+ " ParentModule_ProvideListBFactory<A, B, C> create(",
+ " ParentModule<A, B, C> module, Provider<B> bProvider) {",
+ " return new ParentModule_ProvideListBFactory<A, B, C>(module, bProvider);",
+ " }",
+ "",
+ " public static <A extends CharSequence, B, C extends Number & Comparable<C>> List<B>",
+ " provideListB(ParentModule<A, B, C> instance, B b) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideListB(b), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ JavaFileObject bElementFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule_ProvideBElementFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ParentModule_ProvideBElementFactory<A extends CharSequence,",
+ " B, C extends Number & Comparable<C>> implements Factory<B> {",
+ " private final ParentModule<A, B, C> module;",
+ " private final Provider<B> bProvider;",
+ "",
+ " public ParentModule_ProvideBElementFactory(",
+ " ParentModule<A, B, C> module, Provider<B> bProvider) {",
+ " this.module = module;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " @Override",
+ " public B get() { ",
+ " return provideBElement(module, bProvider.get());",
+ " }",
+ "",
+ " public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
+ " ParentModule_ProvideBElementFactory<A, B, C> create(",
+ " ParentModule<A, B, C> module, Provider<B> bProvider) {",
+ " return new ParentModule_ProvideBElementFactory<A, B, C>(module, bProvider);",
+ " }",
+ "",
+ " public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
+ " B provideBElement(",
+ " ParentModule<A, B, C> instance, B b) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideBElement(b), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ JavaFileObject bEntryFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule_ProvideBEntryFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ParentModule_ProvideBEntryFactory<A extends CharSequence,",
+ " B, C extends Number & Comparable<C>> implements Factory<B>> {",
+ " private final ParentModule<A, B, C> module;",
+ " private final Provider<B> bProvider;",
+ "",
+ " public ParentModule_ProvideBEntryFactory(",
+ " ParentModule<A, B, C> module, Provider<B> bProvider) {",
+ " this.module = module;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " @Override",
+ " public B get() { ",
+ " return provideBEntry(module, bProvider.get());",
+ " }",
+ "",
+ " public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
+ " ParentModule_ProvideBEntryFactory<A, B, C> create(",
+ " ParentModule<A, B, C> module, Provider<B> bProvider) {",
+ " return new ParentModule_ProvideBEntryFactory<A, B, C>(module, bProvider);",
+ " }",
+ "",
+ " public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
+ " B provideBEntry(",
+ " ParentModule<A, B, C> instance, B b) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideBEntry(b), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ JavaFileObject numberFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ChildNumberModule_ProvideNumberFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ChildNumberModule_ProvideNumberFactory",
+ " implements Factory<Number> {",
+ " private final ChildNumberModule module;",
+ "",
+ " public ChildNumberModule_ProvideNumberFactory(ChildNumberModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override",
+ " public Number get() { ",
+ " return provideNumber(module);",
+ " }",
+ "",
+ " public static ChildNumberModule_ProvideNumberFactory create(",
+ " ChildNumberModule module) {",
+ " return new ChildNumberModule_ProvideNumberFactory(module);",
+ " }",
+ "",
+ " public static Number provideNumber(ChildNumberModule instance) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideNumber(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ JavaFileObject integerFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ChildIntegerModule_ProvideIntegerFactory",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ChildIntegerModule_ProvideIntegerFactory",
+ " implements Factory<Integer> {",
+ " private final ChildIntegerModule module;",
+ "",
+ " public ChildIntegerModule_ProvideIntegerFactory(ChildIntegerModule module) {",
+ " this.module = module;",
+ " }",
+ "",
+ " @Override",
+ " public Integer get() { ",
+ " return provideInteger(module);",
+ " }",
+ "",
+ " public static ChildIntegerModule_ProvideIntegerFactory create(",
+ " ChildIntegerModule module) {",
+ " return new ChildIntegerModule_ProvideIntegerFactory(module);",
+ " }",
+ "",
+ " public static Integer provideInteger(ChildIntegerModule instance) {",
+ " return Preconditions.checkNotNull(",
+ " instance.provideInteger(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(parent, numberChild, integerChild, component))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(
+ listBFactory, bElementFactory, bEntryFactory, numberFactory, integerFactory);
+ }
+
+ @Test public void parameterizedModuleWithStaticProvidesMethodOfGenericType() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.ParameterizedModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.List;",
+ "import java.util.ArrayList;",
+ "import java.util.Map;",
+ "import java.util.HashMap;",
+ "",
+ "@Module abstract class ParameterizedModule<T> {",
+ " @Provides List<T> provideListT() {",
+ " return new ArrayList<>();",
+ " }",
+ "",
+ " @Provides static Map<String, Number> provideMapStringNumber() {",
+ " return new HashMap<>();",
+ " }",
+ "",
+ " @Provides static Object provideNonGenericType() {",
+ " return new Object();",
+ " }",
+ "",
+ " @Provides static String provideNonGenericTypeWithDeps(Object o) {",
+ " return o.toString();",
+ " }",
+ "}");
+
+ JavaFileObject provideMapStringNumberFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ParameterizedModule_ProvideMapStringNumberFactory;",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ "import java.util.Map;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ParameterizedModule_ProvideMapStringNumberFactory",
+ " implements Factory<Map<String, Number>> {",
+ " private static final ParameterizedModule_ProvideMapStringNumberFactory INSTANCE =",
+ " new ParameterizedModule_ProvideMapStringNumberFactory();",
+ "",
+ " @Override",
+ " public Map<String, Number> get() {",
+ " return provideMapStringNumber();",
+ " }",
+ "",
+ " public static ParameterizedModule_ProvideMapStringNumberFactory create() {",
+ " return INSTANCE;",
+ " }",
+ "",
+ " public static Map<String, Number> provideMapStringNumber() {",
+ " return Preconditions.checkNotNull(ParameterizedModule.provideMapStringNumber(),",
+ " " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+
+ JavaFileObject provideNonGenericTypeFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ParameterizedModule_ProvideNonGenericTypeFactory;",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ParameterizedModule_ProvideNonGenericTypeFactory",
+ " implements Factory<Object> {",
+ " private static final ParameterizedModule_ProvideNonGenericTypeFactory INSTANCE = ",
+ " new ParameterizedModule_ProvideNonGenericTypeFactory();",
+ "",
+ " @Override",
+ " public Object get() {",
+ " return provideNonGenericType();",
+ " }",
+ "",
+ " public static ParameterizedModule_ProvideNonGenericTypeFactory create() {",
+ " return INSTANCE;",
+ " }",
+ "",
+ " public static Object provideNonGenericType() {",
+ " return Preconditions.checkNotNull(ParameterizedModule.provideNonGenericType(),",
+ " " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+
+ JavaFileObject provideNonGenericTypeWithDepsFactory =
+ JavaFileObjects.forSourceLines(
+ "test.ParameterizedModule_ProvideNonGenericTypeWithDepsFactory;",
+ "package test;",
+ "",
+ "import dagger.internal.Factory;",
+ "import dagger.internal.Preconditions;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class ParameterizedModule_ProvideNonGenericTypeWithDepsFactory",
+ " implements Factory<String> {",
+ " private final Provider<Object> oProvider;",
+ "",
+ " public ParameterizedModule_ProvideNonGenericTypeWithDepsFactory(",
+ " Provider<Object> oProvider) {",
+ " this.oProvider = oProvider;",
+ " }",
+ "",
+ " @Override",
+ " public String get() {",
+ " return provideNonGenericTypeWithDeps(oProvider.get());",
+ " }",
+ "",
+ " public static ParameterizedModule_ProvideNonGenericTypeWithDepsFactory create(",
+ " Provider<Object> oProvider) {",
+ " return new ParameterizedModule_ProvideNonGenericTypeWithDepsFactory(oProvider);",
+ " }",
+ "",
+ " public static String provideNonGenericTypeWithDeps(Object o) {",
+ " return Preconditions.checkNotNull(",
+ " ParameterizedModule.provideNonGenericTypeWithDeps(o),",
+ " " + NPE_FROM_PROVIDES_METHOD + ");",
+ " }",
+ "}");
+
+ assertAbout(javaSource())
+ .that(moduleFile)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(
+ provideMapStringNumberFactory,
+ provideNonGenericTypeFactory,
+ provideNonGenericTypeWithDepsFactory);
+ }
+
+ private static final JavaFileObject QUALIFIER_A =
+ JavaFileObjects.forSourceLines(
+ "test.QualifierA",
+ "package test;",
+ "",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Qualifier @interface QualifierA {}");
+
+ private static final JavaFileObject QUALIFIER_B =
+ JavaFileObjects.forSourceLines(
+ "test.QualifierB",
+ "package test;",
+ "",
+ "import javax.inject.Qualifier;",
+ "",
+ "@Qualifier @interface QualifierB {}");
+
+ @Test
+ public void providesMethodMultipleQualifiersOnMethod() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides @QualifierA @QualifierB String provideString() {",
+ " return \"foo\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile, QUALIFIER_A, QUALIFIER_B);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("may not use more than one @Qualifier");
+ }
+
+ @Test
+ public void providesMethodMultipleQualifiersOnParameter() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides static String provideString(@QualifierA @QualifierB Object object) {",
+ " return \"foo\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile, QUALIFIER_A, QUALIFIER_B);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("may not use more than one @Qualifier");
+ }
+
+ @Test
+ public void providesMethodWildcardDependency() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides static String provideString(Provider<? extends Number> numberProvider) {",
+ " return \"foo\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile, QUALIFIER_A, QUALIFIER_B);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or Produced<T> "
+ + "when T is a wildcard type such as ? extends java.lang.Number");
+ }
+
+ private static final JavaFileObject SCOPE_A =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeA",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeA {}");
+
+ private static final JavaFileObject SCOPE_B =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeB",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeB {}");
+
+ @Test
+ public void providesMethodMultipleScopes() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides",
+ " @ScopeA",
+ " @ScopeB",
+ " String provideString() {",
+ " return \"foo\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile, SCOPE_A, SCOPE_B);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("cannot use more than one @Scope")
+ .inFile(moduleFile)
+ .onLineContaining("@ScopeA");
+ assertThat(compilation)
+ .hadErrorContaining("cannot use more than one @Scope")
+ .inFile(moduleFile)
+ .onLineContaining("@ScopeB");
+ }
+
+ @Test public void providerDependsOnProduced() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.Producer;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String provideString(Producer<Integer> producer) {",
+ " return \"foo\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Producer may only be injected in @Produces methods");
+ }
+
+ @Test public void providerDependsOnProducer() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.Produced;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String provideString(Produced<Integer> produced) {",
+ " return \"foo\";",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Produced may only be injected in @Produces methods");
+ }
+
+ @Test
+ public void proxyMethodsConflictWithOtherFactoryMethods() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static int get() { return 1; }",
+ "",
+ " @Provides",
+ " static boolean create() { return true; }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(module);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.TestModule_GetFactory")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.TestModule_GetFactory",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_GetFactory implements Factory<Integer> {",
+ " @Override",
+ " public Integer get() {",
+ " return proxyGet();",
+ " }",
+ "",
+ " public static TestModule_GetFactory create() {",
+ " return INSTANCE;",
+ " }",
+ "",
+ " public static int proxyGet() {",
+ " return TestModule.get();",
+ " }",
+ "}"));
+
+ assertThat(compilation)
+ .generatedSourceFile("test.TestModule_CreateFactory")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.TestModule_CreateFactory",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_CreateFactory implements Factory<Boolean> {",
+ " @Override",
+ " public Boolean get() {",
+ " return proxyCreate();",
+ " }",
+ "",
+ " public static TestModule_CreateFactory create() {",
+ " return INSTANCE;",
+ " }",
+ "",
+ " public static boolean proxyCreate() {",
+ " return TestModule.create();",
+ " }",
+ "}"));
+ }
+
+ private static final String BINDS_METHOD = "@Binds abstract Foo bindFoo(FooImpl impl);";
+ private static final String MULTIBINDS_METHOD = "@Multibinds abstract Set<Foo> foos();";
+ private static final String STATIC_PROVIDES_METHOD =
+ "@Provides static Bar provideBar() { return new Bar(); }";
+ private static final String INSTANCE_PROVIDES_METHOD =
+ "@Provides Baz provideBaz() { return new Baz(); }";
+ private static final String SOME_ABSTRACT_METHOD = "abstract void blah();";
+
+ @Test
+ public void bindsWithInstanceProvides() {
+ Compilation compilation = compileMethodCombination(BINDS_METHOD, INSTANCE_PROVIDES_METHOD);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "A @Module may not contain both non-static and abstract binding methods");
+ }
+
+ @Test
+ public void multibindsWithInstanceProvides() {
+ Compilation compilation = compileMethodCombination(MULTIBINDS_METHOD, INSTANCE_PROVIDES_METHOD);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "A @Module may not contain both non-static and abstract binding methods");
+ }
+
+ @Test
+ public void bindsWithStaticProvides() {
+ assertThat(compileMethodCombination(BINDS_METHOD, STATIC_PROVIDES_METHOD)).succeeded();
+ }
+
+ @Test
+ public void bindsWithMultibinds() {
+ assertThat(compileMethodCombination(BINDS_METHOD, MULTIBINDS_METHOD)).succeeded();
+ }
+
+ @Test
+ public void multibindsWithStaticProvides() {
+ assertThat(compileMethodCombination(MULTIBINDS_METHOD, STATIC_PROVIDES_METHOD)).succeeded();
+ }
+
+ @Test
+ public void instanceProvidesWithAbstractMethod() {
+ assertThat(compileMethodCombination(INSTANCE_PROVIDES_METHOD, SOME_ABSTRACT_METHOD))
+ .succeeded();
+ }
+
+ private Compilation compileMethodCombination(String... methodLines) {
+ JavaFileObject fooFile =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "interface Foo {}");
+ JavaFileObject fooImplFile =
+ JavaFileObjects.forSourceLines(
+ "test.FooImpl",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class FooImpl implements Foo {",
+ " @Inject FooImpl() {}",
+ "}");
+ JavaFileObject barFile =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "final class Bar {}");
+ JavaFileObject bazFile =
+ JavaFileObjects.forSourceLines(
+ "test.Baz",
+ "package test;",
+ "",
+ "final class Baz {}");
+
+ ImmutableList<String> moduleLines =
+ new ImmutableList.Builder<String>()
+ .add(
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Set;",
+ "",
+ "@Module abstract class TestModule {")
+ .add(methodLines)
+ .add("}")
+ .build();
+
+ JavaFileObject bindsMethodAndInstanceProvidesMethodModuleFile =
+ JavaFileObjects.forSourceLines("test.TestModule", moduleLines);
+ return daggerCompiler()
+ .compile(
+ fooFile, fooImplFile, barFile, bazFile, bindsMethodAndInstanceProvidesMethodModuleFile);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ModuleValidationTest.java b/javatests/dagger/internal/codegen/ModuleValidationTest.java
new file mode 100644
index 0000000..649649a
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ModuleValidationTest.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.Module;
+import dagger.producers.ProducerModule;
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public final class ModuleValidationTest {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return Arrays.asList(new Object[][] {{ModuleType.MODULE}, {ModuleType.PRODUCER_MODULE}});
+ }
+
+ private enum ModuleType {
+ MODULE(Module.class),
+ PRODUCER_MODULE(ProducerModule.class),
+ ;
+
+ private final Class<? extends Annotation> annotation;
+
+ ModuleType(Class<? extends Annotation> annotation) {
+ this.annotation = annotation;
+ }
+
+ String annotationWithSubcomponent(String subcomponent) {
+ return String.format("@%s(subcomponents = %s)", annotation.getSimpleName(), subcomponent);
+ }
+
+ String importStatement() {
+ return String.format("import %s;", annotation.getName());
+ }
+
+ String simpleName() {
+ return annotation.getSimpleName();
+ }
+ }
+
+ private final ModuleType moduleType;
+
+ public ModuleValidationTest(ModuleType moduleType) {
+ this.moduleType = moduleType;
+ }
+
+ @Test
+ public void moduleSubcomponents_notASubcomponent() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "",
+ moduleType.annotationWithSubcomponent("NotASubcomponent.class"),
+ "class TestModule {}");
+ JavaFileObject notASubcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.NotASubcomponent", "package test;", "", "class NotASubcomponent {}");
+ Compilation compilation = daggerCompiler().compile(module, notASubcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.NotASubcomponent is not a @Subcomponent or @ProductionSubcomponent")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void moduleSubcomponents_listsSubcomponentBuilder() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "",
+ moduleType.annotationWithSubcomponent("Sub.Builder.class"),
+ "class TestModule {}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Sub {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Sub build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.Sub.Builder is a @Subcomponent.Builder. Did you mean to use test.Sub?")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void moduleSubcomponents_listsSubcomponentFactory() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "",
+ moduleType.annotationWithSubcomponent("Sub.Factory.class"),
+ "class TestModule {}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Sub {",
+ " @Subcomponent.Factory",
+ " interface Factory {",
+ " Sub creator();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.Sub.Factory is a @Subcomponent.Factory. Did you mean to use test.Sub?")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void moduleSubcomponents_listsProductionSubcomponentBuilder() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "",
+ moduleType.annotationWithSubcomponent("Sub.Builder.class"),
+ "class TestModule {}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionSubcomponent;",
+ "",
+ "@ProductionSubcomponent",
+ "interface Sub {",
+ " @ProductionSubcomponent.Builder",
+ " interface Builder {",
+ " Sub build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.Sub.Builder is a @ProductionSubcomponent.Builder. Did you mean to use test.Sub?")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void moduleSubcomponents_listsProductionSubcomponentFactory() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "",
+ moduleType.annotationWithSubcomponent("Sub.Factory.class"),
+ "class TestModule {}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionSubcomponent;",
+ "",
+ "@ProductionSubcomponent",
+ "interface Sub {",
+ " @ProductionSubcomponent.Factory",
+ " interface Factory {",
+ " Sub create();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.Sub.Factory is a @ProductionSubcomponent.Factory. Did you mean to use test.Sub?")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void moduleSubcomponents_noSubcomponentCreator() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "",
+ moduleType.annotationWithSubcomponent("NoBuilder.class"),
+ "class TestModule {}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.NoBuilder",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface NoBuilder {}");
+ Compilation compilation = daggerCompiler().compile(module, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.NoBuilder doesn't have a @Subcomponent.Builder or @Subcomponent.Factory, which "
+ + "is required when used with @"
+ + moduleType.simpleName()
+ + ".subcomponents")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void moduleSubcomponents_noProductionSubcomponentCreator() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "",
+ moduleType.annotationWithSubcomponent("NoBuilder.class"),
+ "class TestModule {}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.NoBuilder",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionSubcomponent;",
+ "",
+ "@ProductionSubcomponent",
+ "interface NoBuilder {}");
+ Compilation compilation = daggerCompiler().compile(module, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.NoBuilder doesn't have a @ProductionSubcomponent.Builder or "
+ + "@ProductionSubcomponent.Factory, which is required when used with @"
+ + moduleType.simpleName()
+ + ".subcomponents")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void moduleSubcomponentsAreTypes() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(subcomponents = int.class)",
+ "class TestModule {}");
+ Compilation compilation = daggerCompiler().compile(module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("int is not a valid subcomponent type")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void tooManyAnnotations() {
+ assertThatModuleMethod(
+ "@BindsOptionalOf @Multibinds abstract Set<Object> tooManyAnnotations();")
+ .hasError("is annotated with more than one of");
+ }
+
+ @Test
+ public void invalidIncludedModule() {
+ JavaFileObject badModule =
+ JavaFileObjects.forSourceLines(
+ "test.BadModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "abstract class BadModule {",
+ " @Binds abstract Object noParameters();",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.IncludesBadModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(includes = BadModule.class)",
+ "abstract class IncludesBadModule {}");
+ assertThat(daggerCompiler().compile(badModule, module))
+ .hadErrorContaining("test.BadModule has errors")
+ .inFile(module)
+ .onLine(5);
+ }
+
+ @Test
+ public void scopeOnModule() {
+ JavaFileObject badModule =
+ JavaFileObjects.forSourceLines(
+ "test.BadModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Module",
+ "interface BadModule {}");
+ Compilation compilation = daggerCompiler().compile(badModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Modules cannot be scoped")
+ .inFile(badModule)
+ .onLineContaining("@Singleton");
+ }
+
+ @Test
+ public void moduleIncludesSelfCycle() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ moduleType.importStatement(),
+ "import dagger.Provides;",
+ "",
+ String.format("@%s(", moduleType.simpleName()),
+ " includes = {",
+ " TestModule.class, // first",
+ " OtherModule.class,",
+ " TestModule.class, // second",
+ " }",
+ ")",
+ "class TestModule {",
+ " @Provides int i() { return 0; }",
+ "}");
+
+ JavaFileObject otherModule =
+ JavaFileObjects.forSourceLines(
+ "test.OtherModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "class OtherModule {}");
+
+ Compilation compilation = daggerCompiler().compile(module, otherModule);
+ assertThat(compilation).failed();
+ String error = String.format("@%s cannot include themselves", moduleType.simpleName());
+ assertThat(compilation).hadErrorContaining(error).inFile(module).onLineContaining("Module(");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MultibindingTest.java b/javatests/dagger/internal/codegen/MultibindingTest.java
new file mode 100644
index 0000000..2bf9494
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MultibindingTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MultibindingTest {
+
+ @Test
+ public void providesWithTwoMultibindingAnnotations_failsToCompile() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.MultibindingModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "@Module",
+ "class MultibindingModule {",
+ " @Provides @IntoSet @IntoMap Integer provideInt() { ",
+ " return 1;",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Provides methods cannot have more than one multibinding annotation")
+ .inFile(module)
+ .onLine(10);
+ }
+
+ @Test
+ public void appliedOnInvalidMethods_failsToCompile() {
+ JavaFileObject someType =
+ JavaFileObjects.forSourceLines(
+ "test.SomeType",
+ "package test;",
+ "",
+ "import java.util.Set;",
+ "import java.util.Map;",
+ "import dagger.Component;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import dagger.multibindings.IntoMap;",
+ "",
+ "interface SomeType {",
+ " @IntoSet Set<Integer> ints();",
+ " @ElementsIntoSet Set<Double> doubles();",
+ " @IntoMap Map<Integer, Double> map();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(someType);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods")
+ .inFile(someType)
+ .onLineContaining("ints();");
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods")
+ .inFile(someType)
+ .onLineContaining("doubles();");
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods")
+ .inFile(someType)
+ .onLineContaining("map();");
+ }
+
+ @Test
+ public void concreteBindingForMultibindingAlias() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.Collections;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "class TestModule {",
+ " @Provides",
+ " Map<String, Provider<String>> mapOfStringToProviderOfString() {",
+ " return Collections.emptyMap();",
+ " }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Map<String, String> mapOfStringToString();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Map<java.lang.String,java.lang.String> "
+ + "cannot be provided without an @Provides-annotated method")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void produceConcreteSet_andRequestSetOfProduced() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ "",
+ "@ProducerModule",
+ "class TestModule {",
+ " @Produces",
+ " Set<String> setOfString() {",
+ " return Collections.emptySet();",
+ " }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.BindsInstance;",
+ "import dagger.producers.Produced;",
+ "import dagger.producers.Production;",
+ "import dagger.producers.ProductionComponent;",
+ "import java.util.concurrent.Executor;",
+ "import java.util.Set;",
+ "",
+ "@ProductionComponent(modules = TestModule.class)",
+ "interface TestComponent {",
+ " ListenableFuture<Set<Produced<String>>> setOfProduced();",
+ "",
+ " @ProductionComponent.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder executor(@Production Executor executor);",
+ " TestComponent build();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Set<dagger.producers.Produced<java.lang.String>> "
+ + "cannot be provided without an @Provides- or @Produces-annotated method")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void provideExplicitSetInParent_AndMultibindingContributionInChild() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Set;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Set<String> set();",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.HashSet;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides",
+ " Set<String> set() {",
+ " return new HashSet();",
+ " }",
+ "}");
+
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface Child {",
+ " Set<String> set();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class ChildModule {",
+ " @Provides",
+ " @IntoSet",
+ " String setContribution() {",
+ " return new String();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("incompatible bindings or declarations")
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MultibindsValidationTest.java b/javatests/dagger/internal/codegen/MultibindsValidationTest.java
new file mode 100644
index 0000000..e5603a4
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MultibindsValidationTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
+
+import com.google.common.collect.ImmutableList;
+import dagger.Module;
+import dagger.producers.ProducerModule;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import javax.inject.Qualifier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MultibindsValidationTest {
+
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return ImmutableList.copyOf(new Object[][] {{Module.class}, {ProducerModule.class}});
+ }
+
+ private final String moduleDeclaration;
+
+ public MultibindsValidationTest(Class<? extends Annotation> moduleAnnotation) {
+ moduleDeclaration = "@" + moduleAnnotation.getCanonicalName() + " abstract class %s { %s }";
+ }
+
+ @Test
+ public void notWithinModule() {
+ assertThatMethodInUnannotatedClass("@Multibinds abstract Set<Object> emptySet();")
+ .hasError("@Multibinds methods can only be present within a @Module or @ProducerModule");
+ }
+
+ @Test
+ public void voidMethod() {
+ assertThatModuleMethod("@Multibinds abstract void voidMethod();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void primitiveMethod() {
+ assertThatModuleMethod("@Multibinds abstract int primitive();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void rawMap() {
+ assertThatModuleMethod("@Multibinds abstract Map rawMap();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void wildcardMap() {
+ assertThatModuleMethod("@Multibinds abstract Map<?, ?> wildcardMap();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void providerMap() {
+ assertThatModuleMethod("@Multibinds abstract Map<String, Provider<Object>> providerMap();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void producerMap() {
+ assertThatModuleMethod("@Multibinds abstract Map<String, Producer<Object>> producerMap();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void producedMap() {
+ assertThatModuleMethod("@Multibinds abstract Map<String, Produced<Object>> producedMap();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void rawSet() {
+ assertThatModuleMethod("@Multibinds abstract Set rawSet();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void wildcardSet() {
+ assertThatModuleMethod("@Multibinds abstract Set<?> wildcardSet();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void providerSet() {
+ assertThatModuleMethod("@Multibinds abstract Set<Provider<Object>> providerSet();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void producerSet() {
+ assertThatModuleMethod("@Multibinds abstract Set<Producer<Object>> producerSet();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void producedSet() {
+ assertThatModuleMethod("@Multibinds abstract Set<Produced<Object>> producedSet();")
+ .withDeclaration(moduleDeclaration)
+ .hasError("@Multibinds methods must return Map<K, V> or Set<T>");
+ }
+
+ @Test
+ public void overqualifiedSet() {
+ assertThatModuleMethod(
+ "@Multibinds @SomeQualifier @OtherQualifier "
+ + "abstract Set<Object> tooManyQualifiersSet();")
+ .withDeclaration(moduleDeclaration)
+ .importing(SomeQualifier.class, OtherQualifier.class)
+ .hasError("may not use more than one @Qualifier");
+ }
+
+ @Test
+ public void overqualifiedMap() {
+ assertThatModuleMethod(
+ "@Multibinds @SomeQualifier @OtherQualifier "
+ + "abstract Map<String, Object> tooManyQualifiersMap();")
+ .withDeclaration(moduleDeclaration)
+ .importing(SomeQualifier.class, OtherQualifier.class)
+ .hasError("may not use more than one @Qualifier");
+ }
+
+ @Test
+ public void hasParameters() {
+ assertThatModuleMethod("@Multibinds abstract Set<String> parameters(Object param);")
+ .hasError("@Multibinds methods cannot have parameters");
+ }
+
+ @Qualifier
+ public @interface SomeQualifier {}
+
+ @Qualifier
+ public @interface OtherQualifier {}
+}
diff --git a/javatests/dagger/internal/codegen/MultipleRequestTest.java b/javatests/dagger/internal/codegen/MultipleRequestTest.java
new file mode 100644
index 0000000..a5514ca
--- /dev/null
+++ b/javatests/dagger/internal/codegen/MultipleRequestTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MultipleRequestTest {
+ private static final JavaFileObject DEP_FILE = JavaFileObjects.forSourceLines("test.Dep",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Dep {",
+ " @Inject Dep() {}",
+ "}");
+
+ @Test public void multipleRequests_constructor() {
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ DEP_FILE,
+ JavaFileObjects.forSourceLines(
+ "test.ConstructorInjectsMultiple",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class ConstructorInjectsMultiple {",
+ " @Inject ConstructorInjectsMultiple(Dep d1, Dep d2) {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " ConstructorInjectsMultiple get();",
+ "}"));
+ assertThat(compilation).succeeded();
+ }
+
+ @Test public void multipleRequests_field() {
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ DEP_FILE,
+ JavaFileObjects.forSourceLines(
+ "test.FieldInjectsMultiple",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class FieldInjectsMultiple {",
+ " @Inject Dep d1;",
+ " @Inject Dep d2;",
+ " @Inject FieldInjectsMultiple() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface SimpleComponent {",
+ " FieldInjectsMultiple get();",
+ "}"));
+ assertThat(compilation).succeeded();
+ }
+
+ @Test public void multipleRequests_providesMethod() {
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ DEP_FILE,
+ JavaFileObjects.forSourceLines(
+ "test.FieldInjectsMultiple",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class SimpleModule {",
+ " @Provides Object provide(Dep d1, Dep d2) {",
+ " return null;",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = SimpleModule.class)",
+ "interface SimpleComponent {",
+ " Object get();",
+ "}"));
+ assertThat(compilation).succeeded();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/NullableBindingValidationTest.java b/javatests/dagger/internal/codegen/NullableBindingValidationTest.java
new file mode 100644
index 0000000..24d5636
--- /dev/null
+++ b/javatests/dagger/internal/codegen/NullableBindingValidationTest.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.NullableBindingValidator.nullableToNonNullable;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NullableBindingValidationTest {
+ private static final JavaFileObject NULLABLE =
+ JavaFileObjects.forSourceLines(
+ "test.Nullable", // force one-string-per-line format
+ "package test;",
+ "",
+ "public @interface Nullable {}");
+
+ @Test public void nullCheckForConstructorParameters() {
+ JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(String string) {}",
+ "}");
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "final class TestModule {",
+ " @Nullable @Provides String provideString() { return null; }",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
+
+ // but if we disable the validation, then it compiles fine.
+ Compilation compilation2 =
+ javac()
+ .withOptions("-Adagger.nullableValidation=WARNING")
+ .withProcessors(new ComponentProcessor())
+ .compile(NULLABLE, a, module, component);
+ assertThat(compilation2).succeeded();
+ }
+
+ @Test public void nullCheckForMembersInjectParam() {
+ JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A() {}",
+ " @Inject void register(String string) {}",
+ "}");
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "final class TestModule {",
+ " @Nullable @Provides String provideString() { return null; }",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
+
+ // but if we disable the validation, then it compiles fine.
+ Compilation compilation2 =
+ javac()
+ .withOptions("-Adagger.nullableValidation=WARNING")
+ .withProcessors(new ComponentProcessor())
+ .compile(NULLABLE, a, module, component);
+ assertThat(compilation2).succeeded();
+ }
+
+ @Test public void nullCheckForVariable() {
+ JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject String string;",
+ " @Inject A() {}",
+ "}");
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "final class TestModule {",
+ " @Nullable @Provides String provideString() { return null; }",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
+
+ // but if we disable the validation, then it compiles fine.
+ Compilation compilation2 =
+ javac()
+ .withOptions("-Adagger.nullableValidation=WARNING")
+ .withProcessors(new ComponentProcessor())
+ .compile(NULLABLE, a, module, component);
+ assertThat(compilation2).succeeded();
+ }
+
+ @Test public void nullCheckForComponentReturn() {
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "final class TestModule {",
+ " @Nullable @Provides String provideString() { return null; }",
+ "}");
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " String string();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
+
+ // but if we disable the validation, then it compiles fine.
+ Compilation compilation2 =
+ javac()
+ .withOptions("-Adagger.nullableValidation=WARNING")
+ .withProcessors(new ComponentProcessor())
+ .compile(NULLABLE, module, component);
+ assertThat(compilation2).succeeded();
+ }
+
+ @Test
+ public void nullCheckForOptionalInstance() {
+ JavaFileObject a =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(Optional<String> optional) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "abstract class TestModule {",
+ " @Nullable @Provides static String provideString() { return null; }",
+ " @BindsOptionalOf abstract String optionalString();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
+ }
+
+ @Test
+ public void nullCheckForOptionalProvider() {
+ JavaFileObject a =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class A {",
+ " @Inject A(Optional<Provider<String>> optional) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "abstract class TestModule {",
+ " @Nullable @Provides static String provideString() { return null; }",
+ " @BindsOptionalOf abstract String optionalString();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void nullCheckForOptionalLazy() {
+ JavaFileObject a =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "import dagger.Lazy;",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject A(Optional<Lazy<String>> optional) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "abstract class TestModule {",
+ " @Nullable @Provides static String provideString() { return null; }",
+ " @BindsOptionalOf abstract String optionalString();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void nullCheckForOptionalProviderOfLazy() {
+ JavaFileObject a =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "import dagger.Lazy;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "final class A {",
+ " @Inject A(Optional<Provider<Lazy<String>>> optional) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@dagger.Module",
+ "abstract class TestModule {",
+ " @Nullable @Provides static String provideString() { return null; }",
+ " @BindsOptionalOf abstract String optionalString();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " A a();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void moduleValidation() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "abstract class TestModule {",
+ " @Provides @Nullable static String nullableString() { return null; }",
+ " @Binds abstract Object object(String string);",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(module, NULLABLE);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@Provides @test.Nullable String test.TestModule.nullableString()"));
+ }
+}
diff --git a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
new file mode 100644
index 0000000..b765166
--- /dev/null
+++ b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class OptionalBindingRequestFulfillmentTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public OptionalBindingRequestFulfillmentTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void inlinedOptionalBindings() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.BindsOptionalOf;",
+ "import other.Maybe;",
+ "import other.DefinitelyNot;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @BindsOptionalOf Maybe maybe();",
+ " @BindsOptionalOf DefinitelyNot definitelyNot();",
+ "}");
+ JavaFileObject maybe =
+ JavaFileObjects.forSourceLines(
+ "other.Maybe",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "public class Maybe {",
+ " @Module",
+ " public static class MaybeModule {",
+ " @Provides static Maybe provideMaybe() { return new Maybe(); }",
+ " }",
+ "}");
+ JavaFileObject definitelyNot =
+ JavaFileObjects.forSourceLines(
+ "other.DefinitelyNot",
+ "package other;",
+ "",
+ "public class DefinitelyNot {}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "import dagger.Component;",
+ "import dagger.Lazy;",
+ "import javax.inject.Provider;",
+ "import other.Maybe;",
+ "import other.DefinitelyNot;",
+ "",
+ "@Component(modules = {TestModule.class, Maybe.MaybeModule.class})",
+ "interface TestComponent {",
+ " Optional<Maybe> maybe();",
+ " Optional<Provider<Lazy<Maybe>>> providerOfLazyOfMaybe();",
+ " Optional<DefinitelyNot> definitelyNot();",
+ " Optional<Provider<Lazy<DefinitelyNot>>> providerOfLazyOfDefinitelyNot();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private volatile Provider<Maybe> provideMaybeProvider;",
+ "",
+ " private Provider<Maybe> getMaybeProvider() {",
+ " Object local = provideMaybeProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " provideMaybeProvider = (Provider<Maybe>) local;",
+ " }",
+ " return (Provider<Maybe>) local;",
+ " }")
+ .addLines(
+ " @Override",
+ " public Optional<Maybe> maybe() {",
+ " return Optional.of(",
+ " Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe());",
+ " }",
+ "",
+ " @Override",
+ " public Optional<Provider<Lazy<Maybe>>> providerOfLazyOfMaybe() {",
+ " return Optional.of(ProviderOfLazy.create(")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " Maybe_MaybeModule_ProvideMaybeFactory.create()));")
+ .addLinesIn(
+ FAST_INIT_MODE, //
+ " getMaybeProvider()));")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Optional<DefinitelyNot> definitelyNot() {",
+ " return Optional.<DefinitelyNot>absent();",
+ " }",
+ "",
+ " @Override",
+ " public Optional<Provider<Lazy<DefinitelyNot>>>",
+ " providerOfLazyOfDefinitelyNot() {",
+ " return Optional.<Provider<Lazy<DefinitelyNot>>>absent();",
+ " }")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe();",
+ " default:",
+ " throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}")
+ .build();
+ Compilation compilation =
+ compilerWithOptions(
+ compilerMode
+ , CompilerMode.JAVA7
+ )
+ .compile(module, maybe, definitelyNot, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void requestForFuture() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.BindsOptionalOf;",
+ "import other.Maybe;",
+ "import other.DefinitelyNot;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @BindsOptionalOf Maybe maybe();",
+ " @BindsOptionalOf DefinitelyNot definitelyNot();",
+ "}");
+ JavaFileObject maybe =
+ JavaFileObjects.forSourceLines(
+ "other.Maybe",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "public class Maybe {",
+ " @Module",
+ " public static class MaybeModule {",
+ " @Provides static Maybe provideMaybe() { return new Maybe(); }",
+ " }",
+ "}");
+ JavaFileObject definitelyNot =
+ JavaFileObjects.forSourceLines(
+ "other.DefinitelyNot",
+ "package other;",
+ "",
+ "public class DefinitelyNot {}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "import javax.inject.Provider;",
+ "import other.Maybe;",
+ "import other.DefinitelyNot;",
+ "",
+ "@ProductionComponent(modules = {TestModule.class, Maybe.MaybeModule.class})",
+ "interface TestComponent {",
+ " ListenableFuture<Optional<Maybe>> maybe();",
+ " ListenableFuture<Optional<DefinitelyNot>> definitelyNot();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import com.google.common.base.Optional;",
+ "import dagger.producers.internal.CancellationListener;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent, CancellationListener {",
+ " @Override",
+ " public ListenableFuture<Optional<Maybe>> maybe() {",
+ " return Futures.immediateFuture(",
+ " Optional.of(Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe()));",
+ " }",
+ "",
+ " @Override",
+ " public ListenableFuture<Optional<DefinitelyNot>> definitelyNot() {",
+ " return Futures.immediateFuture(Optional.<DefinitelyNot>absent());",
+
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
+ "}");
+
+ Compilation compilation =
+ compilerWithOptions(
+ compilerMode
+ , CompilerMode.JAVA7
+ )
+ .compile(module, maybe, definitelyNot, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/OptionalBindingTest.java b/javatests/dagger/internal/codegen/OptionalBindingTest.java
new file mode 100644
index 0000000..1755101
--- /dev/null
+++ b/javatests/dagger/internal/codegen/OptionalBindingTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class OptionalBindingTest {
+ @Test
+ public void provideExplicitOptionalInParent_AndBindsOptionalOfInChild() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Optional;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Optional<String> optional();",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import java.util.Optional;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides",
+ " Optional<String> optional() {",
+ " return Optional.of(new String());",
+ " }",
+ "}");
+
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Optional;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface Child {",
+ " Optional<String> optional();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface ChildModule {",
+ " @BindsOptionalOf",
+ " String optionalString();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Optional<java.lang.String> is bound multiple times")
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
new file mode 100644
index 0000000..0b6ef8f
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(beder): Merge the error-handling tests with the ModuleFactoryGeneratorTest.
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
+import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatProductionModuleMethod;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ProducerModuleFactoryGeneratorTest {
+
+ @Test public void producesMethodNotInModule() {
+ assertThatMethodInUnannotatedClass("@Produces String produceString() { return null; }")
+ .hasError("@Produces methods can only be present within a @ProducerModule");
+ }
+
+ @Test public void producesMethodAbstract() {
+ assertThatProductionModuleMethod("@Produces abstract String produceString();")
+ .hasError("@Produces methods cannot be abstract");
+ }
+
+ @Test public void producesMethodPrivate() {
+ assertThatProductionModuleMethod("@Produces private String produceString() { return null; }")
+ .hasError("@Produces methods cannot be private");
+ }
+
+ @Test public void producesMethodReturnVoid() {
+ assertThatProductionModuleMethod("@Produces void produceNothing() {}")
+ .hasError("@Produces methods must return a value (not void)");
+ }
+
+ @Test
+ public void producesProvider() {
+ assertThatProductionModuleMethod("@Produces Provider<String> produceProvider() {}")
+ .hasError("@Produces methods must not return framework types");
+ }
+
+ @Test
+ public void producesLazy() {
+ assertThatProductionModuleMethod("@Produces Lazy<String> produceLazy() {}")
+ .hasError("@Produces methods must not return framework types");
+ }
+
+ @Test
+ public void producesMembersInjector() {
+ assertThatProductionModuleMethod(
+ "@Produces MembersInjector<String> produceMembersInjector() {}")
+ .hasError("@Produces methods must not return framework types");
+ }
+
+ @Test
+ public void producesProducer() {
+ assertThatProductionModuleMethod("@Produces Producer<String> produceProducer() {}")
+ .hasError("@Produces methods must not return framework types");
+ }
+
+ @Test
+ public void producesProduced() {
+ assertThatProductionModuleMethod("@Produces Produced<String> produceProduced() {}")
+ .hasError("@Produces methods must not return framework types");
+ }
+
+ @Test public void producesMethodReturnRawFuture() {
+ assertThatProductionModuleMethod("@Produces ListenableFuture produceRaw() {}")
+ .importing(ListenableFuture.class)
+ .hasError("@Produces methods cannot return a raw ListenableFuture");
+ }
+
+ @Test public void producesMethodReturnWildcardFuture() {
+ assertThatProductionModuleMethod("@Produces ListenableFuture<?> produceRaw() {}")
+ .importing(ListenableFuture.class)
+ .hasError(
+ "@Produces methods can return only a primitive, an array, a type variable, "
+ + "a declared type, or a ListenableFuture of one of those types");
+ }
+
+ @Test public void producesMethodWithTypeParameter() {
+ assertThatProductionModuleMethod("@Produces <T> String produceString() { return null; }")
+ .hasError("@Produces methods may not have type parameters");
+ }
+
+ @Test public void producesMethodSetValuesWildcard() {
+ assertThatProductionModuleMethod(
+ "@Produces @ElementsIntoSet Set<?> produceWildcard() { return null; }")
+ .hasError(
+ "@Produces methods can return only a primitive, an array, a type variable, "
+ + "a declared type, or a ListenableFuture of one of those types");
+ }
+
+ @Test public void producesMethodSetValuesRawSet() {
+ assertThatProductionModuleMethod(
+ "@Produces @ElementsIntoSet Set produceSomething() { return null; }")
+ .hasError("@Produces methods annotated with @ElementsIntoSet cannot return a raw Set");
+ }
+
+ @Test public void producesMethodSetValuesNotASet() {
+ assertThatProductionModuleMethod(
+ "@Produces @ElementsIntoSet List<String> produceStrings() { return null; }")
+ .hasError(
+ "@Produces methods of type set values must return a Set or ListenableFuture of Set");
+ }
+
+ @Test public void producesMethodSetValuesWildcardInFuture() {
+ assertThatProductionModuleMethod(
+ "@Produces @ElementsIntoSet "
+ + "ListenableFuture<Set<?>> produceWildcard() { return null; }")
+ .importing(ListenableFuture.class)
+ .hasError(
+ "@Produces methods can return only a primitive, an array, a type variable, "
+ + "a declared type, or a ListenableFuture of one of those types");
+ }
+
+ @Test public void producesMethodSetValuesFutureRawSet() {
+ assertThatProductionModuleMethod(
+ "@Produces @ElementsIntoSet ListenableFuture<Set> produceSomething() { return null; }")
+ .importing(ListenableFuture.class)
+ .hasError("@Produces methods annotated with @ElementsIntoSet cannot return a raw Set");
+ }
+
+ @Test public void producesMethodSetValuesFutureNotASet() {
+ assertThatProductionModuleMethod(
+ "@Produces @ElementsIntoSet "
+ + "ListenableFuture<List<String>> produceStrings() { return null; }")
+ .importing(ListenableFuture.class)
+ .hasError(
+ "@Produces methods of type set values must return a Set or ListenableFuture of Set");
+ }
+
+ @Test public void multipleProducesMethodsWithSameName() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class TestModule {",
+ " @Produces Object produce(int i) {",
+ " return i;",
+ " }",
+ "",
+ " @Produces String produce() {",
+ " return \"\";",
+ " }",
+ "}");
+ String errorMessage =
+ "Cannot have more than one binding method with the same name in a single module";
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(errorMessage).inFile(moduleFile).onLine(8);
+ assertThat(compilation).hadErrorContaining(errorMessage).inFile(moduleFile).onLine(12);
+ }
+
+ @Test
+ public void producesMethodThrowsThrowable() {
+ assertThatProductionModuleMethod("@Produces int produceInt() throws Throwable { return 0; }")
+ .hasError(
+ "@Produces methods may only throw unchecked exceptions or exceptions subclassing "
+ + "Exception");
+ }
+
+ @Test public void producesMethodWithScope() {
+ assertThatProductionModuleMethod("@Produces @Singleton String str() { return \"\"; }")
+ .hasError("@Produces methods cannot be scoped");
+ }
+
+ @Test
+ public void privateModule() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "",
+ "final class Enclosing {",
+ " @ProducerModule private static final class PrivateModule {",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Modules cannot be private")
+ .inFile(moduleFile)
+ .onLine(6);
+ }
+
+ @Test
+ public void enclosedInPrivateModule() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "",
+ "final class Enclosing {",
+ " private static final class PrivateEnclosing {",
+ " @ProducerModule static final class TestModule {",
+ " }",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Modules cannot be enclosed in private types")
+ .inFile(moduleFile)
+ .onLine(7);
+ }
+
+ @Test
+ public void includesNonModule() {
+ JavaFileObject xFile =
+ JavaFileObjects.forSourceLines("test.X", "package test;", "", "public final class X {}");
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.FooModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "",
+ "@ProducerModule(includes = X.class)",
+ "public final class FooModule {",
+ "}");
+ Compilation compilation = daggerCompiler().compile(xFile, moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "X is listed as a module, but is not annotated with one of @Module, @ProducerModule");
+ }
+
+ // TODO(ronshapiro): merge this with the equivalent test in ModuleFactoryGeneratorTest and make it
+ // parameterized
+ @Test
+ public void publicModuleNonPublicIncludes() {
+ JavaFileObject publicModuleFile = JavaFileObjects.forSourceLines("test.PublicModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "",
+ "@ProducerModule(includes = {",
+ " BadNonPublicModule.class, OtherPublicModule.class, OkNonPublicModule.class",
+ "})",
+ "public final class PublicModule {",
+ "}");
+ JavaFileObject badNonPublicModuleFile =
+ JavaFileObjects.forSourceLines(
+ "test.BadNonPublicModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class BadNonPublicModule {",
+ " @Produces",
+ " int produceInt() {",
+ " return 42;",
+ " }",
+ "}");
+ JavaFileObject okNonPublicModuleFile = JavaFileObjects.forSourceLines("test.OkNonPublicModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class OkNonPublicModule {",
+ " @Produces",
+ " static String produceString() {",
+ " return \"foo\";",
+ " }",
+ "}");
+ JavaFileObject otherPublicModuleFile = JavaFileObjects.forSourceLines("test.OtherPublicModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "",
+ "@ProducerModule",
+ "public final class OtherPublicModule {",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ publicModuleFile,
+ badNonPublicModuleFile,
+ okNonPublicModuleFile,
+ otherPublicModuleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "This module is public, but it includes non-public (or effectively non-public) modules "
+ + "(test.BadNonPublicModule) that have non-static, non-abstract binding methods. "
+ + "Either reduce the visibility of this module, make the included modules public, "
+ + "or make all of the binding methods on the included modules abstract or static.")
+ .inFile(publicModuleFile)
+ .onLine(8);
+ }
+
+ @Test public void argumentNamedModuleCompiles() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class TestModule {",
+ " @Produces String produceString(int module) {",
+ " return null;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(moduleFile);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test public void singleProducesMethodNoArgsFuture() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class TestModule {",
+ " @Produces ListenableFuture<String> produceString() {",
+ " return null;",
+ " }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProduceStringFactory",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.Futures;",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.internal.AbstractProducesMethodProducer;",
+ "import dagger.producers.monitoring.ProducerToken;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ "@SuppressWarnings(\"FutureReturnValueIgnored\")",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProduceStringFactory",
+ " extends AbstractProducesMethodProducer<Void, String> {",
+ " private final TestModule module;",
+ "",
+ " private TestModule_ProduceStringFactory(",
+ " TestModule module,",
+ " Provider<Executor> executorProvider,",
+ " Provider<ProductionComponentMonitor> productionComponentMonitorProvider) {",
+ " super(",
+ " productionComponentMonitorProvider,",
+ " ProducerToken.create(TestModule_ProduceStringFactory.class),",
+ " executorProvider);",
+ " this.module = module;",
+ " }",
+ "",
+ " public static TestModule_ProduceStringFactory create(",
+ " TestModule module,",
+ " Provider<Executor> executorProvider,",
+ " Provider<ProductionComponentMonitor> productionComponentMonitorProvider) {",
+ " return new TestModule_ProduceStringFactory(",
+ " module, executorProvider, productionComponentMonitorProvider);",
+ " }",
+ "",
+ " @Override protected ListenableFuture<Void> collectDependencies() {",
+ " return Futures.<Void>immediateFuture(null);",
+ " }",
+ "",
+ " @Override public ListenableFuture<String> callProducesMethod(Void ignoredVoidArg) {",
+ " return module.produceString();",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(moduleFile)
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(factoryFile);
+ }
+
+ @Test
+ public void singleProducesMethodNoArgsFutureWithProducerName() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.Futures;",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "@ProducerModule",
+ "final class TestModule {",
+ " @Produces ListenableFuture<String> produceString() {",
+ " return Futures.immediateFuture(\"\");",
+ " }",
+ "}");
+ JavaFileObject factoryFile =
+ JavaFileObjects.forSourceLines(
+ "TestModule_ProduceStringFactory",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.Futures;",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.internal.AbstractProducesMethodProducer;",
+ "import dagger.producers.monitoring.ProducerToken;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ "@SuppressWarnings(\"FutureReturnValueIgnored\")",
+ GENERATED_ANNOTATION,
+ "public final class TestModule_ProduceStringFactory",
+ " extends AbstractProducesMethodProducer<Void, String> {",
+ " private final TestModule module;",
+ "",
+ " private TestModule_ProduceStringFactory(",
+ " TestModule module,",
+ " Provider<Executor> executorProvider,",
+ " Provider<ProductionComponentMonitor> productionComponentMonitorProvider) {",
+ " super(",
+ " productionComponentMonitorProvider,",
+ " ProducerToken.create(\"test.TestModule#produceString\"),",
+ " executorProvider);",
+ " this.module = module;",
+ " }",
+ "",
+ " public static TestModule_ProduceStringFactory create(",
+ " TestModule module,",
+ " Provider<Executor> executorProvider,",
+ " Provider<ProductionComponentMonitor> productionComponentMonitorProvider) {",
+ " return new TestModule_ProduceStringFactory(",
+ " module, executorProvider, productionComponentMonitorProvider);",
+ " }",
+ "",
+ " @Override protected ListenableFuture<Void> collectDependencies() {",
+ " return Futures.<Void>immediateFuture(null);",
+ " }",
+ "",
+ " @Override public ListenableFuture<String> callProducesMethod(Void ignoredVoidArg) {",
+ " return module.produceString();",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(moduleFile)
+ .withCompilerOptions("-Adagger.writeProducerNameInToken=ENABLED")
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(factoryFile);
+ }
+
+ @Test
+ public void producesMethodMultipleQualifiersOnMethod() {
+ assertThatProductionModuleMethod(
+ "@Produces @QualifierA @QualifierB static String produceString() { return null; }")
+ .importing(ListenableFuture.class, QualifierA.class, QualifierB.class)
+ .hasError("may not use more than one @Qualifier");
+ }
+
+ @Test
+ public void producesMethodMultipleQualifiersOnParameter() {
+ assertThatProductionModuleMethod(
+ "@Produces static String produceString(@QualifierA @QualifierB Object input) "
+ + "{ return null; }")
+ .importing(ListenableFuture.class, QualifierA.class, QualifierB.class)
+ .hasError("may not use more than one @Qualifier");
+ }
+
+ @Test
+ public void producesMethodWildcardDependency() {
+ assertThatProductionModuleMethod(
+ "@Produces static String produceString(Provider<? extends Number> numberProvider) "
+ + "{ return null; }")
+ .importing(ListenableFuture.class, QualifierA.class, QualifierB.class)
+ .hasError(
+ "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or Produced<T> "
+ + "when T is a wildcard type such as ? extends java.lang.Number");
+ }
+
+ @Qualifier
+ @Retention(RUNTIME)
+ public @interface QualifierA {}
+
+ @Qualifier
+ @Retention(RUNTIME)
+ public @interface QualifierB {}
+}
diff --git a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
new file mode 100644
index 0000000..9a852c7
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ProductionComponentProcessorTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public ProductionComponentProcessorTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test public void componentOnConcreteClass() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent",
+ "final class NotAComponent {}");
+ Compilation compilation = daggerCompiler().compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("interface");
+ }
+
+ @Test public void componentOnEnum() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent",
+ "enum NotAComponent {",
+ " INSTANCE",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("interface");
+ }
+
+ @Test public void componentOnAnnotation() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent",
+ "@interface NotAComponent {}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("interface");
+ }
+
+ @Test public void nonModuleModule() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(modules = Object.class)",
+ "interface NotAComponent {}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("is not annotated with one of @Module, @ProducerModule");
+ }
+
+ @Test
+ public void dependsOnProductionExecutor() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.ExecutorModule",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.MoreExecutors;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.Production;",
+ "import java.util.concurrent.Executor;",
+ "",
+ "@Module",
+ "final class ExecutorModule {",
+ " @Provides @Production Executor executor() {",
+ " return MoreExecutors.directExecutor();",
+ " }",
+ "}");
+ JavaFileObject producerModuleFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.Production;",
+ "import java.util.concurrent.Executor;",
+ "",
+ "@ProducerModule",
+ "final class SimpleModule {",
+ " @Produces String str(@Production Executor executor) {",
+ " return \"\";",
+ " }",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "import java.util.concurrent.Executor;",
+ "",
+ "@ProductionComponent(modules = {ExecutorModule.class, SimpleModule.class})",
+ "interface SimpleComponent {",
+ " ListenableFuture<String> str();",
+ "",
+ " @ProductionComponent.Builder",
+ " interface Builder {",
+ " SimpleComponent build();",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(moduleFile, producerModuleFile, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("java.lang.String may not depend on the production executor")
+ .inFile(componentFile)
+ .onLineContaining("interface SimpleComponent");
+
+ compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(producerModuleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("java.lang.String may not depend on the production executor")
+ .inFile(producerModuleFile)
+ .onLineContaining("class SimpleModule");
+ // TODO(dpb): Report at the binding if enclosed in the module.
+ }
+
+ @Test
+ public void simpleComponent() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import com.google.common.util.concurrent.MoreExecutors;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.Production;",
+ "import dagger.producers.ProductionComponent;",
+ "import java.util.concurrent.Executor;",
+ "import javax.inject.Inject;",
+ "",
+ "final class TestClass {",
+ " static final class C {",
+ " @Inject C() {}",
+ " }",
+ "",
+ " interface A {}",
+ " interface B {}",
+ "",
+ " @Module",
+ " static final class BModule {",
+ " @Provides B b(C c) {",
+ " return null;",
+ " }",
+ "",
+ " @Provides @Production Executor executor() {",
+ " return MoreExecutors.directExecutor();",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " static final class AModule {",
+ " @Produces ListenableFuture<A> a(B b) {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(modules = {AModule.class, BModule.class})",
+ " interface SimpleComponent {",
+ " ListenableFuture<A> a();",
+ " }",
+ "}");
+ JavaFileObject generatedComponent;
+ switch (compilerMode) {
+ case FAST_INIT_MODE:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestClass_SimpleComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.InstanceFactory;",
+ "import dagger.internal.MemoizedSentinel;",
+ "import dagger.internal.Preconditions;",
+ "import dagger.internal.SetFactory;",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.internal.CancellationListener;",
+ "import dagger.producers.internal.Producers;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestClass_SimpleComponent",
+ " implements TestClass.SimpleComponent, CancellationListener {",
+ " private final TestClass.BModule bModule;",
+ " private volatile Object productionImplementationExecutor =",
+ " new MemoizedSentinel();",
+ " private volatile Provider<Executor> productionImplementationExecutorProvider;",
+ " private volatile Object productionComponentMonitor = new MemoizedSentinel();",
+ " private volatile Provider<ProductionComponentMonitor> monitorProvider;",
+ " private volatile Provider<TestClass.B> bProvider;",
+ " private Producer<TestClass.A> aEntryPoint;",
+ " private Provider<TestClass.SimpleComponent> simpleComponentProvider;",
+ " private Producer<TestClass.B> bProducer;",
+ " private Producer<TestClass.A> aProducer;",
+ "",
+ " private DaggerTestClass_SimpleComponent(",
+ " TestClass.AModule aModuleParam,",
+ " TestClass.BModule bModuleParam) {",
+ " this.bModule = bModuleParam;",
+ " initialize(aModuleParam, bModuleParam);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestClass.SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " private Executor getProductionImplementationExecutor() {",
+ " Object local = productionImplementationExecutor;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = productionImplementationExecutor;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local =",
+ " TestClass_BModule_ExecutorFactory.executor(bModule);",
+ " productionImplementationExecutor =",
+ " DoubleCheck.reentrantCheck(",
+ " productionImplementationExecutor, local);",
+ " }",
+ " }",
+ " }",
+ " return (Executor) local;",
+ " }",
+ "",
+ " private Provider<Executor> getProductionImplementationExecutorProvider() {",
+ " Object local = productionImplementationExecutorProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " productionImplementationExecutorProvider = (Provider<Executor>) local;",
+ " }",
+ " return (Provider<Executor>) local;",
+ " }",
+ "",
+ " private ProductionComponentMonitor getProductionComponentMonitor() {",
+ " Object local = productionComponentMonitor;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = productionComponentMonitor;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local =",
+ " TestClass_SimpleComponent_MonitoringModule_MonitorFactory",
+ " .monitor(",
+ " simpleComponentProvider,",
+ " SetFactory.<ProductionComponentMonitor.Factory>empty());",
+ " productionComponentMonitor =",
+ " DoubleCheck.reentrantCheck(",
+ " productionComponentMonitor, local);",
+ " }",
+ " }",
+ " }",
+ " return (ProductionComponentMonitor) local;",
+ " }",
+ "",
+ " private Provider<ProductionComponentMonitor>",
+ " getProductionComponentMonitorProvider() {",
+ " Object local = monitorProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(1);",
+ " monitorProvider = (Provider<ProductionComponentMonitor>) local;",
+ " }",
+ " return (Provider<ProductionComponentMonitor>) local;",
+ " }",
+ "",
+ " private TestClass.B getB() {",
+ " return TestClass_BModule_BFactory.b(bModule, new TestClass.C());",
+ " }",
+ "",
+ " private Provider<TestClass.B> getBProvider() {",
+ " Object local = bProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(2);",
+ " bProvider = (Provider<TestClass.B>) local;",
+ " }",
+ " return (Provider<TestClass.B>) local;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(",
+ " final TestClass.AModule aModuleParam,",
+ " final TestClass.BModule bModuleParam) {",
+ " this.simpleComponentProvider =",
+ " InstanceFactory.create((TestClass.SimpleComponent) this);",
+ " this.bProducer = Producers.producerFromProvider(getBProvider());",
+ " this.aProducer =",
+ " TestClass_AModule_AFactory.create(",
+ " aModuleParam,",
+ " getProductionImplementationExecutorProvider(),",
+ " getProductionComponentMonitorProvider(),",
+ " bProducer);",
+ " this.aEntryPoint = Producers.entryPointViewOf(aProducer, this);",
+ " }",
+ "",
+ " @Override",
+ " public ListenableFuture<TestClass.A> a() {",
+ " return aEntryPoint.get();",
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
+ " Producers.cancel(aProducer, mayInterruptIfRunning);",
+ " Producers.cancel(bProducer, mayInterruptIfRunning);",
+ " }",
+ "",
+ " static final class Builder {",
+ " private TestClass.AModule aModule;",
+ " private TestClass.BModule bModule;",
+ "",
+ " private Builder() {}",
+ "",
+ " public Builder aModule(TestClass.AModule aModule) {",
+ " this.aModule = Preconditions.checkNotNull(aModule);",
+ " return this;",
+ " }",
+ "",
+ " public Builder bModule(TestClass.BModule bModule) {",
+ " this.bModule = Preconditions.checkNotNull(bModule);",
+ " return this;",
+ " }",
+ "",
+ " public TestClass.SimpleComponent build() {",
+ " if (aModule == null) {",
+ " this.aModule = new TestClass.AModule();",
+ " }",
+ " if (bModule == null) {",
+ " this.bModule = new TestClass.BModule();",
+ " }",
+ " return new DaggerTestClass_SimpleComponent(aModule, bModule);",
+ " }",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " private final int id;",
+ "",
+ " SwitchingProvider(int id) {",
+ " this.id = id;",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: return (T) DaggerTestClass_SimpleComponent.this",
+ " .getProductionImplementationExecutor();",
+ " case 1: return (T)",
+ " DaggerTestClass_SimpleComponent.this.getProductionComponentMonitor();",
+ " case 2: return (T)",
+ " DaggerTestClass_SimpleComponent.this.getB();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}");
+ break;
+ default:
+ generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestClass_SimpleComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.InstanceFactory;",
+ "import dagger.internal.Preconditions;",
+ "import dagger.internal.SetFactory;",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.internal.CancellationListener;",
+ "import dagger.producers.internal.Producers;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import java.util.concurrent.Executor;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestClass_SimpleComponent",
+ " implements TestClass.SimpleComponent, CancellationListener {",
+ " private Producer<TestClass.A> aEntryPoint;",
+ " private Provider<Executor> executorProvider;",
+ " private Provider<Executor> productionImplementationExecutorProvider;",
+ " private Provider<TestClass.SimpleComponent> simpleComponentProvider;",
+ " private Provider<ProductionComponentMonitor> monitorProvider;",
+ " private Provider<TestClass.B> bProvider;",
+ " private Producer<TestClass.B> bProducer;",
+ " private Producer<TestClass.A> aProducer;",
+ "",
+ " private DaggerTestClass_SimpleComponent(",
+ " TestClass.AModule aModuleParam,",
+ " TestClass.BModule bModuleParam) {",
+ " initialize(aModuleParam, bModuleParam);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestClass.SimpleComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(",
+ " final TestClass.AModule aModuleParam,",
+ " final TestClass.BModule bModuleParam) {",
+ " this.executorProvider =",
+ " TestClass_BModule_ExecutorFactory.create(bModuleParam);",
+ " this.productionImplementationExecutorProvider =",
+ " DoubleCheck.provider((Provider) executorProvider);",
+ " this.simpleComponentProvider = ",
+ " InstanceFactory.create((TestClass.SimpleComponent) this);",
+ " this.monitorProvider =",
+ " DoubleCheck.provider(",
+ " TestClass_SimpleComponent_MonitoringModule_MonitorFactory.create(",
+ " simpleComponentProvider,",
+ " SetFactory.<ProductionComponentMonitor.Factory>empty()));",
+ " this.bProvider = TestClass_BModule_BFactory.create(",
+ " bModuleParam, TestClass_C_Factory.create());",
+ " this.bProducer = Producers.producerFromProvider(bProvider);",
+ " this.aProducer = TestClass_AModule_AFactory.create(",
+ " aModuleParam,",
+ " productionImplementationExecutorProvider,",
+ " monitorProvider,",
+ " bProducer);",
+ " this.aEntryPoint = Producers.entryPointViewOf(aProducer, this);",
+ " }",
+ "",
+ " @Override",
+ " public ListenableFuture<TestClass.A> a() {",
+ " return aEntryPoint.get();",
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
+ " Producers.cancel(aProducer, mayInterruptIfRunning);",
+ " Producers.cancel(bProducer, mayInterruptIfRunning);",
+ " }",
+ "",
+ " static final class Builder {",
+ " private TestClass.AModule aModule;",
+ " private TestClass.BModule bModule;",
+ "",
+ " private Builder() {}",
+ "",
+ " public Builder aModule(TestClass.AModule aModule) {",
+ " this.aModule = Preconditions.checkNotNull(aModule);",
+ " return this;",
+ " }",
+ "",
+ " public Builder bModule(TestClass.BModule bModule) {",
+ " this.bModule = Preconditions.checkNotNull(bModule);",
+ " return this;",
+ " }",
+ "",
+ " public TestClass.SimpleComponent build() {",
+ " if (aModule == null) {",
+ " this.aModule = new TestClass.AModule();",
+ " }",
+ " if (bModule == null) {",
+ " this.bModule = new TestClass.BModule();",
+ " }",
+ " return new DaggerTestClass_SimpleComponent(aModule, bModule);",
+ " }",
+ " }",
+ "}");
+ }
+ assertAbout(javaSource())
+ .that(component)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(generatedComponent);
+ }
+
+ @Test public void nullableProducersAreNotErrors() {
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import com.google.common.util.concurrent.MoreExecutors;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.Production;",
+ "import dagger.producers.ProductionComponent;",
+ "import java.util.concurrent.Executor;",
+ "import javax.annotation.Nullable;",
+ "import javax.inject.Inject;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ " interface B {}",
+ " interface C {}",
+ "",
+ " @Module",
+ " static final class CModule {",
+ " @Provides @Nullable C c() {",
+ " return null;",
+ " }",
+ "",
+ " @Provides @Production Executor executor() {",
+ " return MoreExecutors.directExecutor();",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " static final class ABModule {",
+ " @Produces @Nullable B b(@Nullable C c) {",
+ " return null;",
+ " }",
+
+ " @Produces @Nullable ListenableFuture<A> a(B b) {", // NOTE: B not injected as nullable
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(modules = {ABModule.class, CModule.class})",
+ " interface SimpleComponent {",
+ " ListenableFuture<A> a();",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining("@Nullable on @Produces methods does not do anything")
+ .inFile(component)
+ .onLine(33);
+ assertThat(compilation)
+ .hadWarningContaining("@Nullable on @Produces methods does not do anything")
+ .inFile(component)
+ .onLine(36);
+ }
+
+ @Test
+ public void productionScope_injectConstructor() {
+ JavaFileObject productionScoped =
+ JavaFileObjects.forSourceLines(
+ "test.ProductionScoped",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionScope;",
+ "import javax.inject.Inject;",
+ "",
+ "@ProductionScope",
+ "class ProductionScoped {",
+ " @Inject ProductionScoped() {}",
+ "}");
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.producers.ProductionSubcomponent;",
+ "",
+ "@ProductionSubcomponent",
+ "interface Child {",
+ " ProductionScoped productionScoped();",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(productionScoped, parent, child);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .containsElementsIn(
+ new JavaFileBuilder(compilerMode, "test.DaggerRoot")
+ .addLines(
+ "package test;",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent, CancellationListener {",
+ " private final class ChildImpl implements Child, CancellationListener {",
+ " @Override",
+ " public ProductionScoped productionScoped() {")
+ .addLinesIn(
+ CompilerMode.DEFAULT_MODE, //
+ " return DaggerParent.this.productionScopedProvider.get();")
+ .addLinesIn(
+ CompilerMode.FAST_INIT_MODE, //
+ " return DaggerParent.this.getProductionScoped();")
+ .addLines(
+ " }", //
+ " }", //
+ "}")
+ .build());
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java b/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
new file mode 100644
index 0000000..8453e03
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Producer-specific validation tests. */
+@RunWith(JUnit4.class)
+public class ProductionGraphValidationTest {
+ private static final JavaFileObject EXECUTOR_MODULE =
+ JavaFileObjects.forSourceLines(
+ "test.ExecutorModule",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.MoreExecutors;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.Production;",
+ "import java.util.concurrent.Executor;",
+ "",
+ "@Module",
+ "class ExecutorModule {",
+ " @Provides @Production Executor executor() {",
+ " return MoreExecutors.directExecutor();",
+ " }",
+ "}");
+
+ @Test public void componentWithUnprovidedInput() {
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.MyComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(modules = {ExecutorModule.class, FooModule.class})",
+ "interface MyComponent {",
+ " ListenableFuture<Foo> getFoo();",
+ "}");
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.FooModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "",
+ "class Foo {}",
+ "class Bar {}",
+ "",
+ "@ProducerModule",
+ "class FooModule {",
+ " @Produces Foo foo(Bar bar) {",
+ " return null;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.Bar cannot be provided without an @Inject constructor or an @Provides- or "
+ + "@Produces-annotated method.")
+ .inFile(component)
+ .onLineContaining("interface MyComponent");
+ }
+
+ @Test public void componentProductionWithNoDependencyChain() {
+ JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " @ProductionComponent(modules = ExecutorModule.class)",
+ " interface AComponent {",
+ " ListenableFuture<A> getA();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.TestClass.A cannot be provided without an @Provides- or @Produces-annotated "
+ + "method.")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test public void provisionDependsOnProduction() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ " interface B {}",
+ "",
+ " @ProducerModule(includes = BModule.class)",
+ " final class AModule {",
+ " @Provides A a(B b) {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " final class BModule {",
+ " @Produces ListenableFuture<B> b() {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(modules = {ExecutorModule.class, AModule.class})",
+ " interface AComponent {",
+ " ListenableFuture<A> getA();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.TestClass.A is a provision, which cannot depend on a production.")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+
+ compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(EXECUTOR_MODULE, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.TestClass.A is a provision, which cannot depend on a production.")
+ .inFile(component)
+ .onLineContaining("class AModule");
+ }
+
+ @Test public void provisionEntryPointDependsOnProduction() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " @ProducerModule",
+ " static final class AModule {",
+ " @Produces ListenableFuture<A> a() {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(modules = {ExecutorModule.class, AModule.class})",
+ " interface AComponent {",
+ " A getA();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.TestClass.A is a provision entry-point, which cannot depend on a production.")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test
+ public void providingMultibindingWithProductions() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.ProductionComponent;",
+ "import java.util.Map;",
+ "import javax.inject.Provider;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ " interface B {}",
+ "",
+ " @Module",
+ " static final class AModule {",
+ " @Provides static A a(Map<String, Provider<Object>> map) {",
+ " return null;",
+ " }",
+ "",
+ " @Provides @IntoMap @StringKey(\"a\") static Object aEntry() {",
+ " return \"a\";",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " static final class BModule {",
+ " @Produces static B b(A a) {",
+ " return null;",
+ " }",
+ "",
+ " @Produces @IntoMap @StringKey(\"b\") static Object bEntry() {",
+ " return \"b\";",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(",
+ " modules = {ExecutorModule.class, AModule.class, BModule.class})",
+ " interface AComponent {",
+ " ListenableFuture<B> b();",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.TestClass.A is a provision, which cannot depend on a production")
+ .inFile(component)
+ .onLineContaining("interface AComponent");
+ }
+
+ @Test
+ public void monitoringDependsOnUnboundType() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.ProductionComponent;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " @Module",
+ " final class MonitoringModule {",
+ " @Provides @IntoSet",
+ " ProductionComponentMonitor.Factory monitorFactory(A unbound) {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " final class StringModule {",
+ " @Produces ListenableFuture<String> str() {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(",
+ " modules = {ExecutorModule.class, MonitoringModule.class, StringModule.class}",
+ " )",
+ " interface StringComponent {",
+ " ListenableFuture<String> getString();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.TestClass.A cannot be provided without an @Provides-annotated method.")
+ .inFile(component)
+ .onLineContaining("interface StringComponent");
+ }
+
+ @Test
+ public void monitoringDependsOnProduction() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.ProductionComponent;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " @Module",
+ " final class MonitoringModule {",
+ " @Provides @IntoSet ProductionComponentMonitor.Factory monitorFactory(A a) {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " final class StringModule {",
+ " @Produces A a() {",
+ " return null;",
+ " }",
+ "",
+ " @Produces ListenableFuture<String> str() {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(",
+ " modules = {ExecutorModule.class, MonitoringModule.class, StringModule.class}",
+ " )",
+ " interface StringComponent {",
+ " ListenableFuture<String> getString();",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.Set<dagger.producers.monitoring.ProductionComponentMonitor.Factory>"
+ + " test.TestClass.MonitoringModule#monitorFactory is a provision,"
+ + " which cannot depend on a production.")
+ .inFile(component)
+ .onLineContaining("interface StringComponent");
+ }
+
+ @Test
+ public void cycleNotBrokenByMap() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(modules = {ExecutorModule.class, TestModule.class})",
+ "interface TestComponent {",
+ " ListenableFuture<String> string();",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "import java.util.Map;",
+ "",
+ "@ProducerModule",
+ "final class TestModule {",
+ " @Produces static String string(Map<String, String> map) {",
+ " return \"string\";",
+ " }",
+ "",
+ " @Produces @IntoMap @StringKey(\"key\")",
+ " static String entry(String string) {",
+ " return string;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component, module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("cycle")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void cycleNotBrokenByProducerMap() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(modules = {ExecutorModule.class, TestModule.class})",
+ "interface TestComponent {",
+ " ListenableFuture<String> string();",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.multibindings.StringKey;",
+ "import dagger.multibindings.IntoMap;",
+ "import java.util.Map;",
+ "",
+ "@ProducerModule",
+ "final class TestModule {",
+ " @Produces static String string(Map<String, Producer<String>> map) {",
+ " return \"string\";",
+ " }",
+ "",
+ " @Produces @IntoMap @StringKey(\"key\")",
+ " static String entry(String string) {",
+ " return string;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component, module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("cycle")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void componentWithBadModule() {
+ JavaFileObject badModule =
+ JavaFileObjects.forSourceLines(
+ "test.BadModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.multibindings.Multibinds;",
+ "import dagger.Module;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "abstract class BadModule {",
+ " @Multibinds",
+ " @BindsOptionalOf",
+ " abstract Set<String> strings();",
+ "}");
+ JavaFileObject badComponent =
+ JavaFileObjects.forSourceLines(
+ "test.BadComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Optional;",
+ "import java.util.Set;",
+ "",
+ "@Component(modules = BadModule.class)",
+ "interface BadComponent {",
+ " Set<String> strings();",
+ " Optional<Set<String>> optionalStrings();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(badModule, badComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("test.BadModule has errors")
+ .inFile(badComponent)
+ .onLine(7);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/RepeatedModuleValidationTest.java b/javatests/dagger/internal/codegen/RepeatedModuleValidationTest.java
new file mode 100644
index 0000000..60a9bd5
--- /dev/null
+++ b/javatests/dagger/internal/codegen/RepeatedModuleValidationTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RepeatedModuleValidationTest {
+ private static final JavaFileObject MODULE_FILE =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "final class TestModule {}");
+
+ @Test
+ public void moduleRepeatedInSubcomponentFactoryMethod() {
+ JavaFileObject subcomponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "interface TestSubcomponent {",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " TestSubcomponent newTestSubcomponent(TestModule module);",
+ "}");
+ Compilation compilation =
+ daggerCompiler().compile(MODULE_FILE, subcomponentFile, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("TestModule is present in test.TestComponent.")
+ .inFile(componentFile)
+ .onLine(7)
+ .atColumn(51);
+ }
+
+ @Test
+ public void moduleRepeatedInSubcomponentBuilderMethod() {
+ JavaFileObject subcomponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "interface TestSubcomponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Builder testModule(TestModule testModule);",
+ " TestSubcomponent build();",
+ " }",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " TestSubcomponent.Builder newTestSubcomponentBuilder();",
+ "}");
+ Compilation compilation =
+ daggerCompiler().compile(MODULE_FILE, subcomponentFile, componentFile);
+ assertThat(compilation).succeeded();
+ // TODO(gak): assert about the warning when we have that ability
+ }
+
+ @Test
+ public void moduleRepeatedButNotPassed() {
+ JavaFileObject subcomponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "interface TestSubcomponent {",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " TestSubcomponent newTestSubcomponent();",
+ "}");
+ Compilation compilation =
+ daggerCompiler().compile(MODULE_FILE, subcomponentFile, componentFile);
+ assertThat(compilation).succeeded();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ScopingValidationTest.java b/javatests/dagger/internal/codegen/ScopingValidationTest.java
new file mode 100644
index 0000000..9efcc2a
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ScopingValidationTest.java
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ScopingValidationTest {
+ @Test
+ public void componentWithoutScopeIncludesScopedBindings_Fail() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.MyComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Component(modules = ScopedModule.class)",
+ "interface MyComponent {",
+ " ScopedType string();",
+ "}");
+ JavaFileObject typeFile =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "class ScopedType {",
+ " @Inject ScopedType(String s, long l, float f) {}",
+ "}");
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "class ScopedModule {",
+ " @Provides @Singleton String string() { return \"a string\"; }",
+ " @Provides long integer() { return 0L; }",
+ " @Provides float floatingPoint() { return 0.0f; }",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(componentFile, typeFile, moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.MyComponent (unscoped) may not reference scoped bindings:",
+ " @Singleton class test.ScopedType",
+ " @Provides @Singleton String test.ScopedModule.string()"));
+ }
+
+ @Test // b/79859714
+ public void bindsWithChildScope_inParentModule_notAllowed() {
+ JavaFileObject childScope =
+ JavaFileObjects.forSourceLines(
+ "test.ChildScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface ChildScope {}");
+
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "", //
+ "interface Foo {}");
+
+ JavaFileObject fooImpl =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class FooImpl implements Foo {",
+ " @Inject FooImpl() {}",
+ "}");
+
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface ParentModule {",
+ " @Binds @ChildScope Foo bind(FooImpl fooImpl);",
+ "}");
+
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@ChildScope",
+ "@Subcomponent",
+ "interface Child {",
+ " Foo foo();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(childScope, foo, fooImpl, parentModule, parent, child);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.Parent scoped with @Singleton may not reference bindings with different "
+ + "scopes:",
+ " @Binds @test.ChildScope test.Foo test.ParentModule.bind(test.FooImpl)"));
+ }
+
+ @Test
+ public void componentWithScopeIncludesIncompatiblyScopedBindings_Fail() {
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.MyComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = ScopedModule.class)",
+ "interface MyComponent {",
+ " ScopedType string();",
+ "}");
+ JavaFileObject scopeFile =
+ JavaFileObjects.forSourceLines(
+ "test.PerTest",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface PerTest {}");
+ JavaFileObject scopeWithAttribute =
+ JavaFileObjects.forSourceLines(
+ "test.Per",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface Per {",
+ " Class<?> value();",
+ "}");
+ JavaFileObject typeFile =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "@PerTest", // incompatible scope
+ "class ScopedType {",
+ " @Inject ScopedType(String s, long l, float f, boolean b) {}",
+ "}");
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "class ScopedModule {",
+ " @Provides @PerTest String string() { return \"a string\"; }", // incompatible scope
+ " @Provides long integer() { return 0L; }", // unscoped - valid
+ " @Provides @Singleton float floatingPoint() { return 0.0f; }", // same scope - valid
+ " @Provides @Per(MyComponent.class) boolean bool() { return false; }", // incompatible
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.MyComponent scoped with @Singleton "
+ + "may not reference bindings with different scopes:",
+ " @test.PerTest class test.ScopedType",
+ " @Provides @test.PerTest String test.ScopedModule.string()",
+ " @Provides @test.Per(test.MyComponent.class) boolean "
+ + "test.ScopedModule.bool()"))
+ .inFile(componentFile)
+ .onLineContaining("interface MyComponent");
+
+ compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
+ // The @Inject binding for ScopedType should not appear here, but the @Singleton binding should.
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ScopedModule contains bindings with different scopes:",
+ " @Provides @test.PerTest String test.ScopedModule.string()",
+ " @Provides @Singleton float test.ScopedModule.floatingPoint()",
+ " @Provides @test.Per(test.MyComponent.class) boolean "
+ + "test.ScopedModule.bool()"))
+ .inFile(moduleFile)
+ .onLineContaining("class ScopedModule");
+ }
+
+ @Test
+ public void fullBindingGraphValidationDoesNotReportForOneScope() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(
+ "-Adagger.fullBindingGraphValidation=ERROR",
+ "-Adagger.moduleHasDifferentScopesValidation=ERROR")
+ .compile(
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides @Singleton static Object object() { return \"object\"; }",
+ " @Provides @Singleton static String string() { return \"string\"; }",
+ " @Provides static int integer() { return 4; }",
+ "}"));
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void fullBindingGraphValidationDoesNotReportInjectBindings() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(
+ "-Adagger.fullBindingGraphValidation=ERROR",
+ "-Adagger.moduleHasDifferentScopesValidation=ERROR")
+ .compile(
+ JavaFileObjects.forSourceLines(
+ "test.UsedInRootRedScoped",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "@RedScope",
+ "final class UsedInRootRedScoped {",
+ " @Inject UsedInRootRedScoped() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.UsedInRootBlueScoped",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "@BlueScope",
+ "final class UsedInRootBlueScoped {",
+ " @Inject UsedInRootBlueScoped() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.RedScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface RedScope {}"),
+ JavaFileObjects.forSourceLines(
+ "test.BlueScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope",
+ "@interface BlueScope {}"),
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module(subcomponents = Child.class)",
+ "interface TestModule {",
+ " @Provides @Singleton",
+ " static Object object(",
+ " UsedInRootRedScoped usedInRootRedScoped,",
+ " UsedInRootBlueScoped usedInRootBlueScoped) {",
+ " return \"object\";",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " UsedInChildRedScoped usedInChildRedScoped();",
+ " UsedInChildBlueScoped usedInChildBlueScoped();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Child child();",
+ " }",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.UsedInChildRedScoped",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "@RedScope",
+ "final class UsedInChildRedScoped {",
+ " @Inject UsedInChildRedScoped() {}",
+ "}"),
+ JavaFileObjects.forSourceLines(
+ "test.UsedInChildBlueScoped",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "@BlueScope",
+ "final class UsedInChildBlueScoped {",
+ " @Inject UsedInChildBlueScoped() {}",
+ "}"));
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void componentWithScopeMayDependOnOnlyOneScopedComponent() {
+ // If a scoped component will have dependencies, they must only include, at most, a single
+ // scoped component
+ JavaFileObject type =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class SimpleType {",
+ " @Inject SimpleType() {}",
+ " static class A { @Inject A() {} }",
+ " static class B { @Inject B() {} }",
+ "}");
+ JavaFileObject simpleScope =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface SimpleScope {}");
+ JavaFileObject singletonScopedA =
+ JavaFileObjects.forSourceLines(
+ "test.SingletonComponentA",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface SingletonComponentA {",
+ " SimpleType.A type();",
+ "}");
+ JavaFileObject singletonScopedB =
+ JavaFileObjects.forSourceLines(
+ "test.SingletonComponentB",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface SingletonComponentB {",
+ " SimpleType.B type();",
+ "}");
+ JavaFileObject scopeless =
+ JavaFileObjects.forSourceLines(
+ "test.ScopelessComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ScopelessComponent {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject simpleScoped =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleScopedComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@SimpleScope",
+ "@Component(dependencies = {SingletonComponentA.class, SingletonComponentB.class})",
+ "interface SimpleScopedComponent {",
+ " SimpleType.A type();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ type, simpleScope, simpleScoped, singletonScopedA, singletonScopedB, scopeless);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "@test.SimpleScope test.SimpleScopedComponent depends on more than one scoped "
+ + "component:",
+ " @Singleton test.SingletonComponentA",
+ " @Singleton test.SingletonComponentB"));
+ }
+
+ @Test
+ public void componentWithoutScopeCannotDependOnScopedComponent() {
+ JavaFileObject type =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class SimpleType {",
+ " @Inject SimpleType() {}",
+ "}");
+ JavaFileObject scopedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ScopedComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface ScopedComponent {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject unscopedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.UnscopedComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Component(dependencies = ScopedComponent.class)",
+ "interface UnscopedComponent {",
+ " SimpleType type();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(type, scopedComponent, unscopedComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.UnscopedComponent (unscoped) cannot depend on scoped components:",
+ " @Singleton test.ScopedComponent"));
+ }
+
+ @Test
+ public void componentWithSingletonScopeMayNotDependOnOtherScope() {
+ // Singleton must be the widest lifetime of present scopes.
+ JavaFileObject type =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class SimpleType {",
+ " @Inject SimpleType() {}",
+ "}");
+ JavaFileObject simpleScope =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface SimpleScope {}");
+ JavaFileObject simpleScoped =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleScopedComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@SimpleScope",
+ "@Component",
+ "interface SimpleScopedComponent {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject singletonScoped =
+ JavaFileObjects.forSourceLines(
+ "test.SingletonComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(dependencies = SimpleScopedComponent.class)",
+ "interface SingletonComponent {",
+ " SimpleType type();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(type, simpleScope, simpleScoped, singletonScoped);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "This @Singleton component cannot depend on scoped components:",
+ " @test.SimpleScope test.SimpleScopedComponent"));
+ }
+
+ @Test
+ public void componentScopeAncestryMustNotCycle() {
+ // The dependency relationship of components is necessarily from shorter lifetimes to
+ // longer lifetimes. The scoping annotations must reflect this, and so one cannot declare
+ // scopes on components such that they cycle.
+ JavaFileObject type =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class SimpleType {",
+ " @Inject SimpleType() {}",
+ "}");
+ JavaFileObject scopeA =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeA",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeA {}");
+ JavaFileObject scopeB =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeB",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeB {}");
+ JavaFileObject longLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentLong",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeA",
+ "@Component",
+ "interface ComponentLong {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject mediumLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentMedium",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeB",
+ "@Component(dependencies = ComponentLong.class)",
+ "interface ComponentMedium {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject shortLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentShort",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeA",
+ "@Component(dependencies = ComponentMedium.class)",
+ "interface ComponentShort {",
+ " SimpleType type();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(type, scopeA, scopeB, longLifetime, mediumLifetime, shortLifetime);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ComponentShort depends on scoped components in a non-hierarchical scope "
+ + "ordering:",
+ " @test.ScopeA test.ComponentLong",
+ " @test.ScopeB test.ComponentMedium",
+ " @test.ScopeA test.ComponentShort"));
+ }
+
+ @Test
+ public void reusableNotAllowedOnComponent() {
+ JavaFileObject someComponent =
+ JavaFileObjects.forSourceLines(
+ "test.SomeComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Reusable;",
+ "",
+ "@Reusable",
+ "@Component",
+ "interface SomeComponent {}");
+ Compilation compilation = daggerCompiler().compile(someComponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Reusable cannot be applied to components or subcomponents")
+ .inFile(someComponent)
+ .onLine(6);
+ }
+
+ @Test
+ public void reusableNotAllowedOnSubcomponent() {
+ JavaFileObject someSubcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.SomeComponent",
+ "package test;",
+ "",
+ "import dagger.Reusable;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Reusable",
+ "@Subcomponent",
+ "interface SomeSubcomponent {}");
+ Compilation compilation = daggerCompiler().compile(someSubcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Reusable cannot be applied to components or subcomponents")
+ .inFile(someSubcomponent)
+ .onLine(6);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
new file mode 100644
index 0000000..3fb0e9c
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SetBindingRequestFulfillmentTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public SetBindingRequestFulfillmentTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void setBindings() {
+ JavaFileObject emptySetModuleFile = JavaFileObjects.forSourceLines("test.EmptySetModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "abstract class EmptySetModule {",
+ " @Multibinds abstract Set<Object> objects();",
+ "",
+ " @Provides @ElementsIntoSet",
+ " static Set<String> emptySet() { ",
+ " return Collections.emptySet();",
+ " }",
+ "}");
+ JavaFileObject setModuleFile = JavaFileObjects.forSourceLines("test.SetModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "final class SetModule {",
+ " @Provides @IntoSet static String string() { return \"\"; }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {EmptySetModule.class, SetModule.class})",
+ "interface TestComponent {",
+ " Set<String> strings();",
+ " Set<Object> objects();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.SetBuilder;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public Set<String> strings() {",
+ " return SetBuilder.<String>newSetBuilder(2)",
+ " .addAll(EmptySetModule_EmptySetFactory.emptySet())",
+ " .add(SetModule_StringFactory.string())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Set<Object> objects() {",
+ " return Collections.<Object>emptySet();",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompilerWithoutGuava().compile(emptySetModuleFile, setModuleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void inaccessible() {
+ JavaFileObject inaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible",
+ "package other;",
+ "",
+ "class Inaccessible {}");
+ JavaFileObject inaccessible2 =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible2",
+ "package other;",
+ "",
+ "class Inaccessible2 {}");
+ JavaFileObject usesInaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.UsesInaccessible",
+ "package other;",
+ "",
+ "import java.util.Set;",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesInaccessible {",
+ " @Inject UsesInaccessible(Set<Inaccessible> set1, Set<Inaccessible2> set2) {}",
+ "}");
+
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.TestModule",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "public abstract class TestModule {",
+ " @Multibinds abstract Set<Inaccessible> objects();",
+ "",
+ " @Provides @ElementsIntoSet",
+ " static Set<Inaccessible2> emptySet() { ",
+ " return Collections.emptySet();",
+ " }",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "import other.TestModule;",
+ "import other.UsesInaccessible;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " UsesInaccessible usesInaccessible();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.SetBuilder;",
+ "import other.TestModule_EmptySetFactory;",
+ "import other.UsesInaccessible;",
+ "import other.UsesInaccessible_Factory;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Set getSetOfInaccessible2() {",
+ " return SetBuilder.newSetBuilder(1)",
+ " .addAll(TestModule_EmptySetFactory.emptySet())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public UsesInaccessible usesInaccessible() {",
+ " return UsesInaccessible_Factory.newInstance(",
+ " (Set) Collections.emptySet(),",
+ " (Set) getSetOfInaccessible2());",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompilerWithoutGuava()
+ .compile(module, inaccessible, inaccessible2, usesInaccessible, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void subcomponentOmitsInheritedBindings() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.multibindings.StringKey;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides @IntoSet static Object parentObject() {",
+ " return \"parent object\";",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " Set<Object> objectSet();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ "",
+ "import dagger.internal.Preconditions;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ " private DaggerParent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Parent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Child child() {",
+ " return new ChildImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " @Deprecated",
+ " public Builder parentModule(ParentModule parentModule) {",
+ " Preconditions.checkNotNull(parentModule);",
+ " return this;",
+ " }",
+ "",
+ " public Parent build() {",
+ " return new DaggerParent();",
+ " }",
+ " }",
+ "",
+ " private final class ChildImpl implements Child {",
+ " private ChildImpl() {}",
+ "",
+ " @Override",
+ " public Set<Object> objectSet() {",
+ " return Collections.<Object>singleton(",
+ " ParentModule_ParentObjectFactory.parentObject());",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation = daggerCompilerWithoutGuava().compile(parent, parentModule, child);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+
+ private Compiler daggerCompilerWithoutGuava() {
+ return daggerCompiler()
+ .withOptions(compilerMode.javacopts().append(CLASS_PATH_WITHOUT_GUAVA_OPTION));
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
new file mode 100644
index 0000000..7a47393
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SetBindingRequestFulfillmentWithGuavaTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public SetBindingRequestFulfillmentWithGuavaTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void setBindings() {
+ JavaFileObject emptySetModuleFile = JavaFileObjects.forSourceLines("test.EmptySetModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "abstract class EmptySetModule {",
+ " @Multibinds abstract Set<Object> objects();",
+ "",
+ " @Provides @ElementsIntoSet",
+ " static Set<String> emptySet() { ",
+ " return Collections.emptySet();",
+ " }",
+ " @Provides @ElementsIntoSet",
+ " static Set<Integer> onlyContributionIsElementsIntoSet() { ",
+ " return Collections.emptySet();",
+ " }",
+ "}");
+ JavaFileObject setModuleFile = JavaFileObjects.forSourceLines("test.SetModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "final class SetModule {",
+ " @Provides @IntoSet static String string() { return \"\"; }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = {EmptySetModule.class, SetModule.class})",
+ "interface TestComponent {",
+ " Set<String> strings();",
+ " Set<Object> objects();",
+ " Set<Integer> onlyContributionIsElementsIntoSet();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public Set<String> strings() {",
+ " return ImmutableSet.<String>builderWithExpectedSize(2)",
+ " .addAll(EmptySetModule_EmptySetFactory.emptySet())",
+ " .add(SetModule_StringFactory.string())",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Set<Object> objects() {",
+ " return ImmutableSet.<Object>of();",
+ " }",
+ "",
+ " @Override",
+ " public Set<Integer> onlyContributionIsElementsIntoSet() {",
+ " return ImmutableSet.<Integer>copyOf(",
+ " EmptySetModule_OnlyContributionIsElementsIntoSetFactory",
+ " .onlyContributionIsElementsIntoSet());",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(emptySetModuleFile, setModuleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void inaccessible() {
+ JavaFileObject inaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible",
+ "package other;",
+ "",
+ "class Inaccessible {}");
+ JavaFileObject inaccessible2 =
+ JavaFileObjects.forSourceLines(
+ "other.Inaccessible2",
+ "package other;",
+ "",
+ "class Inaccessible2 {}");
+ JavaFileObject usesInaccessible =
+ JavaFileObjects.forSourceLines(
+ "other.UsesInaccessible",
+ "package other;",
+ "",
+ "import java.util.Set;",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesInaccessible {",
+ " @Inject UsesInaccessible(Set<Inaccessible> set1, Set<Inaccessible2> set2) {}",
+ "}");
+
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.TestModule",
+ "package other;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import dagger.multibindings.Multibinds;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "public abstract class TestModule {",
+ " @Multibinds abstract Set<Inaccessible> objects();",
+ "",
+ " @Provides @ElementsIntoSet",
+ " static Set<Inaccessible2> emptySet() { ",
+ " return Collections.emptySet();",
+ " }",
+ "}");
+ JavaFileObject componentFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "import other.TestModule;",
+ "import other.UsesInaccessible;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " UsesInaccessible usesInaccessible();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import other.TestModule_EmptySetFactory;",
+ "import other.UsesInaccessible;",
+ "import other.UsesInaccessible_Factory;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Set getSetOfInaccessible2() {",
+ " return ImmutableSet.copyOf(TestModule_EmptySetFactory.emptySet());",
+ " }",
+ "",
+ " @Override",
+ " public UsesInaccessible usesInaccessible() {",
+ " return UsesInaccessible_Factory.newInstance(",
+ " (Set) ImmutableSet.of(),",
+ " (Set) getSetOfInaccessible2());",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(module, inaccessible, inaccessible2, usesInaccessible, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void subcomponentOmitsInheritedBindings() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoSet;",
+ "import dagger.multibindings.StringKey;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides @IntoSet static Object parentObject() {",
+ " return \"parent object\";",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " Set<Object> objectSet();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParent implements Parent {",
+ " private final class ChildImpl implements Child {",
+ " @Override",
+ " public Set<Object> objectSet() {",
+ " return ImmutableSet.<Object>of(",
+ " ParentModule_ParentObjectFactory.parentObject());",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void productionComponents() {
+ JavaFileObject emptySetModuleFile = JavaFileObjects.forSourceLines("test.EmptySetModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "abstract class EmptySetModule {",
+ " @Provides @ElementsIntoSet",
+ " static Set<String> emptySet() { ",
+ " return Collections.emptySet();",
+ " }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "import java.util.Set;",
+ "",
+ "@ProductionComponent(modules = EmptySetModule.class)",
+ "interface TestComponent {",
+ " ListenableFuture<Set<String>> strings();",
+ "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import com.google.common.collect.ImmutableSet;",
+ "import com.google.common.util.concurrent.Futures;",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.internal.CancellationListener;",
+ "import java.util.Set;",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent, "
+ + "CancellationListener {",
+ " private DaggerTestComponent() {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " private Set<String> getSetOfString() {",
+ " return ImmutableSet.<String>copyOf(",
+ " EmptySetModule_EmptySetFactory.emptySet());",
+ " }",
+ "",
+ " @Override",
+ " public ListenableFuture<Set<String>> strings() {",
+ " return Futures.immediateFuture(getSetOfString());",
+ " }",
+ "",
+ " @Override",
+ " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
+ "",
+ " static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent();",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(emptySetModuleFile, componentFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SourceFilesTest.java b/javatests/dagger/internal/codegen/SourceFilesTest.java
new file mode 100644
index 0000000..c7fe998
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SourceFilesTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.internal.codegen.SourceFiles.simpleVariableName;
+
+import com.google.testing.compile.CompilationRule;
+import java.util.List;
+import javax.lang.model.element.TypeElement;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link SourceFiles}. */
+@RunWith(JUnit4.class)
+public final class SourceFilesTest {
+ @Rule public CompilationRule compilation = new CompilationRule();
+
+ private TypeElement typeElementFor(Class<?> clazz) {
+ return compilation.getElements().getTypeElement(clazz.getCanonicalName());
+ }
+
+ private static final class Int {}
+
+ @Test
+ public void testSimpleVariableName_typeCollisions() {
+ // a handful of boxed types
+ assertThat(simpleVariableName(typeElementFor(Long.class))).isEqualTo("l");
+ assertThat(simpleVariableName(typeElementFor(Double.class))).isEqualTo("d");
+ // not a boxed type type, but a custom type might collide
+ assertThat(simpleVariableName(typeElementFor(Int.class))).isEqualTo("i");
+ // void is the weird pseudo-boxed type
+ assertThat(simpleVariableName(typeElementFor(Void.class))).isEqualTo("v");
+ // reflective types
+ assertThat(simpleVariableName(typeElementFor(Class.class))).isEqualTo("clazz");
+ assertThat(simpleVariableName(typeElementFor(Package.class))).isEqualTo("pkg");
+ }
+
+ private static final class For {}
+
+ private static final class Goto {}
+
+ @Test
+ public void testSimpleVariableName_randomKeywords() {
+ assertThat(simpleVariableName(typeElementFor(For.class))).isEqualTo("for_");
+ assertThat(simpleVariableName(typeElementFor(Goto.class))).isEqualTo("goto_");
+ }
+
+ @Test
+ public void testSimpleVariableName() {
+ assertThat(simpleVariableName(typeElementFor(Object.class))).isEqualTo("object");
+ assertThat(simpleVariableName(typeElementFor(List.class))).isEqualTo("list");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
new file mode 100644
index 0000000..5dab4c4
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link dagger.Subcomponent.Builder} validation. */
+@RunWith(JUnit4.class)
+public class SubcomponentBuilderValidationTest {
+
+ private static final ErrorMessages.ComponentCreatorMessages MSGS =
+ creatorMessagesFor(SUBCOMPONENT_BUILDER);
+
+ @Test
+ public void testMoreThanOneArgFails() {
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " Builder set(String s, Integer i);",
+ " Builder set(Number n, Double d);",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.setterMethodsMustTakeOneArg())
+ .inFile(childComponentFile)
+ .onLine(10);
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.setterMethodsMustTakeOneArg())
+ .inFile(childComponentFile)
+ .onLine(11);
+ }
+
+ @Test
+ public void testInheritedMoreThanOneArgFails() {
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " interface Parent {",
+ " ChildComponent build();",
+ " Builder set1(String s, Integer i);",
+ " }",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ MSGS.inheritedSetterMethodsMustTakeOneArg(),
+ "set1(java.lang.String,java.lang.Integer)"))
+ .inFile(childComponentFile)
+ .onLine(13);
+ }
+
+ @Test
+ public void testSetterReturningNonVoidOrBuilderFails() {
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " String set(Integer i);",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.setterMethodsMustReturnVoidOrBuilder())
+ .inFile(childComponentFile)
+ .onLine(10);
+ }
+
+ @Test
+ public void testInheritedSetterReturningNonVoidOrBuilderFails() {
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " interface Parent {",
+ " ChildComponent build();",
+ " String set(Integer i);",
+ " }",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ MSGS.inheritedSetterMethodsMustReturnVoidOrBuilder(), "set(java.lang.Integer)"))
+ .inFile(childComponentFile)
+ .onLine(13);
+ }
+
+ @Test
+ public void testGenericsOnSetterMethodFails() {
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " <T> Builder set(T t);",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(MSGS.methodsMayNotHaveTypeParameters())
+ .inFile(childComponentFile)
+ .onLine(10);
+ }
+
+ @Test
+ public void testGenericsOnInheritedSetterMethodFails() {
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " interface Parent {",
+ " ChildComponent build();",
+ " <T> Builder set(T t);",
+ " }",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = daggerCompiler().compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(MSGS.inheritedMethodsMayNotHaveTypeParameters(), "<T>set(T)"))
+ .inFile(childComponentFile)
+ .onLine(13);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
new file mode 100644
index 0000000..de0067f
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Sets.cartesianProduct;
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.testing.compile.Compilation;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SubcomponentCreatorRequestFulfillmentTest extends ComponentCreatorTestHelper {
+ @Parameters(name = "compilerMode={0}, creatorKind={1}")
+ public static Collection<Object[]> parameters() {
+ Set<List<Object>> params =
+ cartesianProduct(
+ immutableEnumSet(DEFAULT_MODE, FAST_INIT_MODE),
+ immutableEnumSet(SUBCOMPONENT_FACTORY, SUBCOMPONENT_BUILDER));
+ return ImmutableList.copyOf(Iterables.transform(params, Collection::toArray));
+ }
+
+ public SubcomponentCreatorRequestFulfillmentTest(
+ CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation) {
+ super(compilerMode, componentCreatorAnnotation);
+ }
+
+ @Test
+ public void testInlinedSubcomponentCreators_componentMethod() {
+ JavaFileObject subcomponent =
+ preprocessedJavaFile(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Sub {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Sub build();",
+ " }",
+ "}");
+ JavaFileObject usesSubcomponent =
+ preprocessedJavaFile(
+ "test.UsesSubcomponent",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class UsesSubcomponent {",
+ " @Inject UsesSubcomponent(Sub.Builder subBuilder) {}",
+ "}");
+ JavaFileObject component =
+ preprocessedJavaFile(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface C {",
+ " Sub.Builder sBuilder();",
+ " UsesSubcomponent usesSubcomponent();",
+ "}");
+
+ JavaFileObject generatedComponent =
+ preprocessedJavaFile(
+ "test.DaggerC",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerC implements C {",
+ " @Override",
+ " public Sub.Builder sBuilder() {",
+ " return new SubBuilder();",
+ " }",
+ "",
+ " @Override",
+ " public UsesSubcomponent usesSubcomponent() {",
+ " return new UsesSubcomponent(new SubBuilder());",
+ " }",
+ "",
+ " private final class SubBuilder implements Sub.Builder {",
+ " @Override",
+ " public Sub build() {",
+ " return new SubImpl();",
+ " }",
+ " }",
+ "",
+ " private final class SubImpl implements Sub {",
+ " private SubImpl() {}",
+ " }",
+ "}");
+
+ Compilation compilation = compile(subcomponent, usesSubcomponent, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerC")
+ .containsElementsIn(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
new file mode 100644
index 0000000..b5753d4
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
@@ -0,0 +1,987 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
+import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.ComponentKind.SUBCOMPONENT;
+import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
+import static dagger.internal.codegen.ErrorMessages.componentMessagesFor;
+import static dagger.internal.codegen.TestUtils.message;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for {@link dagger.Subcomponent.Builder} validation. */
+@RunWith(Parameterized.class)
+public class SubcomponentCreatorValidationTest extends ComponentCreatorTestHelper {
+ @Parameters(name = "creatorKind={0}")
+ public static Collection<Object[]> parameters() {
+ return ImmutableList.copyOf(new Object[][] {{SUBCOMPONENT_BUILDER}, {SUBCOMPONENT_FACTORY}});
+ }
+
+ public SubcomponentCreatorValidationTest(ComponentCreatorAnnotation componentCreatorAnnotation) {
+ super(DEFAULT_MODE, componentCreatorAnnotation);
+ }
+
+ @Test
+ public void testRefSubcomponentAndSubCreatorFails() {
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent child();",
+ " ChildComponent.Builder builder();",
+ "}");
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {",
+ " @Subcomponent.Builder",
+ " static interface Builder {",
+ " ChildComponent build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ moreThanOneRefToSubcomponent(),
+ "test.ChildComponent",
+ process("[child(), builder()]")))
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testRefSubCreatorTwiceFails() {
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder builder1();",
+ " ChildComponent.Builder builder2();",
+ "}");
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {",
+ " @Subcomponent.Builder",
+ " static interface Builder {",
+ " ChildComponent build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ moreThanOneRefToSubcomponent(),
+ "test.ChildComponent", process("[builder1(), builder2()]")))
+ .inFile(componentFile);
+ }
+
+ @Test
+ public void testMoreThanOneCreatorFails() {
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder1 build();",
+ "}");
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {",
+ " @Subcomponent.Builder",
+ " static interface Builder1 {",
+ " ChildComponent build();",
+ " }",
+ "",
+ " @Subcomponent.Builder",
+ " static interface Builder2 {",
+ " ChildComponent build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ componentMessagesFor(SUBCOMPONENT).moreThanOne(),
+ process("[test.ChildComponent.Builder1, test.ChildComponent.Builder2]")))
+ .inFile(childComponentFile);
+ }
+
+ @Test
+ public void testMoreThanOneCreatorFails_differentTypes() {
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder build();",
+ "}");
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {",
+ " @Subcomponent.Builder",
+ " static interface Builder {",
+ " ChildComponent build();",
+ " }",
+ "",
+ " @Subcomponent.Factory",
+ " static interface Factory {",
+ " ChildComponent create();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ componentMessagesFor(SUBCOMPONENT).moreThanOne(),
+ "[test.ChildComponent.Builder, test.ChildComponent.Factory]"))
+ .inFile(childComponentFile);
+ }
+
+ @Test
+ public void testCreatorGenericsFails() {
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder build();",
+ "}");
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {",
+ " @Subcomponent.Builder",
+ " interface Builder<T> {",
+ " ChildComponent build();",
+ " }",
+ "}");
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.generics()).inFile(childComponentFile);
+ }
+
+ @Test
+ public void testCreatorNotInComponentFails() {
+ JavaFileObject builder = preprocessedJavaFile("test.Builder",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent.Builder",
+ "interface Builder {}");
+ Compilation compilation = compile(builder);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.mustBeInComponent()).inFile(builder);
+ }
+
+ @Test
+ public void testCreatorMissingFactoryMethodFails() {
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder builder();",
+ "}");
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {}",
+ "}");
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.missingFactoryMethod())
+ .inFile(childComponentFile);
+ }
+
+ @Test
+ public void testPrivateCreatorFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " private interface Builder {}",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.isPrivate()).inFile(childComponentFile);
+ }
+
+ @Test
+ public void testNonStaticCreatorFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " abstract class Builder {}",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(messages.mustBeStatic()).inFile(childComponentFile);
+ }
+
+ @Test
+ public void testNonAbstractCreatorFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " static class Builder {}",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.mustBeAbstract())
+ .inFile(childComponentFile);
+ }
+
+ @Test
+ public void testCreatorOneConstructorWithArgsFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " static abstract class Builder {",
+ " Builder(String unused) {}",
+ " }",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.invalidConstructor())
+ .inFile(childComponentFile);
+ }
+
+ @Test
+ public void testCreatorMoreThanOneConstructorFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " static abstract class Builder {",
+ " Builder() {}",
+ " Builder(String unused) {}",
+ " }",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.invalidConstructor())
+ .inFile(childComponentFile);
+ }
+
+ @Test
+ public void testCreatorEnumFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " enum Builder {}",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.mustBeClassOrInterface())
+ .inFile(childComponentFile);
+ }
+
+ @Test
+ public void testCreatorFactoryMethodReturnsWrongTypeFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " String build();",
+ " }",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(messages.factoryMethodMustReturnComponentType())
+ .inFile(childComponentFile)
+ .onLine(9);
+ }
+
+ @Test
+ public void testInheritedCreatorFactoryMethodReturnsWrongTypeFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " interface Parent {",
+ " String build();",
+ " }",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.inheritedFactoryMethodMustReturnComponentType(), process("build")))
+ .inFile(childComponentFile)
+ .onLine(12);
+ }
+
+ @Test
+ public void testTwoFactoryMethodsFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " ChildComponent build1();",
+ " }",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(String.format(messages.twoFactoryMethods(), process("build()")))
+ .inFile(childComponentFile)
+ .onLine(10);
+ }
+
+ @Test
+ public void testInheritedTwoFactoryMethodsFails() {
+ JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " interface Parent {",
+ " ChildComponent build();",
+ " ChildComponent build1();",
+ " }",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder extends Parent {}",
+ "}");
+ Compilation compilation = compile(childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.inheritedTwoFactoryMethods(), process("build()"), process("build1()")))
+ .inFile(childComponentFile)
+ .onLine(13);
+ }
+
+ @Test
+ public void testMultipleSettersPerTypeFails() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String s() { return \"\"; }",
+ "}");
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder childComponentBuilder();",
+ "}");
+ JavaFileObject childComponentFile =
+ javaFileBuilder("test.ChildComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "abstract class ChildComponent {",
+ " abstract String s();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " void set1(TestModule s);",
+ " void set2(TestModule s);",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Subcomponent.Factory",
+ " interface Factory {",
+ " ChildComponent create(TestModule m1, TestModule m2);",
+ " }")
+ .addLines( //
+ "}")
+ .build();
+ Compilation compilation = compile(moduleFile, componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ String elements =
+ creatorKind.equals(BUILDER)
+ ? "[void test.ChildComponent.Builder.set1(test.TestModule), "
+ + "void test.ChildComponent.Builder.set2(test.TestModule)]"
+ : "[test.TestModule m1, test.TestModule m2]";
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements))
+ .inFile(childComponentFile)
+ .onLine(11);
+ }
+
+ @Test
+ public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
+ JavaFileObject moduleFile =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String s() { return \"\"; }",
+ "}");
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder childComponentBuilder();",
+ "}");
+ JavaFileObject childComponentFile =
+ javaFileBuilder("test.ChildComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "abstract class ChildComponent {",
+ " abstract String s();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " interface Parent<T> {",
+ " void set1(T t);",
+ " }",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder extends Parent<TestModule> {",
+ " ChildComponent build();",
+ " void set2(TestModule s);",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " interface Parent<C, T> {",
+ " C create(TestModule m1, T t);",
+ " }",
+ "",
+ " @Subcomponent.Factory",
+ " interface Factory extends Parent<ChildComponent, TestModule> {}")
+ .addLines( //
+ "}")
+ .build();
+ Compilation compilation = compile(moduleFile, componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ String elements =
+ creatorKind.equals(BUILDER)
+ ? "[void test.ChildComponent.Builder.set1(test.TestModule), "
+ + "void test.ChildComponent.Builder.set2(test.TestModule)]"
+ : "[test.TestModule m1, test.TestModule t]";
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements))
+ .inFile(childComponentFile)
+ .onLine(15);
+ }
+
+ @Test
+ public void testMultipleSettersPerBoundInstanceTypeFails() {
+ JavaFileObject componentFile =
+ preprocessedJavaFile(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder childComponentBuilder();",
+ "}");
+ JavaFileObject childComponentFile =
+ javaFileBuilder("test.ChildComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {",
+ " abstract String s();",
+ "")
+ .addLinesIf(
+ BUILDER,
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " @BindsInstance void set1(String s);",
+ " @BindsInstance void set2(String s);",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Subcomponent.Factory",
+ " interface Factory {",
+ " ChildComponent create(",
+ " @BindsInstance String s1, @BindsInstance String s2);",
+ " }")
+ .addLines( //
+ "}")
+ .build();
+
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ String firstBinding = creatorKind.equals(FACTORY)
+ ? "test.ChildComponent.Factory.create(s1, …)"
+ : "@BindsInstance void test.ChildComponent.Builder.set1(String)";
+ String secondBinding = creatorKind.equals(FACTORY)
+ ? "test.ChildComponent.Factory.create(…, s2)"
+ : "@BindsInstance void test.ChildComponent.Builder.set2(String)";
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "java.lang.String is bound multiple times:",
+ " " + firstBinding,
+ " " + secondBinding,
+ " java.lang.String is provided at",
+ " test.ChildComponent.s() [test.ParentComponent → test.ChildComponent]"))
+ .inFile(componentFile)
+ .onLineContaining("interface ParentComponent {");
+ }
+
+ @Test
+ public void testExtraSettersFails() {
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder build();",
+ "}");
+ JavaFileObject childComponentFile =
+ javaFileBuilder("test.ChildComponent")
+ .addLines(
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import javax.inject.Provider;",
+ "",
+ "@Subcomponent",
+ "abstract class ChildComponent {")
+ .addLinesIf(
+ BUILDER,
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " void set1(String s);",
+ " void set2(Integer s);",
+ " }")
+ .addLinesIf(
+ FACTORY,
+ " @Subcomponent.Factory",
+ " interface Factory {",
+ " ChildComponent create(String s, Integer i);",
+ " }")
+ .addLines( //
+ "}")
+ .build();
+ Compilation compilation = compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ String elements =
+ creatorKind.equals(FACTORY)
+ ? "[String s, Integer i]"
+ : "[void test.ChildComponent.Builder.set1(String),"
+ + " void test.ChildComponent.Builder.set2(Integer)]";
+ assertThat(compilation)
+ .hadErrorContaining(
+ String.format(
+ messages.extraSetters(),
+ elements))
+ .inFile(childComponentFile)
+ .onLine(9);
+ }
+
+ @Test
+ public void testMissingSettersFail() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " TestModule(String unused) {}",
+ " @Provides String s() { return null; }",
+ "}");
+ JavaFileObject module2File = JavaFileObjects.forSourceLines("test.Test2Module",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class Test2Module {",
+ " @Provides Integer i() { return null; }",
+ "}");
+ JavaFileObject module3File = JavaFileObjects.forSourceLines("test.Test3Module",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class Test3Module {",
+ " Test3Module(String unused) {}",
+ " @Provides Double d() { return null; }",
+ "}");
+ JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent.Builder build();",
+ "}");
+ JavaFileObject childComponentFile =
+ preprocessedJavaFile(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = {TestModule.class, Test2Module.class, Test3Module.class})",
+ "interface ChildComponent {",
+ " String string();",
+ " Integer integer();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " ChildComponent build();",
+ " }",
+ "}");
+ Compilation compilation =
+ compile(moduleFile, module2File, module3File, componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ // Ignores Test2Module because we can construct it ourselves.
+ // TODO(sameb): Ignore Test3Module because it's not used within transitive dependencies.
+ String.format(messages.missingSetters(), "[test.TestModule, test.Test3Module]"))
+ .inFile(childComponentFile)
+ .onLine(11);
+ }
+
+ @Test
+ public void covariantFactoryMethodReturnType() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "test.Supertype",
+ "package test;",
+ "",
+ "interface Supertype {",
+ " Foo foo();",
+ "}");
+
+ JavaFileObject subcomponent =
+ preprocessedJavaFile(
+ "test.HasSupertype",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface HasSupertype extends Supertype {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Supertype build();",
+ " }",
+ "}");
+
+ Compilation compilation = compile(foo, supertype, subcomponent);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void covariantFactoryMethodReturnType_hasNewMethod() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar() {}",
+ "}");
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "test.Supertype",
+ "package test;",
+ "",
+ "interface Supertype {",
+ " Foo foo();",
+ "}");
+
+ JavaFileObject subcomponent =
+ preprocessedJavaFile(
+ "test.HasSupertype",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface HasSupertype extends Supertype {",
+ " Bar bar();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Supertype build();",
+ " }",
+ "}");
+
+ Compilation compilation = compile(foo, bar, supertype, subcomponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining(
+ process(
+ "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype "
+ + "declares additional component method(s): bar(). In order to provide "
+ + "type-safe access to these methods, override build() to return "
+ + "test.HasSupertype"))
+ .inFile(subcomponent)
+ .onLine(11);
+ }
+
+ @Test
+ public void covariantFactoryMethodReturnType_hasNewMethod_buildMethodInherited() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar() {}",
+ "}");
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "test.Supertype",
+ "package test;",
+ "",
+ "interface Supertype {",
+ " Foo foo();",
+ "}");
+
+ JavaFileObject creatorSupertype =
+ preprocessedJavaFile(
+ "test.CreatorSupertype",
+ "package test;",
+ "",
+ "interface CreatorSupertype {",
+ " Supertype build();",
+ "}");
+
+ JavaFileObject subcomponent =
+ preprocessedJavaFile(
+ "test.HasSupertype",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface HasSupertype extends Supertype {",
+ " Bar bar();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder extends CreatorSupertype {}",
+ "}");
+
+ Compilation compilation = compile(foo, bar, supertype, creatorSupertype, subcomponent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining(
+ process(
+ "[test.CreatorSupertype.build()] test.HasSupertype.Builder.build() returns "
+ + "test.Supertype, but test.HasSupertype declares additional component "
+ + "method(s): bar(). In order to provide type-safe access to these methods, "
+ + "override build() to return test.HasSupertype"));
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
new file mode 100644
index 0000000..ad08160
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
@@ -0,0 +1,1165 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Collection;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SubcomponentValidationTest {
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public SubcomponentValidationTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test public void factoryMethod_missingModulesWithParameters() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " ChildComponent newChildComponent();",
+ "}");
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = ModuleWithParameters.class)",
+ "interface ChildComponent {",
+ " Object object();",
+ "}");
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class ModuleWithParameters {",
+ " private final Object object;",
+ "",
+ " ModuleWithParameters(Object object) {",
+ " this.object = object;",
+ " }",
+ "",
+ " @Provides Object object() {",
+ " return object;",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(componentFile, childComponentFile, moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.ChildComponent requires modules which have no visible default constructors. "
+ + "Add the following modules as parameters to this method: "
+ + "test.ModuleWithParameters")
+ .inFile(componentFile)
+ .onLineContaining("ChildComponent newChildComponent();");
+ }
+
+ @Test
+ public void factoryMethod_grandchild() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " ChildComponent newChildComponent();",
+ "}");
+ JavaFileObject childComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {",
+ " GrandchildComponent newGrandchildComponent();",
+ "}");
+ JavaFileObject grandchildComponent =
+ JavaFileObjects.forSourceLines(
+ "test.GrandchildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = GrandchildModule.class)",
+ "interface GrandchildComponent {",
+ " Object object();",
+ "}");
+ JavaFileObject grandchildModule =
+ JavaFileObjects.forSourceLines(
+ "test.GrandchildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class GrandchildModule {",
+ " private final Object object;",
+ "",
+ " GrandchildModule(Object object) {",
+ " this.object = object;",
+ " }",
+ "",
+ " @Provides Object object() {",
+ " return object;",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(component, childComponent, grandchildComponent, grandchildModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[test.ChildComponent.newGrandchildComponent()] "
+ + "test.GrandchildComponent requires modules which have no visible default "
+ + "constructors. Add the following modules as parameters to this method: "
+ + "test.GrandchildModule")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test public void factoryMethod_nonModuleParameter() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " ChildComponent newChildComponent(String someRandomString);",
+ "}");
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Subcomponent factory methods may only accept modules, but java.lang.String is not.")
+ .inFile(componentFile)
+ .onLine(7)
+ .atColumn(43);
+ }
+
+ @Test public void factoryMethod_duplicateParameter() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "final class TestModule {}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " ChildComponent newChildComponent(TestModule testModule1, TestModule testModule2);",
+ "}");
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "interface ChildComponent {}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(moduleFile, componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "A module may only occur once an an argument in a Subcomponent factory method, "
+ + "but test.TestModule was already passed.")
+ .inFile(componentFile)
+ .onLine(7)
+ .atColumn(71);
+ }
+
+ @Test public void factoryMethod_superflouousModule() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "final class TestModule {}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " ChildComponent newChildComponent(TestModule testModule);",
+ "}");
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface ChildComponent {}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(moduleFile, componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.TestModule is present as an argument to the test.ChildComponent factory method, "
+ + "but is not one of the modules used to implement the subcomponent.")
+ .inFile(componentFile)
+ .onLine(7);
+ }
+
+ @Test public void missingBinding() {
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class TestModule {",
+ " @Provides String provideString(int i) {",
+ " return Integer.toString(i);",
+ " }",
+ "}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " ChildComponent newChildComponent();",
+ "}");
+ JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = TestModule.class)",
+ "interface ChildComponent {",
+ " String getString();",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(moduleFile, componentFile, childComponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.lang.Integer cannot be provided without an @Inject constructor or an "
+ + "@Provides-annotated method")
+ .inFile(componentFile)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test public void subcomponentOnConcreteType() {
+ JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.NotASubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "final class NotASubcomponent {}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(subcomponentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("interface");
+ }
+
+ @Test public void scopeMismatch() {
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Component",
+ "@Singleton",
+ "interface ParentComponent {",
+ " ChildComponent childComponent();",
+ "}");
+ JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface ChildComponent {",
+ " Object getObject();",
+ "}");
+ JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "final class ChildModule {",
+ " @Provides @Singleton Object provideObject() { return null; }",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(componentFile, subcomponentFile, moduleFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("@Singleton");
+ }
+
+ @Test
+ public void delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent() {
+ JavaFileObject parentComponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface ParentComponent {",
+ " ChildComponent childComponent();",
+ " Dep1 getDep1();",
+ " Dep2 getDep2();",
+ "}");
+ JavaFileObject childComponentFile =
+ JavaFileObjects.forSourceLines(
+ "test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface ChildComponent {",
+ " Object getObject();",
+ "}");
+ JavaFileObject childModuleFile =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "final class ChildModule {",
+ " @Provides Object provideObject(A a) { return null; }",
+ "}");
+ JavaFileObject aFile =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class A {",
+ " @Inject public A(NeedsDep1 a, Dep1 b, Dep2 c) { }",
+ " @Inject public void methodA() { }",
+ "}");
+ JavaFileObject needsDep1File =
+ JavaFileObjects.forSourceLines(
+ "test.NeedsDep1",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class NeedsDep1 {",
+ " @Inject public NeedsDep1(Dep1 d) { }",
+ "}");
+ JavaFileObject dep1File =
+ JavaFileObjects.forSourceLines(
+ "test.Dep1",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "final class Dep1 {",
+ " @Inject public Dep1() { }",
+ " @Inject public void dep1Method() { }",
+ "}");
+ JavaFileObject dep2File =
+ JavaFileObjects.forSourceLines(
+ "test.Dep2",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "final class Dep2 {",
+ " @Inject public Dep2() { }",
+ " @Inject public void dep2Method() { }",
+ "}");
+
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerParentComponent")
+ .addLines(
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParentComponent implements ParentComponent {")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.dep1Provider = DoubleCheck.provider(Dep1_Factory.create());",
+ " this.dep2Provider = DoubleCheck.provider(Dep2_Factory.create());",
+ " }",
+ "")
+ .addLines(
+ " @Override", //
+ " public Dep1 getDep1() {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " Object local = dep1;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = dep1;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = injectDep1(Dep1_Factory.newInstance());",
+ " dep1 = DoubleCheck.reentrantCheck(dep1, local);",
+ " }",
+ " }",
+ " }",
+ " return (Dep1) local;")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " return dep1Provider.get();")
+ .addLines(
+ " }", //
+ "",
+ " @Override",
+ " public Dep2 getDep2() {")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " Object local = dep2;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = dep2;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = injectDep2(Dep2_Factory.newInstance());",
+ " dep2 = DoubleCheck.reentrantCheck(dep2, local);",
+ " }",
+ " }",
+ " }",
+ " return (Dep2) local;")
+ .addLinesIn(
+ DEFAULT_MODE, //
+ " return dep2Provider.get();")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public ChildComponent childComponent() {",
+ " return new ChildComponentImpl();",
+ " }",
+ "")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " @CanIgnoreReturnValue",
+ " private Dep1 injectDep1(Dep1 instance) {",
+ " Dep1_MembersInjector.injectDep1Method(instance);",
+ " return instance;",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private Dep2 injectDep2(Dep2 instance) {",
+ " Dep2_MembersInjector.injectDep2Method(instance);",
+ " return instance;",
+ " }")
+ .addLines(
+ "",
+ " private final class ChildComponentImpl implements ChildComponent {",
+ " private final ChildModule childModule;",
+ "",
+ " private ChildComponentImpl() {",
+ " this.childModule = new ChildModule();",
+ " }",
+ "")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " private NeedsDep1 getNeedsDep1() {",
+ " return new NeedsDep1(DaggerParentComponent.this.dep1Provider.get());",
+ " }")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " private NeedsDep1 getNeedsDep1() {",
+ " return new NeedsDep1(DaggerParentComponent.this.getDep1());",
+ " }")
+ .addLines(
+ " private A getA() {",
+ " return injectA(",
+ " A_Factory.newInstance(",
+ " getNeedsDep1(),")
+ .addLinesIn(
+ DEFAULT_MODE,
+ " DaggerParentComponent.this.dep1Provider.get(),",
+ " DaggerParentComponent.this.dep2Provider.get()));")
+ .addLinesIn(
+ FAST_INIT_MODE,
+ " DaggerParentComponent.this.getDep1(),",
+ " DaggerParentComponent.this.getDep2()));")
+ .addLines(
+ " }",
+ "",
+ " @Override",
+ " public Object getObject() {",
+ " return ChildModule_ProvideObjectFactory.provideObject(",
+ " childModule, getA());",
+ " }",
+ "",
+ " @CanIgnoreReturnValue",
+ " private A injectA(A instance) {",
+ " A_MembersInjector.injectMethodA(instance);",
+ " return instance;",
+ " }",
+ " }",
+ "}")
+ .build();
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(
+ parentComponentFile,
+ childComponentFile,
+ childModuleFile,
+ aFile,
+ needsDep1File,
+ dep1File,
+ dep2File);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParentComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void multipleSubcomponentsWithSameSimpleNamesCanExistInSameComponent() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " Foo.Sub newInstanceSubcomponent();",
+ " NoConflict newNoConflictSubcomponent();",
+ "}");
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "interface Foo {",
+ " @Subcomponent interface Sub {",
+ " Bar.Sub newBarSubcomponent();",
+ " }",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "interface Bar {",
+ " @Subcomponent interface Sub {",
+ " test.subpackage.Sub newSubcomponentInSubpackage();",
+ " }",
+ "}");
+ JavaFileObject baz =
+ JavaFileObjects.forSourceLines(
+ "test.subpackage.Sub",
+ "package test.subpackage;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent public interface Sub {}");
+ JavaFileObject noConflict =
+ JavaFileObjects.forSourceLines(
+ "test.NoConflict",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent interface NoConflict {}");
+
+ JavaFileObject componentGeneratedFile =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParentComponent",
+ "package test;",
+ "",
+ "import test.subpackage.Sub;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParentComponent implements ParentComponent {",
+ " @Override",
+ " public Foo.Sub newInstanceSubcomponent() {",
+ " return new F_SubImpl();",
+ " }",
+ "",
+ " @Override",
+ " public NoConflict newNoConflictSubcomponent() {",
+ " return new NoConflictImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " public ParentComponent build() {",
+ " return new DaggerParentComponent();",
+ " }",
+ " }",
+ "",
+ " private final class F_SubImpl implements Foo.Sub {",
+ " @Override",
+ " public Bar.Sub newBarSubcomponent() {",
+ " return new B_SubImpl();",
+ " }",
+ "",
+ " private final class B_SubImpl implements Bar.Sub {",
+ " @Override",
+ " public Sub newSubcomponentInSubpackage() {",
+ " return new ts_SubImpl();",
+ " }",
+ "",
+ " private final class ts_SubImpl implements Sub {}",
+ " }",
+ " }",
+ "",
+ " private final class NoConflictImpl implements NoConflict {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(parent, foo, bar, baz, noConflict);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParentComponent")
+ .containsElementsIn(componentGeneratedFile);
+ }
+
+ @Test
+ public void subcomponentSimpleNamesDisambiguated() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " Sub newSubcomponent();",
+ "}");
+ JavaFileObject sub =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent interface Sub {",
+ " test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();",
+ "}");
+ JavaFileObject deepSub =
+ JavaFileObjects.forSourceLines(
+ "test.deep.many.levels.that.match.test.Sub",
+ "package test.deep.many.levels.that.match.test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent public interface Sub {}");
+
+ JavaFileObject componentGeneratedFile =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParentComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParentComponent implements ParentComponent {",
+ " @Override",
+ " public Sub newSubcomponent() {",
+ " return new t_SubImpl();",
+ " }",
+ "",
+ " static final class Builder {",
+ " public ParentComponent build() {",
+ " return new DaggerParentComponent();",
+ " }",
+ " }",
+ "",
+ " private final class t_SubImpl implements Sub {",
+ " @Override",
+ " public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {",
+ " return new tdmltmt_SubImpl();",
+ " }",
+ "",
+ " private final class tdmltmt_SubImpl implements ",
+ " test.deep.many.levels.that.match.test.Sub {",
+ " private tdmltmt_SubImpl() {}",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParentComponent")
+ .containsElementsIn(componentGeneratedFile);
+ }
+
+ @Test
+ public void subcomponentSimpleNamesDisambiguatedInRoot() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "ParentComponent",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " Sub newSubcomponent();",
+ "}");
+ JavaFileObject sub =
+ JavaFileObjects.forSourceLines(
+ "Sub",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent interface Sub {",
+ " test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();",
+ "}");
+ JavaFileObject deepSub =
+ JavaFileObjects.forSourceLines(
+ "test.deep.many.levels.that.match.test.Sub",
+ "package test.deep.many.levels.that.match.test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent public interface Sub {}");
+
+ JavaFileObject componentGeneratedFile =
+ JavaFileObjects.forSourceLines(
+ "DaggerParentComponent",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParentComponent implements ParentComponent {",
+ " @Override",
+ " public Sub newSubcomponent() {",
+ " return new $_SubImpl();",
+ " }",
+ "",
+ " private final class $_SubImpl implements Sub {",
+ " private $_SubImpl() {}",
+ "",
+ " @Override",
+ " public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {",
+ " return new tdmltmt_SubImpl();",
+ " }",
+ "",
+ " private final class tdmltmt_SubImpl implements ",
+ " test.deep.many.levels.that.match.test.Sub {",
+ " private tdmltmt_SubImpl() {}",
+ " }",
+ " }",
+ "}",
+ "");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("DaggerParentComponent")
+ .containsElementsIn(componentGeneratedFile);
+ }
+
+ @Test
+ public void subcomponentImplNameUsesFullyQualifiedClassNameIfNecessary() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface ParentComponent {",
+ " top1.a.b.c.d.E.F.Sub top1();",
+ " top2.a.b.c.d.E.F.Sub top2();",
+ "}");
+ JavaFileObject top1 =
+ JavaFileObjects.forSourceLines(
+ "top1.a.b.c.d.E",
+ "package top1.a.b.c.d;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "public interface E {",
+ " interface F {",
+ " @Subcomponent interface Sub {}",
+ " }",
+ "}");
+ JavaFileObject top2 =
+ JavaFileObjects.forSourceLines(
+ "top2.a.b.c.d.E",
+ "package top2.a.b.c.d;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "public interface E {",
+ " interface F {",
+ " @Subcomponent interface Sub {}",
+ " }",
+ "}");
+
+ JavaFileObject componentGeneratedFile =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParentComponent",
+ "package test;",
+ "",
+ "import top1.a.b.c.d.E;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerParentComponent implements ParentComponent {",
+ " @Override",
+ " public E.F.Sub top1() {",
+ " return new F_SubImpl();",
+ " }",
+ "",
+ " @Override",
+ " public top2.a.b.c.d.E.F.Sub top2() {",
+ " return new F2_SubImpl();",
+ " }",
+ "",
+ " private final class F_SubImpl implements E.F.Sub {",
+ " private F_SubImpl() {}",
+ " }",
+ " private final class F2_SubImpl implements top2.a.b.c.d.E.F.Sub {",
+ " private F2_SubImpl() {}",
+ " }",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, top1, top2);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerParentComponent")
+ .containsElementsIn(componentGeneratedFile);
+ }
+
+ @Test
+ public void parentComponentNameShouldNotBeDisambiguatedWhenItConflictsWithASubcomponent() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface C {",
+ " test.Foo.C newInstanceC();",
+ "}");
+ JavaFileObject subcomponentWithSameSimpleNameAsParent =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "interface Foo {",
+ " @Subcomponent interface C {}",
+ "}");
+
+ JavaFileObject componentGeneratedFile =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerC",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerC implements C {",
+ " @Override",
+ " public Foo.C newInstanceC() {",
+ " return new F_CImpl();",
+ " }",
+ "",
+ " private final class F_CImpl implements Foo.C {}",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(parent, subcomponentWithSameSimpleNameAsParent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerC")
+ .containsElementsIn(componentGeneratedFile);
+ }
+
+ @Test
+ public void subcomponentBuilderNamesShouldNotConflict() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.Subcomponent;",
+ "",
+ "@Component",
+ "interface C {",
+ " Foo.Sub.Builder fooBuilder();",
+ " Bar.Sub.Builder barBuilder();",
+ "",
+ " interface Foo {",
+ " @Subcomponent",
+ " interface Sub {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Sub build();",
+ " }",
+ " }",
+ " }",
+ "",
+ " interface Bar {",
+ " @Subcomponent",
+ " interface Sub {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Sub build();",
+ " }",
+ " }",
+ " }",
+ "}");
+ JavaFileObject componentGeneratedFile =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerC",
+ "package test;",
+ "",
+ IMPORT_GENERATED_ANNOTATION,
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerC implements C {",
+ " @Override",
+ " public C.Foo.Sub.Builder fooBuilder() {",
+ " return new F_SubBuilder();",
+ " }",
+ "",
+ " @Override",
+ " public C.Bar.Sub.Builder barBuilder() {",
+ " return new B_SubBuilder();",
+ " }",
+ "",
+ // TODO(user): Reverse the order of subcomponent and builder so that subcomponent
+ // comes first.
+ " private final class F_SubBuilder implements C.Foo.Sub.Builder {",
+ " @Override",
+ " public C.Foo.Sub build() {",
+ " return new F_SubImpl();",
+ " }",
+ " }",
+ "",
+ " private final class F_SubImpl implements C.Foo.Sub {",
+ " private F_SubImpl() {}",
+ " }",
+ "",
+ " private final class B_SubBuilder implements C.Bar.Sub.Builder {",
+ " @Override",
+ " public C.Bar.Sub build() {",
+ " return new B_SubImpl();",
+ " }",
+ " }",
+ "",
+ " private final class B_SubImpl implements C.Bar.Sub {",
+ " private B_SubImpl() {}",
+ " }",
+ "}");
+ Compilation compilation =
+ daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerC")
+ .containsElementsIn(componentGeneratedFile);
+ }
+
+ @Test
+ public void duplicateBindingWithSubcomponentDeclaration() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module(subcomponents = Sub.class)",
+ "class TestModule {",
+ " @Provides Sub.Builder providesConflictsWithModuleSubcomponents() { return null; }",
+ " @Provides Object usesSubcomponentBuilder(Sub.Builder builder) {",
+ " return new Builder().toString();",
+ " }",
+ "}");
+
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Sub {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Sub build();",
+ " }",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.Sub",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface C {",
+ " Object dependsOnBuilder();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions(compilerMode.javacopts())
+ .compile(module, component, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("test.Sub.Builder is bound multiple times:");
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@Provides test.Sub.Builder "
+ + "test.TestModule.providesConflictsWithModuleSubcomponents()");
+ assertThat(compilation)
+ .hadErrorContaining("@Module(subcomponents = test.Sub.class) for test.TestModule");
+ }
+
+ @Test
+ public void subcomponentDependsOnGeneratedType() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Parent {",
+ " Child.Builder childBuilder();",
+ "}");
+
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child extends ChildSupertype {",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " Child build();",
+ " }",
+ "}");
+
+ JavaFileObject childSupertype =
+ JavaFileObjects.forSourceLines(
+ "test.ChildSupertype",
+ "package test;",
+ "",
+ "interface ChildSupertype {",
+ " GeneratedType generatedType();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler(
+ new GeneratingProcessor(
+ "test.GeneratedType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class GeneratedType {",
+ " @Inject GeneratedType() {}",
+ "}"))
+ .compile(parent, child, childSupertype);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/SwitchingProviderTest.java b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
new file mode 100644
index 0000000..2898f2e
--- /dev/null
+++ b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SwitchingProviderTest {
+ @Test
+ public void switchingProviderTest() {
+ ImmutableList.Builder<JavaFileObject> javaFileObjects = ImmutableList.builder();
+ StringBuilder entryPoints = new StringBuilder();
+ for (int i = 0; i <= 100; i++) {
+ String bindingName = "Binding" + i;
+ javaFileObjects.add(
+ JavaFileObjects.forSourceLines(
+ "test." + bindingName,
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class " + bindingName + " {",
+ " @Inject",
+ " " + bindingName + "() {}",
+ "}"));
+ entryPoints.append(String.format(" Provider<%1$s> get%1$sProvider();\n", bindingName));
+ }
+
+ javaFileObjects.add(
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ entryPoints.toString(),
+ "}"));
+
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " private T get0() {",
+ " switch (id) {",
+ " case 0: return (T) new Binding0();",
+ " case 1: return (T) new Binding1();",
+ " case 2: return (T) new Binding2();",
+ " case 3: return (T) new Binding3();",
+ " case 4: return (T) new Binding4();",
+ " case 5: return (T) new Binding5();",
+ " case 6: return (T) new Binding6();",
+ " case 7: return (T) new Binding7();",
+ " case 8: return (T) new Binding8();",
+ " case 9: return (T) new Binding9();",
+ " case 10: return (T) new Binding10();",
+ " case 11: return (T) new Binding11();",
+ " case 12: return (T) new Binding12();",
+ " case 13: return (T) new Binding13();",
+ " case 14: return (T) new Binding14();",
+ " case 15: return (T) new Binding15();",
+ " case 16: return (T) new Binding16();",
+ " case 17: return (T) new Binding17();",
+ " case 18: return (T) new Binding18();",
+ " case 19: return (T) new Binding19();",
+ " case 20: return (T) new Binding20();",
+ " case 21: return (T) new Binding21();",
+ " case 22: return (T) new Binding22();",
+ " case 23: return (T) new Binding23();",
+ " case 24: return (T) new Binding24();",
+ " case 25: return (T) new Binding25();",
+ " case 26: return (T) new Binding26();",
+ " case 27: return (T) new Binding27();",
+ " case 28: return (T) new Binding28();",
+ " case 29: return (T) new Binding29();",
+ " case 30: return (T) new Binding30();",
+ " case 31: return (T) new Binding31();",
+ " case 32: return (T) new Binding32();",
+ " case 33: return (T) new Binding33();",
+ " case 34: return (T) new Binding34();",
+ " case 35: return (T) new Binding35();",
+ " case 36: return (T) new Binding36();",
+ " case 37: return (T) new Binding37();",
+ " case 38: return (T) new Binding38();",
+ " case 39: return (T) new Binding39();",
+ " case 40: return (T) new Binding40();",
+ " case 41: return (T) new Binding41();",
+ " case 42: return (T) new Binding42();",
+ " case 43: return (T) new Binding43();",
+ " case 44: return (T) new Binding44();",
+ " case 45: return (T) new Binding45();",
+ " case 46: return (T) new Binding46();",
+ " case 47: return (T) new Binding47();",
+ " case 48: return (T) new Binding48();",
+ " case 49: return (T) new Binding49();",
+ " case 50: return (T) new Binding50();",
+ " case 51: return (T) new Binding51();",
+ " case 52: return (T) new Binding52();",
+ " case 53: return (T) new Binding53();",
+ " case 54: return (T) new Binding54();",
+ " case 55: return (T) new Binding55();",
+ " case 56: return (T) new Binding56();",
+ " case 57: return (T) new Binding57();",
+ " case 58: return (T) new Binding58();",
+ " case 59: return (T) new Binding59();",
+ " case 60: return (T) new Binding60();",
+ " case 61: return (T) new Binding61();",
+ " case 62: return (T) new Binding62();",
+ " case 63: return (T) new Binding63();",
+ " case 64: return (T) new Binding64();",
+ " case 65: return (T) new Binding65();",
+ " case 66: return (T) new Binding66();",
+ " case 67: return (T) new Binding67();",
+ " case 68: return (T) new Binding68();",
+ " case 69: return (T) new Binding69();",
+ " case 70: return (T) new Binding70();",
+ " case 71: return (T) new Binding71();",
+ " case 72: return (T) new Binding72();",
+ " case 73: return (T) new Binding73();",
+ " case 74: return (T) new Binding74();",
+ " case 75: return (T) new Binding75();",
+ " case 76: return (T) new Binding76();",
+ " case 77: return (T) new Binding77();",
+ " case 78: return (T) new Binding78();",
+ " case 79: return (T) new Binding79();",
+ " case 80: return (T) new Binding80();",
+ " case 81: return (T) new Binding81();",
+ " case 82: return (T) new Binding82();",
+ " case 83: return (T) new Binding83();",
+ " case 84: return (T) new Binding84();",
+ " case 85: return (T) new Binding85();",
+ " case 86: return (T) new Binding86();",
+ " case 87: return (T) new Binding87();",
+ " case 88: return (T) new Binding88();",
+ " case 89: return (T) new Binding89();",
+ " case 90: return (T) new Binding90();",
+ " case 91: return (T) new Binding91();",
+ " case 92: return (T) new Binding92();",
+ " case 93: return (T) new Binding93();",
+ " case 94: return (T) new Binding94();",
+ " case 95: return (T) new Binding95();",
+ " case 96: return (T) new Binding96();",
+ " case 97: return (T) new Binding97();",
+ " case 98: return (T) new Binding98();",
+ " case 99: return (T) new Binding99();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private T get1() {",
+ " switch (id) {",
+ " case 100: return (T) new Binding100();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ "",
+ " @Override",
+ " public T get() {",
+ " switch (id / 100) {",
+ " case 0: return get0();",
+ " case 1: return get1();",
+ " default: throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation = compilerWithAndroidMode().compile(javaFileObjects.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void unscopedBinds() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static String s() {",
+ " return new String();",
+ " }",
+ "",
+ " @Binds CharSequence c(String s);",
+ " @Binds Object o(CharSequence c);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<Object> objectProvider();",
+ " Provider<CharSequence> charSequenceProvider();",
+ "}");
+
+ Compilation compilation = compilerWithAndroidMode().compile(module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private volatile Provider<String> sProvider;",
+ "",
+ " private Provider<String> getStringProvider() {",
+ " Object local = sProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " sProvider = (Provider<String>) local;",
+ " }",
+ " return (Provider<String>) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Object> objectProvider() {",
+ " return (Provider) getStringProvider();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<CharSequence> charSequenceProvider() {",
+ " return (Provider) getStringProvider();",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) TestModule_SFactory.s();",
+ " default:",
+ " throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void scopedBinds() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static String s() {",
+ " return new String();",
+ " }",
+ "",
+ " @Binds @Singleton Object o(CharSequence s);",
+ " @Binds @Singleton CharSequence c(String s);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<Object> objectProvider();",
+ " Provider<CharSequence> charSequenceProvider();",
+ "}");
+
+ Compilation compilation = compilerWithAndroidMode().compile(module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private volatile Object charSequence = new MemoizedSentinel();",
+ " private volatile Provider<CharSequence> cProvider;",
+ "",
+ " private CharSequence getCharSequence() {",
+ " Object local = charSequence;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = charSequence;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = TestModule_SFactory.s();",
+ " charSequence = DoubleCheck.reentrantCheck(charSequence, local);",
+ " }",
+ " }",
+ " }",
+ " return (CharSequence) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Object> objectProvider() {",
+ " return (Provider) charSequenceProvider();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<CharSequence> charSequenceProvider() {",
+ " Object local = cProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " cProvider = (Provider<CharSequence>) local;",
+ " }",
+ " return (Provider<CharSequence>) local;",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0:",
+ " return (T) DaggerTestComponent.this.getCharSequence();",
+ " default:",
+ " throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void emptyMultibindings_avoidSwitchProviders() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.multibindings.Multibinds;",
+ "import dagger.Module;",
+ "import java.util.Map;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Multibinds Set<String> set();",
+ " @Multibinds Map<String, String> map();",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Map;",
+ "import java.util.Set;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<Set<String>> setProvider();",
+ " Provider<Map<String, String>> mapProvider();",
+ "}");
+
+ Compilation compilation = compilerWithAndroidMode().compile(module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @Override",
+ " public Provider<Set<String>> setProvider() {",
+ " return SetFactory.<String>empty();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Map<String, String>> mapProvider() {",
+ " return MapFactory.<String, String>emptyMapProvider();",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void memberInjectors() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "class Foo {}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.MembersInjector;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " Provider<MembersInjector<Foo>> providerOfMembersInjector();",
+ "}");
+
+ Compilation compilation = compilerWithAndroidMode().compile(foo, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private Provider<MembersInjector<Foo>> fooMembersInjectorProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.fooMembersInjectorProvider = ",
+ " InstanceFactory.create(MembersInjectors.<Foo>noOp());",
+ " }",
+ "",
+ " @Override",
+ " public Provider<MembersInjector<Foo>> providerOfMembersInjector() {",
+ " return fooMembersInjectorProvider;",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void optionals() {
+ JavaFileObject present =
+ JavaFileObjects.forSourceLines(
+ "test.Present",
+ "package test;",
+ "",
+ "class Present {}");
+ JavaFileObject absent =
+ JavaFileObjects.forSourceLines(
+ "test.Absent",
+ "package test;",
+ "",
+ "class Absent {}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.BindsOptionalOf;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @BindsOptionalOf Present bindOptionalOfPresent();",
+ " @BindsOptionalOf Absent bindOptionalOfAbsent();",
+ "",
+ " @Provides static Present p() { return new Present(); }",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import java.util.Optional;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<Optional<Present>> providerOfOptionalOfPresent();",
+ " Provider<Optional<Absent>> providerOfOptionalOfAbsent();",
+ "}");
+
+ Compilation compilation = compilerWithAndroidMode().compile(present, absent, module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ GENERATED_ANNOTATION,
+ "final class DaggerTestComponent implements TestComponent {",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private static final Provider ABSENT_JDK_OPTIONAL_PROVIDER =",
+ " InstanceFactory.create(Optional.empty());",
+ "",
+ " private volatile Provider<Optional<Present>> optionalOfPresentProvider;",
+ "",
+ " private Provider<Optional<Absent>> optionalOfAbsentProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.optionalOfAbsentProvider = absentJdkOptionalProvider();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Optional<Present>> providerOfOptionalOfPresent() {",
+ " Object local = optionalOfPresentProvider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(0);",
+ " optionalOfPresentProvider = (Provider<Optional<Present>>) local;",
+ " }",
+ " return (Provider<Optional<Present>>) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Optional<Absent>> providerOfOptionalOfAbsent() {",
+ " return optionalOfAbsentProvider;",
+ " }",
+ "",
+ " private static <T> Provider<Optional<T>> absentJdkOptionalProvider() {",
+ " @SuppressWarnings(\"unchecked\")",
+ " Provider<Optional<T>> provider = ",
+ " (Provider<Optional<T>>) ABSENT_JDK_OPTIONAL_PROVIDER;",
+ " return provider;",
+ " }",
+ "",
+ " private final class SwitchingProvider<T> implements Provider<T> {",
+ " @SuppressWarnings(\"unchecked\")",
+ " @Override",
+ " public T get() {",
+ " switch (id) {",
+ " case 0: // java.util.Optional<test.Present>",
+ " return (T) Optional.of(TestModule_PFactory.p());",
+ " default:",
+ " throw new AssertionError(id);",
+ " }",
+ " }",
+ " }",
+ "}"));
+ }
+
+ private Compiler compilerWithAndroidMode() {
+ return javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions(CompilerMode.FAST_INIT_MODE.javacopts());
+ }
+}
diff --git a/javatests/dagger/internal/codegen/TestUtils.java b/javatests/dagger/internal/codegen/TestUtils.java
new file mode 100644
index 0000000..69b27511
--- /dev/null
+++ b/javatests/dagger/internal/codegen/TestUtils.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.base.Joiner;
+import java.util.regex.Pattern;
+
+/** Utility methods useful for codegen tests. */
+final class TestUtils {
+
+ private static final Joiner MESSAGE_JOINER = Joiner.on("\n ");
+
+ /**
+ * Returns the lines joined by {@code "\n "}. Useful for passing to {@link
+ * com.google.testing.compile.CompilationSubject#hadErrorContaining(String)}, etc.
+ */
+ static String message(String... lines) {
+ return MESSAGE_JOINER.join(lines);
+ }
+
+ /**
+ * Returns a pattern that matches strings that end with the lines joined by {@code "\n "}. Useful
+ * for passing to {@link
+ * com.google.testing.compile.CompilationSubject#hadErrorContainingMatch(Pattern)}, etc.
+ */
+ static Pattern endsWithMessage(String... lines) {
+ return Pattern.compile(Pattern.quote(message(lines)) + "$");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/TypeProtoConverterTest.java b/javatests/dagger/internal/codegen/TypeProtoConverterTest.java
new file mode 100644
index 0000000..8c8628c
--- /dev/null
+++ b/javatests/dagger/internal/codegen/TypeProtoConverterTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+import static javax.lang.model.util.ElementFilter.fieldsIn;
+
+import com.google.testing.compile.CompilationRule;
+import dagger.internal.Factory;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.TypeProto;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.lang.model.type.TypeMirror;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests {@link TypeProtoConverter}. */
+@RunWith(JUnit4.class)
+public class TypeProtoConverterTest {
+ @Rule public CompilationRule compilationRule = new CompilationRule();
+
+ private DaggerElements elements;
+ private DaggerTypes types;
+ private TypeProtoConverter typeProtoConverter;
+
+ @Before
+ public void setUp() {
+ this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+ this.types = new DaggerTypes(compilationRule.getTypes(), elements);
+ this.typeProtoConverter = new TypeProtoConverter(types, elements);
+ }
+
+ static class Outer<O> {
+ @SuppressWarnings("ClassCanBeStatic") // We want to specifically test inner classes
+ class Inner<I> {}
+ }
+
+ @SuppressWarnings({"rawtypes", "unused"})
+ static class TypeMirrorConversionSubjects {
+ private Map rawMap;
+ private List<String> listOfString;
+ private List<HashMap<String, Integer>> listOfHashMapOfStringToInteger;
+ private Map<HashMap<String, Integer>, Set<Factory>> mapOfHashMapOfStringToIntegerToSetOfFactory;
+ private Map<HashMap<String, Integer>, Set<Factory>>[][]
+ arrayOfArrayOfMapOfHashMapOfStringToIntegerToSetOfFactory;
+ private Map<HashMap<?, Integer>, ?> mapOfHashMapOfWildcardToIntegerToWildcard;
+ private List<? extends String> listOfWildcardExtendsString;
+ private List<? extends Set<? super String>> listOfWildcardExtendsSetOfWildcardSuperString;
+ private Outer<Object>.Inner<Integer> outerOfObjectDotInnerOfInteger;
+ private List<int[]> listOfIntArray;
+ private List<? extends CharSequence[]> listOfWildcardExtendsCharSequenceArray;
+ }
+
+ @Test
+ public void typeMirrorProtoConversions() {
+ assertProtoConversionEquality(fieldType("rawMap"));
+ assertProtoConversionEquality(fieldType("listOfString"));
+ assertProtoConversionEquality(fieldType("listOfHashMapOfStringToInteger"));
+ assertProtoConversionEquality(fieldType("mapOfHashMapOfStringToIntegerToSetOfFactory"));
+ assertProtoConversionEquality(
+ fieldType("arrayOfArrayOfMapOfHashMapOfStringToIntegerToSetOfFactory"));
+ assertProtoConversionEquality(fieldType("mapOfHashMapOfWildcardToIntegerToWildcard"));
+ assertProtoConversionEquality(fieldType("listOfWildcardExtendsString"));
+ assertProtoConversionEquality(fieldType("listOfWildcardExtendsSetOfWildcardSuperString"));
+ assertProtoConversionEquality(fieldType("outerOfObjectDotInnerOfInteger"));
+ assertProtoConversionEquality(fieldType("listOfIntArray"));
+ assertProtoConversionEquality(fieldType("listOfWildcardExtendsCharSequenceArray"));
+ }
+
+ private TypeMirror fieldType(String fieldName) {
+ return fieldsIn(
+ elements.getTypeElement(TypeMirrorConversionSubjects.class).getEnclosedElements())
+ .stream()
+ .filter(field -> field.getSimpleName().contentEquals(fieldName))
+ .findFirst()
+ .get()
+ .asType();
+ }
+
+ /**
+ * Converts {@link TypeMirror} to a {@link dagger.internal.codegen.serialization.TypeProto} and
+ * back to a {@link TypeMirror}. Asserts that the round-trip conversion is lossless.
+ */
+ private void assertProtoConversionEquality(TypeMirror typeMirror) {
+ TypeProto toProto = TypeProtoConverter.toProto(typeMirror);
+ TypeMirror fromProto = typeProtoConverter.fromProto(toProto);
+ assertWithMessage("expected: %s\nactual : %s", typeMirror, fromProto)
+ .that(types.isSameType(typeMirror, fromProto))
+ .isTrue();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ValidationReportTest.java b/javatests/dagger/internal/codegen/ValidationReportTest.java
new file mode 100644
index 0000000..b0f2f2f
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ValidationReportTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.ValidationReport.Builder;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ValidationReportTest {
+ private static final JavaFileObject TEST_CLASS_FILE =
+ JavaFileObjects.forSourceLines("test.TestClass",
+ "package test;",
+ "",
+ "final class TestClass {}");
+
+ @Test
+ public void basicReport() {
+ Compilation compilation =
+ javac()
+ .withProcessors(
+ new SimpleTestProcessor() {
+ @Override
+ void test() {
+ Builder<TypeElement> reportBuilder =
+ ValidationReport.about(getTypeElement("test.TestClass"));
+ reportBuilder.addError("simple error");
+ reportBuilder.build().printMessagesTo(processingEnv.getMessager());
+ }
+ })
+ .compile(TEST_CLASS_FILE);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("simple error").inFile(TEST_CLASS_FILE).onLine(3);
+ }
+
+ @Test
+ public void messageOnDifferentElement() {
+ Compilation compilation =
+ javac()
+ .withProcessors(
+ new SimpleTestProcessor() {
+ @Override
+ void test() {
+ Builder<TypeElement> reportBuilder =
+ ValidationReport.about(getTypeElement("test.TestClass"));
+ reportBuilder.addError("simple error", getTypeElement(String.class));
+ reportBuilder.build().printMessagesTo(processingEnv.getMessager());
+ }
+ })
+ .compile(TEST_CLASS_FILE);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("[java.lang.String] simple error")
+ .inFile(TEST_CLASS_FILE)
+ .onLine(3);
+ }
+
+ @Test
+ public void subreport() {
+ Compilation compilation =
+ javac()
+ .withProcessors(
+ new SimpleTestProcessor() {
+ @Override
+ void test() {
+ Builder<TypeElement> reportBuilder =
+ ValidationReport.about(getTypeElement("test.TestClass"));
+ reportBuilder.addError("simple error");
+ ValidationReport<TypeElement> parentReport =
+ ValidationReport.about(getTypeElement(String.class))
+ .addSubreport(reportBuilder.build())
+ .build();
+ assertThat(parentReport.isClean()).isFalse();
+ parentReport.printMessagesTo(processingEnv.getMessager());
+ }
+ })
+ .compile(TEST_CLASS_FILE);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("simple error").inFile(TEST_CLASS_FILE).onLine(3);
+ }
+
+ private static abstract class SimpleTestProcessor extends AbstractProcessor {
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of("*");
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ test();
+ return false;
+ }
+
+ protected final TypeElement getTypeElement(Class<?> clazz) {
+ return getTypeElement(clazz.getCanonicalName());
+ }
+
+ protected final TypeElement getTypeElement(String canonicalName) {
+ return processingEnv.getElementUtils().getTypeElement(canonicalName);
+ }
+
+ abstract void test();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/javapoet/BUILD b/javatests/dagger/internal/codegen/javapoet/BUILD
new file mode 100644
index 0000000..99c11bd
--- /dev/null
+++ b/javatests/dagger/internal/codegen/javapoet/BUILD
@@ -0,0 +1,37 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tests for dagger.internal.codegen.javapoet
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "javapoet_tests",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/langmodel",
+ "@google_bazel_common//third_party/java/auto:common",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/internal/codegen/javapoet/CodeBlocksTest.java b/javatests/dagger/internal/codegen/javapoet/CodeBlocksTest.java
new file mode 100644
index 0000000..ee3681e
--- /dev/null
+++ b/javatests/dagger/internal/codegen/javapoet/CodeBlocksTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javapoet;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.internal.codegen.javapoet.CodeBlocks.javadocLinkTo;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static javax.lang.model.element.ElementKind.METHOD;
+
+import com.google.testing.compile.CompilationRule;
+import com.squareup.javapoet.CodeBlock;
+import java.util.stream.Stream;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.util.Elements;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link CodeBlocks}. */
+@RunWith(JUnit4.class)
+public final class CodeBlocksTest {
+ private static final CodeBlock objectO = CodeBlock.of("$T o", Object.class);
+ private static final CodeBlock stringS = CodeBlock.of("$T s", String.class);
+ private static final CodeBlock intI = CodeBlock.of("$T i", int.class);
+
+ @Rule public CompilationRule compilationRule = new CompilationRule();
+ private Elements elements;
+
+ @Before
+ public void setUp() {
+ this.elements = compilationRule.getElements();
+ }
+
+ @Test
+ public void testToParametersCodeBlock() {
+ assertThat(Stream.of(objectO, stringS, intI).collect(toParametersCodeBlock()))
+ .isEqualTo(CodeBlock.of("$T o, $T s, $T i", Object.class, String.class, int.class));
+ }
+
+ @Test
+ public void testToParametersCodeBlock_empty() {
+ assertThat(Stream.<CodeBlock>of().collect(toParametersCodeBlock())).isEqualTo(CodeBlock.of(""));
+ }
+
+ @Test
+ public void testToParametersCodeBlock_oneElement() {
+ assertThat(Stream.of(objectO).collect(toParametersCodeBlock())).isEqualTo(objectO);
+ }
+
+ @Test
+ public void testJavadocLinkTo() {
+ ExecutableElement equals =
+ elements
+ .getTypeElement(Object.class.getCanonicalName())
+ .getEnclosedElements()
+ .stream()
+ .filter(element -> element.getKind().equals(METHOD))
+ .map(ExecutableElement.class::cast)
+ .filter(method -> method.getSimpleName().contentEquals("equals"))
+ .findFirst()
+ .get();
+ assertThat(javadocLinkTo(equals))
+ .isEqualTo(CodeBlock.of("{@link $T#equals($T)}", Object.class, Object.class));
+ }
+}
diff --git a/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java b/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
new file mode 100644
index 0000000..acfa460
--- /dev/null
+++ b/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javapoet;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.common.MoreTypes;
+import com.google.testing.compile.CompilationRule;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ExpressionTest {
+ @Rule public CompilationRule compilationRule = new CompilationRule();
+ private DaggerElements elements;
+ private DaggerTypes types;
+
+ interface Supertype {}
+
+ interface Subtype extends Supertype {}
+
+ @Before
+ public void setUp() {
+ elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+ types = new DaggerTypes(compilationRule.getTypes(), elements);
+ }
+
+ @Test
+ public void castTo() {
+ TypeMirror subtype = type(Subtype.class);
+ TypeMirror supertype = type(Supertype.class);
+ Expression expression = Expression.create(subtype, "new $T() {}", subtype);
+
+ Expression castTo = expression.castTo(supertype);
+
+ assertThat(castTo.type()).isSameInstanceAs(supertype);
+ assertThat(castTo.codeBlock().toString())
+ .isEqualTo(
+ "(dagger.internal.codegen.javapoet.ExpressionTest.Supertype) "
+ + "new dagger.internal.codegen.javapoet.ExpressionTest.Subtype() {}");
+ }
+
+ @Test
+ public void box() {
+ PrimitiveType primitiveInt = types.getPrimitiveType(TypeKind.INT);
+
+ Expression primitiveExpression = Expression.create(primitiveInt, "5");
+ Expression boxedExpression = primitiveExpression.box(types);
+
+ assertThat(boxedExpression.codeBlock().toString()).isEqualTo("(java.lang.Integer) 5");
+ assertThat(MoreTypes.equivalence().equivalent(boxedExpression.type(), type(Integer.class)))
+ .isTrue();
+ }
+
+ private TypeMirror type(Class<?> clazz) {
+ return elements.getTypeElement(clazz).asType();
+ }
+}
diff --git a/javatests/dagger/producers/BUILD b/javatests/dagger/producers/BUILD
new file mode 100644
index 0000000..1e1bd66
--- /dev/null
+++ b/javatests/dagger/producers/BUILD
@@ -0,0 +1,41 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tests for dagger.producers
+
+package(default_visibility = ["//:src"])
+
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+ "SOURCE_7_TARGET_7",
+)
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+ name = "producers_tests",
+ srcs = glob(["**/*.java"]),
+ functional = 0,
+ javacopts = SOURCE_7_TARGET_7 + DOCLINT_REFERENCES + DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "//java/dagger/producers",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/guava:testlib",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/mockito",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/producers/ProducedTest.java b/javatests/dagger/producers/ProducedTest.java
new file mode 100644
index 0000000..5804141
--- /dev/null
+++ b/javatests/dagger/producers/ProducedTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.testing.EqualsTester;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link Produced}.
+ */
+@RunWith(JUnit4.class)
+public class ProducedTest {
+ @Test public void successfulProduced() throws ExecutionException {
+ Object o = new Object();
+ assertThat(Produced.successful(5).get()).isEqualTo(5);
+ assertThat(Produced.successful("monkey").get()).isEqualTo("monkey");
+ assertThat(Produced.successful(o).get()).isSameInstanceAs(o);
+ }
+
+ @Test public void failedProduced() {
+ RuntimeException cause = new RuntimeException("monkey");
+ try {
+ Produced.failed(cause).get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isSameInstanceAs(cause);
+ }
+ }
+
+ @Test public void producedEquivalence() {
+ RuntimeException e1 = new RuntimeException("monkey");
+ RuntimeException e2 = new CancellationException();
+ new EqualsTester()
+ .addEqualityGroup(Produced.successful(132435), Produced.successful(132435))
+ .addEqualityGroup(Produced.successful("hi"), Produced.successful("hi"))
+ .addEqualityGroup(Produced.failed(e1), Produced.failed(e1))
+ .addEqualityGroup(Produced.failed(e2), Produced.failed(e2))
+ .testEquals();
+ }
+}
diff --git a/javatests/dagger/producers/internal/AbstractProducerTest.java b/javatests/dagger/producers/internal/AbstractProducerTest.java
new file mode 100644
index 0000000..da24125
--- /dev/null
+++ b/javatests/dagger/producers/internal/AbstractProducerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link AbstractProducer}.
+ */
+@RunWith(JUnit4.class)
+public class AbstractProducerTest {
+ @Test
+ @SuppressWarnings("CheckReturnValue")
+ public void get_nullPointerException() {
+ Producer<Object> producer = new DelegateProducer<>(null);
+ try {
+ producer.get();
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test public void get() throws Exception {
+ Producer<Integer> producer =
+ new AbstractProducer<Integer>() {
+ int i = 0;
+
+ @Override
+ public ListenableFuture<Integer> compute() {
+ return Futures.immediateFuture(i++);
+ }
+ };
+ assertThat(producer.get().get()).isEqualTo(0);
+ assertThat(producer.get().get()).isEqualTo(0);
+ assertThat(producer.get().get()).isEqualTo(0);
+ }
+
+ static final class DelegateProducer<T> extends AbstractProducer<T> {
+ private final ListenableFuture<T> delegate;
+
+ DelegateProducer(ListenableFuture<T> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ListenableFuture<T> compute() {
+ return delegate;
+ }
+ }
+}
diff --git a/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java b/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
new file mode 100644
index 0000000..b29cb3c
--- /dev/null
+++ b/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import dagger.producers.Producer;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import javax.inject.Provider;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests {@link AbstractProducer}.
+ */
+@RunWith(JUnit4.class)
+public class AbstractProducesMethodProducerTest {
+ @Mock private ProductionComponentMonitor componentMonitor;
+ private ProducerMonitor monitor;
+ private Provider<ProductionComponentMonitor> componentMonitorProvider;
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ monitor = Mockito.mock(ProducerMonitor.class, Mockito.CALLS_REAL_METHODS);
+ when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor);
+ componentMonitorProvider =
+ new Provider<ProductionComponentMonitor>() {
+ @Override
+ public ProductionComponentMonitor get() {
+ return componentMonitor;
+ }
+ };
+ }
+
+ @Test
+ public void monitor_success() throws Exception {
+ SettableFuture<Integer> delegateFuture = SettableFuture.create();
+ Producer<Integer> producer = new DelegateProducer<>(componentMonitorProvider, delegateFuture);
+
+ ListenableFuture<Integer> future = producer.get();
+ assertThat(future.isDone()).isFalse();
+ verify(monitor).ready();
+ verify(monitor).requested();
+ verify(monitor).addCallbackTo(anyListenableFuture());
+ verify(monitor).methodStarting();
+ verify(monitor).methodFinished();
+ delegateFuture.set(-42);
+ assertThat(future.get()).isEqualTo(-42);
+ verify(monitor).succeeded(-42);
+ verifyNoMoreInteractions(monitor);
+ }
+
+ @Test
+ public void monitor_failure() throws Exception {
+ SettableFuture<Integer> delegateFuture = SettableFuture.create();
+ Producer<Integer> producer = new DelegateProducer<>(componentMonitorProvider, delegateFuture);
+
+ ListenableFuture<Integer> future = producer.get();
+ assertThat(future.isDone()).isFalse();
+ verify(monitor).ready();
+ verify(monitor).requested();
+ verify(monitor).addCallbackTo(anyListenableFuture());
+ verify(monitor).methodStarting();
+ verify(monitor).methodFinished();
+ Throwable t = new RuntimeException("monkey");
+ delegateFuture.setException(t);
+ try {
+ future.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isSameInstanceAs(t);
+ }
+ verify(monitor).failed(t);
+ verifyNoMoreInteractions(monitor);
+ }
+
+ private ListenableFuture<?> anyListenableFuture() {
+ return any(ListenableFuture.class);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void monitor_null() throws Exception {
+ new DelegateProducer<>(null, Futures.immediateFuture(42));
+ }
+
+ static final class DelegateProducer<T> extends AbstractProducesMethodProducer<Void, T> {
+ private final ListenableFuture<T> delegate;
+
+ DelegateProducer(
+ Provider<ProductionComponentMonitor> componentMonitorProvider,
+ ListenableFuture<T> delegate) {
+ super(
+ componentMonitorProvider,
+ null, // token
+ new Provider<Executor>() {
+ @Override
+ public Executor get() {
+ return MoreExecutors.directExecutor();
+ }
+ });
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected ListenableFuture<Void> collectDependencies() {
+ return Futures.immediateFuture(null);
+ }
+
+ @Override
+ protected ListenableFuture<T> callProducesMethod(Void asyncDependencies) {
+ return delegate;
+ }
+ }
+}
diff --git a/javatests/dagger/producers/internal/MapOfProducerProducerTest.java b/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
new file mode 100644
index 0000000..f4be15c
--- /dev/null
+++ b/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import dagger.producers.Producer;
+import dagger.producers.Producers;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MapOfProducerProducerTest {
+ @Test
+ public void success() throws Exception {
+ MapOfProducerProducer<Integer, String> mapOfProducerProducer =
+ MapOfProducerProducer.<Integer, String>builder(2)
+ .put(15, Producers.<String>immediateProducer("fifteen"))
+ .put(42, Producers.<String>immediateProducer("forty two"))
+ .build();
+ Map<Integer, Producer<String>> map = mapOfProducerProducer.get().get();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsKey(15);
+ assertThat(map.get(15).get().get()).isEqualTo("fifteen");
+ assertThat(map).containsKey(42);
+ assertThat(map.get(42).get().get()).isEqualTo("forty two");
+ }
+
+ @Test
+ public void failingContributionDoesNotFailMap() throws Exception {
+ RuntimeException cause = new RuntimeException("monkey");
+ MapOfProducerProducer<Integer, String> mapOfProducerProducer =
+ MapOfProducerProducer.<Integer, String>builder(2)
+ .put(15, Producers.<String>immediateProducer("fifteen"))
+ .put(42, Producers.<String>immediateFailedProducer(cause))
+ .build();
+ Map<Integer, Producer<String>> map = mapOfProducerProducer.get().get();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsKey(15);
+ assertThat(map.get(15).get().get()).isEqualTo("fifteen");
+ assertThat(map).containsKey(42);
+ try {
+ map.get(42).get().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isSameInstanceAs(cause);
+ }
+ }
+}
diff --git a/javatests/dagger/producers/internal/MapProducerTest.java b/javatests/dagger/producers/internal/MapProducerTest.java
new file mode 100644
index 0000000..f74bc41
--- /dev/null
+++ b/javatests/dagger/producers/internal/MapProducerTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import dagger.producers.Producer;
+import dagger.producers.Producers;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MapProducerTest {
+ @Test
+ public void success() throws Exception {
+ Producer<Map<Integer, String>> mapProducer =
+ MapProducer.<Integer, String>builder(2)
+ .put(15, Producers.immediateProducer("fifteen"))
+ .put(42, Producers.immediateProducer("forty two"))
+ .build();
+ Map<Integer, String> map = mapProducer.get().get();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsEntry(15, "fifteen");
+ assertThat(map).containsEntry(42, "forty two");
+ }
+
+ @Test
+ public void failingContribution() throws Exception {
+ RuntimeException cause = new RuntimeException("monkey");
+ Producer<Map<Integer, String>> mapProducer =
+ MapProducer.<Integer, String>builder(2)
+ .put(15, Producers.immediateProducer("fifteen"))
+ // TODO(ronshapiro): remove the type parameter when we drop java7 support
+ .put(42, Producers.<String>immediateFailedProducer(cause))
+ .build();
+ try {
+ mapProducer.get().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isSameInstanceAs(cause);
+ }
+ }
+}
diff --git a/javatests/dagger/producers/internal/ProducersTest.java b/javatests/dagger/producers/internal/ProducersTest.java
new file mode 100644
index 0000000..1cfe121
--- /dev/null
+++ b/javatests/dagger/producers/internal/ProducersTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link Producers}.
+ */
+@RunWith(JUnit4.class)
+public class ProducersTest {
+ @Test public void createFutureProduced_success() throws Exception {
+ ListenableFuture<String> future = Futures.immediateFuture("monkey");
+ ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
+ assertThat(producedFuture.isDone()).isTrue();
+ assertThat(producedFuture.get().get()).isEqualTo("monkey");
+ }
+
+ @Test public void createFutureProduced_failure() throws Exception {
+ ListenableFuture<String> future = Futures.immediateFailedFuture(new RuntimeException("monkey"));
+ ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
+ assertThat(producedFuture.isDone()).isTrue();
+ assertThat(getProducedException(producedFuture.get()))
+ .hasCauseThat()
+ .hasMessageThat()
+ .isEqualTo("monkey");
+ }
+
+ @Test public void createFutureProduced_cancelPropagatesBackwards() throws Exception {
+ ListenableFuture<String> future = SettableFuture.create();
+ ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
+ assertThat(producedFuture.isDone()).isFalse();
+ producedFuture.cancel(false);
+ assertThat(future.isCancelled()).isTrue();
+ }
+
+ @Test public void createFutureProduced_cancelDoesNotPropagateForwards() throws Exception {
+ ListenableFuture<String> future = SettableFuture.create();
+ ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
+ assertThat(producedFuture.isDone()).isFalse();
+ future.cancel(false);
+ assertThat(producedFuture.isCancelled()).isFalse();
+ assertThat(getProducedException(producedFuture.get()))
+ .hasCauseThat()
+ .isInstanceOf(CancellationException.class);
+ }
+
+ private <T> ExecutionException getProducedException(Produced<T> produced) {
+ try {
+ T value = produced.get();
+ throw new IllegalArgumentException("produced did not throw, but returned " + value);
+ } catch (ExecutionException e) {
+ return e;
+ }
+ }
+
+ @Test public void createFutureSingletonSet_success() throws Exception {
+ ListenableFuture<String> future = Futures.immediateFuture("monkey");
+ ListenableFuture<Set<String>> setFuture = Producers.createFutureSingletonSet(future);
+ assertThat(setFuture.isDone()).isTrue();
+ assertThat(setFuture.get()).containsExactly("monkey");
+ }
+
+ @Test public void createFutureSingletonSet_failure() throws Exception {
+ ListenableFuture<String> future = Futures.immediateFailedFuture(new RuntimeException("monkey"));
+ ListenableFuture<Set<String>> setFuture = Producers.createFutureSingletonSet(future);
+ assertThat(setFuture.isDone()).isTrue();
+ try {
+ setFuture.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("monkey");
+ }
+ }
+
+ @Test
+ public void allAsSet_success() throws Exception {
+ ListenableFuture<Set<String>> future =
+ Producers.allAsSet(
+ ImmutableList.of(
+ Futures.immediateFuture("monkey"), Futures.immediateFuture("gorilla")));
+ assertThat(future.isDone()).isTrue();
+ assertThat(future.get()).containsExactly("monkey", "gorilla");
+ }
+
+ @Test
+ public void allAsSet_failure() throws Exception {
+ ListenableFuture<Set<String>> future =
+ Producers.allAsSet(
+ ImmutableList.of(
+ Futures.immediateFuture("monkey"),
+ Futures.<String>immediateFailedFuture(new RuntimeException("gorilla"))));
+ assertThat(future.isDone()).isTrue();
+ try {
+ future.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("gorilla");
+ }
+ }
+
+ @Test public void producerFromProvider_doesntCache() throws Exception {
+ Producer<Integer> producer = Producers.producerFromProvider(new Provider<Integer>() {
+ int i = 0;
+
+ @Override public Integer get() {
+ return i++;
+ }
+ });
+ assertThat(producer.get().get()).isEqualTo(0);
+ assertThat(producer.get().get()).isEqualTo(1);
+ assertThat(producer.get().get()).isEqualTo(2);
+ }
+}
diff --git a/javatests/dagger/producers/internal/SetOfProducedProducerTest.java b/javatests/dagger/producers/internal/SetOfProducedProducerTest.java
new file mode 100644
index 0000000..4dffd74
--- /dev/null
+++ b/javatests/dagger/producers/internal/SetOfProducedProducerTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.Producers;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link SetOfProducedProducer}.
+ */
+@RunWith(JUnit4.class)
+public class SetOfProducedProducerTest {
+ @Test
+ public void success() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.<Integer>builder(1, 1)
+ .addProducer(Producers.immediateProducer(1))
+ .addCollectionProducer(Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(5, 7)))
+ .build();
+ assertThat(producer.get().get())
+ .containsExactly(
+ Produced.successful(1),
+ Produced.successful(5),
+ Produced.successful(7));
+ }
+
+ @Test
+ public void failure() throws Exception {
+ RuntimeException e = new RuntimeException("monkey");
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.<Integer>builder(1, 1)
+ .addCollectionProducer(Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)))
+ .addProducer(Producers.<Integer>immediateFailedProducer(e))
+ .build();
+ assertThat(producer.get().get())
+ .containsExactly(
+ Produced.successful(1), Produced.successful(2), Produced.<Integer>failed(e));
+ }
+
+ @Test
+ public void delegateNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.<Integer>builder(1, 0)
+ .addProducer(Producers.<Integer>immediateProducer(null))
+ .build();
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).isEmpty();
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures))
+ .hasCauseThat()
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void delegateSetNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.<Integer>builder(0, 1)
+ .addCollectionProducer(Producers.<Set<Integer>>immediateProducer(null))
+ .build();
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).isEmpty();
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures))
+ .hasCauseThat()
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void delegateElementNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.<Integer>builder(0, 1)
+ .addCollectionProducer(
+ Producers.<Set<Integer>>immediateProducer(Collections.<Integer>singleton(null)))
+ .build();
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).isEmpty();
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures))
+ .hasCauseThat()
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void oneOfDelegateElementNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.<Integer>builder(0, 1)
+ .addCollectionProducer(
+ Producers.<Set<Integer>>immediateProducer(
+ Sets.newHashSet(Arrays.asList(5, 2, null))))
+ .build();
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).containsExactly(2, 5);
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures))
+ .hasCauseThat()
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ static final class Results<T> {
+ final ImmutableSet<T> successes;
+ final ImmutableSet<ExecutionException> failures;
+
+ private Results(ImmutableSet<T> successes, ImmutableSet<ExecutionException> failures) {
+ this.successes = successes;
+ this.failures = failures;
+ }
+
+ static <T> Results<T> create(Set<Produced<T>> setOfProduced) {
+ ImmutableSet.Builder<T> successes = ImmutableSet.builder();
+ ImmutableSet.Builder<ExecutionException> failures = ImmutableSet.builder();
+ for (Produced<T> produced : setOfProduced) {
+ try {
+ successes.add(produced.get());
+ } catch (ExecutionException e) {
+ failures.add(e);
+ }
+ }
+ return new Results<T>(successes.build(), failures.build());
+ }
+ }
+}
diff --git a/javatests/dagger/producers/internal/SetProducerTest.java b/javatests/dagger/producers/internal/SetProducerTest.java
new file mode 100644
index 0000000..2bfd51a
--- /dev/null
+++ b/javatests/dagger/producers/internal/SetProducerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Producer;
+import dagger.producers.Producers;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link SetProducer}.
+ */
+@RunWith(JUnit4.class)
+public class SetProducerTest {
+ @Test
+ public void success() throws Exception {
+ Producer<Set<Integer>> producer =
+ SetProducer.<Integer>builder(1, 1)
+ .addProducer(Producers.immediateProducer(1))
+ .addCollectionProducer(Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(5, 7)))
+ .build();
+ assertThat(producer.get().get()).containsExactly(1, 5, 7);
+ }
+
+ @Test
+ public void delegateNpe() throws Exception {
+ Producer<Set<Integer>> producer =
+ SetProducer.<Integer>builder(1, 0)
+ .addProducer(Producers.<Integer>immediateProducer(null))
+ .build();
+ ListenableFuture<Set<Integer>> future = producer.get();
+ try {
+ future.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isInstanceOf(NullPointerException.class);
+ }
+ }
+
+ @Test
+ public void delegateSetNpe() throws Exception {
+ Producer<Set<Integer>> producer =
+ SetProducer.<Integer>builder(0, 1)
+ .addCollectionProducer(Producers.<Set<Integer>>immediateProducer(null))
+ .build();
+ ListenableFuture<Set<Integer>> future = producer.get();
+ try {
+ future.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isInstanceOf(NullPointerException.class);
+ }
+ }
+
+ @Test
+ public void delegateElementNpe() throws Exception {
+ Producer<Set<Integer>> producer =
+ SetProducer.<Integer>builder(0, 2)
+ .addCollectionProducer(Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)))
+ .addCollectionProducer(
+ Producers.<Set<Integer>>immediateProducer(Collections.<Integer>singleton(null)))
+ .build();
+ ListenableFuture<Set<Integer>> future = producer.get();
+ try {
+ future.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e).hasCauseThat().isInstanceOf(NullPointerException.class);
+ }
+ }
+}
diff --git a/javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java b/javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java
new file mode 100644
index 0000000..449b5a6
--- /dev/null
+++ b/javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.testing.FakeTicker;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public final class TimingProductionComponentMonitorTest {
+ private static final class ProducerClassA {}
+
+ private static final class ProducerClassB {}
+
+ @Mock private ProductionComponentTimingRecorder.Factory productionComponentTimingRecorderFactory;
+ @Mock private ProductionComponentTimingRecorder productionComponentTimingRecorder;
+ @Mock private ProducerTimingRecorder producerTimingRecorderA;
+ @Mock private ProducerTimingRecorder producerTimingRecorderB;
+
+ private FakeTicker ticker;
+ private ProductionComponentMonitor.Factory monitorFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(productionComponentTimingRecorderFactory.create(any(Object.class)))
+ .thenReturn(productionComponentTimingRecorder);
+ when(
+ productionComponentTimingRecorder.producerTimingRecorderFor(
+ ProducerToken.create(ProducerClassA.class)))
+ .thenReturn(producerTimingRecorderA);
+ when(
+ productionComponentTimingRecorder.producerTimingRecorderFor(
+ ProducerToken.create(ProducerClassB.class)))
+ .thenReturn(producerTimingRecorderB);
+ ticker = new FakeTicker();
+ monitorFactory =
+ new TimingProductionComponentMonitor.Factory(
+ productionComponentTimingRecorderFactory, ticker);
+ }
+
+ @Test
+ public void normalExecution_success() {
+ ProductionComponentMonitor monitor = monitorFactory.create(new Object());
+ ProducerMonitor producerMonitorA =
+ monitor.producerMonitorFor(ProducerToken.create(ProducerClassA.class));
+ ticker.advance(5000222);
+ producerMonitorA.methodStarting();
+ ticker.advance(1333);
+ producerMonitorA.methodFinished();
+ ticker.advance(40000555);
+ ProducerMonitor producerMonitorB =
+ monitor.producerMonitorFor(ProducerToken.create(ProducerClassB.class));
+ producerMonitorB.methodStarting();
+ ticker.advance(2000777);
+ producerMonitorA.succeeded(new Object());
+ ticker.advance(3000999);
+ producerMonitorB.methodFinished();
+ ticker.advance(100000222);
+ producerMonitorB.succeeded(new Object());
+
+ verify(producerTimingRecorderA).recordMethod(5000222, 1333);
+ verify(producerTimingRecorderA).recordSuccess(1333 + 40000555 + 2000777);
+ verify(producerTimingRecorderB).recordMethod(5000222 + 1333 + 40000555, 2000777 + 3000999);
+ verify(producerTimingRecorderB).recordSuccess(2000777 + 3000999 + 100000222);
+ verifyNoMoreInteractions(producerTimingRecorderA, producerTimingRecorderB);
+ }
+
+ @Test
+ public void normalExecution_failure() {
+ Throwable failureA = new RuntimeException("monkey");
+ Throwable failureB = new RuntimeException("gorilla");
+ ProductionComponentMonitor monitor = monitorFactory.create(new Object());
+ ProducerMonitor producerMonitorA =
+ monitor.producerMonitorFor(ProducerToken.create(ProducerClassA.class));
+ ticker.advance(5000222);
+ producerMonitorA.methodStarting();
+ ticker.advance(1333);
+ producerMonitorA.methodFinished();
+ ticker.advance(40000555);
+ ProducerMonitor producerMonitorB =
+ monitor.producerMonitorFor(ProducerToken.create(ProducerClassB.class));
+ producerMonitorB.methodStarting();
+ ticker.advance(2000777);
+ producerMonitorA.failed(failureA);
+ ticker.advance(3000999);
+ producerMonitorB.methodFinished();
+ ticker.advance(100000222);
+ producerMonitorB.failed(failureB);
+
+ verify(producerTimingRecorderA).recordMethod(5000222, 1333);
+ verify(producerTimingRecorderA).recordFailure(failureA, 1333 + 40000555 + 2000777);
+ verify(producerTimingRecorderB).recordMethod(5000222 + 1333 + 40000555, 2000777 + 3000999);
+ verify(producerTimingRecorderB).recordFailure(failureB, 2000777 + 3000999 + 100000222);
+ verifyNoMoreInteractions(producerTimingRecorderA, producerTimingRecorderB);
+ }
+}
diff --git a/javatests/dagger/producers/monitoring/TimingRecordersTest.java b/javatests/dagger/producers/monitoring/TimingRecordersTest.java
new file mode 100644
index 0000000..4e5d74f
--- /dev/null
+++ b/javatests/dagger/producers/monitoring/TimingRecordersTest.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public final class TimingRecordersTest {
+ @Mock
+ private ProductionComponentTimingRecorder.Factory mockProductionComponentTimingRecorderFactory;
+
+ @Mock private ProductionComponentTimingRecorder mockProductionComponentTimingRecorder;
+ @Mock private ProducerTimingRecorder mockProducerTimingRecorder;
+
+ @Mock
+ private ProductionComponentTimingRecorder.Factory mockProductionComponentTimingRecorderFactoryA;
+
+ @Mock
+ private ProductionComponentTimingRecorder.Factory mockProductionComponentTimingRecorderFactoryB;
+
+ @Mock
+ private ProductionComponentTimingRecorder.Factory mockProductionComponentTimingRecorderFactoryC;
+
+ @Mock private ProductionComponentTimingRecorder mockProductionComponentTimingRecorderA;
+ @Mock private ProductionComponentTimingRecorder mockProductionComponentTimingRecorderB;
+ @Mock private ProductionComponentTimingRecorder mockProductionComponentTimingRecorderC;
+ @Mock private ProducerTimingRecorder mockProducerTimingRecorderA;
+ @Mock private ProducerTimingRecorder mockProducerTimingRecorderB;
+ @Mock private ProducerTimingRecorder mockProducerTimingRecorderC;
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void zeroRecordersReturnsNoOp() {
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.<ProductionComponentTimingRecorder.Factory>of());
+ assertThat(factory)
+ .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorderFactory());
+ }
+
+ @Test
+ public void singleRecorder_nullProductionComponentTimingRecorder() {
+ when(mockProductionComponentTimingRecorderFactory.create(any(Object.class))).thenReturn(null);
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(mockProductionComponentTimingRecorderFactory));
+ assertThat(factory.create(new Object()))
+ .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+ }
+
+ @Test
+ public void singleRecorder_throwingProductionComponentTimingRecorderFactory() {
+ when(mockProductionComponentTimingRecorderFactory.create(any(Object.class)))
+ .thenThrow(new RuntimeException("monkey"));
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(mockProductionComponentTimingRecorderFactory));
+ assertThat(factory.create(new Object()))
+ .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+ }
+
+ @Test
+ public void singleRecorder_nullProducerTimingRecorder() {
+ when(mockProductionComponentTimingRecorderFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorder);
+ when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenReturn(null);
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(mockProductionComponentTimingRecorderFactory));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ assertThat(recorder.producerTimingRecorderFor(ProducerToken.create(Object.class)))
+ .isSameInstanceAs(ProducerTimingRecorder.noOp());
+ }
+
+ @Test
+ public void singleRecorder_throwingProductionComponentTimingRecorder() {
+ when(mockProductionComponentTimingRecorderFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorder);
+ when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenThrow(new RuntimeException("monkey"));
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(mockProductionComponentTimingRecorderFactory));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ assertThat(recorder.producerTimingRecorderFor(ProducerToken.create(Object.class)))
+ .isSameInstanceAs(ProducerTimingRecorder.noOp());
+ }
+
+ @Test
+ public void singleRecorder_normalProducerTimingRecorderSuccess() {
+ setUpNormalSingleRecorder();
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(mockProductionComponentTimingRecorderFactory));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ ProducerTimingRecorder producerTimingRecorder =
+ recorder.producerTimingRecorderFor(ProducerToken.create(Object.class));
+ producerTimingRecorder.recordMethod(15, 42);
+ producerTimingRecorder.recordSuccess(100);
+
+ InOrder order = inOrder(mockProducerTimingRecorder);
+ order.verify(mockProducerTimingRecorder).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorder).recordSuccess(100);
+ verifyNoMoreInteractions(mockProducerTimingRecorder);
+ }
+
+ @Test
+ public void singleRecorder_normalProducerTimingRecorderFailure() {
+ setUpNormalSingleRecorder();
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(mockProductionComponentTimingRecorderFactory));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ ProducerTimingRecorder producerTimingRecorder =
+ recorder.producerTimingRecorderFor(ProducerToken.create(Object.class));
+ Throwable t = new RuntimeException("monkey");
+ producerTimingRecorder.recordMethod(15, 42);
+ producerTimingRecorder.recordFailure(t, 100);
+
+ InOrder order = inOrder(mockProducerTimingRecorder);
+ order.verify(mockProducerTimingRecorder).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorder).recordFailure(t, 100);
+ verifyNoMoreInteractions(mockProducerTimingRecorder);
+ }
+
+ @Test
+ public void singleRecorder_throwingProducerTimingRecorderSuccess() {
+ setUpNormalSingleRecorder();
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProducerTimingRecorder)
+ .recordMethod(anyLong(), anyLong());
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProducerTimingRecorder)
+ .recordSuccess(anyLong());
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(mockProductionComponentTimingRecorderFactory));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ ProducerTimingRecorder producerTimingRecorder =
+ recorder.producerTimingRecorderFor(ProducerToken.create(Object.class));
+ producerTimingRecorder.recordMethod(15, 42);
+ producerTimingRecorder.recordSuccess(100);
+
+ InOrder order = inOrder(mockProducerTimingRecorder);
+ order.verify(mockProducerTimingRecorder).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorder).recordSuccess(100);
+ verifyNoMoreInteractions(mockProducerTimingRecorder);
+ }
+
+ @Test
+ public void multipleRecorders_nullProductionComponentTimingRecorders() {
+ when(mockProductionComponentTimingRecorderFactoryA.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentTimingRecorderFactoryB.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class))).thenReturn(null);
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(
+ mockProductionComponentTimingRecorderFactoryA,
+ mockProductionComponentTimingRecorderFactoryB,
+ mockProductionComponentTimingRecorderFactoryC));
+ assertThat(factory.create(new Object()))
+ .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+ }
+
+ @Test
+ public void multipleRecorders_throwingProductionComponentTimingRecorderFactories() {
+ when(mockProductionComponentTimingRecorderFactoryA.create(any(Object.class)))
+ .thenThrow(new RuntimeException("monkey"));
+ when(mockProductionComponentTimingRecorderFactoryB.create(any(Object.class)))
+ .thenThrow(new RuntimeException("monkey"));
+ when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class)))
+ .thenThrow(new RuntimeException("monkey"));
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(
+ mockProductionComponentTimingRecorderFactoryA,
+ mockProductionComponentTimingRecorderFactoryB,
+ mockProductionComponentTimingRecorderFactoryC));
+ assertThat(factory.create(new Object()))
+ .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+ }
+
+ @Test
+ public void multipleRecorders_someNullProductionComponentTimingRecorders() {
+ when(mockProductionComponentTimingRecorderFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorderA);
+ when(mockProductionComponentTimingRecorderFactoryB.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerTimingRecorderA);
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(
+ mockProductionComponentTimingRecorderFactoryA,
+ mockProductionComponentTimingRecorderFactoryB,
+ mockProductionComponentTimingRecorderFactoryC));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ ProducerTimingRecorder producerTimingRecorder =
+ recorder.producerTimingRecorderFor(ProducerToken.create(Object.class));
+
+ producerTimingRecorder.recordMethod(15, 42);
+ producerTimingRecorder.recordSuccess(100);
+
+ InOrder order = inOrder(mockProducerTimingRecorderA);
+ order.verify(mockProducerTimingRecorderA).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderA).recordSuccess(100);
+ verifyNoMoreInteractions(mockProducerTimingRecorderA);
+ }
+
+ @Test
+ public void multipleRecorders_someThrowingProductionComponentTimingRecorderFactories() {
+ when(mockProductionComponentTimingRecorderFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorderA);
+ when(mockProductionComponentTimingRecorderFactoryB.create(any(Object.class)))
+ .thenThrow(new RuntimeException("monkey"));
+ when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class)))
+ .thenThrow(new RuntimeException("monkey"));
+ when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerTimingRecorderA);
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(
+ mockProductionComponentTimingRecorderFactoryA,
+ mockProductionComponentTimingRecorderFactoryB,
+ mockProductionComponentTimingRecorderFactoryC));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ ProducerTimingRecorder producerTimingRecorder =
+ recorder.producerTimingRecorderFor(ProducerToken.create(Object.class));
+
+ producerTimingRecorder.recordMethod(15, 42);
+ producerTimingRecorder.recordSuccess(100);
+
+ InOrder order = inOrder(mockProducerTimingRecorderA);
+ order.verify(mockProducerTimingRecorderA).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderA).recordSuccess(100);
+ verifyNoMoreInteractions(mockProducerTimingRecorderA);
+ }
+
+ @Test
+ public void multipleRecorders_normalProductionComponentTimingRecorderSuccess() {
+ setUpNormalMultipleRecorders();
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(
+ mockProductionComponentTimingRecorderFactoryA,
+ mockProductionComponentTimingRecorderFactoryB,
+ mockProductionComponentTimingRecorderFactoryC));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ ProducerTimingRecorder producerTimingRecorder =
+ recorder.producerTimingRecorderFor(ProducerToken.create(Object.class));
+
+ producerTimingRecorder.recordMethod(15, 42);
+ producerTimingRecorder.recordSuccess(100);
+
+ InOrder order =
+ inOrder(
+ mockProducerTimingRecorderA, mockProducerTimingRecorderB, mockProducerTimingRecorderC);
+ order.verify(mockProducerTimingRecorderA).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderB).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderC).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderA).recordSuccess(100);
+ order.verify(mockProducerTimingRecorderB).recordSuccess(100);
+ order.verify(mockProducerTimingRecorderC).recordSuccess(100);
+ verifyNoMoreInteractions(
+ mockProducerTimingRecorderA, mockProducerTimingRecorderB, mockProducerTimingRecorderC);
+ }
+
+ @Test
+ public void multipleRecorders_someThrowingProducerTimingRecordersSuccess() {
+ setUpNormalMultipleRecorders();
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProducerTimingRecorderA)
+ .recordMethod(anyLong(), anyLong());
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProducerTimingRecorderB)
+ .recordSuccess(anyLong());
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProducerTimingRecorderC)
+ .recordMethod(anyLong(), anyLong());
+ ProductionComponentTimingRecorder.Factory factory =
+ TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
+ ImmutableList.of(
+ mockProductionComponentTimingRecorderFactoryA,
+ mockProductionComponentTimingRecorderFactoryB,
+ mockProductionComponentTimingRecorderFactoryC));
+ ProductionComponentTimingRecorder recorder = factory.create(new Object());
+ ProducerTimingRecorder producerTimingRecorder =
+ recorder.producerTimingRecorderFor(ProducerToken.create(Object.class));
+
+ producerTimingRecorder.recordMethod(15, 42);
+ producerTimingRecorder.recordSuccess(100);
+
+ InOrder order =
+ inOrder(
+ mockProducerTimingRecorderA, mockProducerTimingRecorderB, mockProducerTimingRecorderC);
+ order.verify(mockProducerTimingRecorderA).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderB).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderC).recordMethod(15, 42);
+ order.verify(mockProducerTimingRecorderA).recordSuccess(100);
+ order.verify(mockProducerTimingRecorderB).recordSuccess(100);
+ order.verify(mockProducerTimingRecorderC).recordSuccess(100);
+ verifyNoMoreInteractions(
+ mockProducerTimingRecorderA, mockProducerTimingRecorderB, mockProducerTimingRecorderC);
+ }
+
+ private void setUpNormalSingleRecorder() {
+ when(mockProductionComponentTimingRecorderFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorder);
+ when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerTimingRecorder);
+ }
+
+ private void setUpNormalMultipleRecorders() {
+ when(mockProductionComponentTimingRecorderFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorderA);
+ when(mockProductionComponentTimingRecorderFactoryB.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorderB);
+ when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class)))
+ .thenReturn(mockProductionComponentTimingRecorderC);
+ when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerTimingRecorderA);
+ when(mockProductionComponentTimingRecorderB.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerTimingRecorderB);
+ when(mockProductionComponentTimingRecorderC.producerTimingRecorderFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerTimingRecorderC);
+ }
+}
diff --git a/javatests/dagger/producers/monitoring/internal/MonitorsTest.java b/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
new file mode 100644
index 0000000..47ccccb
--- /dev/null
+++ b/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.producers.monitoring.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableList;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public final class MonitorsTest {
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactory;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitor;
+ @Mock private ProducerMonitor mockProducerMonitor;
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryA;
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryB;
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryC;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitorA;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitorB;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitorC;
+ @Mock private ProducerMonitor mockProducerMonitorA;
+ @Mock private ProducerMonitor mockProducerMonitorB;
+ @Mock private ProducerMonitor mockProducerMonitorC;
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void zeroMonitorsReturnsNoOp() {
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.<ProductionComponentMonitor.Factory>of());
+ assertThat(factory).isSameInstanceAs(ProductionComponentMonitor.Factory.noOp());
+ }
+
+ @Test
+ public void singleMonitor_nullProductionComponentMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class))).thenReturn(null);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
+ }
+
+ @Test
+ public void singleMonitor_throwingProductionComponentMonitorFactory() {
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactory)
+ .create(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
+ }
+
+ @Test
+ public void singleMonitor_nullProducerMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitor);
+ when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(null);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
+ .isSameInstanceAs(ProducerMonitor.noOp());
+ }
+
+ @Test
+ public void singleMonitor_throwingProductionComponentMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitor);
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitor)
+ .producerMonitorFor(any(ProducerToken.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
+ .isSameInstanceAs(ProducerMonitor.noOp());
+ }
+
+ @Test
+ public void singleMonitor_normalProducerMonitorSuccess() {
+ setUpNormalSingleMonitor();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Object o = new Object();
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).requested();
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void singleMonitor_normalProducerMonitorFailure() {
+ setUpNormalSingleMonitor();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Throwable t = new RuntimeException("monkey");
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).requested();
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void singleMonitor_throwingProducerMonitorSuccess() {
+ setUpNormalSingleMonitor();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).requested();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).succeeded(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Object o = new Object();
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).requested();
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void singleMonitor_throwingProducerMonitorFailure() {
+ setUpNormalSingleMonitor();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).requested();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).failed(any(Throwable.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Throwable t = new RuntimeException("gorilla");
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).requested();
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void multipleMonitors_nullProductionComponentMonitors() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorFactoryB.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorFactoryC.create(any(Object.class))).thenReturn(null);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
+ }
+
+ @Test
+ public void multipleMonitors_throwingProductionComponentMonitorFactories() {
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryA)
+ .create(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryB)
+ .create(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryC)
+ .create(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
+ }
+
+ @Test
+ public void multipleMonitors_someNullProductionComponentMonitors() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorA);
+ when(mockProductionComponentMonitorFactoryB.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorFactoryC.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorA);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA);
+ order.verify(mockProducerMonitorA).requested();
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA);
+ }
+
+ @Test
+ public void multipleMonitors_someThrowingProductionComponentMonitorFactories() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorA);
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryB)
+ .create(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryC)
+ .create(any(Object.class));
+ when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorA);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA);
+ order.verify(mockProducerMonitorA).requested();
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA);
+ }
+
+ @Test
+ public void multipleMonitors_normalProductionComponentMonitorSuccess() {
+ setUpNormalMultipleMonitors();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).requested();
+ order.verify(mockProducerMonitorB).requested();
+ order.verify(mockProducerMonitorC).requested();
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).succeeded(o);
+ order.verify(mockProducerMonitorB).succeeded(o);
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ @Test
+ public void multipleMonitors_normalProductionComponentMonitorFailure() {
+ setUpNormalMultipleMonitors();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Throwable t = new RuntimeException("chimpanzee");
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).requested();
+ order.verify(mockProducerMonitorB).requested();
+ order.verify(mockProducerMonitorC).requested();
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).failed(t);
+ order.verify(mockProducerMonitorB).failed(t);
+ order.verify(mockProducerMonitorA).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ @Test
+ public void multipleMonitors_someThrowingProducerMonitorsSuccess() {
+ setUpNormalMultipleMonitors();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).requested();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorB).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorC).succeeded(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).requested();
+ order.verify(mockProducerMonitorB).requested();
+ order.verify(mockProducerMonitorC).requested();
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).succeeded(o);
+ order.verify(mockProducerMonitorB).succeeded(o);
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ @Test
+ public void multipleMonitors_someThrowingProducerMonitorsFailure() {
+ setUpNormalMultipleMonitors();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).requested();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorB).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorC).failed(any(Throwable.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Throwable t = new RuntimeException("chimpanzee");
+ producerMonitor.requested();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).requested();
+ order.verify(mockProducerMonitorB).requested();
+ order.verify(mockProducerMonitorC).requested();
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).failed(t);
+ order.verify(mockProducerMonitorB).failed(t);
+ order.verify(mockProducerMonitorA).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ private void setUpNormalSingleMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitor);
+ when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitor);
+ }
+
+ private void setUpNormalMultipleMonitors() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorA);
+ when(mockProductionComponentMonitorFactoryB.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorB);
+ when(mockProductionComponentMonitorFactoryC.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorC);
+ when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorA);
+ when(mockProductionComponentMonitorB.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorB);
+ when(mockProductionComponentMonitorC.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorC);
+ }
+}
diff --git a/javatests/dagger/spi/BUILD b/javatests/dagger/spi/BUILD
new file mode 100644
index 0000000..a94461b
--- /dev/null
+++ b/javatests/dagger/spi/BUILD
@@ -0,0 +1,44 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tests for the Dagger SPI
+
+package(default_visibility = ["//:src"])
+
+load("//:test_defs.bzl", "GenJavaTests")
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+)
+
+GenJavaTests(
+ name = "spi_tests",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen:processor",
+ "//java/dagger/model",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
diff --git a/javatests/dagger/spi/FailingPlugin.java b/javatests/dagger/spi/FailingPlugin.java
new file mode 100644
index 0000000..8fd0e35
--- /dev/null
+++ b/javatests/dagger/spi/FailingPlugin.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.spi;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import dagger.model.BindingGraph;
+import java.util.Map;
+import java.util.Set;
+
+@AutoService(BindingGraphPlugin.class)
+public final class FailingPlugin implements BindingGraphPlugin {
+ private Map<String, String> options;
+
+ @Override
+ public Set<String> supportedOptions() {
+ return ImmutableSet.of(
+ "error_on_binding",
+ "error_on_dependency",
+ "error_on_component",
+ "error_on_subcomponents");
+ }
+
+ @Override
+ public void initOptions(Map<String, String> options) {
+ this.options = options;
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ if (options.containsKey("error_on_binding")) {
+ String key = options.get("error_on_binding");
+ bindingGraph.bindings().stream()
+ .filter(binding -> binding.key().toString().equals(key))
+ .forEach(
+ binding ->
+ diagnosticReporter.reportBinding(ERROR, binding, "Bad Binding: %s", binding));
+ }
+
+ if (options.containsKey("error_on_component")) {
+ diagnosticReporter.reportComponent(
+ ERROR,
+ bindingGraph.rootComponentNode(),
+ "Bad Component: %s",
+ bindingGraph.rootComponentNode());
+ }
+
+ if (options.containsKey("error_on_subcomponents")) {
+ bindingGraph.componentNodes().stream()
+ .filter(componentNode -> !componentNode.componentPath().atRoot())
+ .forEach(
+ componentNode ->
+ diagnosticReporter.reportComponent(
+ ERROR, componentNode, "Bad Subcomponent: %s", componentNode));
+ }
+
+ if (options.containsKey("error_on_dependency")) {
+ String dependency = options.get("error_on_dependency");
+ bindingGraph.dependencyEdges().stream()
+ .filter(
+ edge ->
+ edge.dependencyRequest()
+ .requestElement()
+ .get()
+ .getSimpleName()
+ .contentEquals(dependency))
+ .forEach(
+ edge -> diagnosticReporter.reportDependency(ERROR, edge, "Bad Dependency: %s", edge));
+ }
+
+ }
+
+ @Override
+ public String pluginName() {
+ return "FailingPlugin";
+ }
+}
diff --git a/javatests/dagger/spi/SpiPluginTest.java b/javatests/dagger/spi/SpiPluginTest.java
new file mode 100644
index 0000000..613a915
--- /dev/null
+++ b/javatests/dagger/spi/SpiPluginTest.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.spi;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.ComponentProcessor;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class SpiPluginTest {
+ @Test
+ public void moduleBinding() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static int provideInt() {",
+ " return 0;",
+ " }",
+ "}");
+
+ Compilation compilation =
+ javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions(
+ "-Aerror_on_binding=java.lang.Integer", "-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message("[FailingPlugin] Bad Binding: @Provides int test.TestModule.provideInt()"))
+ .inFile(module)
+ .onLineContaining("interface TestModule");
+ }
+
+ @Test
+ public void dependencyTraceAtBinding() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " Foo foo();",
+ "}");
+
+ Compilation compilation =
+ javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions("-Aerror_on_binding=test.Foo")
+ .compile(component, foo);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "[FailingPlugin] Bad Binding: @Inject test.Foo()",
+ " test.Foo is provided at",
+ " test.TestComponent.foo()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void dependencyTraceAtDependencyRequest() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo(Duplicated inFooDep) {}",
+ "}");
+ JavaFileObject duplicated =
+ JavaFileObjects.forSourceLines(
+ "test.Duplicated",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Duplicated {",
+ " @Inject Duplicated() {}",
+ "}");
+ JavaFileObject entryPoint =
+ JavaFileObjects.forSourceLines(
+ "test.EntryPoint",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class EntryPoint {",
+ " @Inject EntryPoint(Foo foo, Duplicated dup1, Duplicated dup2) {}",
+ "}");
+ JavaFileObject chain1 =
+ JavaFileObjects.forSourceLines(
+ "test.Chain1",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Chain1 {",
+ " @Inject Chain1(Chain2 chain) {}",
+ "}");
+ JavaFileObject chain2 =
+ JavaFileObjects.forSourceLines(
+ "test.Chain2",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Chain2 {",
+ " @Inject Chain2(Chain3 chain) {}",
+ "}");
+ JavaFileObject chain3 =
+ JavaFileObjects.forSourceLines(
+ "test.Chain3",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Chain3 {",
+ " @Inject Chain3(Foo foo) {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " EntryPoint entryPoint();",
+ " Chain1 chain();",
+ "}");
+
+ CompilationFactory compilationFactory =
+ new CompilationFactory(component, foo, duplicated, entryPoint, chain1, chain2, chain3);
+
+ assertThat(compilationFactory.compilationWithErrorOnDependency("entryPoint"))
+ .hadErrorContaining(
+ message(
+ "[FailingPlugin] Bad Dependency: test.TestComponent.entryPoint() (entry point)",
+ " test.EntryPoint is provided at",
+ " test.TestComponent.entryPoint()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ assertThat(compilationFactory.compilationWithErrorOnDependency("dup1"))
+ .hadErrorContaining(
+ message(
+ "[FailingPlugin] Bad Dependency: test.EntryPoint(…, dup1, …)",
+ " test.Duplicated is injected at",
+ " test.EntryPoint(…, dup1, …)",
+ " test.EntryPoint is provided at",
+ " test.TestComponent.entryPoint()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ assertThat(compilationFactory.compilationWithErrorOnDependency("dup2"))
+ .hadErrorContaining(
+ message(
+ "[FailingPlugin] Bad Dependency: test.EntryPoint(…, dup2)",
+ " test.Duplicated is injected at",
+ " test.EntryPoint(…, dup2)",
+ " test.EntryPoint is provided at",
+ " test.TestComponent.entryPoint()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+
+ Compilation inFooDepCompilation =
+ compilationFactory.compilationWithErrorOnDependency("inFooDep");
+ assertThat(inFooDepCompilation)
+ .hadErrorContaining(
+ message(
+ "[FailingPlugin] Bad Dependency: test.Foo(inFooDep)",
+ " test.Duplicated is injected at",
+ " test.Foo(inFooDep)",
+ " test.Foo is injected at",
+ " test.EntryPoint(foo, …)",
+ " test.EntryPoint is provided at",
+ " test.TestComponent.entryPoint()",
+ "The following other entry points also depend on it:",
+ " test.TestComponent.chain()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void dependencyTraceAtDependencyRequest_subcomponents() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo() {}",
+ "}");
+ JavaFileObject entryPoint =
+ JavaFileObjects.forSourceLines(
+ "test.EntryPoint",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class EntryPoint {",
+ " @Inject EntryPoint(Foo foo) {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " TestSubcomponent sub();",
+ "}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface TestSubcomponent {",
+ " EntryPoint childEntryPoint();",
+ "}");
+
+ CompilationFactory compilationFactory =
+ new CompilationFactory(component, subcomponent, foo, entryPoint);
+ assertThat(compilationFactory.compilationWithErrorOnDependency("childEntryPoint"))
+ .hadErrorContaining(
+ message(
+ "[FailingPlugin] Bad Dependency: "
+ + "test.TestSubcomponent.childEntryPoint() (entry point)",
+ " test.EntryPoint is provided at",
+ " test.TestSubcomponent.childEntryPoint()"
+ + " [test.TestComponent → test.TestSubcomponent]"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ assertThat(compilationFactory.compilationWithErrorOnDependency("foo"))
+ .hadErrorContaining(
+ // TODO(ronshapiro): Maybe make the component path resemble a stack trace:
+ // test.TestSubcomponent is a child of
+ // test.TestComponent
+ // TODO(dpb): Or invert the order: Child → Parent
+ message(
+ "[FailingPlugin] Bad Dependency: test.EntryPoint(foo)",
+ " test.Foo is injected at",
+ " test.EntryPoint(foo)",
+ " test.EntryPoint is provided at",
+ " test.TestSubcomponent.childEntryPoint() "
+ + "[test.TestComponent → test.TestSubcomponent]"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void errorOnComponent() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {}");
+
+ Compilation compilation =
+ javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions("-Aerror_on_component")
+ .compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("[FailingPlugin] Bad Component: test.TestComponent")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
+ public void errorOnSubcomponent() {
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface TestSubcomponent {}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " TestSubcomponent subcomponent();",
+ "}");
+
+ Compilation compilation =
+ javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions("-Aerror_on_subcomponents")
+ .compile(component, subcomponent);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[FailingPlugin] Bad Subcomponent: test.TestComponent → test.TestSubcomponent "
+ + "[test.TestComponent → test.TestSubcomponent]")
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ // SpiDiagnosticReporter uses a shortest path algorithm to determine a dependency trace to a
+ // binding. Without modifications, this would produce a strange error if a shorter path exists
+ // from one entrypoint, through a @Module.subcomponents builder binding edge, and to the binding
+ // usage within the subcomponent. Therefore, when scanning for the shortest path, we only consider
+ // BindingNodes so we don't cross component boundaries. This test exhibits this case.
+ @Test
+ public void shortestPathToBindingExistsThroughSubcomponentBuilder() {
+ JavaFileObject chain1 =
+ JavaFileObjects.forSourceLines(
+ "test.Chain1",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Chain1 {",
+ " @Inject Chain1(Chain2 chain) {}",
+ "}");
+ JavaFileObject chain2 =
+ JavaFileObjects.forSourceLines(
+ "test.Chain2",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Chain2 {",
+ " @Inject Chain2(Chain3 chain) {}",
+ "}");
+ JavaFileObject chain3 =
+ JavaFileObjects.forSourceLines(
+ "test.Chain3",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Chain3 {",
+ " @Inject Chain3(ExposedOnSubcomponent exposedOnSubcomponent) {}",
+ "}");
+ JavaFileObject exposedOnSubcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.ExposedOnSubcomponent",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class ExposedOnSubcomponent {",
+ " @Inject ExposedOnSubcomponent() {}",
+ "}");
+ JavaFileObject subcomponent =
+ JavaFileObjects.forSourceLines(
+ "test.TestSubcomponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface TestSubcomponent {",
+ " ExposedOnSubcomponent exposedOnSubcomponent();",
+ "",
+ " @Subcomponent.Builder",
+ " interface Builder {",
+ " TestSubcomponent build();",
+ " }",
+ "}");
+ JavaFileObject subcomponentModule =
+ JavaFileObjects.forSourceLines(
+ "test.SubcomponentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module(subcomponents = TestSubcomponent.class)",
+ "interface SubcomponentModule {}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = SubcomponentModule.class)",
+ "interface TestComponent {",
+ " Chain1 chain();",
+ " TestSubcomponent.Builder subcomponent();",
+ "}");
+
+ Compilation compilation =
+ javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions("-Aerror_on_binding=test.ExposedOnSubcomponent")
+ .compile(
+ component,
+ subcomponent,
+ chain1,
+ chain2,
+ chain3,
+ exposedOnSubcomponent,
+ subcomponentModule);
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "[FailingPlugin] Bad Binding: @Inject test.ExposedOnSubcomponent()",
+ " test.ExposedOnSubcomponent is injected at",
+ " test.Chain3(exposedOnSubcomponent)",
+ " test.Chain3 is injected at",
+ " test.Chain2(chain)",
+ " test.Chain2 is injected at",
+ " test.Chain1(chain)",
+ " test.Chain1 is provided at",
+ " test.TestComponent.chain()",
+ "The following other entry points also depend on it:",
+ " test.TestSubcomponent.exposedOnSubcomponent() "
+ + "[test.TestComponent → test.TestSubcomponent]"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ // This works around an issue in the opensource compile testing where only one diagnostic is
+ // recorded per line. When multiple validation items resolve to the same entry point, we can
+ // only see the first. This helper class makes it easier to compile all of the files in the test
+ // multiple times with different options to single out each error
+ private static class CompilationFactory {
+ private final ImmutableList<JavaFileObject> javaFileObjects;
+
+ CompilationFactory(JavaFileObject... javaFileObjects) {
+ this.javaFileObjects = ImmutableList.copyOf(javaFileObjects);
+ }
+
+ private Compilation compilationWithErrorOnDependency(String dependencySimpleName) {
+ return javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions("-Aerror_on_dependency=" + dependencySimpleName)
+ .compile(javaFileObjects);
+ }
+ }
+
+ private static String message(String... lines) {
+ return Joiner.on("\n ").join(lines);
+ }
+}
diff --git a/lib/auto-common-1.0-20151022.071545-39.jar.txt b/lib/NOTICE
similarity index 100%
rename from lib/auto-common-1.0-20151022.071545-39.jar.txt
rename to lib/NOTICE
diff --git a/lib/auto-common-0.10-sources.jar b/lib/auto-common-0.10-sources.jar
new file mode 100644
index 0000000..59bf056
--- /dev/null
+++ b/lib/auto-common-0.10-sources.jar
Binary files differ
diff --git a/lib/auto-common-0.10-sources.jar.asc b/lib/auto-common-0.10-sources.jar.asc
new file mode 100644
index 0000000..ecc3339
--- /dev/null
+++ b/lib/auto-common-0.10-sources.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQEcBAABAgAGBQJaYLeuAAoJEF4feafCmGYeVzIH/A0rkeHli229WFOj5c3HnFqK
+hbyy32JRw8GZXTqNFamkuxWDF03jHwQn0ymp0nxRQ+I3JRTzbfuTnT73e+kiNSI8
+02PowzbrghgsuaifiOGpHaO/FRSBaexzjE1TbZncO6jM8RIg1J7GPqgUenRwA5YT
+7VL7Ig+5G9bXOZMcQ4OuHwqi2O1rSfnSDDIFGlDqmKNiJWHi4KijxNred9CvUGeW
+7zFBOzQkqUqm7Vs2MHmBvNM79fcD1F1SFa2I7+p6/oD1ecC0TAHLdlLhR1/sLBaG
+iw3uQeeflVZ6ilzJg93Rl2kNUw77nnywkt12SY76j1tpF5Ny2l8tid3bBrQEUPE=
+=0e1a
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-common-0.10-sources.jar.sha1 b/lib/auto-common-0.10-sources.jar.sha1
new file mode 100644
index 0000000..b9b3bee
--- /dev/null
+++ b/lib/auto-common-0.10-sources.jar.sha1
@@ -0,0 +1 @@
+913c8de9604380c6e135086132adb26c77fa6c53
\ No newline at end of file
diff --git a/lib/auto-common-0.10.jar b/lib/auto-common-0.10.jar
new file mode 100644
index 0000000..8cbfa72
--- /dev/null
+++ b/lib/auto-common-0.10.jar
Binary files differ
diff --git a/lib/auto-common-0.10.jar.asc b/lib/auto-common-0.10.jar.asc
new file mode 100644
index 0000000..6d81bdf
--- /dev/null
+++ b/lib/auto-common-0.10.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQEcBAABAgAGBQJaYLeuAAoJEF4feafCmGYe/1gIAInqdd+9NqxfOKRw6ujpL3nT
+XmmsP9gR/Tvvi3xRKj8PjcO9ydO2IfQ9ySAS6qbKuS9SIQn+Plq+6E+rwYkKy5t/
+MZ7Ff59XgMW0uxFEA2zUbcprgQyR6M4A31MYVmKuZ2fGBs5OsoN0+sZPCDmo4EzN
+TIgZ/LdNUWXLIsIRAKzCnUkYjlnNHKcJbW527obNH0UF/TBARrEAtmfCbKT6f91/
+zl1GtnxNR/9LVRxErFHQPcHrRxVP72xRHjdTCJHsL0t5cJJPr9maqkXq8DAyyptU
+XcjisvelIubTqnBDXcqJBIM3beGYPa5rot+3NjnNbylbfSqMED1dSGJL/fypY/U=
+=5BoX
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-common-0.10.jar.sha1 b/lib/auto-common-0.10.jar.sha1
new file mode 100644
index 0000000..2056a80
--- /dev/null
+++ b/lib/auto-common-0.10.jar.sha1
@@ -0,0 +1 @@
+c8f153ebe04a17183480ab4016098055fb474364
\ No newline at end of file
diff --git a/lib/auto-common-1.0-20151022.071545-39-sources.jar b/lib/auto-common-1.0-20151022.071545-39-sources.jar
deleted file mode 100644
index 66b78b0..0000000
--- a/lib/auto-common-1.0-20151022.071545-39-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-common-1.0-20151022.071545-39.jar b/lib/auto-common-1.0-20151022.071545-39.jar
deleted file mode 100644
index 8967dea..0000000
--- a/lib/auto-common-1.0-20151022.071545-39.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-factory-1.0-20150915.183854-35-sources.jar b/lib/auto-factory-1.0-20150915.183854-35-sources.jar
deleted file mode 100644
index 5c12407..0000000
--- a/lib/auto-factory-1.0-20150915.183854-35-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-factory-1.0-20150915.183854-35.jar b/lib/auto-factory-1.0-20150915.183854-35.jar
deleted file mode 100644
index 09f408b..0000000
--- a/lib/auto-factory-1.0-20150915.183854-35.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-factory-1.0-20150915.183854-35.jar.txt b/lib/auto-factory-1.0-20150915.183854-35.jar.txt
deleted file mode 100644
index d645695..0000000
--- a/lib/auto-factory-1.0-20150915.183854-35.jar.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/lib/auto-factory-1.0-beta6-sources.jar b/lib/auto-factory-1.0-beta6-sources.jar
new file mode 100644
index 0000000..56c6909
--- /dev/null
+++ b/lib/auto-factory-1.0-beta6-sources.jar
Binary files differ
diff --git a/lib/auto-factory-1.0-beta6-sources.jar.asc b/lib/auto-factory-1.0-beta6-sources.jar.asc
new file mode 100644
index 0000000..be05c6e
--- /dev/null
+++ b/lib/auto-factory-1.0-beta6-sources.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlvN1rYACgkQxR5svH/0
+bwsyngf8C60gBeqMcbTJ05w6IccmLONiVEBfXXVASLGxJRW6A4C6CZCOaeu/JTGV
+RlyKSL4RxT6LdN4IE1k/b+SWwhAGxES6n2J1G9TFq/SiRSEjPL2a41qRynjXo9Sx
+D/jWX4flGS4fT229EDx18d+Vy3So5+jPNLN7KyB4E9yBZfnTTmeENsgvs0Y1jw5D
+MNK73Nwwg+f7R42J+07FauRU8YXPYcG1UYZcnmAeHNDmwfL1UOsfSzrglHbU59mw
+MrupMb3h5VSBZcqknQUBbPQUUWo7e+jSJZKDB750CxBNSBfF3tN2D1vJ1/ynCIDA
+i+iY39xggnIbyKTPj1/aOGTBVfiVJQ==
+=xs9M
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-factory-1.0-beta6-sources.jar.sha1 b/lib/auto-factory-1.0-beta6-sources.jar.sha1
new file mode 100644
index 0000000..9b5c0a8
--- /dev/null
+++ b/lib/auto-factory-1.0-beta6-sources.jar.sha1
@@ -0,0 +1 @@
+fab31580230ac3001579238a8857a74bd045e3b9
\ No newline at end of file
diff --git a/lib/auto-factory-1.0-beta6.jar b/lib/auto-factory-1.0-beta6.jar
new file mode 100644
index 0000000..e47130f
--- /dev/null
+++ b/lib/auto-factory-1.0-beta6.jar
Binary files differ
diff --git a/lib/auto-factory-1.0-beta6.jar.asc b/lib/auto-factory-1.0-beta6.jar.asc
new file mode 100644
index 0000000..5da4253
--- /dev/null
+++ b/lib/auto-factory-1.0-beta6.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlvN1rIACgkQxR5svH/0
+bwt0VAf/S4AELEOE7cdcSkSF8vNCUhHgJbyRIb9SLT9Jh6ZXt0sVt1ZMB4Jcx9WL
+GfIwCAZhPcidTF3yVRvK0Sj2zHq53bGNsejL5BLO73oSoVowv7NV96v42zbBtMDU
+K6AI8G7lXEHYDlW4gwiyz/5CjP8VYSJgQ5vuC81rNL0vrBY0S30MlzHmCLjCvE2b
+DpV+thGWluhStwJRwvQArtUDgXYW6BPGetls/Mr0LKiSvz9uS9EvRvcQHiiwqxPu
+Jj5vheKyyeaRq7BOOswoUvfidr+UrNBt/Jit3L2kuo5n4n7sfb6irR610X8mMMN/
+kx5cA78f+p7XHbLZh6w3pySp+HCvKw==
+=1KAq
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-factory-1.0-beta6.jar.sha1 b/lib/auto-factory-1.0-beta6.jar.sha1
new file mode 100644
index 0000000..57a5e61
--- /dev/null
+++ b/lib/auto-factory-1.0-beta6.jar.sha1
@@ -0,0 +1 @@
+58c804763a4d80c0884ac8a740fcff4d61da72bc
\ No newline at end of file
diff --git a/lib/auto-service-1.0-rc2-sources.jar b/lib/auto-service-1.0-rc2-sources.jar
deleted file mode 100644
index cd8f687..0000000
--- a/lib/auto-service-1.0-rc2-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-service-1.0-rc2.jar b/lib/auto-service-1.0-rc2.jar
deleted file mode 100644
index ea8fb68..0000000
--- a/lib/auto-service-1.0-rc2.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-service-1.0-rc2.jar.txt b/lib/auto-service-1.0-rc2.jar.txt
deleted file mode 100644
index d645695..0000000
--- a/lib/auto-service-1.0-rc2.jar.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/lib/auto-service-1.0-rc5-sources.jar b/lib/auto-service-1.0-rc5-sources.jar
new file mode 100644
index 0000000..8a05c9d
--- /dev/null
+++ b/lib/auto-service-1.0-rc5-sources.jar
Binary files differ
diff --git a/lib/auto-service-1.0-rc5-sources.jar.asc b/lib/auto-service-1.0-rc5-sources.jar.asc
new file mode 100644
index 0000000..08ea732
--- /dev/null
+++ b/lib/auto-service-1.0-rc5-sources.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH5AACgkQxR5svH/0
+bwuBWwf/QMOlEF0td8ykweHGoOt5B6JmBLk4lPNhZDZdGmvqn+RzbWHo7F5QoBL6
+RQ+Wn97UDroLrIeMRsWvOzL/dA/kMhE2tNX8OKP1kaU7d1ahSkY6PRrPrdcf34IA
+Ku62psSXM5L78HdmhfVpf6UBsa3QJ9ZtLrBMjubxJ7aVrHCTIPP8obVgfgFWHnZD
+nGiynMUNXT6H+L9mDtkWHnVEkC07VBTCFr+6HA3TIf76KQ8LiIj6h+7l6KlPz48h
+eyaElzUUn5deTIG7jrPC2SaVhC0fKu/j1ZazN8tj/25+v9Q9mgZieYYEtiKjrEJU
+tli80ajN26E2NduLldkSVPt6N1Nf/w==
+=6nn7
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-service-1.0-rc5-sources.jar.sha1 b/lib/auto-service-1.0-rc5-sources.jar.sha1
new file mode 100644
index 0000000..3638b53
--- /dev/null
+++ b/lib/auto-service-1.0-rc5-sources.jar.sha1
@@ -0,0 +1 @@
+76bf7fbfc5a924f13115005a134546c4e2d1b245
\ No newline at end of file
diff --git a/lib/auto-service-1.0-rc5.jar b/lib/auto-service-1.0-rc5.jar
new file mode 100644
index 0000000..8c43e8f
--- /dev/null
+++ b/lib/auto-service-1.0-rc5.jar
Binary files differ
diff --git a/lib/auto-service-1.0-rc5.jar.2 b/lib/auto-service-1.0-rc5.jar.2
new file mode 100644
index 0000000..8c43e8f
--- /dev/null
+++ b/lib/auto-service-1.0-rc5.jar.2
Binary files differ
diff --git a/lib/auto-service-1.0-rc5.jar.asc b/lib/auto-service-1.0-rc5.jar.asc
new file mode 100644
index 0000000..e1d0163
--- /dev/null
+++ b/lib/auto-service-1.0-rc5.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH48ACgkQxR5svH/0
+bwuZSAgAgmJ8gEx+MLtdt8IJY0ZGZtzntCOv2kTmieTQdwLKbmEc/WeQBXZeAWjb
+xKctEnesbNGwJY5jpPBiQH0nDd0MyIOc25gCvug2ezveo9eNe9ptOQFi+4gsG3mv
+0SGD9ZnRkzW8wNyMMWdBUJYdGPJp/FshsOVajBVsMDSev3OBxw8qfT4ZqhTO3LN6
+UnFydeqtbukqBiQRBrWEO7zXDmeHP+6GMWOD4Tkt60VRrSo7Sk6WeUkSJcB0rrmw
++/blQ3jBlN2/ummOc1dWUu7EyBoWhJhChQDQAuwev5oMMyQnhTLiFcHPvoKWFTlO
+GsBFENSqqxlW4j2ZYpoMX42inuaqbQ==
+=AMUZ
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-service-1.0-rc5.jar.sha1 b/lib/auto-service-1.0-rc5.jar.sha1
new file mode 100644
index 0000000..2edbbf4
--- /dev/null
+++ b/lib/auto-service-1.0-rc5.jar.sha1
@@ -0,0 +1 @@
+d25246bae325b4bcc63b55d6d782515fac32215a
\ No newline at end of file
diff --git a/lib/auto-service-annotations-1.0-rc5-sources.jar b/lib/auto-service-annotations-1.0-rc5-sources.jar
new file mode 100644
index 0000000..72fd4a9
--- /dev/null
+++ b/lib/auto-service-annotations-1.0-rc5-sources.jar
Binary files differ
diff --git a/lib/auto-service-annotations-1.0-rc5-sources.jar.asc b/lib/auto-service-annotations-1.0-rc5-sources.jar.asc
new file mode 100644
index 0000000..0058623
--- /dev/null
+++ b/lib/auto-service-annotations-1.0-rc5-sources.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH4IACgkQxR5svH/0
+bwtnEwgAntYeAk/i4kaPhOdKCni4pZpkJ3WcEaDIiZROIvxn3GLFxDp1+WaFOuZS
+kYrqKLmDJdOwEK31FGP/XjsedmB6PP90pp01apaw2XS7pAzXQyNGMOypgCntJsN6
+ZkQYNLum8HQ6XXvEQh9j0eKU/9D+x7DRaxQZ69GkjFS1EwpytEYIEdFsjvHz3SYW
+G15++0H5+l7ZyNjXCs//x6tQ73nqhEu6UylwR4A9YvDuZhpMoFPNvB1+L6mlARgi
+cDRtm8hGLhsNeIYqGmbUEzwwKtKY/n7NwJX1zpXAg86/9E8R1xdbdzfGL6ctqQal
+w3WSYcNnmiZQfPinZuGHnrqDk/rN2w==
+=lgp6
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-service-annotations-1.0-rc5-sources.jar.sha1 b/lib/auto-service-annotations-1.0-rc5-sources.jar.sha1
new file mode 100644
index 0000000..fc01aa3
--- /dev/null
+++ b/lib/auto-service-annotations-1.0-rc5-sources.jar.sha1
@@ -0,0 +1 @@
+a2e50e3ba1f9a88f89142e7ea9a0f5380574f4e4
\ No newline at end of file
diff --git a/lib/auto-service-annotations-1.0-rc5.jar b/lib/auto-service-annotations-1.0-rc5.jar
new file mode 100644
index 0000000..fbc08ab
--- /dev/null
+++ b/lib/auto-service-annotations-1.0-rc5.jar
Binary files differ
diff --git a/lib/auto-service-annotations-1.0-rc5.jar.asc b/lib/auto-service-annotations-1.0-rc5.jar.asc
new file mode 100644
index 0000000..b7e43ce
--- /dev/null
+++ b/lib/auto-service-annotations-1.0-rc5.jar.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH4EACgkQxR5svH/0
+bwtNcwf+IvcoHSI7AvrM+z6Lhqes8wEdU9Q59ixakvEos/umjYwCXoGJyAG/w8N5
+4woRDJRg8KgWC0Kls7CGs5ZwplQTLRte/SmXWux8CV0/3v6OlIP4voWvXBueqKST
+gNN/Miy0Qx5uX1Qwh0inqlFF7jOj+IuTofQ4ZM3UjZPLVYhSfS/wTwLmNWNawQq7
+b/PkxaYgC1RuNv9onA8Vru2UCIlH8NFP9iPqPMwhRVVxoSaZzo5NxrRNRPZUfPzF
+GUH/SRDOfXHJ1joeZRK21s8adOGzZZJsSxxZ393xx1jl5qLdoowjTtENgc9gOSQG
+FzxOfWCoWncsJP10KfyYRV70JnJMrQ==
+=dWz/
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-service-annotations-1.0-rc5.jar.sha1 b/lib/auto-service-annotations-1.0-rc5.jar.sha1
new file mode 100644
index 0000000..4c48b47
--- /dev/null
+++ b/lib/auto-service-annotations-1.0-rc5.jar.sha1
@@ -0,0 +1 @@
+6ea999af2b6262a7179a09c51a3d54e7b40a3833
\ No newline at end of file
diff --git a/lib/auto-value-1.4.1-sources.jar b/lib/auto-value-1.4.1-sources.jar
deleted file mode 100644
index c6b307f..0000000
--- a/lib/auto-value-1.4.1-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-value-1.4.1.jar b/lib/auto-value-1.4.1.jar
deleted file mode 100644
index eeeac6a..0000000
--- a/lib/auto-value-1.4.1.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-value-1.4.1.jar.txt b/lib/auto-value-1.4.1.jar.txt
deleted file mode 100644
index d645695..0000000
--- a/lib/auto-value-1.4.1.jar.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/lib/auto-value-1.6.5-sources.jar b/lib/auto-value-1.6.5-sources.jar
new file mode 100644
index 0000000..7d08fa7
--- /dev/null
+++ b/lib/auto-value-1.6.5-sources.jar
Binary files differ
diff --git a/lib/auto-value-1.6.5-sources.jar.asc b/lib/auto-value-1.6.5-sources.jar.asc
new file mode 100644
index 0000000..36551c1
--- /dev/null
+++ b/lib/auto-value-1.6.5-sources.jar.asc
@@ -0,0 +1,12 @@
+-----BEGIN PGP SIGNATURE-----
+Comment: GPGTools - https://gpgtools.org
+
+iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMe0ACgkQsPNxD6ZJ
+AOc+4wgAkYRN5mdnQ54oLb/YGL9jnU7tdcFnq73ddH9wJH9cTGE8dAHIlNT4lEjy
+ligUXdi6YbuAC2Fjs7nMZiSOjEEDlKRozbwE82WOiIdV/MltMJgFCdBbVpszHFmu
+NWxDq5KocdgLODHwmIWJ0Xey6M1rjVZEshNf/U70604eEbGl9Hu0aTVFFVcASMM5
+CC38/X/G72Ja1Cz404WqGpmAsrHgDHdcPUVT3xxDReV1B+yPKxazzuKZA8/+KkWP
+RddLifE3W2NYnLVX49DS9iWCBiEbh5P6jtHvQSiyXIawexhQJGhjQMwHoi0+jHJd
+INp5YyrB64PHqaSgtuXhwgTfXmxkMA==
+=5Cw9
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-value-1.6.5-sources.jar.md5 b/lib/auto-value-1.6.5-sources.jar.md5
new file mode 100644
index 0000000..ca18ea4
--- /dev/null
+++ b/lib/auto-value-1.6.5-sources.jar.md5
@@ -0,0 +1 @@
+4a433a939d3f77766c785288d5c24c10
\ No newline at end of file
diff --git a/lib/auto-value-1.6.5-sources.jar.sha1 b/lib/auto-value-1.6.5-sources.jar.sha1
new file mode 100644
index 0000000..17f16a8
--- /dev/null
+++ b/lib/auto-value-1.6.5-sources.jar.sha1
@@ -0,0 +1 @@
+bfc251753f9bbdd8855825361d5f8c7fec8a1471
\ No newline at end of file
diff --git a/lib/auto-value-1.6.5.jar b/lib/auto-value-1.6.5.jar
new file mode 100644
index 0000000..be17cd1
--- /dev/null
+++ b/lib/auto-value-1.6.5.jar
Binary files differ
diff --git a/lib/auto-value-1.6.5.jar.asc b/lib/auto-value-1.6.5.jar.asc
new file mode 100644
index 0000000..f0cdc39
--- /dev/null
+++ b/lib/auto-value-1.6.5.jar.asc
@@ -0,0 +1,12 @@
+-----BEGIN PGP SIGNATURE-----
+Comment: GPGTools - https://gpgtools.org
+
+iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMewACgkQsPNxD6ZJ
+AOd5ygf8DOjgClYv8hCaiHiu2mDwFV4s24SbyvSZrAeBrHOoY6/E7leSqilJ+mF9
+3kjDv/VtHTv9Ds2cX2daH5hnvuYIwiCzdze3ZWdKJ+PtWsKpqEAevfEMRmSPPUV4
+ZZ5Zd6VZspD+rUViKfwF8ZW1zjKjdcIPbhuiYR4TF718kPL8WCV2bGruYidjYBoe
+hzLUUqY0rv+IwV+OycbyZwtmx1DVCTkYlDdepSyswr2RGb/UFIf44E0koboI+Xue
+6nF+w6AaOaufjdqe2ObIWNa2tsPTj4KABh3tHmOMs35x367/PzwQmh0I25gFAQrX
+KYo6dYVZeLWmNpdIJxeMIZRok0MAKA==
+=KnCZ
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-value-1.6.5.jar.md5 b/lib/auto-value-1.6.5.jar.md5
new file mode 100644
index 0000000..11a5e9d
--- /dev/null
+++ b/lib/auto-value-1.6.5.jar.md5
@@ -0,0 +1 @@
+d3730b8e2f9c6f62153181618a13f3a2
\ No newline at end of file
diff --git a/lib/auto-value-1.6.5.jar.sha1 b/lib/auto-value-1.6.5.jar.sha1
new file mode 100644
index 0000000..edf62d8
--- /dev/null
+++ b/lib/auto-value-1.6.5.jar.sha1
@@ -0,0 +1 @@
+816872c85048f36a67a276ef7a49cc2e4595711c
\ No newline at end of file
diff --git a/lib/auto-value-annotations-1.6.5-sources.jar b/lib/auto-value-annotations-1.6.5-sources.jar
new file mode 100644
index 0000000..b1a8b08
--- /dev/null
+++ b/lib/auto-value-annotations-1.6.5-sources.jar
Binary files differ
diff --git a/lib/auto-value-annotations-1.6.5-sources.jar.asc b/lib/auto-value-annotations-1.6.5-sources.jar.asc
new file mode 100644
index 0000000..81a4515
--- /dev/null
+++ b/lib/auto-value-annotations-1.6.5-sources.jar.asc
@@ -0,0 +1,12 @@
+-----BEGIN PGP SIGNATURE-----
+Comment: GPGTools - https://gpgtools.org
+
+iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMdUACgkQsPNxD6ZJ
+AOfNYwgAqoJ9hi7JyS1iwf646TPLPbtRaaRzl/w9hrZntVlMiHL5Ov+LHq6WW1tS
+IWqz9K6dbEDW4O3WbNXUsiWTGzUvkNc/bjQOY4MTCthTj/LO9PqHVSDGgWo/Y37c
+jazTbUNxctFSziu9mVZdw1VdCI65LVCbhSaOeIc7Msmr/ALtZ1pBGdJ8dzxuC3Dl
+BeT2khADj7C13Kicu7QtZfZypbTqB6JnjAwMO56c4pRviIqRpJsOKHbJM1KBxms+
+6JGv63XTBP22f83XX38wAAPocT5NZBn+lZcDcbC6Mh0NIhaISdx73ujWtTwHX5fC
+x0NYKIIBdyEb5GkPeD1D/IjB4w8o5w==
+=Hbou
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-value-annotations-1.6.5-sources.jar.sha1 b/lib/auto-value-annotations-1.6.5-sources.jar.sha1
new file mode 100644
index 0000000..897752c
--- /dev/null
+++ b/lib/auto-value-annotations-1.6.5-sources.jar.sha1
@@ -0,0 +1 @@
+3499fd80025705c502699d1154c4b9631cb7a95e
\ No newline at end of file
diff --git a/lib/auto-value-annotations-1.6.5.jar b/lib/auto-value-annotations-1.6.5.jar
new file mode 100644
index 0000000..f68a013
--- /dev/null
+++ b/lib/auto-value-annotations-1.6.5.jar
Binary files differ
diff --git a/lib/auto-value-annotations-1.6.5.jar.asc b/lib/auto-value-annotations-1.6.5.jar.asc
new file mode 100644
index 0000000..1356995
--- /dev/null
+++ b/lib/auto-value-annotations-1.6.5.jar.asc
@@ -0,0 +1,12 @@
+-----BEGIN PGP SIGNATURE-----
+Comment: GPGTools - https://gpgtools.org
+
+iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMdQACgkQsPNxD6ZJ
+AOeeQQf/cRfXuh8RCDR5WWAHPXpK9JzA5OEEvtXl13uSqpcU+f2/APNOmG9tJz2B
+53bp6d18knaoSAWtC1Hl2kM8eZPYNlpB+MC8VJ+/JAZMf2arLMKGuvii9eYmGrvJ
+kXfRHqWgZQMQIKrd2ne/PMgnmixLPSF6HtyFh2JN/LbjR7ipQR3VAJe89QGmIFFf
+njSptIgKuDauQrjq//yXpWRyhUNheAH42y3aIaUvydcSOLsGIqQfTJLOe3sSMxlU
+d0fJWP47uW04y2j6kXv4SYKXGnBrv2M+c/6YRriPv69tSBc+2DY6SpMZsYdV9Car
+pDM+7m5lxCN8mI/KRRNcXaSR9GLJ2A==
+=ey6m
+-----END PGP SIGNATURE-----
diff --git a/lib/auto-value-annotations-1.6.5.jar.sha1 b/lib/auto-value-annotations-1.6.5.jar.sha1
new file mode 100644
index 0000000..858c96e
--- /dev/null
+++ b/lib/auto-value-annotations-1.6.5.jar.sha1
@@ -0,0 +1 @@
+c3dad10377f0e2242c9a4b88e9704eaf79103679
\ No newline at end of file
diff --git a/lib/google-java-format-0.1-20151017.042846-2.jar b/lib/google-java-format-0.1-20151017.042846-2.jar
deleted file mode 100644
index 964760f..0000000
--- a/lib/google-java-format-0.1-20151017.042846-2.jar
+++ /dev/null
Binary files differ
diff --git a/lib/google-java-format-0.1-20151017.042846-2.jar.txt b/lib/google-java-format-0.1-20151017.042846-2.jar.txt
deleted file mode 100644
index d645695..0000000
--- a/lib/google-java-format-0.1-20151017.042846-2.jar.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/lib/google-java-format-1.7-all-deps.jar b/lib/google-java-format-1.7-all-deps.jar
new file mode 100644
index 0000000..e2d40de
--- /dev/null
+++ b/lib/google-java-format-1.7-all-deps.jar
Binary files differ
diff --git a/lib/google-java-format-1.7-all-deps.jar.asc b/lib/google-java-format-1.7-all-deps.jar.asc
new file mode 100644
index 0000000..73ae0e2
--- /dev/null
+++ b/lib/google-java-format-1.7-all-deps.jar.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEE53QXrBlBYKP6vQSWmiWcfuY2xe0FAlw2hwoACgkQmiWcfuY2
+xe22Ww//YIHLWoQApuSmLdq4A1fcNOYbSpm6J9I2J1i2VoK84XSXP1XJTWsoYNbx
+Y3Xx6I8p28AxH7LOWcdtds9enbF06blzz+1CxuSxvN32M7DDursiihsUYU9wmZVU
+7V1ZYBtaYTYYw8C5U+UchcIVJrHdG9KrAiBcn1s76gigNAtCYV/1PyryupS1wcDZ
+bdj48mI6adcnIpV+9D9W4rLuXw2Hg+ewktXTj3HYqkg+5Nvc/zP9ueBvqEsyh5Iz
+3+69egblhuN7gRaEWarlYfJ2n/kF9T/UMAkQrWD8+9cOgUcgTHkNADNOLpmDH6t7
+OYvZhgwQT920ZJgGP8e3DGyvMxBMxlr19ciTT19HL5qglyhFm/SeHvFdpaHkIjb9
+CmASWrlkALdEv2gPx+m79EJifyK5tYtViZgLhfjwYkS/opw5TfJX0+Ngh9ehgune
+c6TgEIzRQAEQ/9QoVk2crGT/lhXnJI3Zid7cSP2/fRdui91KaKGi9qFvXyJf7XKI
+vH0De+e3yZN/GXLYyQSUzmdQU8HMF9iuc81CT1ANgU+e9HU5k9FWSPHrA3gN7CKt
+/a3qIsIBpcdKe9zcPxSoEqRzVNept4IhszgiIVxZPNrPShzCRbS1sGURoOTbSFSP
++aKWNFoswX/RANGQVajOyfF5mJ3lB4N0u2eI0HIR3NzNx0HN+2Q=
+=+/96
+-----END PGP SIGNATURE-----
diff --git a/lib/google-java-format-1.7-all-deps.jar.sha1 b/lib/google-java-format-1.7-all-deps.jar.sha1
new file mode 100644
index 0000000..b247a7c
--- /dev/null
+++ b/lib/google-java-format-1.7-all-deps.jar.sha1
@@ -0,0 +1 @@
+b6d34a51e579b08db7c624505bdf9af4397f1702
\ No newline at end of file
diff --git a/lib/google-java-format-1.7-sources.jar b/lib/google-java-format-1.7-sources.jar
new file mode 100644
index 0000000..70fca1b
--- /dev/null
+++ b/lib/google-java-format-1.7-sources.jar
Binary files differ
diff --git a/lib/google-java-format-1.7-sources.jar.asc b/lib/google-java-format-1.7-sources.jar.asc
new file mode 100644
index 0000000..713333c
--- /dev/null
+++ b/lib/google-java-format-1.7-sources.jar.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEE53QXrBlBYKP6vQSWmiWcfuY2xe0FAlw2hwsACgkQmiWcfuY2
+xe1j6BAAsDY9DTTcjRtrT6AiZchELcJleOdqK3TRdbl193Mtw5eR+BdKqtcLZSmZ
+1EqYNcldOu9wgbZFpFL99xWNAPOt8tTU9hTsZvrRN0e5CwKXrlJl0MeJi/6yYquG
+FerYGZuDMMEza1q9D2TG5erLOjDEWVLjkSs3jw8mSyMvN0OJFhhMrU4sqhlXMGNV
+lDxeYXLXr+QtIi0tXAUQ5XSwtR7eukSevA7n3zQJKfYMlFbG82lMGJccfazJpdPf
+cJLAGY9/q3RVZx5vk00kz3sDmL+rMU01Lh+CHwT9D8ASRfzjkssHuKaMYBysuh7s
+YZ6X3Txvtkzf3fJjYl5jID3jEui82iR5jv5ncZFZFCsZsTRM0sLAP5/KeX128dHM
+fJzVH4GIUvOqt9aIDKQ+3aNsARsIH2Z2AHymUxVZoP33V9HoC8x0ghxM0xohvkh5
+L+VgUOEjekOneqWD1QEF+HD7S3hpy36+g37PYGcQRRaCIDn00Se+UbJ9RZrUVdvu
+JgmVXcpagrLmcSnkG265zdDW9Vu7vjAKhxjLXmbE0aowzdo/HZ+u3R2hpZbOyv33
+NmakhTwaUtQtDm34GVPqoypnu85va27vxVDQgTsk3im9M1l/UP65NidPuuSk7iOI
+UECD7wc6Ddh80jmDlXdlxSNG+zzGp95gS5UVoDXgwNEok7UgrY4=
+=zj6k
+-----END PGP SIGNATURE-----
diff --git a/lib/google-java-format-1.7-sources.jar.sha1 b/lib/google-java-format-1.7-sources.jar.sha1
new file mode 100644
index 0000000..31d6337
--- /dev/null
+++ b/lib/google-java-format-1.7-sources.jar.sha1
@@ -0,0 +1 @@
+dbb14f20004dfaf01a5ce9f5e75c5993f18cc0a5
\ No newline at end of file
diff --git a/lib/google-java-format-1.7.jar b/lib/google-java-format-1.7.jar
new file mode 100644
index 0000000..953541c
--- /dev/null
+++ b/lib/google-java-format-1.7.jar
Binary files differ
diff --git a/lib/google-java-format-1.7.jar.asc b/lib/google-java-format-1.7.jar.asc
new file mode 100644
index 0000000..3642d69
--- /dev/null
+++ b/lib/google-java-format-1.7.jar.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEE53QXrBlBYKP6vQSWmiWcfuY2xe0FAlw2hwkACgkQmiWcfuY2
+xe1uIhAAt9sWjStHkfHmWNFd5exYsjBmN0eObgOU5A386j5Q5P4hZjFDYsDvKlVo
+DWZKI2A9ZvpisuI083DwGJCXvbtD1WICh5Xx1B2tK2oCgl5bYiU88LCw5jS9F2IZ
+thPFhFDMIw4tQZp2J4o+itO7gtoUMfN5J9Y7ZcOWG60F3ouGc6vd8PnMk/CfxNZ0
+Zy21dkBzJVJsNghUWsHfP6WS2ySVBNQaarY+/4IuIrBVqRNZBhD+qEtZRG3mwc4X
+ydd4hCvoAQ7lysrdZEMDRx0qLQoqEWyfNFX7b8X/Oi6Ape765mD3Ss1WFgsjLqo+
+kt4Ge09f4QOCtMP95M8AnnHGjpVuibC0tb2E7qXfcrMWf+EX8ksFJKwaXZQfG4lv
+7kxZDTeeDdU6SUakiwfkEKN6s4v0MrGi8TM+776gLihcD/tx09555h+EwXrbp3IE
+1SWJCiYCuaqe1UXNEsZGYKv72GkPSEjY813Tn3endvGXX+QrLCU6DrLRyy1IWFKJ
+Yu6THeT0Z+M6h+oTO0ug2gJ6Ur7apQC4JBIi12XE+QhwuTuV0byiUArmO+3elLH4
+Nd9O0yFhKClaJ+ye+X+XLYkIjh+IJGFmbUahvCFuMMKuwbFRHwn5pw0DOJvE7qjg
+dBZhwjzKN8RHzik5nFwzhBBIzj3UglacGkUPFv7JW1TqcY2X6kE=
+=VIxo
+-----END PGP SIGNATURE-----
diff --git a/lib/google-java-format-1.7.jar.sha1 b/lib/google-java-format-1.7.jar.sha1
new file mode 100644
index 0000000..d0a76c2
--- /dev/null
+++ b/lib/google-java-format-1.7.jar.sha1
@@ -0,0 +1 @@
+97cb6afc835d65682edc248e19170a8e4ecfe4c4
\ No newline at end of file
diff --git a/lib/javax-inject-src.jar b/lib/javax-inject-src.jar
deleted file mode 100644
index a8a5aa7..0000000
--- a/lib/javax-inject-src.jar
+++ /dev/null
Binary files differ
diff --git a/lib/javax-inject.jar b/lib/javax-inject.jar
deleted file mode 100644
index 4c86c52..0000000
--- a/lib/javax-inject.jar
+++ /dev/null
Binary files differ
diff --git a/lib/javax-inject.jar.txt b/lib/javax-inject.jar.txt
deleted file mode 100644
index d645695..0000000
--- a/lib/javax-inject.jar.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/lib/javax.inject-1-sources.jar b/lib/javax.inject-1-sources.jar
new file mode 100644
index 0000000..0c98053
--- /dev/null
+++ b/lib/javax.inject-1-sources.jar
Binary files differ
diff --git a/lib/javax.inject-1-sources.jar.sha1 b/lib/javax.inject-1-sources.jar.sha1
new file mode 100644
index 0000000..b69cdf0
--- /dev/null
+++ b/lib/javax.inject-1-sources.jar.sha1
@@ -0,0 +1 @@
+a00123f261762a7c5e0ec916a2c7c8298d29c400 /home/maven/repository-staging/to-ibiblio/maven2/javax/inject/javax.inject/1/javax.inject-1-sources.jar
diff --git a/lib/javax.inject-1.jar b/lib/javax.inject-1.jar
new file mode 100644
index 0000000..b2a9d0b
--- /dev/null
+++ b/lib/javax.inject-1.jar
Binary files differ
diff --git a/lib/javax.inject-1.jar.sha1 b/lib/javax.inject-1.jar.sha1
new file mode 100644
index 0000000..41e75ef
--- /dev/null
+++ b/lib/javax.inject-1.jar.sha1
@@ -0,0 +1 @@
+6975da39a7040257bd51d21a231b76c915872d38 /home/maven/repository-staging/to-ibiblio/maven2/javax/inject/javax.inject/1/javax.inject-1.jar
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index fcdfe0c..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,246 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2012 Square, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.sonatype.oss</groupId>
- <artifactId>oss-parent</artifactId>
- <version>7</version>
- </parent>
-
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-parent</artifactId>
- <packaging>pom</packaging>
- <version>2.1-SNAPSHOT</version>
- <name>Dagger (Parent)</name>
- <description>A fast dependency injector for Android and Java.</description>
- <url>https://github.com/square/dagger</url>
-
- <modules>
- <module>compiler</module>
- <module>core</module>
- <!-- examples are handled in a default profile (see below) -->
- <module>producers</module>
- </modules>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-
- <!-- Compilation -->
- <java.version>1.7</java.version>
- <javax.inject.version>1</javax.inject.version>
- <javax.annotation.version>2.0.1</javax.annotation.version>
- <javawriter.version>2.5.0</javawriter.version>
- <auto.common.version>1.0-SNAPSHOT</auto.common.version>
- <auto.factory.version>1.0-beta3</auto.factory.version>
- <auto.service.version>1.0-rc2</auto.service.version>
- <auto.value.version>1.0</auto.value.version>
- <guava.version>18.0</guava.version>
- <google.java.format.version>0.1-SNAPSHOT</google.java.format.version>
-
- <!-- Test Dependencies -->
- <compile-testing.version>1.0-SNAPSHOT</compile-testing.version>
- <junit.version>4.11</junit.version>
- <mockito.version>1.9.5</mockito.version>
- <truth.version>0.26</truth.version>
- </properties>
-
- <scm>
- <url>http://github.com/google/dagger/</url>
- <connection>scm:git:git://github.com/google/dagger.git</connection>
- <developerConnection>scm:git:ssh://git@github.com/google/dagger.git</developerConnection>
- <tag>HEAD</tag>
- </scm>
-
- <issueManagement>
- <system>GitHub Issues</system>
- <url>http://github.com/google/dagger/issues</url>
- </issueManagement>
-
- <licenses>
- <license>
- <name>Apache 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- </license>
- </licenses>
-
- <organization>
- <name>Google, Inc.</name>
- <url>http://www.google.com</url>
- </organization>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject</artifactId>
- <version>${javax.inject.version}</version>
- </dependency>
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject-tck</artifactId>
- <version>${javax.inject.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>jsr305</artifactId>
- <version>${javax.annotation.version}</version>
- </dependency>
- <dependency>
- <groupId>com.squareup</groupId>
- <artifactId>javawriter</artifactId>
- <version>${javawriter.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>${guava.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava-testlib</artifactId>
- <version>${guava.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.googlejavaformat</groupId>
- <artifactId>google-java-format</artifactId>
- <version>${google.java.format.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.auto</groupId>
- <artifactId>auto-common</artifactId>
- <version>${auto.common.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.auto.service</groupId>
- <artifactId>auto-service</artifactId>
- <version>${auto.service.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.auto.value</groupId>
- <artifactId>auto-value</artifactId>
- <version>${auto.value.version}</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>${junit.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.testing.compile</groupId>
- <artifactId>compile-testing</artifactId>
- <version>${compile-testing.version}</version>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>${mockito.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.truth</groupId>
- <artifactId>truth</artifactId>
- <version>${truth.version}</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <artifactId>maven-invoker-plugin</artifactId>
- <version>1.7</version>
- </plugin>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.1</version>
- </plugin>
- <plugin>
- <artifactId>maven-jar-plugin</artifactId>
- <version>2.5</version>
- </plugin>
- </plugins>
- </pluginManagement>
-
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>${java.version}</source>
- <target>${java.version}</target>
- <compilerArgument>-Xlint:all</compilerArgument>
- <showWarnings>true</showWarnings>
- <showDeprecation>true</showDeprecation>
- </configuration>
- </plugin>
-
- <plugin>
- <artifactId>maven-release-plugin</artifactId>
- <version>2.3.2</version><!--$NO-MVN-MAN-VER$-->
- <configuration>
- <autoVersionSubmodules>true</autoVersionSubmodules>
- </configuration>
- </plugin>
-
- <plugin>
- <artifactId>maven-javadoc-plugin</artifactId>
- <configuration>
- <doctitle>Dagger Dependency Injection ${project.version} API</doctitle>
- </configuration>
- </plugin>
-
- <plugin>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>2.10</version>
- <configuration>
- <failsOnError>false</failsOnError>
- <consoleOutput>true</consoleOutput>
- <configLocation>checkstyle.xml</configLocation>
- </configuration>
- <executions>
- <execution>
- <phase>compile</phase>
- <goals>
- <goal>checkstyle</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-
- <!--
- A profile which when switched off excludes example modules. By default the profile
- is on and invokes examples. However, when processing javadocs, it is switched off
- omitting the example code from the javadoc.
- -->
- <profiles>
- <profile>
- <id>examples</id>
- <activation>
- <activeByDefault>true</activeByDefault>
- </activation>
- <modules>
- <module>core</module>
- <module>compiler</module>
- <module>examples</module>
- <module>producers</module>
- </modules>
- </profile>
- </profiles>
-</project>
diff --git a/producers/pom.xml b/producers/pom.xml
deleted file mode 100644
index edaeca4..0000000
--- a/producers/pom.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2014 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-parent</artifactId>
- <version>2.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>dagger-producers</artifactId>
- <name>Dagger Production Graphs</name>
- <description>
- An asynchronous dependency injection system that extends JSR-330.
- </description>
-
- <dependencies>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>jsr305</artifactId>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.truth</groupId>
- <artifactId>truth</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava-testlib</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/producers/src/main/java/dagger/producers/Produced.java b/producers/src/main/java/dagger/producers/Produced.java
deleted file mode 100644
index db5c133..0000000
--- a/producers/src/main/java/dagger/producers/Produced.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers;
-
-import com.google.common.base.Objects;
-import dagger.internal.Beta;
-import java.util.concurrent.ExecutionException;
-import javax.annotation.Nullable;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * An interface that represents the result of a {@linkplain Producer production} of type {@code T},
- * or an exception that was thrown during that production. For any type {@code T} that can be
- * injected, you can also inject {@code Produced<T>}, which enables handling of any exceptions that
- * were thrown during the production of {@code T}.
- *
- * <p>For example: <pre><code>
- * {@literal @}Produces Html getResponse(
- * UserInfo criticalInfo, {@literal Produced<ExtraInfo>} noncriticalInfo) {
- * try {
- * return new Html(criticalInfo, noncriticalInfo.get());
- * } catch (ExecutionException e) {
- * logger.warning(e, "Noncritical info");
- * return new Html(criticalInfo);
- * }
- * }
- * </code></pre>
- *
- * @author Jesse Beder
- */
-@Beta
-public abstract class Produced<T> {
- /**
- * Returns the result of a production.
- *
- * @throws ExecutionException if the production threw an exception
- */
- public abstract T get() throws ExecutionException;
-
- /**
- * Two {@code Produced} objects compare equal if both are successful with equal values, or both
- * are failed with equal exceptions.
- */
- @Override
- public abstract boolean equals(Object o);
-
- /** Returns an appropriate hash code to match {@link #equals). */
- @Override
- public abstract int hashCode();
-
- /** Returns a successful {@code Produced}, whose {@link #get} will return the given value. */
- public static <T> Produced<T> successful(@Nullable T value) {
- return new Successful<T>(value);
- }
-
- /**
- * Returns a failed {@code Produced}, whose {@link #get} will throw an
- * {@code ExecutionException} with the given cause.
- */
- public static <T> Produced<T> failed(Throwable throwable) {
- return new Failed<T>(checkNotNull(throwable));
- }
-
- private static final class Successful<T> extends Produced<T> {
- @Nullable private final T value;
-
- private Successful(@Nullable T value) {
- this.value = value;
- }
-
- @Override public T get() {
- return value;
- }
-
- @Override public boolean equals(Object o) {
- if (o == this) {
- return true;
- } else if (o instanceof Successful) {
- Successful<?> that = (Successful<?>) o;
- return Objects.equal(this.value, that.value);
- } else {
- return false;
- }
- }
-
- @Override public int hashCode() {
- return value == null ? 0 : value.hashCode();
- }
- }
-
- private static final class Failed<T> extends Produced<T> {
- private final Throwable throwable;
-
- private Failed(Throwable throwable) {
- this.throwable = checkNotNull(throwable);
- }
-
- @Override public T get() throws ExecutionException {
- throw new ExecutionException(throwable);
- }
-
- @Override public boolean equals(Object o) {
- if (o == this) {
- return true;
- } else if (o instanceof Failed) {
- Failed<?> that = (Failed<?>) o;
- return this.throwable.equals(that.throwable);
- } else {
- return false;
- }
- }
-
- @Override public int hashCode() {
- return throwable.hashCode();
- }
- }
-
- private Produced() {}
-}
diff --git a/producers/src/main/java/dagger/producers/Producer.java b/producers/src/main/java/dagger/producers/Producer.java
deleted file mode 100644
index eb159bb..0000000
--- a/producers/src/main/java/dagger/producers/Producer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers;
-
-import dagger.internal.Beta;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * An interface that represents the production of a type {@code T}. You can also inject
- * {@code Producer<T>} instead of {@code T}, which will delay the execution of any code that
- * produces the {@code T} until {@link #get} is called.
- *
- * <p>For example, you might inject {@code Producer} to lazily choose between several different
- * implementations of some type: <pre><code>
- * {@literal @Produces ListenableFuture<Heater>} getHeater(
- * HeaterFlag flag,
- * {@literal @Electric Producer<Heater>} electricHeater,
- * {@literal @Gas Producer<Heater>} gasHeater) {
- * return flag.useElectricHeater() ? electricHeater.get() : gasHeater.get();
- * }
- * </code></pre>
- *
- * <p>Here is a complete example that demonstrates how calling {@code get()} will cause each
- * method to be executed: <pre><code>
- *
- * {@literal @}ProducerModule
- * final class MyModule {
- * {@literal @Produces ListenableFuture<A>} a() {
- * System.out.println("a");
- * return Futures.immediateFuture(new A());
- * }
- *
- * {@literal @Produces ListenableFuture<B>} b(A a) {
- * System.out.println("b");
- * return Futures.immediateFuture(new B(a));
- * }
- *
- * {@literal @Produces ListenableFuture<C>} c(B b) {
- * System.out.println("c");
- * return Futures.immediateFuture(new C(b));
- * }
- *
- * {@literal @Produces @Delayed ListenableFuture<C>} delayedC(A a, {@literal Producer<C>} c) {
- * System.out.println("delayed c");
- * return c.get();
- * }
- * }
- *
- * {@literal @}ProductionComponent(modules = MyModule.class)
- * interface MyComponent {
- * {@literal @Delayed ListenableFuture<C>} delayedC();
- * }
- * </code></pre>
- * Suppose we instantiate the generated implementation of this component and call
- * {@code delayedC()}: <pre><code>
- * MyComponent component = DaggerMyComponent
- * .builder()
- * .executor(MoreExecutors.directExecutor())
- * .build();
- * System.out.println("Constructed component");
- * {@literal ListenableFuture<C>} cFuture = component.delayedC();
- * System.out.println("Retrieved future");
- * C c = cFuture.get();
- * System.out.println("Retrieved c");
- * </code></pre>
- * Here, we're using {@code MoreExecutors.directExecutor} in order to illustrate how each call
- * directly causes code to execute. The above code will print: <pre><code>
- * Constructed component
- * a
- * delayed c
- * b
- * c
- * Retrieved future
- * Retrieved c
- * </code></pre>
- *
- * @author Jesse Beder
- */
-@Beta
-public interface Producer<T> {
- /**
- * Returns a future representing a running task that produces a value. Calling this method will
- * trigger the submission of this task to the executor, if it has not already been triggered. In
- * order to trigger this task's submission, the transitive dependencies required to produce the
- * {@code T} will be submitted to the executor, as their dependencies become available.
- *
- * <p>If the key is bound to a {@link Produces} method, then calling this method multiple times
- * will return the same future.
- */
- ListenableFuture<T> get();
-}
diff --git a/producers/src/main/java/dagger/producers/ProducerModule.java b/producers/src/main/java/dagger/producers/ProducerModule.java
deleted file mode 100644
index 714e3fa..0000000
--- a/producers/src/main/java/dagger/producers/ProducerModule.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers;
-
-import dagger.Module;
-import dagger.internal.Beta;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-
-/**
- * Annotates a class that contributes {@link Produces} bindings to the production component.
- *
- * @author Jesse Beder
- */
-@Documented
-@Target(TYPE)
-@Beta
-public @interface ProducerModule {
- /**
- * Additional {@code @ProducerModule}- or {@link Module}-annotated classes from which this module
- * is composed. The de-duplicated contributions of the modules in {@code includes}, and of their
- * inclusions recursively, are all contributed to the object graph.
- */
- Class<?>[] includes() default {};
-}
diff --git a/producers/src/main/java/dagger/producers/Produces.java b/producers/src/main/java/dagger/producers/Produces.java
deleted file mode 100644
index 1b57bc3..0000000
--- a/producers/src/main/java/dagger/producers/Produces.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers;
-
-import dagger.internal.Beta;
-import com.google.common.util.concurrent.ListenableFuture;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-/**
- * Annotates methods of a producer module to create a production binding. If the method returns
- * a {@link ListenableFuture}, then the parameter type of the future is bound to the value that the
- * future provides; otherwise, the return type is bound to the returned value. The production
- * component will pass dependencies to the method as parameters.
- *
- * @author Jesse Beder
- */
-@Documented
-@Target(METHOD)
-@Beta
-public @interface Produces {
- /** The type of binding into which the return type of the annotated method contributes. */
- enum Type {
- /**
- * The method is the only one which can produce the value for the specified type. This is the
- * default behavior.
- */
- UNIQUE,
-
- /**
- * The method's resulting type forms the generic type argument of a {@code Set<T>}, and the
- * returned value or future is contributed to the set. The {@code Set<T>} produced from the
- * accumulation of values will be immutable.
- */
- SET,
-
- /**
- * Like {@link #SET}, except the method's return type is either {@code Set<T>} or
- * {@code Set<ListenableFuture<T>>}, where any values are contributed to the set. An example use
- * is to provide a default empty set binding, which is otherwise not possible using
- * {@link #SET}.
- */
- SET_VALUES,
-
- /**
- * The method's return type forms the type argument for the value of a
- * {@code Map<K, Producer<V>>}, and the combination of the annotated key and the returned value
- * is contributed to the map as a key/value pair. The {@code Map<K, Producer<V>>} produced from
- * the accumulation of values will be immutable.
- */
- MAP;
- }
-
- Type type() default Type.UNIQUE;
-}
diff --git a/producers/src/main/java/dagger/producers/ProductionComponent.java b/producers/src/main/java/dagger/producers/ProductionComponent.java
deleted file mode 100644
index 8ccdb44..0000000
--- a/producers/src/main/java/dagger/producers/ProductionComponent.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.Beta;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Target;
-import javax.inject.Inject;
-import javax.inject.Qualifier;
-
-import static java.lang.annotation.ElementType.TYPE;
-
-/**
- * Annotates an interface or abstract class for which a fully-formed, dependency-injected
- * implementation is to be generated from a set of {@linkplain #modules}. The generated class will
- * have the name of the type annotated with {@code @ProductionComponent} prepended with
- * {@code Dagger}. For example, {@code @ProductionComponent interface MyComponent {...}} will
- * produce an implementation named {@code DaggerMyComponent}.
- *
- * <p>Each {@link Produces} method that contributes to the component will be called at most once per
- * component instance, no matter how many times that binding is used as a dependency.
- * TODO(user): Decide on how scope works for producers.
- *
- * <h2>Component methods</h2>
- *
- * <p>Every type annotated with {@code @ProductionComponent} must contain at least one abstract
- * component method. Component methods must represent {@linkplain Producer production}.
- *
- * Production methods have no arguments and return either a {@link ListenableFuture} or
- * {@link Producer} of a type that is {@link Inject injected}, {@link Provides provided}, or
- * {@link Produces produced}. Each may have a {@link Qualifier} annotation as well. The following
- * are all valid production method declarations: <pre><code>
- * ListenableFuture<SomeType> getSomeType();
- * {@literal Producer<Set<SomeType>>} getSomeTypes();
- * {@literal @Response ListenableFuture<Html>} getResponse();
- * </code></pre>
- *
- * <h2>Exceptions</h2>
- *
- * <p>When a producer throws an exception, the exception will be propagated to its downstream
- * producers in the following way: if the downstream producer injects a type {@code T}, then that
- * downstream producer will be skipped, and the exception propagated to its downstream producers;
- * and if the downstream producer injects a {@code Produced<T>}, then the downstream producer will
- * be run with the exception stored in the {@code Produced<T>}.
- *
- * <p>If a non-execution exception is thrown (e.g., an {@code InterruptedException} or
- * {@code CancellationException}), then exception is handled as in
- * {@link com.google.common.util.concurrent.Futures#transform}.
- * <!-- TODO(beder): Explain this more thoroughly, and update the javadocs of those utilities. -->
- *
- * @author Jesse Beder
- */
-@Documented
-@Target(TYPE)
-@Beta
-public @interface ProductionComponent {
- /**
- * A list of classes annotated with {@link Module} or {@link ProducerModule} whose bindings are
- * used to generate the component implementation.
- */
- Class<?>[] modules() default {};
-
- /**
- * A list of types that are to be used as component dependencies.
- */
- Class<?>[] dependencies() default {};
-
- /**
- * A builder for a component. Components may have a single nested static abstract class or
- * interface annotated with {@code @ProductionComponent.Builder}. If they do, then the component's
- * generated builder will match the API in the type. Builders must follow some rules:
- * <ul>
- * <li> A single abstract method with no arguments must exist, and must return the component.
- * (This is typically the {@code build()} method.)
- * <li> All other abstract methods must take a single argument and must return void,
- * the builder type, or a supertype of the builder.
- * <li> There <b>must</b> be an abstract method whose parameter is
- * {@link java.util.concurrent.Executor}.
- * <li> Each component dependency <b>must</b> have an abstract setter method.
- * <li> Each module dependency that Dagger can't instantiate itself (i.e., the module
- * doesn't have a visible no-args constructor) <b>must</b> have an abstract setter method.
- * Other module dependencies (ones that Dagger can instantiate) are allowed, but not
- * required.
- * <li> Non-abstract methods are allowed, but ignored as far as validation and builder generation
- * are concerned.
- * </ul>
- *
- * For example, this could be a valid {@code ProductionComponent} with a builder: <pre><code>
- * {@literal @}ProductionComponent(modules = {BackendModule.class, FrontendModule.class})
- * interface MyComponent {
- * {@literal ListenableFuture<MyWidget>} myWidget();
- *
- * {@literal @}ProductionComponent.Builder
- * interface Builder {
- * MyComponent build();
- * Builder executor(Executor executor);
- * Builder backendModule(BackendModule bm);
- * Builder frontendModule(FrontendModule fm);
- * }
- * }</code></pre>
- */
- @Target(TYPE)
- @Documented
- @interface Builder {}
-}
diff --git a/producers/src/main/java/dagger/producers/internal/AbstractProducer.java b/producers/src/main/java/dagger/producers/internal/AbstractProducer.java
deleted file mode 100644
index f7e8ec0..0000000
--- a/producers/src/main/java/dagger/producers/internal/AbstractProducer.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Producer;
-import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import dagger.producers.monitoring.internal.Monitors;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import javax.annotation.Nullable;
-import javax.inject.Provider;
-
-/**
- * An abstract {@link Producer} implementation that memoizes the result of its compute method.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-public abstract class AbstractProducer<T> implements Producer<T> {
- private final Provider<ProductionComponentMonitor> monitorProvider;
- @Nullable private final ProducerToken token;
- private volatile ListenableFuture<T> instance = null;
-
- protected AbstractProducer() {
- this(Monitors.noOpProductionComponentMonitorProvider(), null);
- }
-
- protected AbstractProducer(
- Provider<ProductionComponentMonitor> monitorProvider, @Nullable ProducerToken token) {
- this.monitorProvider = checkNotNull(monitorProvider);
- this.token = token;
- }
-
- /** Computes this producer's future, which is then cached in {@link #get}. */
- protected abstract ListenableFuture<T> compute(ProducerMonitor monitor);
-
- @Override
- public final ListenableFuture<T> get() {
- // double-check idiom from EJ2: Item 71
- ListenableFuture<T> result = instance;
- if (result == null) {
- synchronized (this) {
- result = instance;
- if (result == null) {
- ProducerMonitor monitor = monitorProvider.get().producerMonitorFor(token);
- instance = result = compute(monitor);
- if (result == null) {
- throw new NullPointerException("compute returned null");
- }
- monitor.addCallbackTo(result);
- }
- }
- }
- return result;
- }
-}
diff --git a/producers/src/main/java/dagger/producers/internal/Producers.java b/producers/src/main/java/dagger/producers/internal/Producers.java
deleted file mode 100644
index 7052c35..0000000
--- a/producers/src/main/java/dagger/producers/internal/Producers.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.FutureFallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.monitoring.ProducerMonitor;
-import java.util.Set;
-import javax.inject.Provider;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Utility methods for use in generated producer code.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-public final class Producers {
- /**
- * Returns a future of {@link Produced} that represents the completion (either success or failure)
- * of the given future. If the input future succeeds, then the resulting future also succeeds with
- * a successful {@code Produced}; if the input future fails, then the resulting future succeeds
- * with a failing {@code Produced}.
- *
- * <p>Cancelling the resulting future will propagate the cancellation to the input future; but
- * cancelling the input future will trigger the resulting future to succeed with a failing
- * {@code Produced}.
- */
- // TODO(user): Document what happens with an InterruptedException after you figure out how to
- // trigger one in a test.
- public static <T> ListenableFuture<Produced<T>> createFutureProduced(ListenableFuture<T> future) {
- // TODO(dpb): Switch to Futures.catchAsync once guava_jdk5 gets to v19.
- return Futures.withFallback(
- Futures.transform(
- future,
- new Function<T, Produced<T>>() {
- @Override
- public Produced<T> apply(final T value) {
- return Produced.successful(value);
- }
- }),
- Producers.<T>futureFallbackForProduced());
-
- }
-
- private static final FutureFallback<Produced<Object>> FUTURE_FALLBACK_FOR_PRODUCED =
- new FutureFallback<Produced<Object>>() {
- @Override public ListenableFuture<Produced<Object>> create(final Throwable t) {
- Produced<Object> produced = Produced.failed(t);
- return Futures.immediateFuture(produced);
- }
- };
-
- @SuppressWarnings({"unchecked", "rawtypes"}) // bivariant implementation
- private static <T> FutureFallback<Produced<T>> futureFallbackForProduced() {
- return (FutureFallback) FUTURE_FALLBACK_FOR_PRODUCED;
- }
-
- /**
- * Returns a future of a {@code Set} that contains a single element: the result of the input
- * future.
- */
- public static <T> ListenableFuture<Set<T>> createFutureSingletonSet(ListenableFuture<T> future) {
- return Futures.transform(future, new Function<T, Set<T>>() {
- @Override public Set<T> apply(T value) {
- return ImmutableSet.of(value);
- }
- });
- }
-
- /**
- * Returns a producer that immediately executes the binding logic for the given provider every
- * time it is called.
- */
- public static <T> Producer<T> producerFromProvider(final Provider<T> provider) {
- checkNotNull(provider);
- return new AbstractProducer<T>() {
- @Override
- protected ListenableFuture<T> compute(ProducerMonitor unusedMonitor) {
- return Futures.immediateFuture(provider.get());
- }
- };
- }
-
- /** Returns a producer that succeeds with the given value. */
- public static <T> Producer<T> immediateProducer(final T value) {
- return new Producer<T>() {
- @Override
- public ListenableFuture<T> get() {
- return Futures.immediateFuture(value);
- }
- };
- }
-
- /** Returns a producer that fails with the given exception. */
- public static <T> Producer<T> immediateFailedProducer(final Throwable throwable) {
- return new Producer<T>() {
- @Override
- public ListenableFuture<T> get() {
- return Futures.immediateFailedFuture(throwable);
- }
- };
- }
-
- private Producers() {}
-}
diff --git a/producers/src/main/java/dagger/producers/internal/SetOfProducedProducer.java b/producers/src/main/java/dagger/producers/internal/SetOfProducedProducer.java
deleted file mode 100644
index e51bcad..0000000
--- a/producers/src/main/java/dagger/producers/internal/SetOfProducedProducer.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.monitoring.ProducerMonitor;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-
-/**
- * A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns a
- * future {@code Set<Produced<T>>} whose elements are populated by subsequent calls to the delegate
- * {@link Producer#get} methods.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-public final class SetOfProducedProducer<T> extends AbstractProducer<Set<Produced<T>>> {
- /**
- * Returns a new producer that creates {@link Set} futures from the union of the given
- * {@link Producer} instances.
- */
- @SafeVarargs
- public static <T> Producer<Set<Produced<T>>> create(Producer<Set<T>>... producers) {
- return new SetOfProducedProducer<T>(ImmutableSet.copyOf(producers));
- }
-
- private final ImmutableSet<Producer<Set<T>>> contributingProducers;
-
- private SetOfProducedProducer(ImmutableSet<Producer<Set<T>>> contributingProducers) {
- this.contributingProducers = contributingProducers;
- }
-
- /**
- * Returns a future {@link Set} of {@link Produced} values whose iteration order is that of the
- * elements given by each of the producers, which are invoked in the order given at creation.
- *
- * <p>If any of the delegate sets, or any elements therein, are null, then that corresponding
- * {@code Produced} element will fail with a NullPointerException.
- *
- * <p>Canceling this future will attempt to cancel all of the component futures; but if any of the
- * delegate futures fail or are canceled, this future succeeds, with the appropriate failed
- * {@link Produced}.
- *
- * @throws NullPointerException if any of the delegate producers return null
- */
- @Override
- public ListenableFuture<Set<Produced<T>>> compute(ProducerMonitor unusedMonitor) {
- List<ListenableFuture<Produced<Set<T>>>> futureProducedSets =
- new ArrayList<ListenableFuture<Produced<Set<T>>>>(contributingProducers.size());
- for (Producer<Set<T>> producer : contributingProducers) {
- ListenableFuture<Set<T>> futureSet = producer.get();
- if (futureSet == null) {
- throw new NullPointerException(producer + " returned null");
- }
- futureProducedSets.add(Producers.createFutureProduced(futureSet));
- }
- return Futures.transform(
- Futures.allAsList(futureProducedSets),
- new Function<List<Produced<Set<T>>>, Set<Produced<T>>>() {
- @Override
- public Set<Produced<T>> apply(List<Produced<Set<T>>> producedSets) {
- ImmutableSet.Builder<Produced<T>> builder = ImmutableSet.builder();
- for (Produced<Set<T>> producedSet : producedSets) {
- try {
- Set<T> set = producedSet.get();
- if (set == null) {
- // TODO(beder): This is a vague exception. Can we somehow point to the failing
- // producer? See the similar comment in the component writer about null
- // provisions.
- builder.add(
- Produced.<T>failed(
- new NullPointerException(
- "Cannot contribute a null set into a producer set binding when it's"
- + " injected as Set<Produced<T>>.")));
- } else {
- for (T value : set) {
- if (value == null) {
- builder.add(
- Produced.<T>failed(
- new NullPointerException(
- "Cannot contribute a null element into a producer set binding"
- + " when it's injected as Set<Produced<T>>.")));
- } else {
- builder.add(Produced.successful(value));
- }
- }
- }
- } catch (ExecutionException e) {
- builder.add(Produced.<T>failed(e.getCause()));
- }
- }
- return builder.build();
- }
- });
- }
-}
diff --git a/producers/src/main/java/dagger/producers/internal/SetProducer.java b/producers/src/main/java/dagger/producers/internal/SetProducer.java
deleted file mode 100644
index 5b3c090..0000000
--- a/producers/src/main/java/dagger/producers/internal/SetProducer.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Producer;
-import dagger.producers.monitoring.ProducerMonitor;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns
- * a future {@link Set} whose elements are populated by subsequent calls to the delegate
- * {@link Producer#get} methods.
- *
- * @author Jesse Beder
- * @since 2.0
- */
-public final class SetProducer<T> extends AbstractProducer<Set<T>> {
- /**
- * Returns a new producer that creates {@link Set} futures from the union of the given
- * {@link Producer} instances.
- */
- @SafeVarargs
- public static <T> Producer<Set<T>> create(Producer<Set<T>>... producers) {
- return new SetProducer<T>(ImmutableSet.copyOf(producers));
- }
-
- private final Set<Producer<Set<T>>> contributingProducers;
-
- private SetProducer(Set<Producer<Set<T>>> contributingProducers) {
- super();
- this.contributingProducers = contributingProducers;
- }
-
- /**
- * Returns a future {@link Set} whose iteration order is that of the elements given by each of the
- * producers, which are invoked in the order given at creation.
- *
- * <p>If any of the delegate sets, or any elements therein, are null, then this future will fail
- * with a NullPointerException.
- *
- * <p>Canceling this future will attempt to cancel all of the component futures, and if any of the
- * delegate futures fails or is canceled, this one is, too.
- *
- * @throws NullPointerException if any of the delegate producers return null
- */
- @Override
- public ListenableFuture<Set<T>> compute(ProducerMonitor unusedMonitor) {
- List<ListenableFuture<Set<T>>> futureSets =
- new ArrayList<ListenableFuture<Set<T>>>(contributingProducers.size());
- for (Producer<Set<T>> producer : contributingProducers) {
- ListenableFuture<Set<T>> futureSet = producer.get();
- if (futureSet == null) {
- throw new NullPointerException(producer + " returned null");
- }
- futureSets.add(futureSet);
- }
- return Futures.transform(Futures.allAsList(futureSets), new Function<List<Set<T>>, Set<T>>() {
- @Override public Set<T> apply(List<Set<T>> sets) {
- ImmutableSet.Builder<T> builder = ImmutableSet.builder();
- for (Set<T> set : sets) {
- builder.addAll(set);
- }
- return builder.build();
- }
- });
- }
-}
diff --git a/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java b/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java
deleted file mode 100644
index 20551c3..0000000
--- a/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.monitoring;
-
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Produces;
-
-/**
- * A hook for monitoring the execution of individual {@linkplain Produces producer methods}. See
- * {@link ProductionComponentMonitor} for how to install these monitors.
- *
- * <p>The lifecycle of the monitor, under normal conditions, is:
- * <ul>
- * <li>{@link #methodStarting()}
- * <li>The method is called
- * <li>{@link #methodFinished()}
- * <li>If the method returns a value, then:
- * <ul>
- * <li>{@link #succeeded(Object)} if the method returned normally; or
- * <li>{@link #failed(Throwable)} if the method threw an exception.
- * </ul>
- * <li>If the method returns a future, then:
- * <ul>
- * <li>{@link #succeeded(Object)} if the method returned normally, and the future succeeded; or
- * <li>{@link #failed(Throwable)} if the method threw an exception, or returned normally and the
- * future failed.
- * </ul>
- * </ul>
- *
- * <p>If any input to the monitored producer fails, {@link #failed(Throwable)} will be called
- * immediately with the failed input's exception. If more than one input fails, an arbitrary failed
- * input's exception is used.
- *
- * <p>If any of the monitor's methods throw, then the exception will be logged and processing will
- * continue unaffected.
- *
- * @author Jesse Beder
- */
-public abstract class ProducerMonitor {
- /**
- * Called when the producer method is about to start executing.
- *
- * <p>When multiple monitors are installed, the order that each monitor will call this method is
- * unspecified, but will remain consistent throughout the course of the execution of a component.
- */
- public void methodStarting() {}
-
- /**
- * Called when the producer method has finished executing.
- *
- * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
- * calls to {@link #methodStarting()}.
- */
- public void methodFinished() {}
-
- /**
- * Called when the producer’s future has completed successfully with a value.
- *
- * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
- * calls to {@link #methodStarting()}.
- */
- public void succeeded(Object o) {}
-
- /**
- * Called when the producer's future has failed with an exception.
- *
- * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
- * calls to {@link #methodStarting()}.
- */
- public void failed(Throwable t) {}
-
- /**
- * Adds this monitor's completion methods as a callback to the future. This is only intended to be
- * overridden in the framework!
- */
- public <T> void addCallbackTo(ListenableFuture<T> future) {
- Futures.addCallback(
- future,
- new FutureCallback<T>() {
- @Override
- public void onSuccess(T value) {
- succeeded(value);
- }
-
- @Override
- public void onFailure(Throwable t) {
- failed(t);
- }
- });
- }
-}
diff --git a/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java b/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java
deleted file mode 100644
index 5834206..0000000
--- a/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.monitoring;
-
-import dagger.producers.Produces;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/** A token that represents an individual {@linkplain Produces producer method}. */
-public final class ProducerToken {
- private final Class<?> classToken;
-
- private ProducerToken(Class<?> classToken) {
- this.classToken = classToken;
- }
-
- /**
- * Creates a token for a class token that represents the generated factory for a producer method.
- *
- * <p><b>Do not use this!</b> This is intended to be called by generated code only, and its
- * signature may change at any time.
- */
- public static ProducerToken create(Class<?> classToken) {
- return new ProducerToken(checkNotNull(classToken));
- }
-
- /** Two tokens are equal if they represent the same method. */
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- } else if (o instanceof ProducerToken) {
- ProducerToken that = (ProducerToken) o;
- return this.classToken.equals(that.classToken);
- } else {
- return false;
- }
- }
-
- /** Returns an appropriate hash code to match {@link #equals). */
- @Override
- public int hashCode() {
- return classToken.hashCode();
- }
-
- /** Returns a representation of the method. */
- @Override
- public String toString() {
- return classToken.toString();
- }
-}
diff --git a/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java b/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java
deleted file mode 100644
index 4dc2903..0000000
--- a/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.monitoring;
-
-import dagger.producers.Produces;
-import dagger.producers.ProductionComponent;
-
-/**
- * A hook for monitoring execution of {@linkplain ProductionComponent production components}. To
- * install a {@code ProductionComponentMonitor}, contribute to a set binding of
- * {@code ProductionComponentMonitor.Factory}. The factory will be asked to create one monitor for
- * the component, and the resulting single instance will be used to create individual monitors for
- * producers.
- *
- * <p>For example: <pre><code>
- * {@literal @Module}
- * final class MyMonitorModule {
- * {@literal @Provides(type = SET)} ProductionComponentMonitor.Factory provideMonitorFactory(
- * MyProductionComponentMonitor.Factory monitorFactory) {
- * return monitorFactory;
- * }
- * }
- *
- * {@literal @ProductionComponent(modules = {MyMonitorModule.class, MyProducerModule.class})}
- * interface MyComponent {
- * {@literal ListenableFuture<SomeType>} someType();
- * }
- * </code></pre>
- *
- * <p>If any of these methods throw, then the exception will be logged, and the framework will act
- * as though a no-op monitor was returned.
- *
- * @author Jesse Beder
- */
-public interface ProductionComponentMonitor {
- /** Returns a monitor for an individual {@linkplain Produces producer method}. */
- ProducerMonitor producerMonitorFor(ProducerToken token);
-
- public interface Factory {
- /** Creates a component-specific monitor when the component is created. */
- ProductionComponentMonitor create(Object component);
- }
-}
diff --git a/producers/src/main/java/dagger/producers/monitoring/internal/MonitorCache.java b/producers/src/main/java/dagger/producers/monitoring/internal/MonitorCache.java
deleted file mode 100644
index 2a76c5f..0000000
--- a/producers/src/main/java/dagger/producers/monitoring/internal/MonitorCache.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.monitoring.internal;
-
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.inject.Provider;
-
-/**
- * A class that provides a {@link ProductionComponentMonitor} for use in production components.
- *
- * <p>This caches the underlying the monitor, since we want a single instance for each component.
- */
-public final class MonitorCache {
- private static final Logger logger = Logger.getLogger(MonitorCache.class.getName());
-
- private volatile ProductionComponentMonitor monitor;
-
- /**
- * Returns the underlying monitor. This will only actually compute the monitor the first time it
- * is called; subsequent calls will simply return the cached value, so the arguments to this
- * method are ignored. It is expected (though not checked) that this method is called with
- * equivalent arguments each time (like a {@link dagger.Provides @Provides} method would).
- */
- public ProductionComponentMonitor monitor(
- Provider<?> componentProvider,
- Provider<Set<ProductionComponentMonitor.Factory>> monitorFactorySetProvider) {
- ProductionComponentMonitor result = monitor;
- if (result == null) {
- synchronized (this) {
- result = monitor;
- if (result == null) {
- try {
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- monitorFactorySetProvider.get());
- result = monitor = factory.create(componentProvider.get());
- } catch (RuntimeException e) {
- logger.log(Level.SEVERE, "RuntimeException while constructing monitor factories.", e);
- result = monitor = Monitors.noOpProductionComponentMonitor();
- }
- }
- }
- }
- return result;
- }
-}
diff --git a/producers/src/main/java/dagger/producers/monitoring/internal/Monitors.java b/producers/src/main/java/dagger/producers/monitoring/internal/Monitors.java
deleted file mode 100644
index f27ce37..0000000
--- a/producers/src/main/java/dagger/producers/monitoring/internal/Monitors.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.monitoring.internal;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import java.util.Collection;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.inject.Provider;
-
-/**
- * Utility methods relating to monitoring, for use in generated producers code.
- *
- * @author Jesse Beder
- */
-public final class Monitors {
- private static final Logger logger = Logger.getLogger(Monitors.class.getName());
-
- /**
- * Returns a monitor factory that delegates to the given factories, and ensures that any method
- * called on this object, even transitively, does not throw a {@link RuntimeException} or return
- * null.
- *
- * <p>If the delegate monitors throw an {@link Error}, then that will escape this monitor
- * implementation. Errors are treated as unrecoverable conditions, and may cause the entire
- * component's execution to fail.
- */
- public static ProductionComponentMonitor.Factory delegatingProductionComponentMonitorFactory(
- Collection<? extends ProductionComponentMonitor.Factory> factories) {
- if (factories.isEmpty()) {
- return noOpProductionComponentMonitorFactory();
- } else if (factories.size() == 1) {
- return new NonThrowingProductionComponentMonitor.Factory(Iterables.getOnlyElement(factories));
- } else {
- return new DelegatingProductionComponentMonitor.Factory(factories);
- }
- }
-
- /**
- * A component monitor that delegates to a single monitor, and catches and logs all exceptions
- * that the delegate throws.
- */
- private static final class NonThrowingProductionComponentMonitor
- implements ProductionComponentMonitor {
- private final ProductionComponentMonitor delegate;
-
- NonThrowingProductionComponentMonitor(ProductionComponentMonitor delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public ProducerMonitor producerMonitorFor(ProducerToken token) {
- try {
- ProducerMonitor monitor = delegate.producerMonitorFor(token);
- return monitor == null ? noOpProducerMonitor() : new NonThrowingProducerMonitor(monitor);
- } catch (RuntimeException e) {
- logProducerMonitorForException(e, delegate, token);
- return noOpProducerMonitor();
- }
- }
-
- static final class Factory implements ProductionComponentMonitor.Factory {
- private final ProductionComponentMonitor.Factory delegate;
-
- Factory(ProductionComponentMonitor.Factory delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public ProductionComponentMonitor create(Object component) {
- try {
- ProductionComponentMonitor monitor = delegate.create(component);
- return monitor == null
- ? noOpProductionComponentMonitor()
- : new NonThrowingProductionComponentMonitor(monitor);
- } catch (RuntimeException e) {
- logCreateException(e, delegate, component);
- return noOpProductionComponentMonitor();
- }
- }
- }
- }
-
- /**
- * A producer monitor that delegates to a single monitor, and catches and logs all exceptions
- * that the delegate throws.
- */
- private static final class NonThrowingProducerMonitor extends ProducerMonitor {
- private final ProducerMonitor delegate;
-
- NonThrowingProducerMonitor(ProducerMonitor delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public void methodStarting() {
- try {
- delegate.methodStarting();
- } catch (RuntimeException e) {
- logProducerMonitorMethodException(e, delegate, "methodStarting");
- }
- }
-
- @Override
- public void methodFinished() {
- try {
- delegate.methodFinished();
- } catch (RuntimeException e) {
- logProducerMonitorMethodException(e, delegate, "methodFinished");
- }
- }
-
- @Override
- public void succeeded(Object o) {
- try {
- delegate.succeeded(o);
- } catch (RuntimeException e) {
- logProducerMonitorArgMethodException(e, delegate, "succeeded", o);
- }
- }
-
- @Override
- public void failed(Throwable t) {
- try {
- delegate.failed(t);
- } catch (RuntimeException e) {
- logProducerMonitorArgMethodException(e, delegate, "failed", t);
- }
- }
- }
-
- /**
- * A component monitor that delegates to several monitors, and catches and logs all exceptions
- * that the delegates throw.
- */
- private static final class DelegatingProductionComponentMonitor
- implements ProductionComponentMonitor {
- private final ImmutableList<ProductionComponentMonitor> delegates;
-
- DelegatingProductionComponentMonitor(ImmutableList<ProductionComponentMonitor> delegates) {
- this.delegates = delegates;
- }
-
- @Override
- public ProducerMonitor producerMonitorFor(ProducerToken token) {
- ImmutableList.Builder<ProducerMonitor> monitorsBuilder = ImmutableList.builder();
- for (ProductionComponentMonitor delegate : delegates) {
- try {
- ProducerMonitor monitor = delegate.producerMonitorFor(token);
- if (monitor != null) {
- monitorsBuilder.add(monitor);
- }
- } catch (RuntimeException e) {
- logProducerMonitorForException(e, delegate, token);
- }
- }
- ImmutableList<ProducerMonitor> monitors = monitorsBuilder.build();
- if (monitors.isEmpty()) {
- return noOpProducerMonitor();
- } else if (monitors.size() == 1) {
- return new NonThrowingProducerMonitor(Iterables.getOnlyElement(monitors));
- } else {
- return new DelegatingProducerMonitor(monitors);
- }
- }
-
- static final class Factory implements ProductionComponentMonitor.Factory {
- private final ImmutableList<? extends ProductionComponentMonitor.Factory> delegates;
-
- Factory(Iterable<? extends ProductionComponentMonitor.Factory> delegates) {
- this.delegates = ImmutableList.copyOf(delegates);
- }
-
- @Override
- public ProductionComponentMonitor create(Object component) {
- ImmutableList.Builder<ProductionComponentMonitor> monitorsBuilder = ImmutableList.builder();
- for (ProductionComponentMonitor.Factory delegate : delegates) {
- try {
- ProductionComponentMonitor monitor = delegate.create(component);
- if (monitor != null) {
- monitorsBuilder.add(monitor);
- }
- } catch (RuntimeException e) {
- logCreateException(e, delegate, component);
- }
- }
- ImmutableList<ProductionComponentMonitor> monitors = monitorsBuilder.build();
- if (monitors.isEmpty()) {
- return noOpProductionComponentMonitor();
- } else if (monitors.size() == 1) {
- return new NonThrowingProductionComponentMonitor(Iterables.getOnlyElement(monitors));
- } else {
- return new DelegatingProductionComponentMonitor(monitors);
- }
- }
- }
- }
-
- /**
- * A producer monitor that delegates to several monitors, and catches and logs all exceptions
- * that the delegates throw.
- */
- private static final class DelegatingProducerMonitor extends ProducerMonitor {
- private final ImmutableList<ProducerMonitor> delegates;
-
- DelegatingProducerMonitor(ImmutableList<ProducerMonitor> delegates) {
- this.delegates = delegates;
- }
-
- @Override
- public void methodStarting() {
- for (ProducerMonitor delegate : delegates) {
- try {
- delegate.methodStarting();
- } catch (RuntimeException e) {
- logProducerMonitorMethodException(e, delegate, "methodStarting");
- }
- }
- }
-
- @Override
- public void methodFinished() {
- for (ProducerMonitor delegate : delegates.reverse()) {
- try {
- delegate.methodFinished();
- } catch (RuntimeException e) {
- logProducerMonitorMethodException(e, delegate, "methodFinished");
- }
- }
- }
-
- @Override
- public void succeeded(Object o) {
- for (ProducerMonitor delegate : delegates.reverse()) {
- try {
- delegate.succeeded(o);
- } catch (RuntimeException e) {
- logProducerMonitorArgMethodException(e, delegate, "succeeded", o);
- }
- }
- }
-
- @Override
- public void failed(Throwable t) {
- for (ProducerMonitor delegate : delegates.reverse()) {
- try {
- delegate.failed(t);
- } catch (RuntimeException e) {
- logProducerMonitorArgMethodException(e, delegate, "failed", t);
- }
- }
- }
- }
-
- /** Returns a monitor factory that returns no-op component monitors. */
- public static ProductionComponentMonitor.Factory noOpProductionComponentMonitorFactory() {
- return NO_OP_PRODUCTION_COMPONENT_MONITOR_FACTORY;
- }
-
- /** Returns a component monitor that returns no-op producer monitors. */
- public static ProductionComponentMonitor noOpProductionComponentMonitor() {
- return NO_OP_PRODUCTION_COMPONENT_MONITOR;
- }
-
- /** Returns a producer monitor that does nothing. */
- public static ProducerMonitor noOpProducerMonitor() {
- return NO_OP_PRODUCER_MONITOR;
- }
-
- /** Returns a provider of a no-op component monitor. */
- public static Provider<ProductionComponentMonitor> noOpProductionComponentMonitorProvider() {
- return NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER;
- }
-
- private static final ProductionComponentMonitor.Factory
- NO_OP_PRODUCTION_COMPONENT_MONITOR_FACTORY =
- new ProductionComponentMonitor.Factory() {
- @Override
- public ProductionComponentMonitor create(Object component) {
- return noOpProductionComponentMonitor();
- }
- };
-
- private static final ProductionComponentMonitor NO_OP_PRODUCTION_COMPONENT_MONITOR =
- new ProductionComponentMonitor() {
- @Override
- public ProducerMonitor producerMonitorFor(ProducerToken token) {
- return noOpProducerMonitor();
- }
- };
-
- private static final ProducerMonitor NO_OP_PRODUCER_MONITOR =
- new ProducerMonitor() {
- @Override
- public <T> void addCallbackTo(ListenableFuture<T> future) {
- // overridden to avoid adding a do-nothing callback
- }
- };
-
- private static final Provider<ProductionComponentMonitor>
- NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER =
- new Provider() {
- @Override
- public ProductionComponentMonitor get() {
- return noOpProductionComponentMonitor();
- }
- };
-
- private static void logCreateException(
- RuntimeException e, ProductionComponentMonitor.Factory factory, Object component) {
- logger.log(
- Level.SEVERE,
- "RuntimeException while calling ProductionComponentMonitor.Factory.create on factory "
- + factory
- + " with component "
- + component,
- e);
- }
-
- private static void logProducerMonitorForException(
- RuntimeException e, ProductionComponentMonitor monitor, ProducerToken token) {
- logger.log(
- Level.SEVERE,
- "RuntimeException while calling ProductionComponentMonitor.producerMonitorFor on monitor "
- + monitor
- + " with token "
- + token,
- e);
- }
-
- private static void logProducerMonitorMethodException(
- RuntimeException e, ProducerMonitor monitor, String method) {
- logger.log(
- Level.SEVERE,
- "RuntimeException while calling ProducerMonitor." + method + " on monitor " + monitor,
- e);
- }
-
- private static void logProducerMonitorArgMethodException(
- RuntimeException e, ProducerMonitor monitor, String method, Object arg) {
- logger.log(
- Level.SEVERE,
- "RuntimeException while calling ProducerMonitor."
- + method
- + " on monitor "
- + monitor
- + " with "
- + arg,
- e);
- }
-
- private Monitors() {}
-}
diff --git a/producers/src/main/java/dagger/producers/monitoring/package-info.java b/producers/src/main/java/dagger/producers/monitoring/package-info.java
deleted file mode 100644
index d104087..0000000
--- a/producers/src/main/java/dagger/producers/monitoring/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This package provides hooks for monitoring producers.
- *
- * <p>The interfaces in this package are not stable. Do not use these interfaces unless you are
- * prepared to be broken.
- */
-package dagger.producers.monitoring;
diff --git a/producers/src/test/java/dagger/producers/ProducedTest.java b/producers/src/test/java/dagger/producers/ProducedTest.java
deleted file mode 100644
index 165e730..0000000
--- a/producers/src/test/java/dagger/producers/ProducedTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers;
-
-import com.google.common.testing.EqualsTester;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-/**
- * Tests {@link Produced}.
- */
-@RunWith(JUnit4.class)
-public class ProducedTest {
- @Test public void successfulProduced() throws ExecutionException {
- Object o = new Object();
- assertThat(Produced.successful(5).get()).isEqualTo(5);
- assertThat(Produced.successful("monkey").get()).isEqualTo("monkey");
- assertThat(Produced.successful(o).get()).isSameAs(o);
- }
-
- @Test public void failedProduced() {
- RuntimeException cause = new RuntimeException("monkey");
- try {
- Produced.failed(cause).get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isSameAs(cause);
- }
- }
-
- @Test public void producedEquivalence() {
- RuntimeException e1 = new RuntimeException("monkey");
- RuntimeException e2 = new CancellationException();
- new EqualsTester()
- .addEqualityGroup(Produced.successful(132435), Produced.successful(132435))
- .addEqualityGroup(Produced.successful("hi"), Produced.successful("hi"))
- .addEqualityGroup(Produced.failed(e1), Produced.failed(e1))
- .addEqualityGroup(Produced.failed(e2), Produced.failed(e2))
- .testEquals();
- }
-}
diff --git a/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java b/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java
deleted file mode 100644
index 923ea70..0000000
--- a/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-import dagger.producers.Producer;
-import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import java.util.concurrent.ExecutionException;
-import javax.inject.Provider;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-/**
- * Tests {@link AbstractProducer}.
- */
-@RunWith(JUnit4.class)
-public class AbstractProducerTest {
- @Mock private ProductionComponentMonitor componentMonitor;
- private ProducerMonitor monitor;
- private Provider<ProductionComponentMonitor> componentMonitorProvider;
-
- @Before
- public void initMocks() {
- MockitoAnnotations.initMocks(this);
- monitor = Mockito.mock(ProducerMonitor.class, Mockito.CALLS_REAL_METHODS);
- when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor);
- componentMonitorProvider =
- new Provider<ProductionComponentMonitor>() {
- @Override
- public ProductionComponentMonitor get() {
- return componentMonitor;
- }
- };
- }
-
- @Test
- public void get_nullPointerException() {
- Producer<Object> producer = new DelegateProducer<>(componentMonitorProvider, null);
- try {
- producer.get();
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- @Test public void get() throws Exception {
- Producer<Integer> producer =
- new AbstractProducer<Integer>(componentMonitorProvider, null) {
- int i = 0;
-
- @Override
- public ListenableFuture<Integer> compute(ProducerMonitor unusedMonitor) {
- return Futures.immediateFuture(i++);
- }
- };
- assertThat(producer.get().get()).isEqualTo(0);
- assertThat(producer.get().get()).isEqualTo(0);
- assertThat(producer.get().get()).isEqualTo(0);
- }
-
- @Test
- public void monitor_success() throws Exception {
- SettableFuture<Integer> delegateFuture = SettableFuture.create();
- Producer<Integer> producer = new DelegateProducer<>(componentMonitorProvider, delegateFuture);
-
- ListenableFuture<Integer> future = producer.get();
- assertThat(future.isDone()).isFalse();
- verify(monitor).addCallbackTo(any(ListenableFuture.class));
- delegateFuture.set(-42);
- assertThat(future.get()).isEqualTo(-42);
- verify(monitor).succeeded(-42);
- verifyNoMoreInteractions(monitor);
- }
-
- @Test
- public void monitor_failure() throws Exception {
- SettableFuture<Integer> delegateFuture = SettableFuture.create();
- Producer<Integer> producer = new DelegateProducer<>(componentMonitorProvider, delegateFuture);
-
- ListenableFuture<Integer> future = producer.get();
- assertThat(future.isDone()).isFalse();
- verify(monitor).addCallbackTo(any(ListenableFuture.class));
- Throwable t = new RuntimeException("monkey");
- delegateFuture.setException(t);
- try {
- future.get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isSameAs(t);
- }
- verify(monitor).failed(t);
- verifyNoMoreInteractions(monitor);
- }
-
- @Test(expected = NullPointerException.class)
- public void monitor_null() throws Exception {
- new DelegateProducer<>(null, Futures.immediateFuture(42));
- }
-
- static final class DelegateProducer<T> extends AbstractProducer<T> {
- private final ListenableFuture<T> delegate;
-
- DelegateProducer(
- Provider<ProductionComponentMonitor> componentMonitorProvider,
- ListenableFuture<T> delegate) {
- super(componentMonitorProvider, null);
- this.delegate = delegate;
- }
-
- @Override
- public ListenableFuture<T> compute(ProducerMonitor unusedMonitor) {
- return delegate;
- }
- }
-}
diff --git a/producers/src/test/java/dagger/producers/internal/ProducersTest.java b/producers/src/test/java/dagger/producers/internal/ProducersTest.java
deleted file mode 100644
index e2707b3..0000000
--- a/producers/src/test/java/dagger/producers/internal/ProducersTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import java.util.Set;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-/**
- * Tests {@link Producers}.
- */
-@RunWith(JUnit4.class)
-public class ProducersTest {
- @Test public void createFutureProduced_success() throws Exception {
- ListenableFuture<String> future = Futures.immediateFuture("monkey");
- ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
- assertThat(producedFuture.isDone()).isTrue();
- assertThat(producedFuture.get().get()).isEqualTo("monkey");
- }
-
- @Test public void createFutureProduced_failure() throws Exception {
- ListenableFuture<String> future = Futures.immediateFailedFuture(new RuntimeException("monkey"));
- ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
- assertThat(producedFuture.isDone()).isTrue();
- assertThat(getProducedException(producedFuture.get()).getCause()).hasMessage("monkey");
- }
-
- @Test public void createFutureProduced_cancelPropagatesBackwards() throws Exception {
- ListenableFuture<String> future = SettableFuture.create();
- ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
- assertThat(producedFuture.isDone()).isFalse();
- producedFuture.cancel(false);
- assertThat(future.isCancelled()).isTrue();
- }
-
- @Test public void createFutureProduced_cancelDoesNotPropagateForwards() throws Exception {
- ListenableFuture<String> future = SettableFuture.create();
- ListenableFuture<Produced<String>> producedFuture = Producers.createFutureProduced(future);
- assertThat(producedFuture.isDone()).isFalse();
- future.cancel(false);
- assertThat(producedFuture.isCancelled()).isFalse();
- assertThat(getProducedException(producedFuture.get()).getCause())
- .isInstanceOf(CancellationException.class);
- }
-
- private <T> ExecutionException getProducedException(Produced<T> produced) {
- try {
- produced.get();
- throw new IllegalArgumentException("produced did not throw");
- } catch (ExecutionException e) {
- return e;
- }
- }
-
- @Test public void createFutureSingletonSet_success() throws Exception {
- ListenableFuture<String> future = Futures.immediateFuture("monkey");
- ListenableFuture<Set<String>> setFuture = Producers.createFutureSingletonSet(future);
- assertThat(setFuture.isDone()).isTrue();
- assertThat(setFuture.get()).containsExactly("monkey");
- }
-
- @Test public void createFutureSingletonSet_failure() throws Exception {
- ListenableFuture<String> future = Futures.immediateFailedFuture(new RuntimeException("monkey"));
- ListenableFuture<Set<String>> setFuture = Producers.createFutureSingletonSet(future);
- assertThat(setFuture.isDone()).isTrue();
- try {
- setFuture.get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).hasMessage("monkey");
- }
- }
-
- @Test public void producerFromProvider() throws Exception {
- Producer<Integer> producer = Producers.producerFromProvider(new Provider<Integer>() {
- int i = 0;
-
- @Override public Integer get() {
- return i++;
- }
- });
- assertThat(producer.get().get()).isEqualTo(0);
- assertThat(producer.get().get()).isEqualTo(0);
- assertThat(producer.get().get()).isEqualTo(0);
- }
-}
diff --git a/producers/src/test/java/dagger/producers/internal/SetOfProducedProducerTest.java b/producers/src/test/java/dagger/producers/internal/SetOfProducedProducerTest.java
deleted file mode 100644
index e36ba05..0000000
--- a/producers/src/test/java/dagger/producers/internal/SetOfProducedProducerTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-/**
- * Tests {@link SetOfProducedProducer}.
- */
-@RunWith(JUnit4.class)
-public class SetOfProducedProducerTest {
- @Test
- public void success() throws Exception {
- Producer<Set<Produced<Integer>>> producer =
- SetOfProducedProducer.create(
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(5, 7)));
- assertThat(producer.get().get())
- .containsExactly(
- Produced.successful(1),
- Produced.successful(2),
- Produced.successful(5),
- Produced.successful(7));
- }
-
- @Test
- public void failure() throws Exception {
- RuntimeException e = new RuntimeException("monkey");
- Producer<Set<Produced<Integer>>> producer =
- SetOfProducedProducer.create(
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
- Producers.<Set<Integer>>immediateFailedProducer(e));
- assertThat(producer.get().get())
- .containsExactly(
- Produced.successful(1), Produced.successful(2), Produced.<Integer>failed(e));
- }
-
- @Test
- public void delegateSetNpe() throws Exception {
- Producer<Set<Produced<Integer>>> producer =
- SetOfProducedProducer.create(Producers.<Set<Integer>>immediateProducer(null));
- Results<Integer> results = Results.create(producer.get().get());
- assertThat(results.successes).isEmpty();
- assertThat(results.failures).hasSize(1);
- assertThat(Iterables.getOnlyElement(results.failures).getCause())
- .isInstanceOf(NullPointerException.class);
- }
-
- @Test
- public void oneOfDelegateSetNpe() throws Exception {
- Producer<Set<Produced<Integer>>> producer =
- SetOfProducedProducer.create(
- Producers.<Set<Integer>>immediateProducer(null),
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(7, 3)));
- Results<Integer> results = Results.create(producer.get().get());
- assertThat(results.successes).containsExactly(3, 7);
- assertThat(results.failures).hasSize(1);
- assertThat(Iterables.getOnlyElement(results.failures).getCause())
- .isInstanceOf(NullPointerException.class);
- }
-
- @Test
- public void delegateElementNpe() throws Exception {
- Producer<Set<Produced<Integer>>> producer =
- SetOfProducedProducer.create(
- Producers.<Set<Integer>>immediateProducer(Collections.<Integer>singleton(null)));
- Results<Integer> results = Results.create(producer.get().get());
- assertThat(results.successes).isEmpty();
- assertThat(results.failures).hasSize(1);
- assertThat(Iterables.getOnlyElement(results.failures).getCause())
- .isInstanceOf(NullPointerException.class);
- }
-
- @Test
- public void oneOfDelegateElementNpe() throws Exception {
- Producer<Set<Produced<Integer>>> producer =
- SetOfProducedProducer.create(
- Producers.<Set<Integer>>immediateProducer(Sets.newHashSet(Arrays.asList(5, 2, null))));
- Results<Integer> results = Results.create(producer.get().get());
- assertThat(results.successes).containsExactly(2, 5);
- assertThat(results.failures).hasSize(1);
- assertThat(Iterables.getOnlyElement(results.failures).getCause())
- .isInstanceOf(NullPointerException.class);
- }
-
- static final class Results<T> {
- final ImmutableSet<T> successes;
- final ImmutableSet<ExecutionException> failures;
-
- private Results(ImmutableSet<T> successes, ImmutableSet<ExecutionException> failures) {
- this.successes = successes;
- this.failures = failures;
- }
-
- static <T> Results<T> create(Set<Produced<T>> setOfProduced) {
- ImmutableSet.Builder<T> successes = ImmutableSet.builder();
- ImmutableSet.Builder<ExecutionException> failures = ImmutableSet.builder();
- for (Produced<T> produced : setOfProduced) {
- try {
- successes.add(produced.get());
- } catch (ExecutionException e) {
- failures.add(e);
- }
- }
- return new Results<T>(successes.build(), failures.build());
- }
- }
-}
diff --git a/producers/src/test/java/dagger/producers/internal/SetProducerTest.java b/producers/src/test/java/dagger/producers/internal/SetProducerTest.java
deleted file mode 100644
index a38830f..0000000
--- a/producers/src/test/java/dagger/producers/internal/SetProducerTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.internal;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Producer;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-/**
- * Tests {@link SetProducer}.
- */
-@RunWith(JUnit4.class)
-public class SetProducerTest {
- @Test public void success() throws Exception {
- Producer<Set<Integer>> producer =
- SetProducer.create(
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(5, 7)));
- assertThat(producer.get().get()).containsExactly(1, 2, 5, 7);
- }
-
- @Test public void delegateSetNpe() throws Exception {
- Producer<Set<Integer>> producer =
- SetProducer.create(
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
- Producers.<Set<Integer>>immediateProducer(null));
- ListenableFuture<Set<Integer>> future = producer.get();
- try {
- future.get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isInstanceOf(NullPointerException.class);
- }
- }
-
- @Test public void delegateElementNpe() throws Exception {
- Producer<Set<Integer>> producer =
- SetProducer.create(
- Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
- Producers.<Set<Integer>>immediateProducer(Collections.<Integer>singleton(null)));
- ListenableFuture<Set<Integer>> future = producer.get();
- try {
- future.get();
- fail();
- } catch (ExecutionException e) {
- assertThat(e.getCause()).isInstanceOf(NullPointerException.class);
- }
- }
-}
diff --git a/producers/src/test/java/dagger/producers/monitoring/internal/MonitorsTest.java b/producers/src/test/java/dagger/producers/monitoring/internal/MonitorsTest.java
deleted file mode 100644
index e7f4274..0000000
--- a/producers/src/test/java/dagger/producers/monitoring/internal/MonitorsTest.java
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.producers.monitoring.internal;
-
-import com.google.common.collect.ImmutableList;
-import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-@RunWith(JUnit4.class)
-public final class MonitorsTest {
- @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactory;
- @Mock private ProductionComponentMonitor mockProductionComponentMonitor;
- @Mock private ProducerMonitor mockProducerMonitor;
- @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryA;
- @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryB;
- @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryC;
- @Mock private ProductionComponentMonitor mockProductionComponentMonitorA;
- @Mock private ProductionComponentMonitor mockProductionComponentMonitorB;
- @Mock private ProductionComponentMonitor mockProductionComponentMonitorC;
- @Mock private ProducerMonitor mockProducerMonitorA;
- @Mock private ProducerMonitor mockProducerMonitorB;
- @Mock private ProducerMonitor mockProducerMonitorC;
-
- @Before
- public void initMocks() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void zeroMonitorsReturnsNoOp() {
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.<ProductionComponentMonitor.Factory>of());
- assertThat(factory).isSameAs(Monitors.noOpProductionComponentMonitorFactory());
- }
-
- @Test
- public void singleMonitor_nullProductionComponentMonitor() {
- when(mockProductionComponentMonitorFactory.create(any(Object.class))).thenReturn(null);
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
- }
-
- @Test
- public void singleMonitor_throwingProductionComponentMonitorFactory() {
- doThrow(new RuntimeException("monkey"))
- .when(mockProductionComponentMonitorFactory)
- .create(any(Object.class));
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
- }
-
- @Test
- public void singleMonitor_nullProducerMonitor() {
- when(mockProductionComponentMonitorFactory.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitor);
- when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
- .thenReturn(null);
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- ProductionComponentMonitor monitor = factory.create(new Object());
- assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
- .isSameAs(Monitors.noOpProducerMonitor());
- }
-
- @Test
- public void singleMonitor_throwingProductionComponentMonitor() {
- when(mockProductionComponentMonitorFactory.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitor);
- doThrow(new RuntimeException("monkey"))
- .when(mockProductionComponentMonitor)
- .producerMonitorFor(any(ProducerToken.class));
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- ProductionComponentMonitor monitor = factory.create(new Object());
- assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
- .isSameAs(Monitors.noOpProducerMonitor());
- }
-
- @Test
- public void singleMonitor_normalProducerMonitorSuccess() {
- setUpNormalSingleMonitor();
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
- Object o = new Object();
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.succeeded(o);
-
- InOrder order = inOrder(mockProducerMonitor);
- order.verify(mockProducerMonitor).methodStarting();
- order.verify(mockProducerMonitor).methodFinished();
- order.verify(mockProducerMonitor).succeeded(o);
- verifyNoMoreInteractions(mockProducerMonitor);
- }
-
- @Test
- public void singleMonitor_normalProducerMonitorFailure() {
- setUpNormalSingleMonitor();
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
- Throwable t = new RuntimeException("monkey");
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.failed(t);
-
- InOrder order = inOrder(mockProducerMonitor);
- order.verify(mockProducerMonitor).methodStarting();
- order.verify(mockProducerMonitor).methodFinished();
- order.verify(mockProducerMonitor).failed(t);
- verifyNoMoreInteractions(mockProducerMonitor);
- }
-
- @Test
- public void singleMonitor_throwingProducerMonitorSuccess() {
- setUpNormalSingleMonitor();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodStarting();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodFinished();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).succeeded(any(Object.class));
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
- Object o = new Object();
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.succeeded(o);
-
- InOrder order = inOrder(mockProducerMonitor);
- order.verify(mockProducerMonitor).methodStarting();
- order.verify(mockProducerMonitor).methodFinished();
- order.verify(mockProducerMonitor).succeeded(o);
- verifyNoMoreInteractions(mockProducerMonitor);
- }
-
- @Test
- public void singleMonitor_throwingProducerMonitorFailure() {
- setUpNormalSingleMonitor();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodStarting();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodFinished();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).failed(any(Throwable.class));
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(mockProductionComponentMonitorFactory));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
- Throwable t = new RuntimeException("gorilla");
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.failed(t);
-
- InOrder order = inOrder(mockProducerMonitor);
- order.verify(mockProducerMonitor).methodStarting();
- order.verify(mockProducerMonitor).methodFinished();
- order.verify(mockProducerMonitor).failed(t);
- verifyNoMoreInteractions(mockProducerMonitor);
- }
-
- @Test
- public void multipleMonitors_nullProductionComponentMonitors() {
- when(mockProductionComponentMonitorFactoryA.create(any(Object.class))).thenReturn(null);
- when(mockProductionComponentMonitorFactoryB.create(any(Object.class))).thenReturn(null);
- when(mockProductionComponentMonitorFactoryC.create(any(Object.class))).thenReturn(null);
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
- }
-
- @Test
- public void multipleMonitors_throwingProductionComponentMonitorFactories() {
- doThrow(new RuntimeException("monkey"))
- .when(mockProductionComponentMonitorFactoryA)
- .create(any(Object.class));
- doThrow(new RuntimeException("monkey"))
- .when(mockProductionComponentMonitorFactoryB)
- .create(any(Object.class));
- doThrow(new RuntimeException("monkey"))
- .when(mockProductionComponentMonitorFactoryC)
- .create(any(Object.class));
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
- }
-
- @Test
- public void multipleMonitors_someNullProductionComponentMonitors() {
- when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitorA);
- when(mockProductionComponentMonitorFactoryB.create(any(Object.class))).thenReturn(null);
- when(mockProductionComponentMonitorFactoryC.create(any(Object.class))).thenReturn(null);
- when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
- .thenReturn(mockProducerMonitorA);
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
-
- Object o = new Object();
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.succeeded(o);
-
- InOrder order = inOrder(mockProducerMonitorA);
- order.verify(mockProducerMonitorA).methodStarting();
- order.verify(mockProducerMonitorA).methodFinished();
- order.verify(mockProducerMonitorA).succeeded(o);
- verifyNoMoreInteractions(mockProducerMonitorA);
- }
-
- @Test
- public void multipleMonitors_someThrowingProductionComponentMonitorFactories() {
- when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitorA);
- doThrow(new RuntimeException("monkey"))
- .when(mockProductionComponentMonitorFactoryB)
- .create(any(Object.class));
- doThrow(new RuntimeException("monkey"))
- .when(mockProductionComponentMonitorFactoryC)
- .create(any(Object.class));
- when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
- .thenReturn(mockProducerMonitorA);
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
-
- Object o = new Object();
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.succeeded(o);
-
- InOrder order = inOrder(mockProducerMonitorA);
- order.verify(mockProducerMonitorA).methodStarting();
- order.verify(mockProducerMonitorA).methodFinished();
- order.verify(mockProducerMonitorA).succeeded(o);
- verifyNoMoreInteractions(mockProducerMonitorA);
- }
-
- @Test
- public void multipleMonitors_normalProductionComponentMonitorSuccess() {
- setUpNormalMultipleMonitors();
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
-
- Object o = new Object();
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.succeeded(o);
-
- InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- order.verify(mockProducerMonitorA).methodStarting();
- order.verify(mockProducerMonitorB).methodStarting();
- order.verify(mockProducerMonitorC).methodStarting();
- order.verify(mockProducerMonitorC).methodFinished();
- order.verify(mockProducerMonitorB).methodFinished();
- order.verify(mockProducerMonitorA).methodFinished();
- order.verify(mockProducerMonitorC).succeeded(o);
- order.verify(mockProducerMonitorB).succeeded(o);
- order.verify(mockProducerMonitorA).succeeded(o);
- verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- }
-
- @Test
- public void multipleMonitors_normalProductionComponentMonitorFailure() {
- setUpNormalMultipleMonitors();
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
-
- Throwable t = new RuntimeException("chimpanzee");
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.failed(t);
-
- InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- order.verify(mockProducerMonitorA).methodStarting();
- order.verify(mockProducerMonitorB).methodStarting();
- order.verify(mockProducerMonitorC).methodStarting();
- order.verify(mockProducerMonitorC).methodFinished();
- order.verify(mockProducerMonitorB).methodFinished();
- order.verify(mockProducerMonitorA).methodFinished();
- order.verify(mockProducerMonitorC).failed(t);
- order.verify(mockProducerMonitorB).failed(t);
- order.verify(mockProducerMonitorA).failed(t);
- verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- }
-
- @Test
- public void multipleMonitors_someThrowingProducerMonitorsSuccess() {
- setUpNormalMultipleMonitors();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).methodStarting();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitorB).methodFinished();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitorC).succeeded(any(Object.class));
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
-
- Object o = new Object();
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.succeeded(o);
-
- InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- order.verify(mockProducerMonitorA).methodStarting();
- order.verify(mockProducerMonitorB).methodStarting();
- order.verify(mockProducerMonitorC).methodStarting();
- order.verify(mockProducerMonitorC).methodFinished();
- order.verify(mockProducerMonitorB).methodFinished();
- order.verify(mockProducerMonitorA).methodFinished();
- order.verify(mockProducerMonitorC).succeeded(o);
- order.verify(mockProducerMonitorB).succeeded(o);
- order.verify(mockProducerMonitorA).succeeded(o);
- verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- }
-
- @Test
- public void multipleMonitors_someThrowingProducerMonitorsFailure() {
- setUpNormalMultipleMonitors();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).methodStarting();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitorB).methodFinished();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitorC).failed(any(Throwable.class));
- ProductionComponentMonitor.Factory factory =
- Monitors.delegatingProductionComponentMonitorFactory(
- ImmutableList.of(
- mockProductionComponentMonitorFactoryA,
- mockProductionComponentMonitorFactoryB,
- mockProductionComponentMonitorFactoryC));
- ProductionComponentMonitor monitor = factory.create(new Object());
- ProducerMonitor producerMonitor =
- monitor.producerMonitorFor(ProducerToken.create(Object.class));
-
- Throwable t = new RuntimeException("chimpanzee");
- producerMonitor.methodStarting();
- producerMonitor.methodFinished();
- producerMonitor.failed(t);
-
- InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- order.verify(mockProducerMonitorA).methodStarting();
- order.verify(mockProducerMonitorB).methodStarting();
- order.verify(mockProducerMonitorC).methodStarting();
- order.verify(mockProducerMonitorC).methodFinished();
- order.verify(mockProducerMonitorB).methodFinished();
- order.verify(mockProducerMonitorA).methodFinished();
- order.verify(mockProducerMonitorC).failed(t);
- order.verify(mockProducerMonitorB).failed(t);
- order.verify(mockProducerMonitorA).failed(t);
- verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
- }
-
- private void setUpNormalSingleMonitor() {
- when(mockProductionComponentMonitorFactory.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitor);
- when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
- .thenReturn(mockProducerMonitor);
- }
-
- private void setUpNormalMultipleMonitors() {
- when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitorA);
- when(mockProductionComponentMonitorFactoryB.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitorB);
- when(mockProductionComponentMonitorFactoryC.create(any(Object.class)))
- .thenReturn(mockProductionComponentMonitorC);
- when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
- .thenReturn(mockProducerMonitorA);
- when(mockProductionComponentMonitorB.producerMonitorFor(any(ProducerToken.class)))
- .thenReturn(mockProducerMonitorB);
- when(mockProductionComponentMonitorC.producerMonitorFor(any(ProducerToken.class)))
- .thenReturn(mockProducerMonitorC);
- }
-}
diff --git a/test_defs.bzl b/test_defs.bzl
new file mode 100644
index 0000000..15ab299
--- /dev/null
+++ b/test_defs.bzl
@@ -0,0 +1,195 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# Defines a set of build variants and the list of extra javacopts to build with.
+# The key will be appended to the generated test names to ensure uniqueness.
+BUILD_VARIANTS = {
+ "FastInit": ["-Adagger.fastInit=enabled"],
+ "AheadOfTimeSubcomponents": ["-Adagger.experimentalAheadOfTimeSubcomponents=enabled"],
+ "FastInitAndAheadOfTimeSubcomponents": [
+ "-Adagger.fastInit=enabled",
+ "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
+ ],
+ "AheadOfTimeSubcomponents_ForceUseSerializedComponentImplementations": [
+ "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
+ "-Adagger.forceUseSerializedComponentImplementations=enabled",
+ ],
+}
+
+# TODO(ronshapiro): convert this to use bazel_common
+# TODO(user): split into two functions for functional vs non-functional tests?
+def GenJavaTests(
+ name,
+ srcs,
+ deps,
+ test_only_deps = None,
+ plugins = None,
+ javacopts = None,
+ lib_javacopts = None,
+ test_javacopts = None,
+ functional = True):
+ _GenTests(
+ native.java_library,
+ native.java_test,
+ name,
+ srcs,
+ deps,
+ test_only_deps,
+ plugins,
+ javacopts,
+ lib_javacopts,
+ test_javacopts,
+ functional,
+ )
+
+def GenRobolectricTests(
+ name,
+ srcs,
+ deps,
+ test_only_deps = None,
+ plugins = None,
+ javacopts = None,
+ lib_javacopts = None,
+ test_javacopts = None,
+ functional = True,
+ manifest_values = None):
+ # TODO(ronshapiro): enable these with these instructions:
+ # https://docs.bazel.build/versions/master/be/android.html#android_local_test_examples
+ # We probably want to import all of Robolectric's dependencies into bazel-common because there
+ # are some differences (i.e. we both provide Guava).
+ pass
+
+def _GenTests(
+ library_rule_type,
+ test_rule_type,
+ name,
+ srcs,
+ deps,
+ test_only_deps = None,
+ plugins = None,
+ javacopts = None,
+ lib_javacopts = None,
+ test_javacopts = None,
+ functional = True,
+ test_kwargs = {}):
+ _gen_tests(
+ library_rule_type,
+ test_rule_type,
+ name,
+ srcs,
+ deps,
+ test_only_deps,
+ plugins,
+ javacopts,
+ lib_javacopts,
+ test_javacopts,
+ functional,
+ test_kwargs = test_kwargs,
+ )
+
+ if functional:
+ for (variant_name, extra_javacopts) in BUILD_VARIANTS.items():
+ variant_javacopts = (javacopts or []) + extra_javacopts
+ _gen_tests(
+ library_rule_type,
+ test_rule_type,
+ name,
+ srcs,
+ deps,
+ test_only_deps,
+ plugins,
+ variant_javacopts,
+ lib_javacopts,
+ test_javacopts,
+ functional,
+ variant_name,
+ test_kwargs = test_kwargs,
+ )
+
+def _gen_tests(
+ library_rule_type,
+ test_rule_type,
+ name,
+ srcs,
+ deps,
+ test_only_deps,
+ plugins,
+ javacopts,
+ lib_javacopts,
+ test_javacopts,
+ functional,
+ variant_name = None,
+ test_kwargs = {}):
+ if variant_name:
+ suffix = "_" + variant_name
+ tags = [variant_name]
+
+ # Add jvm_flags so that the mode can be accessed from within tests.
+ jvm_flags = ["-Ddagger.mode=" + variant_name]
+ else:
+ suffix = ""
+ tags = []
+ jvm_flags = []
+
+ test_files = []
+ supporting_files = []
+
+ for src in srcs:
+ if src.endswith("Test.java"):
+ test_files.append(src)
+ else:
+ supporting_files.append(src)
+
+ if not test_only_deps:
+ test_only_deps = []
+
+ test_deps = test_only_deps + deps
+ if supporting_files:
+ supporting_files_name = name + suffix + "_lib"
+ test_deps.append(":" + supporting_files_name)
+ library_rule_type(
+ name = supporting_files_name,
+ testonly = 1,
+ srcs = supporting_files,
+ javacopts = (javacopts or []) + (lib_javacopts or []),
+ plugins = plugins,
+ tags = tags,
+ deps = deps,
+ )
+ if functional:
+ _hjar_test(supporting_files_name, tags)
+
+ for test_file in test_files:
+ test_name = test_file.replace(".java", "")
+ prefix_path = "src/test/java/"
+ package_name = native.package_name()
+ if package_name.find("javatests/") != -1:
+ prefix_path = "javatests/"
+ test_class = (package_name + "/" + test_name).rpartition(prefix_path)[2].replace("/", ".")
+ test_rule_type(
+ name = test_name + suffix,
+ srcs = [test_file],
+ javacopts = (javacopts or []) + (test_javacopts or []),
+ jvm_flags = jvm_flags,
+ plugins = plugins,
+ tags = tags,
+ test_class = test_class,
+ deps = test_deps,
+ **test_kwargs
+ )
+
+
+def _hjar_test(name, tags):
+ pass
diff --git a/tools/BUILD b/tools/BUILD
new file mode 100644
index 0000000..7b3cc6c
--- /dev/null
+++ b/tools/BUILD
@@ -0,0 +1,22 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tools for Dagger
+
+package(default_visibility = ["//:src"])
+
+exports_files([
+ "pom-template.xml",
+])
diff --git a/tools/maven.bzl b/tools/maven.bzl
new file mode 100644
index 0000000..2ac0359
--- /dev/null
+++ b/tools/maven.bzl
@@ -0,0 +1,38 @@
+# Copyright (C) 2018 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Macros to simplify generating maven files.
+"""
+
+load("@google_bazel_common//tools/maven:pom_file.bzl", default_pom_file = "pom_file")
+
+def pom_file(name, targets, artifact_name, artifact_id, packaging = None, **kwargs):
+ default_pom_file(
+ name = name,
+ targets = targets,
+ preferred_group_ids = [
+ "com.google.dagger",
+ "com.google",
+ ],
+ template_file = "//tools:pom-template.xml",
+ substitutions = {
+ "{artifact_name}": artifact_name,
+ "{artifact_id}": artifact_id,
+ "{packaging}": packaging or "jar",
+ },
+ excluded_artifacts = ["com.google.auto:auto-common"],
+ **kwargs
+ )
+
+POM_VERSION = "2.23.1"
diff --git a/tools/pom-template.xml b/tools/pom-template.xml
new file mode 100644
index 0000000..39ed62d
--- /dev/null
+++ b/tools/pom-template.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.sonatype.oss</groupId>
+ <artifactId>oss-parent</artifactId>
+ <version>7</version>
+ </parent>
+
+ <groupId>com.google.dagger</groupId>
+ <artifactId>{artifact_id}</artifactId>
+ <name>{artifact_name}</name>
+ <version>{pom_version}</version>
+ <description>A fast dependency injector for Android and Java.</description>
+ <url>https://github.com/google/dagger</url>
+ <packaging>{packaging}</packaging>
+
+ <scm>
+ <url>http://github.com/google/dagger/</url>
+ <connection>scm:git:git://github.com/google/dagger.git</connection>
+ <developerConnection>scm:git:ssh://git@github.com/google/dagger.git</developerConnection>
+ <tag>HEAD</tag>
+ </scm>
+
+ <issueManagement>
+ <system>GitHub Issues</system>
+ <url>http://github.com/google/dagger/issues</url>
+ </issueManagement>
+
+ <licenses>
+ <license>
+ <name>Apache 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ </license>
+ </licenses>
+
+ <organization>
+ <name>Google, Inc.</name>
+ <url>http://www.google.com</url>
+ </organization>
+
+ <dependencies>
+{generated_bzl_deps}
+ </dependencies>
+</project>
diff --git a/util/deploy-to-maven-central.sh b/util/deploy-to-maven-central.sh
new file mode 100755
index 0000000..1f721a1
--- /dev/null
+++ b/util/deploy-to-maven-central.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+set -eu
+
+if [ $# -lt 2 ]; then
+ echo "usage $0 <ssl-key> <version-name> [<param> ...]"
+ exit 1;
+fi
+key=$1
+version_name=$2
+shift 2
+
+if [[ ! "$version_name" =~ ^2\. ]]; then
+ echo 'Version name must begin with "2."'
+ exit 2
+fi
+
+if [[ "$version_name" =~ " " ]]; then
+ echo "Version name must not have any spaces"
+ exit 3
+fi
+
+bazel test //...
+
+bash $(dirname $0)/execute-deploy.sh \
+ "gpg:sign-and-deploy-file" \
+ "$version_name" \
+ "-DrepositoryId=sonatype-nexus-staging" \
+ "-Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/" \
+ "-Dgpg.keyname=${key}"
+
+# Publish javadocs to gh-pages
+bazel build //:user-docs.jar
+git clone --quiet --branch gh-pages \
+ https://github.com/google/dagger gh-pages > /dev/null
+cd gh-pages
+unzip ../bazel-bin/user-docs.jar -d api/$version_name
+rm -rf api/$version_name/META-INF/
+git add api/$version_name
+git commit -m "$version_name docs"
+git push origin gh-pages
+cd ..
+rm -rf gh-pages
+
+git checkout --detach
+# Set the version string that is used as a tag in all of our libraries. If another repo depends on
+# a versioned tag of Dagger, their java_library.tags should match the versioned release.
+sed -i s/'${project.version}'/"${version_name}"/g tools/maven.bzl
+git commit -m "${version_name} release" tools/maven.bzl
+
+git tag -a -m "Dagger ${version_name}" dagger-"${version_name}"
+git push origin tag dagger-"${version_name}"
+
+# Switch back to the original HEAD
+git checkout -
diff --git a/util/execute-deploy.sh b/util/execute-deploy.sh
new file mode 100755
index 0000000..1153468
--- /dev/null
+++ b/util/execute-deploy.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+
+set -eu
+
+readonly MVN_GOAL="$1"
+readonly VERSION_NAME="$2"
+shift 2
+readonly EXTRA_MAVEN_ARGS=("$@")
+
+bazel_output_file() {
+ local library=$1
+ local output_file=bazel-bin/$library
+ if [[ ! -e $output_file ]]; then
+ output_file=bazel-genfiles/$library
+ fi
+ if [[ ! -e $output_file ]]; then
+ echo "Could not find bazel output file for $library"
+ exit 1
+ fi
+ echo -n $output_file
+}
+
+deploy_library() {
+ local library=$1
+ local srcjar=$2
+ local javadoc=$3
+ local pomfile=$4
+ bazel build --define=pom_version="$VERSION_NAME" \
+ $library $srcjar $javadoc $pomfile
+
+ mvn $MVN_GOAL \
+ -Dfile=$(bazel_output_file $library) \
+ -Djavadoc=$(bazel_output_file $javadoc) \
+ -DpomFile=$(bazel_output_file $pomfile) \
+ -Dsources=$(bazel_output_file $srcjar) \
+ "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}"
+}
+
+deploy_library \
+ java/dagger/libcore.jar \
+ java/dagger/libcore-src.jar \
+ java/dagger/core-javadoc.jar \
+ java/dagger/pom.xml
+
+deploy_library \
+ gwt/libgwt.jar \
+ gwt/libgwt.jar \
+ gwt/libgwt.jar \
+ gwt/pom.xml
+
+deploy_library \
+ shaded_compiler.jar \
+ shaded_compiler_src.jar \
+ java/dagger/internal/codegen/codegen-javadoc.jar \
+ java/dagger/internal/codegen/pom.xml
+
+deploy_library \
+ java/dagger/producers/libproducers.jar \
+ java/dagger/producers/libproducers-src.jar \
+ java/dagger/producers/producers-javadoc.jar \
+ java/dagger/producers/pom.xml
+
+deploy_library \
+ shaded_spi.jar \
+ shaded_spi_src.jar \
+ spi-javadoc.jar \
+ java/dagger/spi/pom.xml
+
+deploy_library \
+ java/dagger/android/android.aar \
+ java/dagger/android/libandroid-src.jar \
+ java/dagger/android/android-javadoc.jar \
+ java/dagger/android/pom.xml
+
+# b/37741866 and https://github.com/google/dagger/issues/715
+deploy_library \
+ java/dagger/android/libandroid.jar \
+ java/dagger/android/libandroid-src.jar \
+ java/dagger/android/android-javadoc.jar \
+ java/dagger/android/jarimpl-pom.xml
+
+deploy_library \
+ java/dagger/android/support/support.aar \
+ java/dagger/android/support/libsupport-src.jar \
+ java/dagger/android/support/support-javadoc.jar \
+ java/dagger/android/support/pom.xml
+
+deploy_library \
+ shaded_android_processor.jar \
+ java/dagger/android/processor/libprocessor-src.jar \
+ java/dagger/android/processor/processor-javadoc.jar \
+ java/dagger/android/processor/pom.xml
+
+deploy_library \
+ java/dagger/grpc/server/libserver.jar \
+ java/dagger/grpc/server/libserver-src.jar \
+ java/dagger/grpc/server/javadoc.jar \
+ java/dagger/grpc/server/server-pom.xml
+
+deploy_library \
+ java/dagger/grpc/server/libannotations.jar \
+ java/dagger/grpc/server/libannotations-src.jar \
+ java/dagger/grpc/server/javadoc.jar \
+ java/dagger/grpc/server/annotations-pom.xml
+
+deploy_library \
+ shaded_grpc_server_processor.jar \
+ java/dagger/grpc/server/processor/libprocessor-src.jar \
+ java/dagger/grpc/server/processor/javadoc.jar \
+ java/dagger/grpc/server/processor/pom.xml
diff --git a/util/generate-latest-docs.sh b/util/generate-latest-docs.sh
index b68df0c..8e9304c 100755
--- a/util/generate-latest-docs.sh
+++ b/util/generate-latest-docs.sh
@@ -1,25 +1,30 @@
# see http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ for details
+set -eu
+
if [ "$TRAVIS_REPO_SLUG" == "google/dagger" ] && \
- [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \
+ [ "$TRAVIS_JDK_VERSION" == "$JDK_FOR_PUBLISHING" ] && \
[ "$TRAVIS_PULL_REQUEST" == "false" ] && \
[ "$TRAVIS_BRANCH" == "master" ]; then
echo -e "Publishing javadoc...\n"
- mvn javadoc:aggregate -P!examples
- TARGET="$(pwd)/target"
+ bazel build //:user-docs.jar
+ JAVADOC_JAR="$(pwd)/bazel-bin/user-docs.jar"
cd $HOME
git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/google/dagger gh-pages > /dev/null
-
+
cd gh-pages
git config --global user.email "travis@travis-ci.org"
git config --global user.name "travis-ci"
- git rm -rf api/latest
+ git rm -rf api/latest
mkdir -p api
- mv ${TARGET}/site/apidocs api/latest
+ unzip "$JAVADOC_JAR" -d api/latest
+ rm -rf api/latest/META-INF/
git add -f api/latest
- git commit -m "Lastest javadoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages"
+ git commit -m "Latest javadoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages"
git push -fq origin gh-pages > /dev/null
echo -e "Published Javadoc to gh-pages.\n"
+else
+ echo -e "Not publishing docs for jdk=${TRAVIS_JDK_VERSION} and branch=${TRAVIS_BRANCH}"
fi
diff --git a/util/install-local-snapshot.sh b/util/install-local-snapshot.sh
new file mode 100755
index 0000000..8f77a41
--- /dev/null
+++ b/util/install-local-snapshot.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -eu
+
+echo -e "Installing maven snapshot locally...\n"
+
+bash $(dirname $0)/execute-deploy.sh \
+ "install:install-file" \
+ "LOCAL-SNAPSHOT"
+
+echo -e "Installed local snapshot"
diff --git a/util/mvn-deploy.sh b/util/mvn-deploy.sh
deleted file mode 100755
index ec4b7a0..0000000
--- a/util/mvn-deploy.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-if [ $# -lt 1 ]; then
- echo "usage $0 <ssl-key> [<param> ...]"
- exit 1;
-fi
-key=$1
-shift
-
-#validate key
-keystatus=$(gpg --list-keys | grep ${key} | awk '{print $1}')
-if [ "${keystatus}" != "pub" ]; then
- echo "Could not find public key with label ${key}"
- echo -n "Available keys from: "
- gpg --list-keys | grep --invert-match '^sub'
-
- exit 64
-fi
-
-mvn "$@" -P '!examples' -P sonatype-oss-release \
- -Dgpg.skip=false -Dgpg.keyname=${key} \
- clean site:jar deploy
diff --git a/util/publish-snapshot-on-commit.sh b/util/publish-snapshot-on-commit.sh
index be27cb6..944a2c3 100755
--- a/util/publish-snapshot-on-commit.sh
+++ b/util/publish-snapshot-on-commit.sh
@@ -1,12 +1,21 @@
# see https://coderwall.com/p/9b_lfq
+set -eu
+
if [ "$TRAVIS_REPO_SLUG" == "google/dagger" ] && \
- [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \
+ [ "$TRAVIS_JDK_VERSION" == "$JDK_FOR_PUBLISHING" ] && \
[ "$TRAVIS_PULL_REQUEST" == "false" ] && \
[ "$TRAVIS_BRANCH" == "master" ]; then
echo -e "Publishing maven snapshot...\n"
- mvn clean source:jar deploy --settings="util/settings.xml" -DskipTests=true -Dinvoker.skip=true -Dmaven.javadoc.skip=true
+ bash $(dirname $0)/execute-deploy.sh \
+ "deploy:deploy-file" \
+ "HEAD-SNAPSHOT" \
+ "-DrepositoryId=sonatype-nexus-snapshots" \
+ "-Durl=https://oss.sonatype.org/content/repositories/snapshots" \
+ "--settings=$(dirname $0)/settings.xml"
echo -e "Published maven snapshot"
+else
+ echo -e "Not publishing snapshot for jdk=${TRAVIS_JDK_VERSION} and branch=${TRAVIS_BRANCH}"
fi