diff options
author | Paul Duffin <paulduffin@google.com> | 2015-11-23 13:25:38 +0000 |
---|---|---|
committer | Paul Duffin <paulduffin@google.com> | 2015-11-23 13:25:38 +0000 |
commit | 5d3207ac2713386ed61c6ca8f0356e8f093a62e1 (patch) | |
tree | ead40943ce4e8bab9226a970fe8d41084a811b85 | |
parent | a8fc7fd876069b857b985f63d491e1a8f22d0365 (diff) | |
download | dagger2-5d3207ac2713386ed61c6ca8f0356e8f093a62e1.tar.gz |
Added initial version of dagger2 from upstream
Bug: 24848946
Change-Id: I1b359bbbf8b07de1f84f3b7dfd263a58f0fd439b
1120 files changed, 53522 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..0c4de1d78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.iml +*.ipr +*.iws +.idea diff --git a/Android.mk b/Android.mk new file mode 100644 index 000000000..08b6af514 --- /dev/null +++ b/Android.mk @@ -0,0 +1,88 @@ +# Copyright (C) 2015 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. + +LOCAL_PATH := $(call my-dir) + +# build dagger2 host jar +# ============================================================ + +include $(CLEAR_VARS) + +LOCAL_MODULE := dagger2-host +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := JAVA_LIBRARIES +LOCAL_SRC_FILES := $(call all-java-files-under, core/src/main/java/) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + dagger2-inject-host \ + guavalib + +#LOCAL_JACK_ENABLED := disabled +include $(BUILD_HOST_JAVA_LIBRARY) + +# build dagger2 producers host jar +# ============================================================ + +include $(CLEAR_VARS) + +LOCAL_MODULE := dagger2-producers-host +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := JAVA_LIBRARIES +LOCAL_SRC_FILES := $(call all-java-files-under, producers/src/main/java/) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + dagger2-host \ + dagger2-inject-host \ + guavalib + +#LOCAL_JACK_ENABLED := disabled +include $(BUILD_HOST_JAVA_LIBRARY) + +# build dagger2 compiler host jar +# ============================================================ + +include $(CLEAR_VARS) + +LOCAL_MODULE := dagger2-compiler-host +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := JAVA_LIBRARIES +LOCAL_SRC_FILES := $(call all-java-files-under, compiler/src/main/java/) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + dagger2-host \ + dagger2-auto-common-host \ + dagger2-auto-factory-host \ + dagger2-auto-service-host \ + dagger2-auto-value-host \ + dagger2-google-java-format \ + dagger2-inject-host \ + dagger2-producers-host \ + guavalib + +#LOCAL_JACK_ENABLED := disabled +include $(BUILD_HOST_JAVA_LIBRARY) + +# Build host dependencies. +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + dagger2-auto-common-host:lib/auto-common-1.0-20151022.071545-39$(COMMON_JAVA_PACKAGE_SUFFIX) \ + dagger2-auto-factory-host:lib/auto-factory-1.0-20150915.183854-35$(COMMON_JAVA_PACKAGE_SUFFIX) \ + dagger2-auto-service-host:lib/auto-service-1.0-rc2$(COMMON_JAVA_PACKAGE_SUFFIX) \ + dagger2-auto-value-host:lib/auto-value-1.0$(COMMON_JAVA_PACKAGE_SUFFIX) \ + dagger2-google-java-format:lib/google-java-format-0.1-20151017.042846-2$(COMMON_JAVA_PACKAGE_SUFFIX) \ + dagger2-inject-host:lib/javax-inject$(COMMON_JAVA_PACKAGE_SUFFIX) \ + +include $(BUILD_HOST_PREBUILT) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..957840eca --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +Change Log +========== + +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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..087af22cd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +Contributing +============ + +If you would like to contribute code to Dagger you can do so through GitHub by +forking the repository and sending a pull request. + +When submitting code, please make every effort to follow existing conventions +and style in order to keep the code as readable as possible. + +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`. + +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. + +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. + +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. + +Before your code can be accepted into the project you must sign the +[Individual Contributor License Agreement (CLA)][1]. + + + [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/MODULE_LICENSE_APACHE2 diff --git a/README.android b/README.android new file mode 100644 index 000000000..94ed4714c --- /dev/null +++ b/README.android @@ -0,0 +1,8 @@ +URL: https://github.com/google/dagger.git +License: Apache 2 +Description: "Dagger 2 - A fast dependency injector for Android and Java" + +Version: fdf1a9e905782dd2885799c6b34d9f1da842a7e1 + +Local Patches: + None diff --git a/README.md b/README.md new file mode 100644 index 000000000..3c0e8bc13 --- /dev/null +++ b/README.md @@ -0,0 +1,134 @@ +Dagger 2 +======== + +A fast dependency injector for Android and Java. + +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. + +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. + +## [Dagger 2's main documentation website can be found here.][website] + +Status +------ + + - ***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 +------------- + +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], +[this talk by Greg Kick][gaktalk], and on the dagger-discuss@googlegroups.com +mailing list. + +Installation +-------- + +You will need to include the `dagger-2.0.1.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 +at compile time. + +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: + +```xml +<dependencies> + <dependency> + <groupId>com.google.dagger</groupId> + <artifactId>dagger</artifactId> + <version>2.0.1</version> + </dependency> + <dependency> + <groupId>com.google.dagger</groupId> + <artifactId>dagger-compiler</artifactId> + <version>2.0.1</version> + <optional>true</optional> + </dependency> +</dependencies> +``` + +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> + </dependency> +</dependencies> +``` + + +### 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) + * [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]. + +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 +------- + + Copyright 2012 Square, Inc. + Copyright 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. + + + + [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 + diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000..e7ffbc0d5 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,137 @@ +<?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 new file mode 100644 index 000000000..504f9ffa7 --- /dev/null +++ b/compiler/dependency-reduced-pom.xml @@ -0,0 +1,204 @@ +<?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 new file mode 100644 index 000000000..40e0fa27d --- /dev/null +++ b/compiler/pom.xml @@ -0,0 +1,196 @@ +<?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.auto.service</groupId> + <artifactId>auto-service</artifactId> + <optional>true</optional> + </dependency> + <!-- TODO(gak): Restore this presumably as javapoet when appropriate. + <dependency> + <groupId>com.squareup</groupId> + <artifactId>javawriter</artifactId> + </dependency> + --> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + + <dependency> + <groupId>com.google.auto.value</groupId> + <artifactId>auto-value</artifactId> + <optional>true</optional> + <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> + </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 new file mode 100644 index 000000000..1eca20c14 --- /dev/null +++ b/compiler/src/it/functional-tests/pom.xml @@ -0,0 +1,98 @@ +<?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.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> + <!-- For map-bindings --> + <groupId>com.google.auto.value</groupId> + <artifactId>auto-value</artifactId> + <version>${auto.value.version}</version> + <optional>true</optional> + </dependency> + <dependency> + <!-- For map-bindings --> + <groupId>com.google.auto.factory</groupId> + <artifactId>auto-factory</artifactId> + <version>${auto.factory.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> + </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 new file mode 100644 index 000000000..030f8556c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/A.java @@ -0,0 +1,22 @@ +/* + * 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 new file mode 100644 index 000000000..4fb0f7832 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/AbstractMembersInjectingBaseClass.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..89e94bd2f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/AbstractMiddleClassWithoutMembers.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..dec8e2e58 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/B.java @@ -0,0 +1,22 @@ +/* + * 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 new file mode 100644 index 000000000..78f77dfb8 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/BasicAbstractClassComponent.java @@ -0,0 +1,29 @@ +/* +* 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 new file mode 100644 index 000000000..a04607dd4 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/BasicComponent.java @@ -0,0 +1,81 @@ +/* +* 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 new file mode 100644 index 000000000..4cef79e02 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/BooleanKey.java @@ -0,0 +1,23 @@ +/* +* 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 new file mode 100644 index 000000000..b30522f57 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericComponent.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..6bd7be4fb --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/BoundedGenericModule.java @@ -0,0 +1,81 @@ +/* + * 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 new file mode 100644 index 000000000..e26d64351 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/BoundedGenerics.java @@ -0,0 +1,41 @@ +/* + * 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 new file mode 100644 index 000000000..8e739bd4a --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ByteKey.java @@ -0,0 +1,23 @@ +/* +* 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 new file mode 100644 index 000000000..a4f4e29c8 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/CharKey.java @@ -0,0 +1,23 @@ +/* +* 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 new file mode 100644 index 000000000..09a1e6b72 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ChildDoubleModule.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..ac9c61207 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ChildIntegerModule.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..e2e327494 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ComplexGenerics.java @@ -0,0 +1,54 @@ +/* + * 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 new file mode 100644 index 000000000..6ffe1e07d --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ComponentDependsOnGeneratedCode.java @@ -0,0 +1,23 @@ +/* +* 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 new file mode 100644 index 000000000..f7460c989 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCode.java @@ -0,0 +1,22 @@ +/* + * 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 new file mode 100644 index 000000000..fca90e0f3 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ComponentSupertypeDependsOnGeneratedCodeInterface.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..ee1aa0992 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/Generic.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..4a56df3ec --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/Generic2.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..5c65dc03e --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/GenericChild.java @@ -0,0 +1,33 @@ +/* + * 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 new file mode 100644 index 000000000..da5b9b530 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/GenericComponent.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..6785c7c5f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/GenericDoubleReferences.java @@ -0,0 +1,32 @@ +/* + * 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 new file mode 100644 index 000000000..e065f7926 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/GenericNoDeps.java @@ -0,0 +1,24 @@ +/* + * 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 new file mode 100644 index 000000000..0e01f5f7e --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/GenericParent.java @@ -0,0 +1,37 @@ +/* + * 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 new file mode 100644 index 000000000..73a46e8aa --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/InjectedThing.java @@ -0,0 +1,229 @@ +/* + * 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 new file mode 100644 index 000000000..2a5798a03 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/Injector.java @@ -0,0 +1,32 @@ +/* + * 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 new file mode 100644 index 000000000..7cad3bd1a --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java @@ -0,0 +1,52 @@ +/* +* 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, 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 new file mode 100644 index 000000000..a92e029e2 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/MultibindingDependency.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..f356850b3 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java @@ -0,0 +1,169 @@ +/* +* 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 = 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 new file mode 100644 index 000000000..b78907382 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/NeedsFactory.java @@ -0,0 +1,27 @@ +/* +* 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 new file mode 100644 index 000000000..c57b4ecf4 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/NestedAnnotationContainer.java @@ -0,0 +1,26 @@ +/* +* 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 new file mode 100644 index 000000000..43a088cdc --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/NonComponentDependencyComponent.java @@ -0,0 +1,47 @@ +/* +* 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 new file mode 100644 index 000000000..4164ae5cc --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/NumberClassKey.java @@ -0,0 +1,23 @@ +/* +* 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 new file mode 100644 index 000000000..c7fabdb2b --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/OuterClassBar.java @@ -0,0 +1,25 @@ +/* + * 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 new file mode 100644 index 000000000..86f963f5c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/OuterClassFoo.java @@ -0,0 +1,25 @@ +/* + * 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 new file mode 100644 index 000000000..a161abaa8 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ParentModule.java @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..acbf271df --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/PrimitivesModule.java @@ -0,0 +1,93 @@ +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 new file mode 100644 index 000000000..812c45d35 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ReferencesGeneric.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..37d68e01e --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ScopedGeneric.java @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..01b3aa99d --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/ShortKey.java @@ -0,0 +1,23 @@ +/* +* 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 new file mode 100644 index 000000000..44a2cb553 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/SingletonGenericComponent.java @@ -0,0 +1,13 @@ +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 new file mode 100644 index 000000000..46cbdc999 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/Thing.java @@ -0,0 +1,23 @@ +/* +* 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 new file mode 100644 index 000000000..587baade1 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/TypeWithInheritedMembersInjection.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..21ed95841 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/UnwrappedAnnotationKey.java @@ -0,0 +1,24 @@ +/* +* 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 new file mode 100644 index 000000000..5d6e86dc7 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/WrappedAnnotationKey.java @@ -0,0 +1,28 @@ +/* +* 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 new file mode 100644 index 000000000..8b85d606c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/ByteModule.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..93fd59def --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/DepComponent.java @@ -0,0 +1,22 @@ +/* + * 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 new file mode 100644 index 000000000..2dec4a7a2 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/DoubleModule.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..309e7ee98 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/FloatModule.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..af196eeda --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/GenericParent.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..8cbf67b19 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/Grandchild.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..5e3a92827 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/IntModuleIncludingDoubleAndFloat.java @@ -0,0 +1,33 @@ +/* + * 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 new file mode 100644 index 000000000..c16c9c79f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/LongModule.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..59c29ab34 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java @@ -0,0 +1,32 @@ +/* + * 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(); + + @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 new file mode 100644 index 000000000..e2fbcaa42 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleScope.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..28e43bafe --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/OtherMiddleChild.java @@ -0,0 +1,33 @@ +/* + * 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 new file mode 100644 index 000000000..f901b8863 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java @@ -0,0 +1,29 @@ +/* + * 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(); +} 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 new file mode 100644 index 000000000..474c61701 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/ParentOfGenericComponent.java @@ -0,0 +1,23 @@ +/* + * 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/StringModule.java b/compiler/src/it/functional-tests/src/main/java/test/builder/StringModule.java new file mode 100644 index 000000000..3b979a5ab --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/StringModule.java @@ -0,0 +1,33 @@ +/* + * 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 new file mode 100644 index 000000000..8f39c1401 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderAbstractClass.java @@ -0,0 +1,47 @@ +/* + * 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 new file mode 100644 index 000000000..2add34ed5 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/TestChildComponentWithBuilderInterface.java @@ -0,0 +1,47 @@ +/* + * 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 new file mode 100644 index 000000000..5eef53fe5 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderAbstractClass.java @@ -0,0 +1,65 @@ +/* + * 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 new file mode 100644 index 000000000..55214f836 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithBuilderInterface.java @@ -0,0 +1,46 @@ +/* + * 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 new file mode 100644 index 000000000..8032185b2 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderAbstractClass.java @@ -0,0 +1,48 @@ +/* + * 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 new file mode 100644 index 000000000..f63e3ec90 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/builder/TestComponentWithGenericBuilderInterface.java @@ -0,0 +1,47 @@ +/* + * 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 new file mode 100644 index 000000000..8d67d92eb --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/cycle/Cycles.java @@ -0,0 +1,162 @@ +/* + * 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 new file mode 100644 index 000000000..b4f61e096 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/cycle/LongCycle.java @@ -0,0 +1,132 @@ +/* + * 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 new file mode 100644 index 000000000..22efcf12e --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfArrayOfParentOfStringArray.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..e01c1c266 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfPrimitiveIntArray.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..8ec943b96 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/membersinject/ChildOfStringArray.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..9ab8c1928 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectComponent.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..064b88642 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectGenericParent.java @@ -0,0 +1,24 @@ +/* + * 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 new file mode 100644 index 000000000..a6c1fadb9 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/membersinject/MembersInjectModule.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..108a1b58f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/membersinject/NonRequestedChild.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..3c884159f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/FooComponent.java @@ -0,0 +1,42 @@ +/* + * 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 new file mode 100644 index 000000000..85ce40aac --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/MembersInjectionVisibilityComponent.java @@ -0,0 +1,34 @@ +/* + * 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 new file mode 100644 index 000000000..8f0f1f39c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AGrandchild.java @@ -0,0 +1,50 @@ +/* + * 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 new file mode 100644 index 000000000..d62506a56 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AModule.java @@ -0,0 +1,29 @@ +/* + * 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 new file mode 100644 index 000000000..d60413331 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APackagePrivateObject.java @@ -0,0 +1,24 @@ +/* + * 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 new file mode 100644 index 000000000..4c91a6f26 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/AParent.java @@ -0,0 +1,38 @@ +/* + * 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 new file mode 100644 index 000000000..90357f661 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/a/APublicObject.java @@ -0,0 +1,24 @@ +/* + * 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 new file mode 100644 index 000000000..188d1201f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BChild.java @@ -0,0 +1,45 @@ +/* + * 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 new file mode 100644 index 000000000..4d817f153 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BModule.java @@ -0,0 +1,29 @@ +/* + * 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 new file mode 100644 index 000000000..c397a02c8 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/b/BPackagePrivateObject.java @@ -0,0 +1,24 @@ +/* + * 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 new file mode 100644 index 000000000..e608afb2c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/c/CModule.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..51f8ace76 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/d/DModule.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..35f5862ad --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/foo/Foo.java @@ -0,0 +1,28 @@ +/* + * 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 new file mode 100644 index 000000000..16a61dd54 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/grandsub/FooGrandchildComponent.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..9050fcd72 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/multipackage/sub/FooChildComponent.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..a8a572473 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponent.java @@ -0,0 +1,29 @@ +/* +* 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 new file mode 100644 index 000000000..05093ed60 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullComponentWithDependency.java @@ -0,0 +1,28 @@ +/* +* 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 new file mode 100644 index 000000000..9ed4b5dea --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullFoo.java @@ -0,0 +1,56 @@ +/* +* 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 new file mode 100644 index 000000000..652d5ebbb --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/nullables/NullModule.java @@ -0,0 +1,35 @@ +/* +* 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 new file mode 100644 index 000000000..86776406a --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/nullables/Nullable.java @@ -0,0 +1,3 @@ +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 new file mode 100644 index 000000000..f47d36c6a --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/AllStaticModule.java @@ -0,0 +1,35 @@ +/* + * 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 new file mode 100644 index 000000000..53ee14d95 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/SomeStaticModule.java @@ -0,0 +1,32 @@ +/* + * 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 new file mode 100644 index 000000000..4be51ed7b --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponent.java @@ -0,0 +1,28 @@ +/* + * 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 new file mode 100644 index 000000000..d778fc5c5 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/staticprovides/StaticTestComponentWithBuilder.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..b10ac4533 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/sub/ContributionsModule.java @@ -0,0 +1,41 @@ +/* + * 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 new file mode 100644 index 000000000..9195b33f9 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/sub/Exposed.java @@ -0,0 +1,19 @@ +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 new file mode 100644 index 000000000..94935171f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/sub/OtherThing.java @@ -0,0 +1,22 @@ +/* + * 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 new file mode 100644 index 000000000..9af646a50 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivate.java @@ -0,0 +1,7 @@ +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 new file mode 100644 index 000000000..765b01504 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/sub/PackagePrivateContainer.java @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..586d55d93 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass.java @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..c356fa8f0 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/sub/PublicSubclass2.java @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..8aaa015d1 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/AnInterface.java @@ -0,0 +1,19 @@ +/* + * 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 new file mode 100644 index 000000000..8ae147415 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/BoundAsSingleton.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..6c061bc5c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java @@ -0,0 +1,22 @@ +/* + * 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) +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 new file mode 100644 index 000000000..67d66cae9 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java @@ -0,0 +1,31 @@ +/* + * 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) +interface ChildComponent { + Provider<UnscopedType> getUnscopedTypeProvider(); + + RequiresSingletons requiresSingleton(); + + Set<Object> objectSet(); + + GrandchildComponent newGrandchildComponent(); +} 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 new file mode 100644 index 000000000..905c68990 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentRequiringModules.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..9ed266aec --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponentWithMultibindings.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..ef28bd47a --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModule.java @@ -0,0 +1,32 @@ +/* + * 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 new file mode 100644 index 000000000..e18b4a6da --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithParameters.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..5908a005b --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildModuleWithState.java @@ -0,0 +1,32 @@ +/* + * 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 new file mode 100644 index 000000000..ae02b9e6f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildMultibindingModule.java @@ -0,0 +1,38 @@ +/* + * 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 new file mode 100644 index 000000000..5580ab8d4 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GenericParentComponent.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..9f724edfc --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildComponent.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..b2885412f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/GrandchildModule.java @@ -0,0 +1,40 @@ +/* + * 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 new file mode 100644 index 000000000..ff3170cba --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ImplementsAnInterface.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..bccde8589 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/NeedsAnInterface.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..ebb067d6f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponent.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..46fe8835c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithMultibindings.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..3d4431ca9 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentComponentWithoutMultibindings.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..3ff855a9d --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentGetters.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..dbe1a534a --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentModule.java @@ -0,0 +1,46 @@ +/* + * 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 new file mode 100644 index 000000000..e4ec173de --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentMultibindingModule.java @@ -0,0 +1,55 @@ +/* + * 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 new file mode 100644 index 000000000..bf8553772 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ParentOfGenericComponent.java @@ -0,0 +1,24 @@ +/* + * 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 new file mode 100644 index 000000000..4ec0469de --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInChild.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..a48d38bfa --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultibindingsInParent.java @@ -0,0 +1,47 @@ +/* + * 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 new file mode 100644 index 000000000..d787153e3 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundObjects.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..410bdf2c0 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresMultiboundStrings.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..2d4053824 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/RequiresSingletons.java @@ -0,0 +1,37 @@ +/* + * 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 new file mode 100644 index 000000000..663e858a9 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/SingletonType.java @@ -0,0 +1,24 @@ +/* + * 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/UnscopedType.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/UnscopedType.java new file mode 100644 index 000000000..89c00855f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/UnscopedType.java @@ -0,0 +1,22 @@ +/* + * 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 new file mode 100644 index 000000000..b95502cf8 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ChildComponent.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..d7c66a679 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/ParentComponent.java @@ -0,0 +1,28 @@ +/* + * 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 new file mode 100644 index 000000000..ad692896f --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonModuleName.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..b2aefda06 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/a/CommonName.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..66deab5aa --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonModuleName.java @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 000000000..023cbdbf2 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/hiding/b/CommonName.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..2dd8d20d3 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInChild.java @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000..cc22b1e05 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/OnlyUsedInParent.java @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000..f0af00237 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/ParentComponent.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..d0997513d --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/RepeatedModule.java @@ -0,0 +1,51 @@ +/* + * 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 new file mode 100644 index 000000000..279bc954c --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithRepeatedModule.java @@ -0,0 +1,37 @@ +/* + * 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 new file mode 100644 index 000000000..e63c9a0ae --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/repeat/SubcomponentWithoutRepeatedModule.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..bc7851739 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/tck/CarModule.java @@ -0,0 +1,29 @@ +/* + * 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 new file mode 100644 index 000000000..e42532e9d --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/tck/CarShop.java @@ -0,0 +1,35 @@ +/* + * 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 new file mode 100644 index 000000000..577fb5b70 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/tck/EngineModule.java @@ -0,0 +1,34 @@ +/* + * 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 new file mode 100644 index 000000000..931556cfe --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/tck/FuelTankModule.java @@ -0,0 +1,28 @@ +/* + * 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 new file mode 100644 index 000000000..5c6b72988 --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/tck/SeatModule.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..914a6d6ec --- /dev/null +++ b/compiler/src/it/functional-tests/src/main/java/test/tck/TireModule.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..fe9c6afe9 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/BasicTest.java @@ -0,0 +1,116 @@ +/* +* 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 new file mode 100644 index 000000000..0310df6ef --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/DependsOnGeneratedCodeTest.java @@ -0,0 +1,29 @@ +/* +* 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 new file mode 100644 index 000000000..f1c981f25 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/GenericTest.java @@ -0,0 +1,148 @@ +/* + * 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 new file mode 100644 index 000000000..5e06f848f --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java @@ -0,0 +1,165 @@ +/* + * 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 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 new file mode 100644 index 000000000..14c3e5385 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/NestedTest.java @@ -0,0 +1,35 @@ +/* + * 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 new file mode 100644 index 000000000..37d3f7aec --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/NonComponentDependencyTest.java @@ -0,0 +1,34 @@ +/* +* 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 new file mode 100644 index 000000000..ba590d2d9 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java @@ -0,0 +1,225 @@ +/* + * 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); + } + +} 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 new file mode 100644 index 000000000..d3bc2cbad --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/cycle/CycleTest.java @@ -0,0 +1,91 @@ +/* + * 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 new file mode 100644 index 000000000..e50eaee69 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/cycle/LongCycleTest.java @@ -0,0 +1,47 @@ +/* + * 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 new file mode 100644 index 000000000..411ecb177 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/membersinject/MembersInjectTest.java @@ -0,0 +1,95 @@ +/* + * 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 new file mode 100644 index 000000000..a0e1e22f5 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/nullables/NullabilityTest.java @@ -0,0 +1,110 @@ +/* +* 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 new file mode 100644 index 000000000..397259403 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/staticprovides/StaticProvidesTest.java @@ -0,0 +1,71 @@ +/* + * 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 new file mode 100644 index 000000000..f57a77831 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentMultibindingsTest.java @@ -0,0 +1,89 @@ +/* + * 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 new file mode 100644 index 000000000..cb6292513 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/SubcomponentTest.java @@ -0,0 +1,115 @@ +/* + * 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 new file mode 100644 index 000000000..27dcbb690 --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/hiding/SubcomponentHidingTest.java @@ -0,0 +1,32 @@ +/* + * 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 new file mode 100644 index 000000000..7e92371bf --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/subcomponent/repeat/RepeatedModuleTest.java @@ -0,0 +1,82 @@ +/* + * 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 new file mode 100644 index 000000000..d79b06b6a --- /dev/null +++ b/compiler/src/it/functional-tests/src/test/java/test/tck/TckTest.java @@ -0,0 +1,33 @@ +/* + * 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/functional-tests/target/classes/test/A.class b/compiler/src/it/functional-tests/target/classes/test/A.class Binary files differnew file mode 100644 index 000000000..5b0f08afd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/A.class diff --git a/compiler/src/it/functional-tests/target/classes/test/A_Factory.class b/compiler/src/it/functional-tests/target/classes/test/A_Factory.class Binary files differnew file mode 100644 index 000000000..7252360f9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/A_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/AbstractMembersInjectingBaseClass.class b/compiler/src/it/functional-tests/target/classes/test/AbstractMembersInjectingBaseClass.class Binary files differnew file mode 100644 index 000000000..5d256bc1c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/AbstractMembersInjectingBaseClass.class diff --git a/compiler/src/it/functional-tests/target/classes/test/AbstractMembersInjectingBaseClass_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/AbstractMembersInjectingBaseClass_MembersInjector.class Binary files differnew file mode 100644 index 000000000..951d719e5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/AbstractMembersInjectingBaseClass_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/AbstractMiddleClassWithoutMembers.class b/compiler/src/it/functional-tests/target/classes/test/AbstractMiddleClassWithoutMembers.class Binary files differnew file mode 100644 index 000000000..481151778 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/AbstractMiddleClassWithoutMembers.class diff --git a/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey.class b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey.class Binary files differnew file mode 100644 index 000000000..e64da63d3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey.class b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey.class Binary files differnew file mode 100644 index 000000000..81a137fc3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey.class b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey.class Binary files differnew file mode 100644 index 000000000..21de57ba7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey.class b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey.class Binary files differnew file mode 100644 index 000000000..295d9e2ec --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/B.class b/compiler/src/it/functional-tests/target/classes/test/B.class Binary files differnew file mode 100644 index 000000000..a8f05ad34 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/B.class diff --git a/compiler/src/it/functional-tests/target/classes/test/B_Factory.class b/compiler/src/it/functional-tests/target/classes/test/B_Factory.class Binary files differnew file mode 100644 index 000000000..1e7e7974f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/B_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BasicAbstractClassComponent.class b/compiler/src/it/functional-tests/target/classes/test/BasicAbstractClassComponent.class Binary files differnew file mode 100644 index 000000000..f8487e3ae --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BasicAbstractClassComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BasicComponent.class b/compiler/src/it/functional-tests/target/classes/test/BasicComponent.class Binary files differnew file mode 100644 index 000000000..034057b14 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BasicComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericComponent.class Binary files differnew file mode 100644 index 000000000..29c895e5a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule.class Binary files differnew file mode 100644 index 000000000..55df97392 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideArrayListOfComparableStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideArrayListOfComparableStringFactory.class Binary files differnew file mode 100644 index 000000000..78d93e115 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideArrayListOfComparableStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideArrayListStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideArrayListStringFactory.class Binary files differnew file mode 100644 index 000000000..b3fd3f7c4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideArrayListStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideDoubleFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideDoubleFactory.class Binary files differnew file mode 100644 index 000000000..22b6f3667 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideDoubleFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideIntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideIntegerFactory.class Binary files differnew file mode 100644 index 000000000..85c626bee --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideIntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideLinkedListCharSeqFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideLinkedListCharSeqFactory.class Binary files differnew file mode 100644 index 000000000..8a8fb9bd9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideLinkedListCharSeqFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideLinkedListStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideLinkedListStringFactory.class Binary files differnew file mode 100644 index 000000000..49ee8dc92 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideLinkedListStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideListOfIntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideListOfIntegerFactory.class Binary files differnew file mode 100644 index 000000000..1262b1a0b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideListOfIntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideSetOfDoubleFactory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideSetOfDoubleFactory.class Binary files differnew file mode 100644 index 000000000..8dfa9f37b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenericModule_ProvideSetOfDoubleFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenerics.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenerics.class Binary files differnew file mode 100644 index 000000000..727d42b04 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenerics.class diff --git a/compiler/src/it/functional-tests/target/classes/test/BoundedGenerics_Factory.class b/compiler/src/it/functional-tests/target/classes/test/BoundedGenerics_Factory.class Binary files differnew file mode 100644 index 000000000..ee50c4137 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/BoundedGenerics_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule.class b/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule.class Binary files differnew file mode 100644 index 000000000..990bb389e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule_ProvideDoubleFactory.class b/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule_ProvideDoubleFactory.class Binary files differnew file mode 100644 index 000000000..7c6c0f882 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule_ProvideDoubleFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule_ProvideListOfDoubleFactory.class b/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule_ProvideListOfDoubleFactory.class Binary files differnew file mode 100644 index 000000000..ba4d2324a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ChildDoubleModule_ProvideListOfDoubleFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule.class b/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule.class Binary files differnew file mode 100644 index 000000000..bfd9c3686 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule_ProvideIntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule_ProvideIntegerFactory.class Binary files differnew file mode 100644 index 000000000..4d9791b0f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule_ProvideIntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule_ProvideListOfIntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule_ProvideListOfIntegerFactory.class Binary files differnew file mode 100644 index 000000000..cf4785f1f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ChildIntegerModule_ProvideListOfIntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ComplexGenerics.class b/compiler/src/it/functional-tests/target/classes/test/ComplexGenerics.class Binary files differnew file mode 100644 index 000000000..ca5cc7e2a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ComplexGenerics.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ComplexGenerics_Factory.class b/compiler/src/it/functional-tests/target/classes/test/ComplexGenerics_Factory.class Binary files differnew file mode 100644 index 000000000..bab5eb4bc --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ComplexGenerics_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ComponentDependsOnGeneratedCode.class b/compiler/src/it/functional-tests/target/classes/test/ComponentDependsOnGeneratedCode.class Binary files differnew file mode 100644 index 000000000..0baa3d016 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ComponentDependsOnGeneratedCode.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent$1.class Binary files differnew file mode 100644 index 000000000..11913f2fa --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent$Builder.class Binary files differnew file mode 100644 index 000000000..1c014f15c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent.class Binary files differnew file mode 100644 index 000000000..832bb9103 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicAbstractClassComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent$1.class Binary files differnew file mode 100644 index 000000000..30c29a475 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent$Builder.class Binary files differnew file mode 100644 index 000000000..9b51bb9f9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent.class Binary files differnew file mode 100644 index 000000000..2d8060bdd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBasicComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent$1.class Binary files differnew file mode 100644 index 000000000..bd2e5abad --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent$Builder.class Binary files differnew file mode 100644 index 000000000..443a01e69 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent.class Binary files differnew file mode 100644 index 000000000..2a31cacc4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerBoundedGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode$1.class Binary files differnew file mode 100644 index 000000000..fd47fd2e1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode$Builder.class Binary files differnew file mode 100644 index 000000000..3f139958b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode.class b/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode.class Binary files differnew file mode 100644 index 000000000..d97c40196 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerComponentDependsOnGeneratedCode.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent$1.class Binary files differnew file mode 100644 index 000000000..d6bacc444 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent$Builder.class Binary files differnew file mode 100644 index 000000000..3702f1acd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent.class Binary files differnew file mode 100644 index 000000000..db28d8e1e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent$1.class Binary files differnew file mode 100644 index 000000000..e3b23fd47 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent$Builder.class Binary files differnew file mode 100644 index 000000000..a771169d0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent.class Binary files differnew file mode 100644 index 000000000..2db66f42f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerMultibindingComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent$1.class Binary files differnew file mode 100644 index 000000000..24b088959 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent$Builder.class Binary files differnew file mode 100644 index 000000000..baa205f34 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent.class Binary files differnew file mode 100644 index 000000000..9036479f1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerNonComponentDependencyComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent$1.class Binary files differnew file mode 100644 index 000000000..d2f173c06 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent$Builder.class Binary files differnew file mode 100644 index 000000000..9a70a92cf --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent.class Binary files differnew file mode 100644 index 000000000..e78c1f7f0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassBar_NestedComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent$1.class Binary files differnew file mode 100644 index 000000000..7d3a20fd1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent$Builder.class Binary files differnew file mode 100644 index 000000000..1ce5ec3bb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent.class Binary files differnew file mode 100644 index 000000000..9066f6e4a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerOuterClassFoo_NestedComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent$1.class Binary files differnew file mode 100644 index 000000000..2847d0164 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent$Builder.class Binary files differnew file mode 100644 index 000000000..2db3bca06 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent.class Binary files differnew file mode 100644 index 000000000..f9aea2c80 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/DaggerSingletonGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/Generic.class b/compiler/src/it/functional-tests/target/classes/test/Generic.class Binary files differnew file mode 100644 index 000000000..74c6a2dfe --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/Generic.class diff --git a/compiler/src/it/functional-tests/target/classes/test/Generic2.class b/compiler/src/it/functional-tests/target/classes/test/Generic2.class Binary files differnew file mode 100644 index 000000000..70d0c4d16 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/Generic2.class diff --git a/compiler/src/it/functional-tests/target/classes/test/Generic2_Factory.class b/compiler/src/it/functional-tests/target/classes/test/Generic2_Factory.class Binary files differnew file mode 100644 index 000000000..5a80da9d2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/Generic2_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericChild.class b/compiler/src/it/functional-tests/target/classes/test/GenericChild.class Binary files differnew file mode 100644 index 000000000..3636e1a5d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericChild.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericChild_Factory.class b/compiler/src/it/functional-tests/target/classes/test/GenericChild_Factory.class Binary files differnew file mode 100644 index 000000000..05ac299e9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericChild_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericChild_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/GenericChild_MembersInjector.class Binary files differnew file mode 100644 index 000000000..a43dd7f69 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericChild_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/GenericComponent.class Binary files differnew file mode 100644 index 000000000..2a8a12cdd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericDoubleReferences.class b/compiler/src/it/functional-tests/target/classes/test/GenericDoubleReferences.class Binary files differnew file mode 100644 index 000000000..9d7383d4e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericDoubleReferences.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericDoubleReferences_Factory.class b/compiler/src/it/functional-tests/target/classes/test/GenericDoubleReferences_Factory.class Binary files differnew file mode 100644 index 000000000..5288a1909 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericDoubleReferences_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericNoDeps.class b/compiler/src/it/functional-tests/target/classes/test/GenericNoDeps.class Binary files differnew file mode 100644 index 000000000..20a8b789c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericNoDeps.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericNoDeps_Factory.class b/compiler/src/it/functional-tests/target/classes/test/GenericNoDeps_Factory.class Binary files differnew file mode 100644 index 000000000..69535787c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericNoDeps_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericParent.class b/compiler/src/it/functional-tests/target/classes/test/GenericParent.class Binary files differnew file mode 100644 index 000000000..00e5caf91 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericParent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericParent_Factory.class b/compiler/src/it/functional-tests/target/classes/test/GenericParent_Factory.class Binary files differnew file mode 100644 index 000000000..0ad7a27b0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericParent_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/GenericParent_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/GenericParent_MembersInjector.class Binary files differnew file mode 100644 index 000000000..ed217cc85 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/GenericParent_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/Generic_Factory.class b/compiler/src/it/functional-tests/target/classes/test/Generic_Factory.class Binary files differnew file mode 100644 index 000000000..c01a7ff40 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/Generic_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/InjectedThing.class b/compiler/src/it/functional-tests/target/classes/test/InjectedThing.class Binary files differnew file mode 100644 index 000000000..ccbd10e73 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/InjectedThing.class diff --git a/compiler/src/it/functional-tests/target/classes/test/InjectedThing_Factory.class b/compiler/src/it/functional-tests/target/classes/test/InjectedThing_Factory.class Binary files differnew file mode 100644 index 000000000..3e7589d2f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/InjectedThing_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/InjectedThing_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/InjectedThing_MembersInjector.class Binary files differnew file mode 100644 index 000000000..22230bddf --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/InjectedThing_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/Injector.class b/compiler/src/it/functional-tests/target/classes/test/Injector.class Binary files differnew file mode 100644 index 000000000..39a8f5869 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/Injector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingComponent.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingComponent.class Binary files differnew file mode 100644 index 000000000..2d3391439 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingDependency.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingDependency.class Binary files differnew file mode 100644 index 000000000..e7d6f9c51 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingDependency.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule.class Binary files differnew file mode 100644 index 000000000..57c672bcc --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideBarKeyFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideBarKeyFactory.class Binary files differnew file mode 100644 index 000000000..2bbd4570f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideBarKeyFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideFiveToSetFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideFiveToSetFactory.class Binary files differnew file mode 100644 index 000000000..d0f2b4261 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideFiveToSetFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideFooKeyFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideFooKeyFactory.class Binary files differnew file mode 100644 index 000000000..a6279d068 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideFooKeyFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideMapKeysFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideMapKeysFactory.class Binary files differnew file mode 100644 index 000000000..31f17031c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideMapKeysFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideMapValuesFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideMapValuesFactory.class Binary files differnew file mode 100644 index 000000000..9bd2173fd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideMapValuesFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideSixToSetFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideSixToSetFactory.class Binary files differnew file mode 100644 index 000000000..76121c5a1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ProvideSixToSetFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100ByteFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100ByteFactory.class Binary files differnew file mode 100644 index 000000000..fbd700b14 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100ByteFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100IntFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100IntFactory.class Binary files differnew file mode 100644 index 000000000..b5cf5a43e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100IntFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100LongFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100LongFactory.class Binary files differnew file mode 100644 index 000000000..83e80b4fa --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100LongFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100ShortFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100ShortFactory.class Binary files differnew file mode 100644 index 000000000..1a5716b24 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueFor100ShortFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForAFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForAFactory.class Binary files differnew file mode 100644 index 000000000..bf8d6760c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForAFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForClassIntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForClassIntegerFactory.class Binary files differnew file mode 100644 index 000000000..eb38a7af1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForClassIntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForClassLongFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForClassLongFactory.class Binary files differnew file mode 100644 index 000000000..e7f717817 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForClassLongFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForIntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForIntegerFactory.class Binary files differnew file mode 100644 index 000000000..dea9f0bf2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForIntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForLongFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForLongFactory.class Binary files differnew file mode 100644 index 000000000..a8d262ae5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForLongFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNewlineFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNewlineFactory.class Binary files differnew file mode 100644 index 000000000..80789ac0d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNewlineFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNumberClassBigDecimalFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNumberClassBigDecimalFactory.class Binary files differnew file mode 100644 index 000000000..0f63660c5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNumberClassBigDecimalFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNumberClassBigIntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNumberClassBigIntegerFactory.class Binary files differnew file mode 100644 index 000000000..2ad1fe432 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForNumberClassBigIntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForTrueFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForTrueFactory.class Binary files differnew file mode 100644 index 000000000..e95ddb881 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForTrueFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory.class Binary files differnew file mode 100644 index 000000000..66de50ec2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForWrappedAnnotationKeyFooFactory.class b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForWrappedAnnotationKeyFooFactory.class Binary files differnew file mode 100644 index 000000000..481c7e664 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/MultibindingModule_ValueForWrappedAnnotationKeyFooFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NeedsFactory$Something.class b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory$Something.class Binary files differnew file mode 100644 index 000000000..579b77617 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory$Something.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NeedsFactory.class b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory.class Binary files differnew file mode 100644 index 000000000..b27676afe --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_Factory.class b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_Factory.class Binary files differnew file mode 100644 index 000000000..45b59cc24 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_SomethingFactory.class b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_SomethingFactory.class Binary files differnew file mode 100644 index 000000000..15e599ac5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_SomethingFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_SomethingFactory_Factory.class b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_SomethingFactory_Factory.class Binary files differnew file mode 100644 index 000000000..29ce60f94 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NeedsFactory_SomethingFactory_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingComponent.class b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingComponent.class Binary files differnew file mode 100644 index 000000000..4494f9a8d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingComponentImpl.class Binary files differnew file mode 100644 index 000000000..1ebef3bf3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingTwo.class b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingTwo.class Binary files differnew file mode 100644 index 000000000..97ebdb398 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingTwo.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingTwo_Factory.class b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingTwo_Factory.class Binary files differnew file mode 100644 index 000000000..ebda07e92 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent$ThingTwo_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent.class b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent.class Binary files differnew file mode 100644 index 000000000..7871ef863 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/NonComponentDependencyComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/OuterClassBar$NestedComponent.class b/compiler/src/it/functional-tests/target/classes/test/OuterClassBar$NestedComponent.class Binary files differnew file mode 100644 index 000000000..c0857cda8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/OuterClassBar$NestedComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/OuterClassBar.class b/compiler/src/it/functional-tests/target/classes/test/OuterClassBar.class Binary files differnew file mode 100644 index 000000000..ab2b71967 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/OuterClassBar.class diff --git a/compiler/src/it/functional-tests/target/classes/test/OuterClassFoo$NestedComponent.class b/compiler/src/it/functional-tests/target/classes/test/OuterClassFoo$NestedComponent.class Binary files differnew file mode 100644 index 000000000..d5e118a3e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/OuterClassFoo$NestedComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/OuterClassFoo.class b/compiler/src/it/functional-tests/target/classes/test/OuterClassFoo.class Binary files differnew file mode 100644 index 000000000..6cbc369cb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/OuterClassFoo.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ParentModule.class b/compiler/src/it/functional-tests/target/classes/test/ParentModule.class Binary files differnew file mode 100644 index 000000000..0fa3925a4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ParentModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ParentModule_ProvideIterableOfAWithCFactory.class b/compiler/src/it/functional-tests/target/classes/test/ParentModule_ProvideIterableOfAWithCFactory.class Binary files differnew file mode 100644 index 000000000..0192dbc2c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ParentModule_ProvideIterableOfAWithCFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule.class Binary files differnew file mode 100644 index 000000000..7a1023538 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_BoundDoubleArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_BoundDoubleArrayFactory.class Binary files differnew file mode 100644 index 000000000..44dc60a19 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_BoundDoubleArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_BoundDoubleFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_BoundDoubleFactory.class Binary files differnew file mode 100644 index 000000000..ab7fe896a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_BoundDoubleFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideBooleanArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideBooleanArrayFactory.class Binary files differnew file mode 100644 index 000000000..23281c504 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideBooleanArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideBooleanFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideBooleanFactory.class Binary files differnew file mode 100644 index 000000000..260b1bf29 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideBooleanFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideByteArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideByteArrayFactory.class Binary files differnew file mode 100644 index 000000000..3569991a8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideByteArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideByteFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideByteFactory.class Binary files differnew file mode 100644 index 000000000..d8b073d83 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideByteFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideCharArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideCharArrayFactory.class Binary files differnew file mode 100644 index 000000000..c9f3054c2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideCharArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideCharFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideCharFactory.class Binary files differnew file mode 100644 index 000000000..31fd662bf --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideCharFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideFloatArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideFloatArrayFactory.class Binary files differnew file mode 100644 index 000000000..4bf1a3fee --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideFloatArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideFloatFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideFloatFactory.class Binary files differnew file mode 100644 index 000000000..c6cef2de0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideFloatFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideIntArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideIntArrayFactory.class Binary files differnew file mode 100644 index 000000000..02ba00587 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideIntArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideIntFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideIntFactory.class Binary files differnew file mode 100644 index 000000000..ba9f0115b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideIntFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideLongArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideLongArrayFactory.class Binary files differnew file mode 100644 index 000000000..7ea4d780a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideLongArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideLongFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideLongFactory.class Binary files differnew file mode 100644 index 000000000..127737fd3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideLongFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideShortArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideShortArrayFactory.class Binary files differnew file mode 100644 index 000000000..caa25df0e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideShortArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideShortFactory.class b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideShortFactory.class Binary files differnew file mode 100644 index 000000000..6e439f442 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/PrimitivesModule_ProvideShortFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ReferencesGeneric.class b/compiler/src/it/functional-tests/target/classes/test/ReferencesGeneric.class Binary files differnew file mode 100644 index 000000000..06185b659 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ReferencesGeneric.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ReferencesGeneric_Factory.class b/compiler/src/it/functional-tests/target/classes/test/ReferencesGeneric_Factory.class Binary files differnew file mode 100644 index 000000000..5f82891d0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ReferencesGeneric_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ScopedGeneric.class b/compiler/src/it/functional-tests/target/classes/test/ScopedGeneric.class Binary files differnew file mode 100644 index 000000000..20af34070 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ScopedGeneric.class diff --git a/compiler/src/it/functional-tests/target/classes/test/ScopedGeneric_Factory.class b/compiler/src/it/functional-tests/target/classes/test/ScopedGeneric_Factory.class Binary files differnew file mode 100644 index 000000000..8e6f668a3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/ScopedGeneric_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/SingletonGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/SingletonGenericComponent.class Binary files differnew file mode 100644 index 000000000..f6de44c81 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/SingletonGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestBooleanKey.class b/compiler/src/it/functional-tests/target/classes/test/TestBooleanKey.class Binary files differnew file mode 100644 index 000000000..3eb2852a0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestBooleanKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestByteKey.class b/compiler/src/it/functional-tests/target/classes/test/TestByteKey.class Binary files differnew file mode 100644 index 000000000..74de216f6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestByteKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestCharKey.class b/compiler/src/it/functional-tests/target/classes/test/TestCharKey.class Binary files differnew file mode 100644 index 000000000..153149415 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestCharKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestClassKey.class b/compiler/src/it/functional-tests/target/classes/test/TestClassKey.class Binary files differnew file mode 100644 index 000000000..82e83525a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestClassKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestIntKey.class b/compiler/src/it/functional-tests/target/classes/test/TestIntKey.class Binary files differnew file mode 100644 index 000000000..812d7a711 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestIntKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestLongKey.class b/compiler/src/it/functional-tests/target/classes/test/TestLongKey.class Binary files differnew file mode 100644 index 000000000..08a84bcd6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestLongKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestNumberClassKey.class b/compiler/src/it/functional-tests/target/classes/test/TestNumberClassKey.class Binary files differnew file mode 100644 index 000000000..29cc60f3e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestNumberClassKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestShortKey.class b/compiler/src/it/functional-tests/target/classes/test/TestShortKey.class Binary files differnew file mode 100644 index 000000000..84c9c9718 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestShortKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestStringKey$NestedWrappedKey.class b/compiler/src/it/functional-tests/target/classes/test/TestStringKey$NestedWrappedKey.class Binary files differnew file mode 100644 index 000000000..3558d1c5b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestStringKey$NestedWrappedKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestStringKey$NestedWrappedKeyCreator.class b/compiler/src/it/functional-tests/target/classes/test/TestStringKey$NestedWrappedKeyCreator.class Binary files differnew file mode 100644 index 000000000..c40276320 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestStringKey$NestedWrappedKeyCreator.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestStringKey.class b/compiler/src/it/functional-tests/target/classes/test/TestStringKey.class Binary files differnew file mode 100644 index 000000000..2da81617d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestStringKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestUnwrappedAnnotationKey.class b/compiler/src/it/functional-tests/target/classes/test/TestUnwrappedAnnotationKey.class Binary files differnew file mode 100644 index 000000000..54f727992 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestUnwrappedAnnotationKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestUnwrappedAnnotationKeyCreator.class b/compiler/src/it/functional-tests/target/classes/test/TestUnwrappedAnnotationKeyCreator.class Binary files differnew file mode 100644 index 000000000..cfba81f42 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestUnwrappedAnnotationKeyCreator.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestWrappedAnnotationKey.class b/compiler/src/it/functional-tests/target/classes/test/TestWrappedAnnotationKey.class Binary files differnew file mode 100644 index 000000000..9545b6055 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestWrappedAnnotationKey.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TestWrappedAnnotationKeyCreator.class b/compiler/src/it/functional-tests/target/classes/test/TestWrappedAnnotationKeyCreator.class Binary files differnew file mode 100644 index 000000000..1f2e4a971 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TestWrappedAnnotationKeyCreator.class diff --git a/compiler/src/it/functional-tests/target/classes/test/Thing.class b/compiler/src/it/functional-tests/target/classes/test/Thing.class Binary files differnew file mode 100644 index 000000000..24101196f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/Thing.class diff --git a/compiler/src/it/functional-tests/target/classes/test/Thing_Factory.class b/compiler/src/it/functional-tests/target/classes/test/Thing_Factory.class Binary files differnew file mode 100644 index 000000000..bf7ffaae4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/Thing_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TypeWithInheritedMembersInjection.class b/compiler/src/it/functional-tests/target/classes/test/TypeWithInheritedMembersInjection.class Binary files differnew file mode 100644 index 000000000..6272e296a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TypeWithInheritedMembersInjection.class diff --git a/compiler/src/it/functional-tests/target/classes/test/TypeWithInheritedMembersInjection_Factory.class b/compiler/src/it/functional-tests/target/classes/test/TypeWithInheritedMembersInjection_Factory.class Binary files differnew file mode 100644 index 000000000..e39d4d5bc --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/TypeWithInheritedMembersInjection_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/ByteModule.class b/compiler/src/it/functional-tests/target/classes/test/builder/ByteModule.class Binary files differnew file mode 100644 index 000000000..c7ce4efd0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/ByteModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/ByteModule_BFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/ByteModule_BFactory.class Binary files differnew file mode 100644 index 000000000..d952ff841 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/ByteModule_BFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent$1.class Binary files differnew file mode 100644 index 000000000..35a943118 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent$Builder.class Binary files differnew file mode 100644 index 000000000..bce0df01e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent.class Binary files differnew file mode 100644 index 000000000..8b4f3ae74 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerDepComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$1.class Binary files differnew file mode 100644 index 000000000..9d7936600 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$Builder.class Binary files differnew file mode 100644 index 000000000..e0bff3dcf --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildBuilder.class Binary files differnew file mode 100644 index 000000000..25690f4ec --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl$GrandchildBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl$GrandchildBuilder.class Binary files differnew file mode 100644 index 000000000..b07990ba6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl$GrandchildBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl$GrandchildImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl$GrandchildImpl.class Binary files differnew file mode 100644 index 000000000..137804e91 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl$GrandchildImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl.class Binary files differnew file mode 100644 index 000000000..a92b722db --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$MiddleChildImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildBuilder.class Binary files differnew file mode 100644 index 000000000..01f7f32f0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl$GrandchildBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl$GrandchildBuilder.class Binary files differnew file mode 100644 index 000000000..6bf6832fb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl$GrandchildBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl$GrandchildImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl$GrandchildImpl.class Binary files differnew file mode 100644 index 000000000..66a9181f8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl$GrandchildImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl.class Binary files differnew file mode 100644 index 000000000..c08cc4c26 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$OtherMiddleChildImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderAbstractClassBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderAbstractClassBuilder.class Binary files differnew file mode 100644 index 000000000..0f4079f1d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderAbstractClassBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderAbstractClassImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderAbstractClassImpl.class Binary files differnew file mode 100644 index 000000000..2317e4c04 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderAbstractClassImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderInterfaceBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderInterfaceBuilder.class Binary files differnew file mode 100644 index 000000000..e3ab6a18c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderInterfaceBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderInterfaceImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderInterfaceImpl.class Binary files differnew file mode 100644 index 000000000..5a33eb93e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent$TestChildComponentWithBuilderInterfaceImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent.class Binary files differnew file mode 100644 index 000000000..7c13f8222 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$1.class Binary files differnew file mode 100644 index 000000000..4f8a3d382 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$Builder.class Binary files differnew file mode 100644 index 000000000..2615fecce --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$GrandchildBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$GrandchildBuilder.class Binary files differnew file mode 100644 index 000000000..cfde9e75c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$GrandchildBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$GrandchildImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$GrandchildImpl.class Binary files differnew file mode 100644 index 000000000..219673e81 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent$GrandchildImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent.class Binary files differnew file mode 100644 index 000000000..fa43a6fdb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerParentOfGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass$1.class Binary files differnew file mode 100644 index 000000000..e36e84790 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass$Builder.class Binary files differnew file mode 100644 index 000000000..d8b980d0a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass.class Binary files differnew file mode 100644 index 000000000..2b66078b6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderAbstractClass.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface$1.class Binary files differnew file mode 100644 index 000000000..86045c67c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface$Builder.class Binary files differnew file mode 100644 index 000000000..2a8e75f3e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface.class Binary files differnew file mode 100644 index 000000000..d72fea092 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithBuilderInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass$1.class Binary files differnew file mode 100644 index 000000000..b8864e524 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass$Builder.class Binary files differnew file mode 100644 index 000000000..91feb7d24 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass.class Binary files differnew file mode 100644 index 000000000..8bb8b89d1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface$1.class Binary files differnew file mode 100644 index 000000000..f9dbd60ba --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface$Builder.class Binary files differnew file mode 100644 index 000000000..cdeeb01de --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface.class b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface.class Binary files differnew file mode 100644 index 000000000..817f26dcd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DaggerTestComponentWithGenericBuilderInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DepComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/DepComponent.class Binary files differnew file mode 100644 index 000000000..817678039 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DepComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DoubleModule.class b/compiler/src/it/functional-tests/target/classes/test/builder/DoubleModule.class Binary files differnew file mode 100644 index 000000000..6343fe117 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DoubleModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/DoubleModule_DFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/DoubleModule_DFactory.class Binary files differnew file mode 100644 index 000000000..0057887ba --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/DoubleModule_DFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/FloatModule.class b/compiler/src/it/functional-tests/target/classes/test/builder/FloatModule.class Binary files differnew file mode 100644 index 000000000..613f72c85 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/FloatModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/FloatModule_FFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/FloatModule_FFactory.class Binary files differnew file mode 100644 index 000000000..15938e74f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/FloatModule_FFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/GenericParent.class b/compiler/src/it/functional-tests/target/classes/test/builder/GenericParent.class Binary files differnew file mode 100644 index 000000000..5ad0f4f89 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/GenericParent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/Grandchild$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/Grandchild$Builder.class Binary files differnew file mode 100644 index 000000000..8dc9ef273 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/Grandchild$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/Grandchild.class b/compiler/src/it/functional-tests/target/classes/test/builder/Grandchild.class Binary files differnew file mode 100644 index 000000000..fe6467f04 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/Grandchild.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/IntModuleIncludingDoubleAndFloat.class b/compiler/src/it/functional-tests/target/classes/test/builder/IntModuleIncludingDoubleAndFloat.class Binary files differnew file mode 100644 index 000000000..9f8593303 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/IntModuleIncludingDoubleAndFloat.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/IntModuleIncludingDoubleAndFloat_IntegerFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/IntModuleIncludingDoubleAndFloat_IntegerFactory.class Binary files differnew file mode 100644 index 000000000..7f48d0bd3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/IntModuleIncludingDoubleAndFloat_IntegerFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/LongModule.class b/compiler/src/it/functional-tests/target/classes/test/builder/LongModule.class Binary files differnew file mode 100644 index 000000000..756889a9b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/LongModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/LongModule_LFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/LongModule_LFactory.class Binary files differnew file mode 100644 index 000000000..8af7ab1cc --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/LongModule_LFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/MiddleChild$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/MiddleChild$Builder.class Binary files differnew file mode 100644 index 000000000..5932a2a4e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/MiddleChild$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/MiddleChild.class b/compiler/src/it/functional-tests/target/classes/test/builder/MiddleChild.class Binary files differnew file mode 100644 index 000000000..6095caeb5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/MiddleChild.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/MiddleScope.class b/compiler/src/it/functional-tests/target/classes/test/builder/MiddleScope.class Binary files differnew file mode 100644 index 000000000..69a6d86db --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/MiddleScope.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/OtherMiddleChild$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/OtherMiddleChild$Builder.class Binary files differnew file mode 100644 index 000000000..c66bdf6c4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/OtherMiddleChild$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/OtherMiddleChild.class b/compiler/src/it/functional-tests/target/classes/test/builder/OtherMiddleChild.class Binary files differnew file mode 100644 index 000000000..fb2aa775e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/OtherMiddleChild.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/ParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/ParentComponent.class Binary files differnew file mode 100644 index 000000000..1f8780851 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/ParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/ParentOfGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/ParentOfGenericComponent.class Binary files differnew file mode 100644 index 000000000..3806d0bdd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/ParentOfGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/StringModule.class b/compiler/src/it/functional-tests/target/classes/test/builder/StringModule.class Binary files differnew file mode 100644 index 000000000..08b2472ce --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/StringModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/StringModule_StringFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/StringModule_StringFactory.class Binary files differnew file mode 100644 index 000000000..14f561279 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/StringModule_StringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass$Builder.class Binary files differnew file mode 100644 index 000000000..b8bb84009 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass$SharedBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass$SharedBuilder.class Binary files differnew file mode 100644 index 000000000..24f8a8fbd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass$SharedBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass.class Binary files differnew file mode 100644 index 000000000..b5607f7c0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderAbstractClass.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface$Builder.class Binary files differnew file mode 100644 index 000000000..ea9c38495 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface$SharedBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface$SharedBuilder.class Binary files differnew file mode 100644 index 000000000..d06c2686d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface$SharedBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface.class Binary files differnew file mode 100644 index 000000000..7399f252c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestChildComponentWithBuilderInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass$Builder.class Binary files differnew file mode 100644 index 000000000..b5d735403 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass$SharedBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass$SharedBuilder.class Binary files differnew file mode 100644 index 000000000..98bd32571 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass$SharedBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass.class Binary files differnew file mode 100644 index 000000000..b9d209a17 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderAbstractClass.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface$Builder.class Binary files differnew file mode 100644 index 000000000..9099d5c44 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface$SharedBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface$SharedBuilder.class Binary files differnew file mode 100644 index 000000000..e0cb76d85 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface$SharedBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface.class Binary files differnew file mode 100644 index 000000000..626909db9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithBuilderInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass$Builder.class Binary files differnew file mode 100644 index 000000000..6dcd5945a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass$SharedBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass$SharedBuilder.class Binary files differnew file mode 100644 index 000000000..37c0cf38a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass$SharedBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass.class Binary files differnew file mode 100644 index 000000000..1f9c5061a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderAbstractClass.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface$Builder.class Binary files differnew file mode 100644 index 000000000..0f7b9a230 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface$SharedBuilder.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface$SharedBuilder.class Binary files differnew file mode 100644 index 000000000..2bdb5e1ad --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface$SharedBuilder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface.class b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface.class Binary files differnew file mode 100644 index 000000000..ef5e25432 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/TestComponentWithGenericBuilderInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ApiComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ApiComponent.class Binary files differnew file mode 100644 index 000000000..0bc10dd4b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ApiComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/CloudContactDataStore.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/CloudContactDataStore.class Binary files differnew file mode 100644 index 000000000..a66031958 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/CloudContactDataStore.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ContactDataStore.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ContactDataStore.class Binary files differnew file mode 100644 index 000000000..ad2405284 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ContactDataStore.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ContactDataStore_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ContactDataStore_MembersInjector.class Binary files differnew file mode 100644 index 000000000..c2e23b563 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/ContactDataStore_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent$1.class Binary files differnew file mode 100644 index 000000000..9a6f06b44 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent$Builder.class Binary files differnew file mode 100644 index 000000000..2cbd339a4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent.class Binary files differnew file mode 100644 index 000000000..6cd698ce9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/DaggerApiComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/RestClient.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/RestClient.class Binary files differnew file mode 100644 index 000000000..38ff0921a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/RestClient.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/RestClient_Factory.class b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/RestClient_Factory.class Binary files differnew file mode 100644 index 000000000..94dfe224a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/abstractinjectmethod/RestClient_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent$1.class Binary files differnew file mode 100644 index 000000000..1474902a4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent$Builder.class Binary files differnew file mode 100644 index 000000000..a97a38fbb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent.class Binary files differnew file mode 100644 index 000000000..0661cfb10 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerOneComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent$1.class Binary files differnew file mode 100644 index 000000000..747559a4e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent$Builder.class Binary files differnew file mode 100644 index 000000000..70de723df --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent.class Binary files differnew file mode 100644 index 000000000..a3e7f774c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/DaggerPackagerTwoComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/Packager.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/Packager.class Binary files differnew file mode 100644 index 000000000..cce10a59a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/Packager.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/PackagerOneComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/PackagerOneComponent.class Binary files differnew file mode 100644 index 000000000..b419ad888 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/PackagerOneComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/PackagerTwoComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/PackagerTwoComponent.class Binary files differnew file mode 100644 index 000000000..4cfceaa69 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/PackagerTwoComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/Packager_Factory.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/Packager_Factory.class Binary files differnew file mode 100644 index 000000000..ea9bc0d92 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/Packager_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/Product.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/Product.class Binary files differnew file mode 100644 index 000000000..50a825947 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/Product.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOne.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOne.class Binary files differnew file mode 100644 index 000000000..0acdccb81 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOne.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOneModule.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOneModule.class Binary files differnew file mode 100644 index 000000000..5d234b694 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOneModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOneModule_ProvideProductFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOneModule_ProvideProductFactory.class Binary files differnew file mode 100644 index 000000000..89d723606 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOneModule_ProvideProductFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOne_Factory.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOne_Factory.class Binary files differnew file mode 100644 index 000000000..bdb092bf8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductOne_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwo.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwo.class Binary files differnew file mode 100644 index 000000000..e6c976b17 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwo.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwoModule.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwoModule.class Binary files differnew file mode 100644 index 000000000..3e20d4557 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwoModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwoModule_ProvideProductFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwoModule_ProvideProductFactory.class Binary files differnew file mode 100644 index 000000000..8f041c1ba --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwoModule_ProvideProductFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwo_Factory.class b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwo_Factory.class Binary files differnew file mode 100644 index 000000000..c93aba532 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/product/ProductTwo_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$1.class Binary files differnew file mode 100644 index 000000000..493c9e370 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$Builder.class Binary files differnew file mode 100644 index 000000000..cf66600e1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$SubComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$SubComponentImpl.class Binary files differnew file mode 100644 index 000000000..a42a01c98 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent$SubComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent.class Binary files differnew file mode 100644 index 000000000..f9802fc93 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/DaggerRootComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/ModuleWithParameter.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/ModuleWithParameter.class Binary files differnew file mode 100644 index 000000000..d76d26b7e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/ModuleWithParameter.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/ModuleWithParameter_ProvidesStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/ModuleWithParameter_ProvidesStringFactory.class Binary files differnew file mode 100644 index 000000000..2a67a421f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/ModuleWithParameter_ProvidesStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/RootComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/RootComponent.class Binary files differnew file mode 100644 index 000000000..a37d7b6d4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/RootComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/SubComponent.class b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/SubComponent.class Binary files differnew file mode 100644 index 000000000..49c82146e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/builder/subcomponent/SubComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfArrayOfParentOfStringArray.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfArrayOfParentOfStringArray.class Binary files differnew file mode 100644 index 000000000..0173043f0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfArrayOfParentOfStringArray.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfPrimitiveIntArray.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfPrimitiveIntArray.class Binary files differnew file mode 100644 index 000000000..6033324fb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfPrimitiveIntArray.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfStringArray.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfStringArray.class Binary files differnew file mode 100644 index 000000000..ce1c979e3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/ChildOfStringArray.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent$1.class Binary files differnew file mode 100644 index 000000000..0ee2d134d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent$Builder.class Binary files differnew file mode 100644 index 000000000..33b6eb3ec --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent.class Binary files differnew file mode 100644 index 000000000..6df464913 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/DaggerMembersInjectComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectComponent.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectComponent.class Binary files differnew file mode 100644 index 000000000..1f2fdfb7a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectGenericParent.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectGenericParent.class Binary files differnew file mode 100644 index 000000000..b61d37511 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectGenericParent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectGenericParent_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectGenericParent_MembersInjector.class Binary files differnew file mode 100644 index 000000000..61d659594 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectGenericParent_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule.class Binary files differnew file mode 100644 index 000000000..a68a17e11 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideFooArrayOfStringArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideFooArrayOfStringArrayFactory.class Binary files differnew file mode 100644 index 000000000..7a1ea5930 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideFooArrayOfStringArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideIntArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideIntArrayFactory.class Binary files differnew file mode 100644 index 000000000..fe84b88fa --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideIntArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideStringArrayFactory.class b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideStringArrayFactory.class Binary files differnew file mode 100644 index 000000000..994c86da5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/membersinject/MembersInjectModule_ProvideStringArrayFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$1.class Binary files differnew file mode 100644 index 000000000..af8df0808 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$Builder.class Binary files differnew file mode 100644 index 000000000..05adb9da6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$FooChildComponentImpl$FooGrandchildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$FooChildComponentImpl$FooGrandchildComponentImpl.class Binary files differnew file mode 100644 index 000000000..a85147f1c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$FooChildComponentImpl$FooGrandchildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$FooChildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$FooChildComponentImpl.class Binary files differnew file mode 100644 index 000000000..df11bba99 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent$FooChildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent.class Binary files differnew file mode 100644 index 000000000..25a8947f9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/DaggerFooComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/FooComponent.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/FooComponent.class Binary files differnew file mode 100644 index 000000000..06aaffa2b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/FooComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/a/AModule.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/a/AModule.class Binary files differnew file mode 100644 index 000000000..292e4eea1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/a/AModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/a/AModule_ProvideStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/a/AModule_ProvideStringFactory.class Binary files differnew file mode 100644 index 000000000..d39b39e9f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/a/AModule_ProvideStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/b/BModule.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/b/BModule.class Binary files differnew file mode 100644 index 000000000..85a63abd1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/b/BModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/b/BModule_ProvideStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/b/BModule_ProvideStringFactory.class Binary files differnew file mode 100644 index 000000000..0bafddc01 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/b/BModule_ProvideStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/c/CModule.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/c/CModule.class Binary files differnew file mode 100644 index 000000000..0dd730550 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/c/CModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/c/CModule_ProvideStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/c/CModule_ProvideStringFactory.class Binary files differnew file mode 100644 index 000000000..69a9ebcce --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/c/CModule_ProvideStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/d/DModule.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/d/DModule.class Binary files differnew file mode 100644 index 000000000..11af190f3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/d/DModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/d/DModule_ProvideStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/d/DModule_ProvideStringFactory.class Binary files differnew file mode 100644 index 000000000..c3d7c7169 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/d/DModule_ProvideStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/foo/Foo.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/foo/Foo.class Binary files differnew file mode 100644 index 000000000..03eee0f7c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/foo/Foo.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/foo/Foo_Factory.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/foo/Foo_Factory.class Binary files differnew file mode 100644 index 000000000..ffbe6e764 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/foo/Foo_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/grandsub/FooGrandchildComponent.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/grandsub/FooGrandchildComponent.class Binary files differnew file mode 100644 index 000000000..9f04a7cc1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/grandsub/FooGrandchildComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/multipackage/sub/FooChildComponent.class b/compiler/src/it/functional-tests/target/classes/test/multipackage/sub/FooChildComponent.class Binary files differnew file mode 100644 index 000000000..757dc984c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/multipackage/sub/FooChildComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent$1.class Binary files differnew file mode 100644 index 000000000..d861d0793 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent$Builder.class Binary files differnew file mode 100644 index 000000000..b416d5cdf --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent.class b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent.class Binary files differnew file mode 100644 index 000000000..bf568f925 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$1.class b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$1.class Binary files differnew file mode 100644 index 000000000..72fcf3397 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$2.class b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$2.class Binary files differnew file mode 100644 index 000000000..7147fe63c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$2.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$Builder.class b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$Builder.class Binary files differnew file mode 100644 index 000000000..b18e193d2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency.class b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency.class Binary files differnew file mode 100644 index 000000000..c667dd696 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/DaggerNullComponentWithDependency.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullComponent.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullComponent.class Binary files differnew file mode 100644 index 000000000..1ced792e2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullComponentWithDependency.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullComponentWithDependency.class Binary files differnew file mode 100644 index 000000000..476368df4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullComponentWithDependency.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo.class Binary files differnew file mode 100644 index 000000000..9fc4521fa --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo_Factory.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo_Factory.class Binary files differnew file mode 100644 index 000000000..cd8089692 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo_MembersInjector.class Binary files differnew file mode 100644 index 000000000..5cfbcc8a0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullFoo_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule.class Binary files differnew file mode 100644 index 000000000..5b5ed66b4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule_ProvideNullableStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule_ProvideNullableStringFactory.class Binary files differnew file mode 100644 index 000000000..2d714b490 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule_ProvideNullableStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule_ProvideNumberFactory.class b/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule_ProvideNumberFactory.class Binary files differnew file mode 100644 index 000000000..f086d2c8b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/NullModule_ProvideNumberFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/nullables/Nullable.class b/compiler/src/it/functional-tests/target/classes/test/nullables/Nullable.class Binary files differnew file mode 100644 index 000000000..6fac710a3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/nullables/Nullable.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule.class Binary files differnew file mode 100644 index 000000000..496239799 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule_ContibuteEmptyIntegerSetFactory.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule_ContibuteEmptyIntegerSetFactory.class Binary files differnew file mode 100644 index 000000000..5acb9db36 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule_ContibuteEmptyIntegerSetFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule_ContributeStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule_ContributeStringFactory.class Binary files differnew file mode 100644 index 000000000..7a57a3579 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/AllStaticModule_ContributeStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent$1.class Binary files differnew file mode 100644 index 000000000..e6bbfaa4e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent$Builder.class Binary files differnew file mode 100644 index 000000000..f538647ab --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent.class Binary files differnew file mode 100644 index 000000000..11accabc0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/DaggerStaticTestComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule.class Binary files differnew file mode 100644 index 000000000..3144f7ab4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule_ContributeStringFromAStaticMethodFactory.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule_ContributeStringFromAStaticMethodFactory.class Binary files differnew file mode 100644 index 000000000..124e8e040 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule_ContributeStringFromAStaticMethodFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule_ContributeStringFromAnInstanceMethodFactory.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule_ContributeStringFromAnInstanceMethodFactory.class Binary files differnew file mode 100644 index 000000000..5b4aece3e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/SomeStaticModule_ContributeStringFromAnInstanceMethodFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/staticprovides/StaticTestComponent.class b/compiler/src/it/functional-tests/target/classes/test/staticprovides/StaticTestComponent.class Binary files differnew file mode 100644 index 000000000..1abae9660 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/staticprovides/StaticTestComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule.class b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule.class Binary files differnew file mode 100644 index 000000000..5cabd6846 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeAnIntFactory.class b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeAnIntFactory.class Binary files differnew file mode 100644 index 000000000..c60b25f22 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeAnIntFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeAnotherIntFactory.class b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeAnotherIntFactory.class Binary files differnew file mode 100644 index 000000000..b82189255 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeAnotherIntFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeSomeIntsFactory.class b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeSomeIntsFactory.class Binary files differnew file mode 100644 index 000000000..19430b58c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/ContributionsModule_ContributeSomeIntsFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/DaggerGenericComponent_PackageProxy.class b/compiler/src/it/functional-tests/target/classes/test/sub/DaggerGenericComponent_PackageProxy.class Binary files differnew file mode 100644 index 000000000..0c4664256 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/DaggerGenericComponent_PackageProxy.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/Exposed.class b/compiler/src/it/functional-tests/target/classes/test/sub/Exposed.class Binary files differnew file mode 100644 index 000000000..36514decd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/Exposed.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/Exposed_Factory.class b/compiler/src/it/functional-tests/target/classes/test/sub/Exposed_Factory.class Binary files differnew file mode 100644 index 000000000..d3aab2285 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/Exposed_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/Exposed_MembersInjector.class b/compiler/src/it/functional-tests/target/classes/test/sub/Exposed_MembersInjector.class Binary files differnew file mode 100644 index 000000000..81b2f64f7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/Exposed_MembersInjector.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/OtherThing.class b/compiler/src/it/functional-tests/target/classes/test/sub/OtherThing.class Binary files differnew file mode 100644 index 000000000..2a727d579 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/OtherThing.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/OtherThing_Factory.class b/compiler/src/it/functional-tests/target/classes/test/sub/OtherThing_Factory.class Binary files differnew file mode 100644 index 000000000..cc9285abf --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/OtherThing_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivate.class b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivate.class Binary files differnew file mode 100644 index 000000000..bc12412c6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivate.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer$PublicEnclosed.class b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer$PublicEnclosed.class Binary files differnew file mode 100644 index 000000000..c24c3adc3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer$PublicEnclosed.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer$PublicEnclosed_Factory.class b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer$PublicEnclosed_Factory.class Binary files differnew file mode 100644 index 000000000..89a293b22 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer$PublicEnclosed_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer.class b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer.class Binary files differnew file mode 100644 index 000000000..4f325e603 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivateContainer.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivate_Factory.class b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivate_Factory.class Binary files differnew file mode 100644 index 000000000..472d079f4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PackagePrivate_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass.class b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass.class Binary files differnew file mode 100644 index 000000000..ba723ca84 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass2.class b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass2.class Binary files differnew file mode 100644 index 000000000..c09b83d77 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass2.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass2_Factory.class b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass2_Factory.class Binary files differnew file mode 100644 index 000000000..942ba3db3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass2_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass_Factory.class b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass_Factory.class Binary files differnew file mode 100644 index 000000000..20a2642c6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/sub/PublicSubclass_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/AnInterface.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/AnInterface.class Binary files differnew file mode 100644 index 000000000..77ca460ca --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/AnInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/BoundAsSingleton.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/BoundAsSingleton.class Binary files differnew file mode 100644 index 000000000..f265a15c6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/BoundAsSingleton.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildAbstractClassComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildAbstractClassComponent.class Binary files differnew file mode 100644 index 000000000..0a6e67a37 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildAbstractClassComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildComponent.class Binary files differnew file mode 100644 index 000000000..5075baa71 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildComponentRequiringModules.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildComponentRequiringModules.class Binary files differnew file mode 100644 index 000000000..0ec97391c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildComponentRequiringModules.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule$1.class Binary files differnew file mode 100644 index 000000000..03217f454 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule.class Binary files differnew file mode 100644 index 000000000..90de24748 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithParameters.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithParameters.class Binary files differnew file mode 100644 index 000000000..b56dc2558 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithParameters.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithState.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithState.class Binary files differnew file mode 100644 index 000000000..955327972 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithState.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithState_ProvideIntFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithState_ProvideIntFactory.class Binary files differnew file mode 100644 index 000000000..1e2a0dff5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModuleWithState_ProvideIntFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule_ProvideUnscopedObjectFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule_ProvideUnscopedObjectFactory.class Binary files differnew file mode 100644 index 000000000..65139cafb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ChildModule_ProvideUnscopedObjectFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$1.class Binary files differnew file mode 100644 index 000000000..8f711515b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$Builder.class Binary files differnew file mode 100644 index 000000000..20c38e369 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildAbstractClassComponentImpl$GrandchildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildAbstractClassComponentImpl$GrandchildComponentImpl.class Binary files differnew file mode 100644 index 000000000..cb3420e4f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildAbstractClassComponentImpl$GrandchildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildAbstractClassComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildAbstractClassComponentImpl.class Binary files differnew file mode 100644 index 000000000..3a9deefb4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildAbstractClassComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentImpl$GrandchildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentImpl$GrandchildComponentImpl.class Binary files differnew file mode 100644 index 000000000..ed73304f2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentImpl$GrandchildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentImpl.class Binary files differnew file mode 100644 index 000000000..bf1423745 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentRequiringModulesImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentRequiringModulesImpl.class Binary files differnew file mode 100644 index 000000000..0d174e6ed --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent$ChildComponentRequiringModulesImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent.class Binary files differnew file mode 100644 index 000000000..2439a9460 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$1.class Binary files differnew file mode 100644 index 000000000..2d281e59d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$Builder.class Binary files differnew file mode 100644 index 000000000..b1f5d1d3c --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$ChildComponentImpl$GrandchildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$ChildComponentImpl$GrandchildComponentImpl.class Binary files differnew file mode 100644 index 000000000..00af3fc4e --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$ChildComponentImpl$GrandchildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$ChildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$ChildComponentImpl.class Binary files differnew file mode 100644 index 000000000..a130a3e08 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent$ChildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent.class Binary files differnew file mode 100644 index 000000000..5653e5b2f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/DaggerParentOfGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/GenericParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GenericParentComponent.class Binary files differnew file mode 100644 index 000000000..dfa9b0fdb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GenericParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildComponent.class Binary files differnew file mode 100644 index 000000000..390f0e566 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule$1.class Binary files differnew file mode 100644 index 000000000..c6ac188cb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule.class Binary files differnew file mode 100644 index 000000000..4d275e9b3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideAnInterfaceFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideAnInterfaceFactory.class Binary files differnew file mode 100644 index 000000000..e6a1f1dcb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideAnInterfaceFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideNeedsAnInterfaceFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideNeedsAnInterfaceFactory.class Binary files differnew file mode 100644 index 000000000..1d79a804f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideNeedsAnInterfaceFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideUnscopedObjectFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideUnscopedObjectFactory.class Binary files differnew file mode 100644 index 000000000..75893a16a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/GrandchildModule_ProvideUnscopedObjectFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ImplementsAnInterface.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ImplementsAnInterface.class Binary files differnew file mode 100644 index 000000000..efd6574e1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ImplementsAnInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ImplementsAnInterface_Factory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ImplementsAnInterface_Factory.class Binary files differnew file mode 100644 index 000000000..f36c98c68 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ImplementsAnInterface_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/NeedsAnInterface.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/NeedsAnInterface.class Binary files differnew file mode 100644 index 000000000..ed949d4da --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/NeedsAnInterface.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentComponent.class Binary files differnew file mode 100644 index 000000000..c7bffc5c7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentGetters.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentGetters.class Binary files differnew file mode 100644 index 000000000..7ce269633 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentGetters.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule$1.class Binary files differnew file mode 100644 index 000000000..ae1d91e66 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule$2.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule$2.class Binary files differnew file mode 100644 index 000000000..2aeb06bcf --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule$2.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule.class Binary files differnew file mode 100644 index 000000000..2a25ee1e0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideSingletonObjectFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideSingletonObjectFactory.class Binary files differnew file mode 100644 index 000000000..680e3a6c1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideSingletonObjectFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideUnscopedObjectFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideUnscopedObjectFactory.class Binary files differnew file mode 100644 index 000000000..4ece42f63 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideUnscopedObjectFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.class Binary files differnew file mode 100644 index 000000000..8e35c9426 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentOfGenericComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentOfGenericComponent.class Binary files differnew file mode 100644 index 000000000..b40bbf853 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/ParentOfGenericComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/RequiresSingletons.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/RequiresSingletons.class Binary files differnew file mode 100644 index 000000000..43c7104db --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/RequiresSingletons.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/RequiresSingletons_Factory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/RequiresSingletons_Factory.class Binary files differnew file mode 100644 index 000000000..23be0210a --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/RequiresSingletons_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/SingletonType.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/SingletonType.class Binary files differnew file mode 100644 index 000000000..d716fe63b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/SingletonType.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/SingletonType_Factory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/SingletonType_Factory.class Binary files differnew file mode 100644 index 000000000..63794ef99 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/SingletonType_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/UnscopedType.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/UnscopedType.class Binary files differnew file mode 100644 index 000000000..54272acd3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/UnscopedType.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/UnscopedType_Factory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/UnscopedType_Factory.class Binary files differnew file mode 100644 index 000000000..85ab954b2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/UnscopedType_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/ChildComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/ChildComponent.class Binary files differnew file mode 100644 index 000000000..e6f3594d3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/ChildComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$1.class Binary files differnew file mode 100644 index 000000000..1c5649a25 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$Builder.class Binary files differnew file mode 100644 index 000000000..536e4b27d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$ChildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$ChildComponentImpl.class Binary files differnew file mode 100644 index 000000000..873a34871 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent$ChildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent.class Binary files differnew file mode 100644 index 000000000..acf4107e7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/DaggerParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/ParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/ParentComponent.class Binary files differnew file mode 100644 index 000000000..5ad576eeb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/ParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonModuleName.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonModuleName.class Binary files differnew file mode 100644 index 000000000..6b2607534 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonModuleName.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonModuleName_ProvideStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonModuleName_ProvideStringFactory.class Binary files differnew file mode 100644 index 000000000..30f90da6b --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonModuleName_ProvideStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonName.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonName.class Binary files differnew file mode 100644 index 000000000..ff32b8318 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonName.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonName_Factory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonName_Factory.class Binary files differnew file mode 100644 index 000000000..8a6d851a4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/a/CommonName_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonModuleName.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonModuleName.class Binary files differnew file mode 100644 index 000000000..9310d9d0f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonModuleName.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonModuleName_ProvideStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonModuleName_ProvideStringFactory.class Binary files differnew file mode 100644 index 000000000..61965facc --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonModuleName_ProvideStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonName.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonName.class Binary files differnew file mode 100644 index 000000000..2cec405dd --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonName.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonName_Factory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonName_Factory.class Binary files differnew file mode 100644 index 000000000..ecff3a31f --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/hiding/b/CommonName_Factory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ChildComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ChildComponent.class Binary files differnew file mode 100644 index 000000000..90a566ff0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ChildComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$1.class Binary files differnew file mode 100644 index 000000000..dfadab303 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$Builder.class Binary files differnew file mode 100644 index 000000000..7e777cd56 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$ChildComponentImpl.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$ChildComponentImpl.class Binary files differnew file mode 100644 index 000000000..d70259668 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent$ChildComponentImpl.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent.class Binary files differnew file mode 100644 index 000000000..74e36c166 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/DaggerParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/OnlyUsedInChild.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/OnlyUsedInChild.class Binary files differnew file mode 100644 index 000000000..a621dee34 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/OnlyUsedInChild.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/OnlyUsedInParent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/OnlyUsedInParent.class Binary files differnew file mode 100644 index 000000000..403929149 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/OnlyUsedInParent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ParentComponent$Builder.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ParentComponent$Builder.class Binary files differnew file mode 100644 index 000000000..b20830de7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ParentComponent$Builder.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ParentComponent.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ParentComponent.class Binary files differnew file mode 100644 index 000000000..726575bba --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/ParentComponent.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule$1.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule$1.class Binary files differnew file mode 100644 index 000000000..0051bb506 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule$1.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule$2.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule$2.class Binary files differnew file mode 100644 index 000000000..9974b8dbe --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule$2.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule.class Binary files differnew file mode 100644 index 000000000..19c8865fb --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ContributeStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ContributeStringFactory.class Binary files differnew file mode 100644 index 000000000..475538dea --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ContributeStringFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInChildFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInChildFactory.class Binary files differnew file mode 100644 index 000000000..a47f2071d --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInChildFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInParentFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInParentFactory.class Binary files differnew file mode 100644 index 000000000..7ab4a8ec0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInParentFactory.class diff --git a/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideStringFactory.class b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideStringFactory.class Binary files differnew file mode 100644 index 000000000..80ab0a828 --- /dev/null +++ b/compiler/src/it/functional-tests/target/classes/test/subcomponent/repeat/RepeatedModule_ProvideStringFactory.class diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/A_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/A_Factory.java new file mode 100644 index 000000000..2ef60bf9d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/A_Factory.java @@ -0,0 +1,19 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum A_Factory implements Factory<A> { +INSTANCE; + + @Override + public A get() { + return new A(); + } + + public static Factory<A> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AbstractMembersInjectingBaseClass_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AbstractMembersInjectingBaseClass_MembersInjector.java new file mode 100644 index 000000000..bcc27e590 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AbstractMembersInjectingBaseClass_MembersInjector.java @@ -0,0 +1,28 @@ +package test; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class AbstractMembersInjectingBaseClass_MembersInjector implements MembersInjector<AbstractMembersInjectingBaseClass> { + private final Provider<Thing> thingProvider; + + public AbstractMembersInjectingBaseClass_MembersInjector(Provider<Thing> thingProvider) { + assert thingProvider != null; + this.thingProvider = thingProvider; + } + + @Override + public void injectMembers(AbstractMembersInjectingBaseClass instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.thing = thingProvider.get(); + } + + public static MembersInjector<AbstractMembersInjectingBaseClass> create(Provider<Thing> thingProvider) { + return new AbstractMembersInjectingBaseClass_MembersInjector(thingProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey.java new file mode 100644 index 000000000..8ebfacdd0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey.java @@ -0,0 +1,55 @@ + +package test; + +import javax.annotation.Generated; + +@Generated("com.google.auto.value.processor.AutoAnnotationProcessor") +final class AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey implements TestStringKey.NestedWrappedKey { + + private final Class<?> value; + + AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey( + Class<?> value) { + if (value == null) { + throw new NullPointerException("Null value"); + } + this.value = value; + } + + @Override + public Class<? extends TestStringKey.NestedWrappedKey> annotationType() { + return TestStringKey.NestedWrappedKey.class; + } + + @Override + public Class<?> value() { + return value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("@test.TestStringKey.NestedWrappedKey("); + sb.append(value); + return sb.append(')').toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestStringKey.NestedWrappedKey) { + TestStringKey.NestedWrappedKey that = (TestStringKey.NestedWrappedKey) o; + return (value.equals(that.value())); + } + return false; + } + + @Override + public int hashCode() { + return + ((127 * 111972721) ^ (value.hashCode())); + // 111972721 is "value".hashCode() + } + +} diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey.java new file mode 100644 index 000000000..29a3ac41c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey.java @@ -0,0 +1,90 @@ + +package test; + +import javax.annotation.Generated; + +@Generated("com.google.auto.value.processor.AutoAnnotationProcessor") +final class AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey implements TestStringKey { + + private final String value; + + AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey( + String value) { + if (value == null) { + throw new NullPointerException("Null value"); + } + this.value = value; + } + + @Override + public Class<? extends TestStringKey> annotationType() { + return TestStringKey.class; + } + + @Override + public String value() { + return value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("@test.TestStringKey("); + appendQuoted(sb, value); + return sb.append(')').toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestStringKey) { + TestStringKey that = (TestStringKey) o; + return (value.equals(that.value())); + } + return false; + } + + @Override + public int hashCode() { + return + ((127 * 111972721) ^ (value.hashCode())); + // 111972721 is "value".hashCode() + } + + private static void appendQuoted(StringBuilder sb, String s) { + sb.append('"'); + for (int i = 0; i < s.length(); i++) { + appendEscaped(sb, s.charAt(i)); + } + sb.append('"'); + } + + private static void appendEscaped(StringBuilder sb, char c) { + switch (c) { + case '\\': + case '"': + case '\'': + sb.append('\\').append(c); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + if (c < 0x20) { + sb.append(String.format("\\%03o", (int) c)); + } else if (c < 0x7f || Character.isLetter(c)) { + sb.append(c); + } else { + sb.append(String.format("\\u%04x", (int) c)); + } + break; + } + } +} diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey.java new file mode 100644 index 000000000..f156d97a3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey.java @@ -0,0 +1,90 @@ + +package test; + +import javax.annotation.Generated; + +@Generated("com.google.auto.value.processor.AutoAnnotationProcessor") +final class AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey implements TestStringKey { + + private final String value; + + AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey( + String value) { + if (value == null) { + throw new NullPointerException("Null value"); + } + this.value = value; + } + + @Override + public Class<? extends TestStringKey> annotationType() { + return TestStringKey.class; + } + + @Override + public String value() { + return value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("@test.TestStringKey("); + appendQuoted(sb, value); + return sb.append(')').toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestStringKey) { + TestStringKey that = (TestStringKey) o; + return (value.equals(that.value())); + } + return false; + } + + @Override + public int hashCode() { + return + ((127 * 111972721) ^ (value.hashCode())); + // 111972721 is "value".hashCode() + } + + private static void appendQuoted(StringBuilder sb, String s) { + sb.append('"'); + for (int i = 0; i < s.length(); i++) { + appendEscaped(sb, s.charAt(i)); + } + sb.append('"'); + } + + private static void appendEscaped(StringBuilder sb, char c) { + switch (c) { + case '\\': + case '"': + case '\'': + sb.append('\\').append(c); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + if (c < 0x20) { + sb.append(String.format("\\%03o", (int) c)); + } else if (c < 0x7f || Character.isLetter(c)) { + sb.append(c); + } else { + sb.append(String.format("\\u%04x", (int) c)); + } + break; + } + } +} diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey.java new file mode 100644 index 000000000..73deb28a1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey.java @@ -0,0 +1,120 @@ + +package test; + +import java.util.Arrays; +import javax.annotation.Generated; + +@Generated("com.google.auto.value.processor.AutoAnnotationProcessor") +final class AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey implements TestWrappedAnnotationKey { + + private final TestStringKey value; + + private final int[] integers; + + private final TestClassKey[] annotations; + + private final Class<? extends Number>[] classes; + + AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey( + TestStringKey value, + int[] integers, + TestClassKey[] annotations, + Class<? extends Number>[] classes) { + if (value == null) { + throw new NullPointerException("Null value"); + } + this.value = value; + if (integers == null) { + throw new NullPointerException("Null integers"); + } + this.integers = integers.clone(); + if (annotations == null) { + throw new NullPointerException("Null annotations"); + } + this.annotations = annotations.clone(); + if (classes == null) { + throw new NullPointerException("Null classes"); + } + this.classes = classes.clone(); + } + + @Override + public Class<? extends TestWrappedAnnotationKey> annotationType() { + return TestWrappedAnnotationKey.class; + } + + @Override + public TestStringKey value() { + return value; + } + + @Override + public int[] integers() { + return integers.clone(); + } + + @Override + public TestClassKey[] annotations() { + return annotations.clone(); + } + + @Override + public Class<? extends Number>[] classes() { + return classes.clone(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("@test.TestWrappedAnnotationKey("); + sb.append("value="); + sb.append(value); + sb.append(", "); + sb.append("integers="); + sb.append(Arrays.toString(integers)); + sb.append(", "); + sb.append("annotations="); + sb.append(Arrays.toString(annotations)); + sb.append(", "); + sb.append("classes="); + sb.append(Arrays.toString(classes)); + return sb.append(')').toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestWrappedAnnotationKey) { + TestWrappedAnnotationKey that = (TestWrappedAnnotationKey) o; + return (value.equals(that.value())) + && (Arrays.equals(integers, + (that instanceof AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey) + ? ((AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey) that).integers + : that.integers())) + && (Arrays.equals(annotations, + (that instanceof AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey) + ? ((AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey) that).annotations + : that.annotations())) + && (Arrays.equals(classes, + (that instanceof AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey) + ? ((AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey) that).classes + : that.classes())); + } + return false; + } + + @Override + public int hashCode() { + return + ((127 * 111972721) ^ (value.hashCode())) + + ((127 * 570074869) ^ (Arrays.hashCode(integers))) + + ((127 * -961709276) ^ (Arrays.hashCode(annotations))) + + ((127 * 853620774) ^ (Arrays.hashCode(classes))); + // 111972721 is "value".hashCode() + // 570074869 is "integers".hashCode() + // -961709276 is "annotations".hashCode() + // 853620774 is "classes".hashCode() + } + +} diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/B_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/B_Factory.java new file mode 100644 index 000000000..a4e32538f --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/B_Factory.java @@ -0,0 +1,19 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum B_Factory implements Factory<B> { +INSTANCE; + + @Override + public B get() { + return new B(); + } + + public static Factory<B> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideArrayListOfComparableStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideArrayListOfComparableStringFactory.java new file mode 100644 index 000000000..365c32a6f --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideArrayListOfComparableStringFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.LinkedList; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideArrayListOfComparableStringFactory implements Factory<LinkedList<Comparable<String>>> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideArrayListOfComparableStringFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public LinkedList<Comparable<String>> get() { + LinkedList<Comparable<String>> provided = module.provideArrayListOfComparableString(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<LinkedList<Comparable<String>>> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideArrayListOfComparableStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideArrayListStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideArrayListStringFactory.java new file mode 100644 index 000000000..f35ebe7fc --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideArrayListStringFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.ArrayList; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideArrayListStringFactory implements Factory<ArrayList<String>> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideArrayListStringFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public ArrayList<String> get() { + ArrayList<String> provided = module.provideArrayListString(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<ArrayList<String>> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideArrayListStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideDoubleFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideDoubleFactory.java new file mode 100644 index 000000000..0f28e8e78 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideDoubleFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideDoubleFactory implements Factory<Double> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideDoubleFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public Double get() { + Double provided = module.provideDouble(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Double> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideDoubleFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideIntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideIntegerFactory.java new file mode 100644 index 000000000..ebddc27c7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideIntegerFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideIntegerFactory implements Factory<Integer> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideIntegerFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public Integer get() { + Integer provided = module.provideInteger(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Integer> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideIntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideLinkedListCharSeqFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideLinkedListCharSeqFactory.java new file mode 100644 index 000000000..3acfb315e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideLinkedListCharSeqFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.LinkedList; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideLinkedListCharSeqFactory implements Factory<LinkedList<CharSequence>> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideLinkedListCharSeqFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public LinkedList<CharSequence> get() { + LinkedList<CharSequence> provided = module.provideLinkedListCharSeq(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<LinkedList<CharSequence>> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideLinkedListCharSeqFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideLinkedListStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideLinkedListStringFactory.java new file mode 100644 index 000000000..6064287e7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideLinkedListStringFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.LinkedList; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideLinkedListStringFactory implements Factory<LinkedList<String>> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideLinkedListStringFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public LinkedList<String> get() { + LinkedList<String> provided = module.provideLinkedListString(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<LinkedList<String>> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideLinkedListStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideListOfIntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideListOfIntegerFactory.java new file mode 100644 index 000000000..44f2d896d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideListOfIntegerFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.List; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideListOfIntegerFactory implements Factory<List<Integer>> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideListOfIntegerFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public List<Integer> get() { + List<Integer> provided = module.provideListOfInteger(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<List<Integer>> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideListOfIntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideSetOfDoubleFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideSetOfDoubleFactory.java new file mode 100644 index 000000000..94d569375 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenericModule_ProvideSetOfDoubleFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BoundedGenericModule_ProvideSetOfDoubleFactory implements Factory<Set<Double>> { + private final BoundedGenericModule module; + + public BoundedGenericModule_ProvideSetOfDoubleFactory(BoundedGenericModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Double> get() { + Set<Double> provided = module.provideSetOfDouble(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Set<Double>> create(BoundedGenericModule module) { + return new BoundedGenericModule_ProvideSetOfDoubleFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenerics_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenerics_Factory.java new file mode 100644 index 000000000..d48cfa5c3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/BoundedGenerics_Factory.java @@ -0,0 +1,38 @@ +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 BoundedGenerics_Factory<A extends Number & Comparable<? super A>, B extends List<? extends CharSequence>, C extends List<? super String>, D extends A, E extends Iterable<D>> implements Factory<BoundedGenerics<A, B, C, D, E>> { + private final Provider<A> aProvider; + private final Provider<B> bProvider; + private final Provider<C> cProvider; + private final Provider<D> dProvider; + private final Provider<E> eProvider; + + public BoundedGenerics_Factory(Provider<A> aProvider, Provider<B> bProvider, Provider<C> cProvider, Provider<D> dProvider, Provider<E> eProvider) { + assert aProvider != null; + this.aProvider = aProvider; + assert bProvider != null; + this.bProvider = bProvider; + assert cProvider != null; + this.cProvider = cProvider; + assert dProvider != null; + this.dProvider = dProvider; + assert eProvider != null; + this.eProvider = eProvider; + } + + @Override + public BoundedGenerics<A, B, C, D, E> get() { + return new BoundedGenerics<A, B, C, D, E>(aProvider.get(), bProvider.get(), cProvider.get(), dProvider.get(), eProvider.get()); + } + + public static <A extends Number & Comparable<? super A>, B extends List<? extends CharSequence>, C extends List<? super String>, D extends A, E extends Iterable<D>> Factory<BoundedGenerics<A, B, C, D, E>> create(Provider<A> aProvider, Provider<B> bProvider, Provider<C> cProvider, Provider<D> dProvider, Provider<E> eProvider) { + return new BoundedGenerics_Factory<A, B, C, D, E>(aProvider, bProvider, cProvider, dProvider, eProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildDoubleModule_ProvideDoubleFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildDoubleModule_ProvideDoubleFactory.java new file mode 100644 index 000000000..093c0048f --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildDoubleModule_ProvideDoubleFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ChildDoubleModule_ProvideDoubleFactory implements Factory<Double> { + private final ChildDoubleModule module; + + public ChildDoubleModule_ProvideDoubleFactory(ChildDoubleModule module) { + assert module != null; + this.module = module; + } + + @Override + public Double get() { + Double provided = module.provideDouble(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Double> create(ChildDoubleModule module) { + return new ChildDoubleModule_ProvideDoubleFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildDoubleModule_ProvideListOfDoubleFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildDoubleModule_ProvideListOfDoubleFactory.java new file mode 100644 index 000000000..d75e8778b --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildDoubleModule_ProvideListOfDoubleFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.List; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ChildDoubleModule_ProvideListOfDoubleFactory implements Factory<List<Double>> { + private final ChildDoubleModule module; + + public ChildDoubleModule_ProvideListOfDoubleFactory(ChildDoubleModule module) { + assert module != null; + this.module = module; + } + + @Override + public List<Double> get() { + List<Double> provided = module.provideListOfDouble(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<List<Double>> create(ChildDoubleModule module) { + return new ChildDoubleModule_ProvideListOfDoubleFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildIntegerModule_ProvideIntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildIntegerModule_ProvideIntegerFactory.java new file mode 100644 index 000000000..607eafa1e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildIntegerModule_ProvideIntegerFactory.java @@ -0,0 +1,28 @@ +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("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Integer> create(ChildIntegerModule module) { + return new ChildIntegerModule_ProvideIntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildIntegerModule_ProvideListOfIntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildIntegerModule_ProvideListOfIntegerFactory.java new file mode 100644 index 000000000..23ba97f49 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ChildIntegerModule_ProvideListOfIntegerFactory.java @@ -0,0 +1,29 @@ +package test; + +import dagger.internal.Factory; +import java.util.List; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ChildIntegerModule_ProvideListOfIntegerFactory implements Factory<List<Integer>> { + private final ChildIntegerModule module; + + public ChildIntegerModule_ProvideListOfIntegerFactory(ChildIntegerModule module) { + assert module != null; + this.module = module; + } + + @Override + public List<Integer> get() { + List<Integer> provided = module.provideListOfInteger(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<List<Integer>> create(ChildIntegerModule module) { + return new ChildIntegerModule_ProvideListOfIntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ComplexGenerics_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ComplexGenerics_Factory.java new file mode 100644 index 000000000..60d33693d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ComplexGenerics_Factory.java @@ -0,0 +1,38 @@ +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 ComplexGenerics_Factory implements Factory<ComplexGenerics> { + private final Provider<Generic2<Generic<A>>> g2gaAndG2gaLazyProvider; + private final Provider<Generic2<Generic<B>>> g2gbAndG2gbLazyProvider; + private final Provider<Generic2<A>> g2aProvider; + private final Provider<Generic<Generic2<A>>> gg2aProvider; + private final Provider<Generic<Generic2<B>>> gg2bProvider; + + public ComplexGenerics_Factory(Provider<Generic2<Generic<A>>> g2gaAndG2gaLazyProvider, Provider<Generic2<Generic<B>>> g2gbAndG2gbLazyProvider, Provider<Generic2<A>> g2aProvider, Provider<Generic<Generic2<A>>> gg2aProvider, Provider<Generic<Generic2<B>>> gg2bProvider) { + assert g2gaAndG2gaLazyProvider != null; + this.g2gaAndG2gaLazyProvider = g2gaAndG2gaLazyProvider; + assert g2gbAndG2gbLazyProvider != null; + this.g2gbAndG2gbLazyProvider = g2gbAndG2gbLazyProvider; + assert g2aProvider != null; + this.g2aProvider = g2aProvider; + assert gg2aProvider != null; + this.gg2aProvider = gg2aProvider; + assert gg2bProvider != null; + this.gg2bProvider = gg2bProvider; + } + + @Override + public ComplexGenerics get() { + return new ComplexGenerics(g2gaAndG2gaLazyProvider.get(), DoubleCheckLazy.create(g2gaAndG2gaLazyProvider), g2gaAndG2gaLazyProvider, g2gbAndG2gbLazyProvider.get(), DoubleCheckLazy.create(g2gbAndG2gbLazyProvider), g2gbAndG2gbLazyProvider, g2aProvider.get(), gg2aProvider.get(), gg2bProvider.get()); + } + + public static Factory<ComplexGenerics> create(Provider<Generic2<Generic<A>>> g2gaAndG2gaLazyProvider, Provider<Generic2<Generic<B>>> g2gbAndG2gbLazyProvider, Provider<Generic2<A>> g2aProvider, Provider<Generic<Generic2<A>>> gg2aProvider, Provider<Generic<Generic2<B>>> gg2bProvider) { + return new ComplexGenerics_Factory(g2gaAndG2gaLazyProvider, g2gbAndG2gbLazyProvider, g2aProvider, gg2aProvider, gg2bProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBasicAbstractClassComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBasicAbstractClassComponent.java new file mode 100644 index 000000000..2aaa08650 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBasicAbstractClassComponent.java @@ -0,0 +1,341 @@ +package test; + +import dagger.Lazy; +import dagger.MembersInjector; +import dagger.internal.DoubleCheckLazy; +import dagger.internal.MembersInjectors; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.sub.OtherThing; +import test.sub.OtherThing_Factory; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerBasicAbstractClassComponent extends BasicAbstractClassComponent { + private Provider<OtherThing> otherThingProvider; + private Provider<Thing> thingProvider; + private MembersInjector<InjectedThing> injectedThingMembersInjector; + private Provider<InjectedThing> injectedThingProvider; + private MembersInjector<AbstractMembersInjectingBaseClass> abstractMembersInjectingBaseClassMembersInjector; + private MembersInjector<AbstractMiddleClassWithoutMembers> abstractMiddleClassWithoutMembersMembersInjector; + private MembersInjector<TypeWithInheritedMembersInjection> typeWithInheritedMembersInjectionMembersInjector; + private Provider<TypeWithInheritedMembersInjection> typeWithInheritedMembersInjectionProvider; + + private DaggerBasicAbstractClassComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static BasicAbstractClassComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.otherThingProvider = OtherThing_Factory.create(PrimitivesModule_ProvideIntFactory.create()); + this.thingProvider = Thing_Factory.create(otherThingProvider); + this.injectedThingMembersInjector = InjectedThing_MembersInjector.create(PrimitivesModule_ProvideByteFactory.create(), PrimitivesModule_ProvideCharFactory.create(), PrimitivesModule_ProvideShortFactory.create(), PrimitivesModule_ProvideIntFactory.create(), PrimitivesModule_ProvideLongFactory.create(), PrimitivesModule_ProvideBooleanFactory.create(), PrimitivesModule_ProvideFloatFactory.create(), PrimitivesModule_BoundDoubleFactory.create(), PrimitivesModule_ProvideByteArrayFactory.create(), PrimitivesModule_ProvideCharArrayFactory.create(), PrimitivesModule_ProvideShortArrayFactory.create(), PrimitivesModule_ProvideIntArrayFactory.create(), PrimitivesModule_ProvideLongArrayFactory.create(), PrimitivesModule_ProvideBooleanArrayFactory.create(), PrimitivesModule_ProvideFloatArrayFactory.create(), PrimitivesModule_BoundDoubleArrayFactory.create(), thingProvider, (MembersInjector) MembersInjectors.noOp()); + this.injectedThingProvider = InjectedThing_Factory.create(injectedThingMembersInjector, PrimitivesModule_ProvideByteFactory.create(), PrimitivesModule_ProvideCharFactory.create(), PrimitivesModule_ProvideShortFactory.create(), PrimitivesModule_ProvideIntFactory.create(), PrimitivesModule_ProvideLongFactory.create(), PrimitivesModule_ProvideBooleanFactory.create(), PrimitivesModule_ProvideFloatFactory.create(), PrimitivesModule_BoundDoubleFactory.create(), PrimitivesModule_ProvideByteArrayFactory.create(), PrimitivesModule_ProvideCharArrayFactory.create(), PrimitivesModule_ProvideShortArrayFactory.create(), PrimitivesModule_ProvideIntArrayFactory.create(), PrimitivesModule_ProvideLongArrayFactory.create(), PrimitivesModule_ProvideBooleanArrayFactory.create(), PrimitivesModule_ProvideFloatArrayFactory.create(), PrimitivesModule_BoundDoubleArrayFactory.create(), thingProvider, (MembersInjector) MembersInjectors.noOp()); + this.abstractMembersInjectingBaseClassMembersInjector = AbstractMembersInjectingBaseClass_MembersInjector.create(thingProvider); + this.abstractMiddleClassWithoutMembersMembersInjector = MembersInjectors.delegatingTo(abstractMembersInjectingBaseClassMembersInjector); + this.typeWithInheritedMembersInjectionMembersInjector = MembersInjectors.delegatingTo(abstractMiddleClassWithoutMembersMembersInjector); + this.typeWithInheritedMembersInjectionProvider = TypeWithInheritedMembersInjection_Factory.create(typeWithInheritedMembersInjectionMembersInjector); + } + + @Override + public Thing instance() { + return thingProvider.get(); + } + + @Override + public Provider<Thing> provider() { + return thingProvider; + } + + @Override + public Lazy<Thing> lazy() { + return DoubleCheckLazy.create(thingProvider); + } + + @Override + public MembersInjector<Thing> membersInjector() { + return MembersInjectors.noOp(); + } + + @Override + public void injectMembers(Thing t) { + MembersInjectors.noOp().injectMembers(t); + } + + @Override + public Thing injectMembersAndReturn(Thing t) { + MembersInjectors.noOp().injectMembers(t); + return t; + } + + @Override + public byte getByte() { + return PrimitivesModule_ProvideByteFactory.create().get(); + } + + @Override + public char getChar() { + return PrimitivesModule_ProvideCharFactory.create().get(); + } + + @Override + public short getShort() { + return PrimitivesModule_ProvideShortFactory.create().get(); + } + + @Override + public int getInt() { + return PrimitivesModule_ProvideIntFactory.create().get(); + } + + @Override + public long getLong() { + return PrimitivesModule_ProvideLongFactory.create().get(); + } + + @Override + public boolean getBoolean() { + return PrimitivesModule_ProvideBooleanFactory.create().get(); + } + + @Override + public float getFloat() { + return PrimitivesModule_ProvideFloatFactory.create().get(); + } + + @Override + public double getDouble() { + return PrimitivesModule_BoundDoubleFactory.create().get(); + } + + @Override + public Byte getBoxedByte() { + return PrimitivesModule_ProvideByteFactory.create().get(); + } + + @Override + public Character getBoxedChar() { + return PrimitivesModule_ProvideCharFactory.create().get(); + } + + @Override + public Short getBoxedShort() { + return PrimitivesModule_ProvideShortFactory.create().get(); + } + + @Override + public Integer getBoxedInt() { + return PrimitivesModule_ProvideIntFactory.create().get(); + } + + @Override + public Long getBoxedLong() { + return PrimitivesModule_ProvideLongFactory.create().get(); + } + + @Override + public Boolean getBoxedBoolean() { + return PrimitivesModule_ProvideBooleanFactory.create().get(); + } + + @Override + public Float getBoxedFloat() { + return PrimitivesModule_ProvideFloatFactory.create().get(); + } + + @Override + public Double getBoxedDouble() { + return PrimitivesModule_BoundDoubleFactory.create().get(); + } + + @Override + public Provider<Byte> getByteProvider() { + return PrimitivesModule_ProvideByteFactory.create(); + } + + @Override + public Provider<Character> getCharProvider() { + return PrimitivesModule_ProvideCharFactory.create(); + } + + @Override + public Provider<Short> getShortProvider() { + return PrimitivesModule_ProvideShortFactory.create(); + } + + @Override + public Provider<Integer> getIntProvider() { + return PrimitivesModule_ProvideIntFactory.create(); + } + + @Override + public Provider<Long> getLongProvider() { + return PrimitivesModule_ProvideLongFactory.create(); + } + + @Override + public Provider<Boolean> getBooleanProvider() { + return PrimitivesModule_ProvideBooleanFactory.create(); + } + + @Override + public Provider<Float> getFloatProvider() { + return PrimitivesModule_ProvideFloatFactory.create(); + } + + @Override + public Provider<Double> getDoubleProvider() { + return PrimitivesModule_BoundDoubleFactory.create(); + } + + @Override + public byte[] getByteArray() { + return PrimitivesModule_ProvideByteArrayFactory.create().get(); + } + + @Override + public char[] getCharArray() { + return PrimitivesModule_ProvideCharArrayFactory.create().get(); + } + + @Override + public short[] getShortArray() { + return PrimitivesModule_ProvideShortArrayFactory.create().get(); + } + + @Override + public int[] getIntArray() { + return PrimitivesModule_ProvideIntArrayFactory.create().get(); + } + + @Override + public long[] getLongArray() { + return PrimitivesModule_ProvideLongArrayFactory.create().get(); + } + + @Override + public boolean[] getBooleanArray() { + return PrimitivesModule_ProvideBooleanArrayFactory.create().get(); + } + + @Override + public float[] getFloatArray() { + return PrimitivesModule_ProvideFloatArrayFactory.create().get(); + } + + @Override + public double[] getDoubleArray() { + return PrimitivesModule_BoundDoubleArrayFactory.create().get(); + } + + @Override + public Provider<byte[]> getByteArrayProvider() { + return PrimitivesModule_ProvideByteArrayFactory.create(); + } + + @Override + public Provider<char[]> getCharArrayProvider() { + return PrimitivesModule_ProvideCharArrayFactory.create(); + } + + @Override + public Provider<short[]> getShortArrayProvider() { + return PrimitivesModule_ProvideShortArrayFactory.create(); + } + + @Override + public Provider<int[]> getIntArrayProvider() { + return PrimitivesModule_ProvideIntArrayFactory.create(); + } + + @Override + public Provider<long[]> getLongArrayProvider() { + return PrimitivesModule_ProvideLongArrayFactory.create(); + } + + @Override + public Provider<boolean[]> getBooleanArrayProvider() { + return PrimitivesModule_ProvideBooleanArrayFactory.create(); + } + + @Override + public Provider<float[]> getFloatArrayProvider() { + return PrimitivesModule_ProvideFloatArrayFactory.create(); + } + + @Override + public Provider<double[]> getDoubleArrayProvider() { + return PrimitivesModule_BoundDoubleArrayFactory.create(); + } + + @Override + public Object noOpMembersInjection(Object obviouslyDoesNotHaveMembersToInject) { + MembersInjectors.noOp().injectMembers(obviouslyDoesNotHaveMembersToInject); + return obviouslyDoesNotHaveMembersToInject; + } + + @Override + public Thing thing() { + return thingProvider.get(); + } + + @Override + public InjectedThing injectedThing() { + return injectedThingProvider.get(); + } + + @Override + public Provider<InjectedThing> injectedThingProvider() { + return injectedThingProvider; + } + + @Override + public Lazy<InjectedThing> lazyInjectedThing() { + return DoubleCheckLazy.create(injectedThingProvider); + } + + @Override + public MembersInjector<InjectedThing> injectedThingMembersInjector() { + return injectedThingMembersInjector; + } + + @Override + public TypeWithInheritedMembersInjection typeWithInheritedMembersInjection() { + return typeWithInheritedMembersInjectionProvider.get(); + } + + @Override + public MembersInjector<TypeWithInheritedMembersInjection> typeWithInheritedMembersInjectionMembersInjector() { + return typeWithInheritedMembersInjectionMembersInjector; + } + + public static final class Builder { + private PrimitivesModule primitivesModule; + + private Builder() { + } + + public BasicAbstractClassComponent build() { + if (primitivesModule == null) { + this.primitivesModule = new PrimitivesModule(); + } + return new DaggerBasicAbstractClassComponent(this); + } + + public Builder primitivesModule(PrimitivesModule primitivesModule) { + if (primitivesModule == null) { + throw new NullPointerException("primitivesModule"); + } + this.primitivesModule = primitivesModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBasicComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBasicComponent.java new file mode 100644 index 000000000..a72440a19 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBasicComponent.java @@ -0,0 +1,341 @@ +package test; + +import dagger.Lazy; +import dagger.MembersInjector; +import dagger.internal.DoubleCheckLazy; +import dagger.internal.MembersInjectors; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.sub.OtherThing; +import test.sub.OtherThing_Factory; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerBasicComponent implements BasicComponent { + private Provider<OtherThing> otherThingProvider; + private Provider<Thing> thingProvider; + private MembersInjector<InjectedThing> injectedThingMembersInjector; + private Provider<InjectedThing> injectedThingProvider; + private MembersInjector<AbstractMembersInjectingBaseClass> abstractMembersInjectingBaseClassMembersInjector; + private MembersInjector<AbstractMiddleClassWithoutMembers> abstractMiddleClassWithoutMembersMembersInjector; + private MembersInjector<TypeWithInheritedMembersInjection> typeWithInheritedMembersInjectionMembersInjector; + private Provider<TypeWithInheritedMembersInjection> typeWithInheritedMembersInjectionProvider; + + private DaggerBasicComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static BasicComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.otherThingProvider = OtherThing_Factory.create(PrimitivesModule_ProvideIntFactory.create()); + this.thingProvider = Thing_Factory.create(otherThingProvider); + this.injectedThingMembersInjector = InjectedThing_MembersInjector.create(PrimitivesModule_ProvideByteFactory.create(), PrimitivesModule_ProvideCharFactory.create(), PrimitivesModule_ProvideShortFactory.create(), PrimitivesModule_ProvideIntFactory.create(), PrimitivesModule_ProvideLongFactory.create(), PrimitivesModule_ProvideBooleanFactory.create(), PrimitivesModule_ProvideFloatFactory.create(), PrimitivesModule_BoundDoubleFactory.create(), PrimitivesModule_ProvideByteArrayFactory.create(), PrimitivesModule_ProvideCharArrayFactory.create(), PrimitivesModule_ProvideShortArrayFactory.create(), PrimitivesModule_ProvideIntArrayFactory.create(), PrimitivesModule_ProvideLongArrayFactory.create(), PrimitivesModule_ProvideBooleanArrayFactory.create(), PrimitivesModule_ProvideFloatArrayFactory.create(), PrimitivesModule_BoundDoubleArrayFactory.create(), thingProvider, (MembersInjector) MembersInjectors.noOp()); + this.injectedThingProvider = InjectedThing_Factory.create(injectedThingMembersInjector, PrimitivesModule_ProvideByteFactory.create(), PrimitivesModule_ProvideCharFactory.create(), PrimitivesModule_ProvideShortFactory.create(), PrimitivesModule_ProvideIntFactory.create(), PrimitivesModule_ProvideLongFactory.create(), PrimitivesModule_ProvideBooleanFactory.create(), PrimitivesModule_ProvideFloatFactory.create(), PrimitivesModule_BoundDoubleFactory.create(), PrimitivesModule_ProvideByteArrayFactory.create(), PrimitivesModule_ProvideCharArrayFactory.create(), PrimitivesModule_ProvideShortArrayFactory.create(), PrimitivesModule_ProvideIntArrayFactory.create(), PrimitivesModule_ProvideLongArrayFactory.create(), PrimitivesModule_ProvideBooleanArrayFactory.create(), PrimitivesModule_ProvideFloatArrayFactory.create(), PrimitivesModule_BoundDoubleArrayFactory.create(), thingProvider, (MembersInjector) MembersInjectors.noOp()); + this.abstractMembersInjectingBaseClassMembersInjector = AbstractMembersInjectingBaseClass_MembersInjector.create(thingProvider); + this.abstractMiddleClassWithoutMembersMembersInjector = MembersInjectors.delegatingTo(abstractMembersInjectingBaseClassMembersInjector); + this.typeWithInheritedMembersInjectionMembersInjector = MembersInjectors.delegatingTo(abstractMiddleClassWithoutMembersMembersInjector); + this.typeWithInheritedMembersInjectionProvider = TypeWithInheritedMembersInjection_Factory.create(typeWithInheritedMembersInjectionMembersInjector); + } + + @Override + public Thing instance() { + return thingProvider.get(); + } + + @Override + public Provider<Thing> provider() { + return thingProvider; + } + + @Override + public Lazy<Thing> lazy() { + return DoubleCheckLazy.create(thingProvider); + } + + @Override + public MembersInjector<Thing> membersInjector() { + return MembersInjectors.noOp(); + } + + @Override + public void injectMembers(Thing t) { + MembersInjectors.noOp().injectMembers(t); + } + + @Override + public Thing injectMembersAndReturn(Thing t) { + MembersInjectors.noOp().injectMembers(t); + return t; + } + + @Override + public byte getByte() { + return PrimitivesModule_ProvideByteFactory.create().get(); + } + + @Override + public char getChar() { + return PrimitivesModule_ProvideCharFactory.create().get(); + } + + @Override + public short getShort() { + return PrimitivesModule_ProvideShortFactory.create().get(); + } + + @Override + public int getInt() { + return PrimitivesModule_ProvideIntFactory.create().get(); + } + + @Override + public long getLong() { + return PrimitivesModule_ProvideLongFactory.create().get(); + } + + @Override + public boolean getBoolean() { + return PrimitivesModule_ProvideBooleanFactory.create().get(); + } + + @Override + public float getFloat() { + return PrimitivesModule_ProvideFloatFactory.create().get(); + } + + @Override + public double getDouble() { + return PrimitivesModule_BoundDoubleFactory.create().get(); + } + + @Override + public Byte getBoxedByte() { + return PrimitivesModule_ProvideByteFactory.create().get(); + } + + @Override + public Character getBoxedChar() { + return PrimitivesModule_ProvideCharFactory.create().get(); + } + + @Override + public Short getBoxedShort() { + return PrimitivesModule_ProvideShortFactory.create().get(); + } + + @Override + public Integer getBoxedInt() { + return PrimitivesModule_ProvideIntFactory.create().get(); + } + + @Override + public Long getBoxedLong() { + return PrimitivesModule_ProvideLongFactory.create().get(); + } + + @Override + public Boolean getBoxedBoolean() { + return PrimitivesModule_ProvideBooleanFactory.create().get(); + } + + @Override + public Float getBoxedFloat() { + return PrimitivesModule_ProvideFloatFactory.create().get(); + } + + @Override + public Double getBoxedDouble() { + return PrimitivesModule_BoundDoubleFactory.create().get(); + } + + @Override + public Provider<Byte> getByteProvider() { + return PrimitivesModule_ProvideByteFactory.create(); + } + + @Override + public Provider<Character> getCharProvider() { + return PrimitivesModule_ProvideCharFactory.create(); + } + + @Override + public Provider<Short> getShortProvider() { + return PrimitivesModule_ProvideShortFactory.create(); + } + + @Override + public Provider<Integer> getIntProvider() { + return PrimitivesModule_ProvideIntFactory.create(); + } + + @Override + public Provider<Long> getLongProvider() { + return PrimitivesModule_ProvideLongFactory.create(); + } + + @Override + public Provider<Boolean> getBooleanProvider() { + return PrimitivesModule_ProvideBooleanFactory.create(); + } + + @Override + public Provider<Float> getFloatProvider() { + return PrimitivesModule_ProvideFloatFactory.create(); + } + + @Override + public Provider<Double> getDoubleProvider() { + return PrimitivesModule_BoundDoubleFactory.create(); + } + + @Override + public byte[] getByteArray() { + return PrimitivesModule_ProvideByteArrayFactory.create().get(); + } + + @Override + public char[] getCharArray() { + return PrimitivesModule_ProvideCharArrayFactory.create().get(); + } + + @Override + public short[] getShortArray() { + return PrimitivesModule_ProvideShortArrayFactory.create().get(); + } + + @Override + public int[] getIntArray() { + return PrimitivesModule_ProvideIntArrayFactory.create().get(); + } + + @Override + public long[] getLongArray() { + return PrimitivesModule_ProvideLongArrayFactory.create().get(); + } + + @Override + public boolean[] getBooleanArray() { + return PrimitivesModule_ProvideBooleanArrayFactory.create().get(); + } + + @Override + public float[] getFloatArray() { + return PrimitivesModule_ProvideFloatArrayFactory.create().get(); + } + + @Override + public double[] getDoubleArray() { + return PrimitivesModule_BoundDoubleArrayFactory.create().get(); + } + + @Override + public Provider<byte[]> getByteArrayProvider() { + return PrimitivesModule_ProvideByteArrayFactory.create(); + } + + @Override + public Provider<char[]> getCharArrayProvider() { + return PrimitivesModule_ProvideCharArrayFactory.create(); + } + + @Override + public Provider<short[]> getShortArrayProvider() { + return PrimitivesModule_ProvideShortArrayFactory.create(); + } + + @Override + public Provider<int[]> getIntArrayProvider() { + return PrimitivesModule_ProvideIntArrayFactory.create(); + } + + @Override + public Provider<long[]> getLongArrayProvider() { + return PrimitivesModule_ProvideLongArrayFactory.create(); + } + + @Override + public Provider<boolean[]> getBooleanArrayProvider() { + return PrimitivesModule_ProvideBooleanArrayFactory.create(); + } + + @Override + public Provider<float[]> getFloatArrayProvider() { + return PrimitivesModule_ProvideFloatArrayFactory.create(); + } + + @Override + public Provider<double[]> getDoubleArrayProvider() { + return PrimitivesModule_BoundDoubleArrayFactory.create(); + } + + @Override + public Object noOpMembersInjection(Object obviouslyDoesNotHaveMembersToInject) { + MembersInjectors.noOp().injectMembers(obviouslyDoesNotHaveMembersToInject); + return obviouslyDoesNotHaveMembersToInject; + } + + @Override + public Thing thing() { + return thingProvider.get(); + } + + @Override + public InjectedThing injectedThing() { + return injectedThingProvider.get(); + } + + @Override + public Provider<InjectedThing> injectedThingProvider() { + return injectedThingProvider; + } + + @Override + public Lazy<InjectedThing> lazyInjectedThing() { + return DoubleCheckLazy.create(injectedThingProvider); + } + + @Override + public MembersInjector<InjectedThing> injectedThingMembersInjector() { + return injectedThingMembersInjector; + } + + @Override + public TypeWithInheritedMembersInjection typeWithInheritedMembersInjection() { + return typeWithInheritedMembersInjectionProvider.get(); + } + + @Override + public MembersInjector<TypeWithInheritedMembersInjection> typeWithInheritedMembersInjectionMembersInjector() { + return typeWithInheritedMembersInjectionMembersInjector; + } + + public static final class Builder { + private PrimitivesModule primitivesModule; + + private Builder() { + } + + public BasicComponent build() { + if (primitivesModule == null) { + this.primitivesModule = new PrimitivesModule(); + } + return new DaggerBasicComponent(this); + } + + public Builder primitivesModule(PrimitivesModule primitivesModule) { + if (primitivesModule == null) { + throw new NullPointerException("primitivesModule"); + } + this.primitivesModule = primitivesModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBoundedGenericComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBoundedGenericComponent.java new file mode 100644 index 000000000..4ce853ed6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerBoundedGenericComponent.java @@ -0,0 +1,81 @@ +package test; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerBoundedGenericComponent implements BoundedGenericComponent { + private Provider<Integer> provideIntegerProvider; + private Provider<ArrayList<String>> provideArrayListStringProvider; + private Provider<LinkedList<CharSequence>> provideLinkedListCharSeqProvider; + private Provider<List<Integer>> provideListOfIntegerProvider; + private Provider<BoundedGenerics<Integer, ArrayList<String>, LinkedList<CharSequence>, Integer, List<Integer>>> boundedGenericsProvider; + private Provider<Double> provideDoubleProvider; + private Provider<LinkedList<String>> provideLinkedListStringProvider; + private Provider<LinkedList<Comparable<String>>> provideArrayListOfComparableStringProvider; + private Provider<Set<Double>> provideSetOfDoubleProvider; + private Provider<BoundedGenerics<Double, LinkedList<String>, LinkedList<Comparable<String>>, Double, Set<Double>>> boundedGenericsProvider1; + + private DaggerBoundedGenericComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static BoundedGenericComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideIntegerProvider = BoundedGenericModule_ProvideIntegerFactory.create(builder.boundedGenericModule); + this.provideArrayListStringProvider = BoundedGenericModule_ProvideArrayListStringFactory.create(builder.boundedGenericModule); + this.provideLinkedListCharSeqProvider = BoundedGenericModule_ProvideLinkedListCharSeqFactory.create(builder.boundedGenericModule); + this.provideListOfIntegerProvider = BoundedGenericModule_ProvideListOfIntegerFactory.create(builder.boundedGenericModule); + this.boundedGenericsProvider = BoundedGenerics_Factory.create(provideIntegerProvider, provideArrayListStringProvider, provideLinkedListCharSeqProvider, provideIntegerProvider, provideListOfIntegerProvider); + this.provideDoubleProvider = BoundedGenericModule_ProvideDoubleFactory.create(builder.boundedGenericModule); + this.provideLinkedListStringProvider = BoundedGenericModule_ProvideLinkedListStringFactory.create(builder.boundedGenericModule); + this.provideArrayListOfComparableStringProvider = BoundedGenericModule_ProvideArrayListOfComparableStringFactory.create(builder.boundedGenericModule); + this.provideSetOfDoubleProvider = BoundedGenericModule_ProvideSetOfDoubleFactory.create(builder.boundedGenericModule); + this.boundedGenericsProvider1 = BoundedGenerics_Factory.create(provideDoubleProvider, provideLinkedListStringProvider, provideArrayListOfComparableStringProvider, provideDoubleProvider, provideSetOfDoubleProvider); + } + + @Override + public BoundedGenerics<Integer, ArrayList<String>, LinkedList<CharSequence>, Integer, List<Integer>> bounds1() { + return boundedGenericsProvider.get(); + } + + @Override + public BoundedGenerics<Double, LinkedList<String>, LinkedList<Comparable<String>>, Double, Set<Double>> bounds2() { + return boundedGenericsProvider1.get(); + } + + public static final class Builder { + private BoundedGenericModule boundedGenericModule; + + private Builder() { + } + + public BoundedGenericComponent build() { + if (boundedGenericModule == null) { + this.boundedGenericModule = new BoundedGenericModule(); + } + return new DaggerBoundedGenericComponent(this); + } + + public Builder boundedGenericModule(BoundedGenericModule boundedGenericModule) { + if (boundedGenericModule == null) { + throw new NullPointerException("boundedGenericModule"); + } + this.boundedGenericModule = boundedGenericModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerComponentDependsOnGeneratedCode.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerComponentDependsOnGeneratedCode.java new file mode 100644 index 000000000..848f7b6dd --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerComponentDependsOnGeneratedCode.java @@ -0,0 +1,41 @@ +package test; + +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerComponentDependsOnGeneratedCode implements ComponentDependsOnGeneratedCode { + private Provider<NeedsFactory> needsFactoryProvider; + + private DaggerComponentDependsOnGeneratedCode(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static ComponentDependsOnGeneratedCode create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.needsFactoryProvider = NeedsFactory_Factory.create(NeedsFactory_SomethingFactory_Factory.create()); + } + + @Override + public NeedsFactory needsFactory() { + return needsFactoryProvider.get(); + } + + public static final class Builder { + private Builder() { + } + + public ComponentDependsOnGeneratedCode build() { + return new DaggerComponentDependsOnGeneratedCode(this); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerGenericComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerGenericComponent.java new file mode 100644 index 000000000..3e7848939 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerGenericComponent.java @@ -0,0 +1,193 @@ +package test; + +import dagger.MembersInjector; +import dagger.internal.MembersInjectors; +import java.util.List; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.sub.DaggerGenericComponent_PackageProxy; +import test.sub.Exposed; +import test.sub.Exposed_Factory; +import test.sub.Exposed_MembersInjector; +import test.sub.OtherThing; +import test.sub.OtherThing_Factory; +import test.sub.PackagePrivateContainer$PublicEnclosed_Factory; +import test.sub.PackagePrivate_Factory; +import test.sub.PublicSubclass; +import test.sub.PublicSubclass_Factory; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerGenericComponent implements GenericComponent { + private Provider<Generic<A>> genericProvider; + private Provider<ReferencesGeneric> referencesGenericProvider; + private Provider<Integer> provideIntegerProvider; + private Provider<OtherThing> otherThingProvider; + private Provider<Thing> thingProvider; + private Provider<GenericDoubleReferences<A>> genericDoubleReferencesProvider; + private Provider<GenericDoubleReferences<B>> genericDoubleReferencesProvider1; + private Provider<Generic2<Generic<A>>> generic2Provider; + private Provider<Generic<B>> genericProvider1; + private Provider<Generic2<Generic<B>>> generic2Provider1; + private Provider<Generic2<A>> generic2Provider2; + private Provider<Generic<Generic2<A>>> genericProvider2; + private Provider<Generic2<B>> generic2Provider3; + private Provider<Generic<Generic2<B>>> genericProvider3; + private Provider<ComplexGenerics> complexGenericsProvider; + private MembersInjector<GenericParent<A, B>> genericParentMembersInjector; + private MembersInjector<GenericChild<A>> genericChildMembersInjector; + private MembersInjector<GenericParent<B, B>> genericParentMembersInjector1; + private MembersInjector<GenericChild<B>> genericChildMembersInjector1; + private final DaggerGenericComponent_PackageProxy test_sub_Proxy = new DaggerGenericComponent_PackageProxy(); + private MembersInjector<Exposed> exposedMembersInjector; + private Provider<Exposed> exposedProvider; + private Provider<PublicSubclass> publicSubclassProvider; + private Provider<List<Integer>> provideListOfIntegerProvider; + private Provider<Iterable<Integer>> provideIterableOfAWithCProvider; + private Provider<Double> provideDoubleProvider; + private Provider<List<Double>> provideListOfDoubleProvider; + private Provider<Iterable<Double>> provideIterableOfAWithCProvider1; + + private DaggerGenericComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static GenericComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.genericProvider = Generic_Factory.create(A_Factory.create()); + this.referencesGenericProvider = ReferencesGeneric_Factory.create(genericProvider); + this.provideIntegerProvider = ChildIntegerModule_ProvideIntegerFactory.create(builder.childIntegerModule); + this.otherThingProvider = OtherThing_Factory.create(provideIntegerProvider); + this.thingProvider = Thing_Factory.create(otherThingProvider); + this.genericDoubleReferencesProvider = GenericDoubleReferences_Factory.create(A_Factory.create(), thingProvider); + this.genericDoubleReferencesProvider1 = GenericDoubleReferences_Factory.create(B_Factory.create(), thingProvider); + this.generic2Provider = Generic2_Factory.create(genericProvider); + this.genericProvider1 = Generic_Factory.create(B_Factory.create()); + this.generic2Provider1 = Generic2_Factory.create(genericProvider1); + this.generic2Provider2 = Generic2_Factory.create(A_Factory.create()); + this.genericProvider2 = Generic_Factory.create(generic2Provider2); + this.generic2Provider3 = Generic2_Factory.create(B_Factory.create()); + this.genericProvider3 = Generic_Factory.create(generic2Provider3); + this.complexGenericsProvider = ComplexGenerics_Factory.create(generic2Provider, generic2Provider1, generic2Provider2, genericProvider2, genericProvider3); + this.genericParentMembersInjector = GenericParent_MembersInjector.create(A_Factory.create(), B_Factory.create(), B_Factory.create()); + this.genericChildMembersInjector = GenericChild_MembersInjector.create(genericParentMembersInjector, A_Factory.create(), A_Factory.create()); + this.genericParentMembersInjector1 = GenericParent_MembersInjector.create(B_Factory.create(), B_Factory.create(), B_Factory.create()); + this.genericChildMembersInjector1 = GenericChild_MembersInjector.create(genericParentMembersInjector1, A_Factory.create(), B_Factory.create()); + this.test_sub_Proxy.generic2Provider = Generic2_Factory.create(PackagePrivate_Factory.create()); + this.test_sub_Proxy.generic2Provider1 = Generic2_Factory.create(PackagePrivateContainer$PublicEnclosed_Factory.create()); + this.exposedMembersInjector = Exposed_MembersInjector.create(test_sub_Proxy.generic2Provider, test_sub_Proxy.generic2Provider1); + this.test_sub_Proxy.genericProvider = Generic_Factory.create(PackagePrivate_Factory.create()); + this.test_sub_Proxy.genericProvider1 = Generic_Factory.create(PackagePrivateContainer$PublicEnclosed_Factory.create()); + this.exposedProvider = Exposed_Factory.create(exposedMembersInjector, test_sub_Proxy.genericProvider, test_sub_Proxy.genericProvider1); + this.publicSubclassProvider = PublicSubclass_Factory.create((MembersInjector) MembersInjectors.noOp(), PackagePrivate_Factory.create()); + this.provideListOfIntegerProvider = ChildIntegerModule_ProvideListOfIntegerFactory.create(builder.childIntegerModule); + this.provideIterableOfAWithCProvider = ParentModule_ProvideIterableOfAWithCFactory.create(builder.childIntegerModule, provideIntegerProvider, provideListOfIntegerProvider); + this.provideDoubleProvider = ChildDoubleModule_ProvideDoubleFactory.create(builder.childDoubleModule); + this.provideListOfDoubleProvider = ChildDoubleModule_ProvideListOfDoubleFactory.create(builder.childDoubleModule); + this.provideIterableOfAWithCProvider1 = ParentModule_ProvideIterableOfAWithCFactory.create(builder.childDoubleModule, provideDoubleProvider, provideListOfDoubleProvider); + } + + @Override + public ReferencesGeneric referencesGeneric() { + return referencesGenericProvider.get(); + } + + @Override + public GenericDoubleReferences<A> doubleGenericA() { + return genericDoubleReferencesProvider.get(); + } + + @Override + public GenericDoubleReferences<B> doubleGenericB() { + return genericDoubleReferencesProvider1.get(); + } + + @Override + public ComplexGenerics complexGenerics() { + return complexGenericsProvider.get(); + } + + @Override + public GenericNoDeps<A> noDepsA() { + Provider<GenericNoDeps<A>> factory = GenericNoDeps_Factory.create(); + return factory.get(); + } + + @Override + public GenericNoDeps<B> noDepsB() { + Provider<GenericNoDeps<B>> factory = GenericNoDeps_Factory.create(); + return factory.get(); + } + + @Override + public void injectA(GenericChild<A> childA) { + genericChildMembersInjector.injectMembers(childA); + } + + @Override + public void injectB(GenericChild<B> childB) { + genericChildMembersInjector1.injectMembers(childB); + } + + @Override + public Exposed exposed() { + return exposedProvider.get(); + } + + @Override + public PublicSubclass publicSubclass() { + return publicSubclassProvider.get(); + } + + @Override + public Iterable<Integer> iterableInt() { + return provideIterableOfAWithCProvider.get(); + } + + @Override + public Iterable<Double> iterableDouble() { + return provideIterableOfAWithCProvider1.get(); + } + + public static final class Builder { + private ChildDoubleModule childDoubleModule; + private ChildIntegerModule childIntegerModule; + + private Builder() { + } + + public GenericComponent build() { + if (childDoubleModule == null) { + this.childDoubleModule = new ChildDoubleModule(); + } + if (childIntegerModule == null) { + this.childIntegerModule = new ChildIntegerModule(); + } + return new DaggerGenericComponent(this); + } + + public Builder childDoubleModule(ChildDoubleModule childDoubleModule) { + if (childDoubleModule == null) { + throw new NullPointerException("childDoubleModule"); + } + this.childDoubleModule = childDoubleModule; + return this; + } + + public Builder childIntegerModule(ChildIntegerModule childIntegerModule) { + if (childIntegerModule == null) { + throw new NullPointerException("childIntegerModule"); + } + this.childIntegerModule = childIntegerModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerMultibindingComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerMultibindingComponent.java new file mode 100644 index 000000000..563a6aaa6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerMultibindingComponent.java @@ -0,0 +1,299 @@ +package test; + +import dagger.internal.Factory; +import dagger.internal.MapFactory; +import dagger.internal.MapProviderFactory; +import dagger.internal.SetFactory; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.TestStringKey.NestedWrappedKey; +import test.sub.ContributionsModule; +import test.sub.ContributionsModule_ContributeAnIntFactory; +import test.sub.ContributionsModule_ContributeAnotherIntFactory; +import test.sub.ContributionsModule_ContributeSomeIntsFactory; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerMultibindingComponent implements MultibindingComponent { + private Provider<Double> doubleDependencyProvider; + private Provider<String> mapOfStringAndProviderOfStringContribution1; + private Provider<String> mapOfStringAndProviderOfStringContribution2; + private Provider<Map<String, Provider<String>>> mapOfStringAndProviderOfStringProvider; + private Provider<Map<String, String>> mapOfStringAndStringProvider; + private Provider<Set<String>> provideMapKeysProvider; + private Provider<Collection<String>> provideMapValuesProvider; + private Provider<Set<Integer>> setOfIntegerContribution1Provider; + private Provider<Set<Integer>> setOfIntegerContribution2Provider; + private Provider<Set<Integer>> setOfIntegerContribution3Provider; + private Provider<Set<Integer>> setOfIntegerContribution4Provider; + private Provider<Set<Integer>> setOfIntegerContribution5Provider; + private Provider<Set<Integer>> setOfIntegerProvider; + private Provider<String> mapOfNestedWrappedKeyAndProviderOfStringContribution1; + private Provider<String> mapOfNestedWrappedKeyAndProviderOfStringContribution2; + private Provider<Map<NestedWrappedKey, Provider<String>>> mapOfNestedWrappedKeyAndProviderOfStringProvider; + private Provider<Map<NestedWrappedKey, String>> mapOfNestedWrappedKeyAndStringProvider; + private Provider<String> mapOfClassOfAndProviderOfStringContribution1; + private Provider<String> mapOfClassOfAndProviderOfStringContribution2; + private Provider<Map<Class<? extends Number>, Provider<String>>> mapOfClassOfAndProviderOfStringProvider; + private Provider<Map<Class<? extends Number>, String>> mapOfClassOfAndStringProvider; + private Provider<String> mapOfClassOfAndProviderOfStringContribution11; + private Provider<String> mapOfClassOfAndProviderOfStringContribution21; + private Provider<Map<Class<?>, Provider<String>>> mapOfClassOfAndProviderOfStringProvider1; + private Provider<Map<Class<?>, String>> mapOfClassOfAndStringProvider1; + private Provider<String> mapOfLongAndProviderOfStringContribution1; + private Provider<Map<Long, Provider<String>>> mapOfLongAndProviderOfStringProvider; + private Provider<Map<Long, String>> mapOfLongAndStringProvider; + private Provider<String> mapOfIntegerAndProviderOfStringContribution1; + private Provider<Map<Integer, Provider<String>>> mapOfIntegerAndProviderOfStringProvider; + private Provider<Map<Integer, String>> mapOfIntegerAndStringProvider; + private Provider<String> mapOfShortAndProviderOfStringContribution1; + private Provider<Map<Short, Provider<String>>> mapOfShortAndProviderOfStringProvider; + private Provider<Map<Short, String>> mapOfShortAndStringProvider; + private Provider<String> mapOfByteAndProviderOfStringContribution1; + private Provider<Map<Byte, Provider<String>>> mapOfByteAndProviderOfStringProvider; + private Provider<Map<Byte, String>> mapOfByteAndStringProvider; + private Provider<String> mapOfBooleanAndProviderOfStringContribution1; + private Provider<Map<Boolean, Provider<String>>> mapOfBooleanAndProviderOfStringProvider; + private Provider<Map<Boolean, String>> mapOfBooleanAndStringProvider; + private Provider<String> mapOfCharacterAndProviderOfStringContribution1; + private Provider<String> mapOfCharacterAndProviderOfStringContribution2; + private Provider<Map<Character, Provider<String>>> mapOfCharacterAndProviderOfStringProvider; + private Provider<Map<Character, String>> mapOfCharacterAndStringProvider; + private Provider<String> mapOfTestStringKeyAndProviderOfStringContribution1; + private Provider<Map<TestStringKey, Provider<String>>> mapOfTestStringKeyAndProviderOfStringProvider; + private Provider<Map<TestStringKey, String>> mapOfTestStringKeyAndStringProvider; + private Provider<String> mapOfTestWrappedAnnotationKeyAndProviderOfStringContribution1; + private Provider<Map<TestWrappedAnnotationKey, Provider<String>>> mapOfTestWrappedAnnotationKeyAndProviderOfStringProvider; + private Provider<Map<TestWrappedAnnotationKey, String>> mapOfTestWrappedAnnotationKeyAndStringProvider; + + private DaggerMultibindingComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.doubleDependencyProvider = new Factory<Double>() { + private final MultibindingDependency multibindingDependency = builder.multibindingDependency; + @Override public Double get() { + Double provided = multibindingDependency.doubleDependency(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable component method"); + } + return provided; + } + }; + this.mapOfStringAndProviderOfStringContribution1 = MultibindingModule_ProvideFooKeyFactory.create(builder.multibindingModule, doubleDependencyProvider); + this.mapOfStringAndProviderOfStringContribution2 = MultibindingModule_ProvideBarKeyFactory.create(builder.multibindingModule); + this.mapOfStringAndProviderOfStringProvider = MapProviderFactory.<String, String>builder(2) + .put("foo", mapOfStringAndProviderOfStringContribution1) + .put("bar", mapOfStringAndProviderOfStringContribution2) + .build(); + this.mapOfStringAndStringProvider = MapFactory.create(mapOfStringAndProviderOfStringProvider); + this.provideMapKeysProvider = MultibindingModule_ProvideMapKeysFactory.create(builder.multibindingModule, mapOfStringAndProviderOfStringProvider); + this.provideMapValuesProvider = MultibindingModule_ProvideMapValuesFactory.create(builder.multibindingModule, mapOfStringAndStringProvider); + this.setOfIntegerContribution1Provider = MultibindingModule_ProvideFiveToSetFactory.create(builder.multibindingModule); + this.setOfIntegerContribution2Provider = MultibindingModule_ProvideSixToSetFactory.create(builder.multibindingModule); + this.setOfIntegerContribution3Provider = ContributionsModule_ContributeAnIntFactory.create(builder.contributionsModule, doubleDependencyProvider); + this.setOfIntegerContribution4Provider = ContributionsModule_ContributeAnotherIntFactory.create(builder.contributionsModule); + this.setOfIntegerContribution5Provider = ContributionsModule_ContributeSomeIntsFactory.create(builder.contributionsModule); + this.setOfIntegerProvider = SetFactory.create(setOfIntegerContribution1Provider, setOfIntegerContribution2Provider, setOfIntegerContribution3Provider, setOfIntegerContribution4Provider, setOfIntegerContribution5Provider); + this.mapOfNestedWrappedKeyAndProviderOfStringContribution1 = MultibindingModule_ValueForIntegerFactory.create(builder.multibindingModule); + this.mapOfNestedWrappedKeyAndProviderOfStringContribution2 = MultibindingModule_ValueForLongFactory.create(builder.multibindingModule); + this.mapOfNestedWrappedKeyAndProviderOfStringProvider = MapProviderFactory.<NestedWrappedKey, String>builder(2) + .put(TestStringKey$NestedWrappedKeyCreator.createNestedWrappedKey(Integer.class), mapOfNestedWrappedKeyAndProviderOfStringContribution1) + .put(TestStringKey$NestedWrappedKeyCreator.createNestedWrappedKey(Long.class), mapOfNestedWrappedKeyAndProviderOfStringContribution2) + .build(); + this.mapOfNestedWrappedKeyAndStringProvider = MapFactory.create(mapOfNestedWrappedKeyAndProviderOfStringProvider); + this.mapOfClassOfAndProviderOfStringContribution1 = MultibindingModule_ValueForNumberClassBigDecimalFactory.create(builder.multibindingModule); + this.mapOfClassOfAndProviderOfStringContribution2 = MultibindingModule_ValueForNumberClassBigIntegerFactory.create(builder.multibindingModule); + this.mapOfClassOfAndProviderOfStringProvider = MapProviderFactory.<Class<? extends Number>, String>builder(2) + .put(BigDecimal.class, mapOfClassOfAndProviderOfStringContribution1) + .put(BigInteger.class, mapOfClassOfAndProviderOfStringContribution2) + .build(); + this.mapOfClassOfAndStringProvider = MapFactory.create(mapOfClassOfAndProviderOfStringProvider); + this.mapOfClassOfAndProviderOfStringContribution11 = MultibindingModule_ValueForClassIntegerFactory.create(builder.multibindingModule); + this.mapOfClassOfAndProviderOfStringContribution21 = MultibindingModule_ValueForClassLongFactory.create(builder.multibindingModule); + this.mapOfClassOfAndProviderOfStringProvider1 = MapProviderFactory.<Class<?>, String>builder(2) + .put(Integer.class, mapOfClassOfAndProviderOfStringContribution11) + .put(Long.class, mapOfClassOfAndProviderOfStringContribution21) + .build(); + this.mapOfClassOfAndStringProvider1 = MapFactory.create(mapOfClassOfAndProviderOfStringProvider1); + this.mapOfLongAndProviderOfStringContribution1 = MultibindingModule_ValueFor100LongFactory.create(builder.multibindingModule); + this.mapOfLongAndProviderOfStringProvider = MapProviderFactory.<Long, String>builder(1) + .put(100L, mapOfLongAndProviderOfStringContribution1) + .build(); + this.mapOfLongAndStringProvider = MapFactory.create(mapOfLongAndProviderOfStringProvider); + this.mapOfIntegerAndProviderOfStringContribution1 = MultibindingModule_ValueFor100IntFactory.create(builder.multibindingModule); + this.mapOfIntegerAndProviderOfStringProvider = MapProviderFactory.<Integer, String>builder(1) + .put((int) 100, mapOfIntegerAndProviderOfStringContribution1) + .build(); + this.mapOfIntegerAndStringProvider = MapFactory.create(mapOfIntegerAndProviderOfStringProvider); + this.mapOfShortAndProviderOfStringContribution1 = MultibindingModule_ValueFor100ShortFactory.create(builder.multibindingModule); + this.mapOfShortAndProviderOfStringProvider = MapProviderFactory.<Short, String>builder(1) + .put((short) 100, mapOfShortAndProviderOfStringContribution1) + .build(); + this.mapOfShortAndStringProvider = MapFactory.create(mapOfShortAndProviderOfStringProvider); + this.mapOfByteAndProviderOfStringContribution1 = MultibindingModule_ValueFor100ByteFactory.create(builder.multibindingModule); + this.mapOfByteAndProviderOfStringProvider = MapProviderFactory.<Byte, String>builder(1) + .put((byte) 100, mapOfByteAndProviderOfStringContribution1) + .build(); + this.mapOfByteAndStringProvider = MapFactory.create(mapOfByteAndProviderOfStringProvider); + this.mapOfBooleanAndProviderOfStringContribution1 = MultibindingModule_ValueForTrueFactory.create(builder.multibindingModule); + this.mapOfBooleanAndProviderOfStringProvider = MapProviderFactory.<Boolean, String>builder(1) + .put(true, mapOfBooleanAndProviderOfStringContribution1) + .build(); + this.mapOfBooleanAndStringProvider = MapFactory.create(mapOfBooleanAndProviderOfStringProvider); + this.mapOfCharacterAndProviderOfStringContribution1 = MultibindingModule_ValueForAFactory.create(builder.multibindingModule); + this.mapOfCharacterAndProviderOfStringContribution2 = MultibindingModule_ValueForNewlineFactory.create(builder.multibindingModule); + this.mapOfCharacterAndProviderOfStringProvider = MapProviderFactory.<Character, String>builder(2) + .put('a', mapOfCharacterAndProviderOfStringContribution1) + .put('\n', mapOfCharacterAndProviderOfStringContribution2) + .build(); + this.mapOfCharacterAndStringProvider = MapFactory.create(mapOfCharacterAndProviderOfStringProvider); + this.mapOfTestStringKeyAndProviderOfStringContribution1 = MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory.create(builder.multibindingModule); + this.mapOfTestStringKeyAndProviderOfStringProvider = MapProviderFactory.<TestStringKey, String>builder(1) + .put(TestUnwrappedAnnotationKeyCreator.createTestStringKey("foo\n"), mapOfTestStringKeyAndProviderOfStringContribution1) + .build(); + this.mapOfTestStringKeyAndStringProvider = MapFactory.create(mapOfTestStringKeyAndProviderOfStringProvider); + this.mapOfTestWrappedAnnotationKeyAndProviderOfStringContribution1 = MultibindingModule_ValueForWrappedAnnotationKeyFooFactory.create(builder.multibindingModule); + this.mapOfTestWrappedAnnotationKeyAndProviderOfStringProvider = MapProviderFactory.<TestWrappedAnnotationKey, String>builder(1) + .put(TestWrappedAnnotationKeyCreator.createTestWrappedAnnotationKey(TestWrappedAnnotationKeyCreator.createTestStringKey("foo"), new int[] {(int) 1, (int) 2, (int) 3}, new TestClassKey[] {}, new Class[] {Long.class, Integer.class}), mapOfTestWrappedAnnotationKeyAndProviderOfStringContribution1) + .build(); + this.mapOfTestWrappedAnnotationKeyAndStringProvider = MapFactory.create(mapOfTestWrappedAnnotationKeyAndProviderOfStringProvider); + } + + @Override + public Map<String, String> map() { + return mapOfStringAndStringProvider.get(); + } + + @Override + public Map<String, Provider<String>> mapOfProviders() { + return mapOfStringAndProviderOfStringProvider.get(); + } + + @Override + public Set<String> mapKeys() { + return provideMapKeysProvider.get(); + } + + @Override + public Collection<String> mapValues() { + return provideMapValuesProvider.get(); + } + + @Override + public Set<Integer> set() { + return setOfIntegerProvider.get(); + } + + @Override + public Map<NestedWrappedKey, String> nestedKeyMap() { + return mapOfNestedWrappedKeyAndStringProvider.get(); + } + + @Override + public Map<Class<? extends Number>, String> numberClassKeyMap() { + return mapOfClassOfAndStringProvider.get(); + } + + @Override + public Map<Class<?>, String> classKeyMap() { + return mapOfClassOfAndStringProvider1.get(); + } + + @Override + public Map<Long, String> longKeyMap() { + return mapOfLongAndStringProvider.get(); + } + + @Override + public Map<Integer, String> integerKeyMap() { + return mapOfIntegerAndStringProvider.get(); + } + + @Override + public Map<Short, String> shortKeyMap() { + return mapOfShortAndStringProvider.get(); + } + + @Override + public Map<Byte, String> byteKeyMap() { + return mapOfByteAndStringProvider.get(); + } + + @Override + public Map<Boolean, String> booleanKeyMap() { + return mapOfBooleanAndStringProvider.get(); + } + + @Override + public Map<Character, String> characterKeyMap() { + return mapOfCharacterAndStringProvider.get(); + } + + @Override + public Map<TestStringKey, String> unwrappedAnnotationKeyMap() { + return mapOfTestStringKeyAndStringProvider.get(); + } + + @Override + public Map<TestWrappedAnnotationKey, String> wrappedAnnotationKeyMap() { + return mapOfTestWrappedAnnotationKeyAndStringProvider.get(); + } + + public static final class Builder { + private MultibindingModule multibindingModule; + private ContributionsModule contributionsModule; + private MultibindingDependency multibindingDependency; + + private Builder() { + } + + public MultibindingComponent build() { + if (multibindingModule == null) { + this.multibindingModule = new MultibindingModule(); + } + if (contributionsModule == null) { + this.contributionsModule = new ContributionsModule(); + } + if (multibindingDependency == null) { + throw new IllegalStateException("multibindingDependency must be set"); + } + return new DaggerMultibindingComponent(this); + } + + public Builder multibindingModule(MultibindingModule multibindingModule) { + if (multibindingModule == null) { + throw new NullPointerException("multibindingModule"); + } + this.multibindingModule = multibindingModule; + return this; + } + + public Builder contributionsModule(ContributionsModule contributionsModule) { + if (contributionsModule == null) { + throw new NullPointerException("contributionsModule"); + } + this.contributionsModule = contributionsModule; + return this; + } + + public Builder multibindingDependency(MultibindingDependency multibindingDependency) { + if (multibindingDependency == null) { + throw new NullPointerException("multibindingDependency"); + } + this.multibindingDependency = multibindingDependency; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerNonComponentDependencyComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerNonComponentDependencyComponent.java new file mode 100644 index 000000000..3d90c737b --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerNonComponentDependencyComponent.java @@ -0,0 +1,69 @@ +package test; + +import dagger.internal.Factory; +import dagger.internal.InstanceFactory; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.NonComponentDependencyComponent.ThingComponent; +import test.NonComponentDependencyComponent.ThingTwo; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerNonComponentDependencyComponent implements NonComponentDependencyComponent { + private Provider<Thing> thingProvider; + private Provider<NonComponentDependencyComponent> nonComponentDependencyComponentProvider; + private Provider<ThingComponent> thingComponentProvider; + private Provider<ThingTwo> thingTwoProvider; + + private DaggerNonComponentDependencyComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.thingProvider = new Factory<Thing>() { + private final ThingComponent thingComponent = builder.thingComponent; + @Override public Thing get() { + Thing provided = thingComponent.thing(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable component method"); + } + return provided; + } + }; + this.nonComponentDependencyComponentProvider = InstanceFactory.<NonComponentDependencyComponent>create(this); + this.thingComponentProvider = InstanceFactory.<ThingComponent>create(builder.thingComponent); + this.thingTwoProvider = NonComponentDependencyComponent$ThingTwo_Factory.create(thingProvider, nonComponentDependencyComponentProvider, thingComponentProvider); + } + + @Override + public ThingTwo thingTwo() { + return thingTwoProvider.get(); + } + + public static final class Builder { + private ThingComponent thingComponent; + + private Builder() { + } + + public NonComponentDependencyComponent build() { + if (thingComponent == null) { + throw new IllegalStateException("thingComponent must be set"); + } + return new DaggerNonComponentDependencyComponent(this); + } + + public Builder thingComponent(ThingComponent thingComponent) { + if (thingComponent == null) { + throw new NullPointerException("thingComponent"); + } + this.thingComponent = thingComponent; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerOuterClassBar_NestedComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerOuterClassBar_NestedComponent.java new file mode 100644 index 000000000..1d6191408 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerOuterClassBar_NestedComponent.java @@ -0,0 +1,65 @@ +package test; + +import dagger.MembersInjector; +import dagger.internal.MembersInjectors; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.OuterClassBar.NestedComponent; +import test.sub.OtherThing; +import test.sub.OtherThing_Factory; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerOuterClassBar_NestedComponent implements NestedComponent { + private Provider<OtherThing> otherThingProvider; + private Provider<Thing> thingProvider; + private MembersInjector<InjectedThing> injectedThingMembersInjector; + private Provider<InjectedThing> injectedThingProvider; + + private DaggerOuterClassBar_NestedComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static NestedComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.otherThingProvider = OtherThing_Factory.create(PrimitivesModule_ProvideIntFactory.create()); + this.thingProvider = Thing_Factory.create(otherThingProvider); + this.injectedThingMembersInjector = InjectedThing_MembersInjector.create(PrimitivesModule_ProvideByteFactory.create(), PrimitivesModule_ProvideCharFactory.create(), PrimitivesModule_ProvideShortFactory.create(), PrimitivesModule_ProvideIntFactory.create(), PrimitivesModule_ProvideLongFactory.create(), PrimitivesModule_ProvideBooleanFactory.create(), PrimitivesModule_ProvideFloatFactory.create(), PrimitivesModule_BoundDoubleFactory.create(), PrimitivesModule_ProvideByteArrayFactory.create(), PrimitivesModule_ProvideCharArrayFactory.create(), PrimitivesModule_ProvideShortArrayFactory.create(), PrimitivesModule_ProvideIntArrayFactory.create(), PrimitivesModule_ProvideLongArrayFactory.create(), PrimitivesModule_ProvideBooleanArrayFactory.create(), PrimitivesModule_ProvideFloatArrayFactory.create(), PrimitivesModule_BoundDoubleArrayFactory.create(), thingProvider, (MembersInjector) MembersInjectors.noOp()); + this.injectedThingProvider = InjectedThing_Factory.create(injectedThingMembersInjector, PrimitivesModule_ProvideByteFactory.create(), PrimitivesModule_ProvideCharFactory.create(), PrimitivesModule_ProvideShortFactory.create(), PrimitivesModule_ProvideIntFactory.create(), PrimitivesModule_ProvideLongFactory.create(), PrimitivesModule_ProvideBooleanFactory.create(), PrimitivesModule_ProvideFloatFactory.create(), PrimitivesModule_BoundDoubleFactory.create(), PrimitivesModule_ProvideByteArrayFactory.create(), PrimitivesModule_ProvideCharArrayFactory.create(), PrimitivesModule_ProvideShortArrayFactory.create(), PrimitivesModule_ProvideIntArrayFactory.create(), PrimitivesModule_ProvideLongArrayFactory.create(), PrimitivesModule_ProvideBooleanArrayFactory.create(), PrimitivesModule_ProvideFloatArrayFactory.create(), PrimitivesModule_BoundDoubleArrayFactory.create(), thingProvider, (MembersInjector) MembersInjectors.noOp()); + } + + @Override + public InjectedThing injectedThing() { + return injectedThingProvider.get(); + } + + public static final class Builder { + private PrimitivesModule primitivesModule; + + private Builder() { + } + + public NestedComponent build() { + if (primitivesModule == null) { + this.primitivesModule = new PrimitivesModule(); + } + return new DaggerOuterClassBar_NestedComponent(this); + } + + public Builder primitivesModule(PrimitivesModule primitivesModule) { + if (primitivesModule == null) { + throw new NullPointerException("primitivesModule"); + } + this.primitivesModule = primitivesModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerOuterClassFoo_NestedComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerOuterClassFoo_NestedComponent.java new file mode 100644 index 000000000..a307b9490 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerOuterClassFoo_NestedComponent.java @@ -0,0 +1,59 @@ +package test; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.OuterClassFoo.NestedComponent; +import test.sub.OtherThing; +import test.sub.OtherThing_Factory; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerOuterClassFoo_NestedComponent implements NestedComponent { + private Provider<OtherThing> otherThingProvider; + private Provider<Thing> thingProvider; + + private DaggerOuterClassFoo_NestedComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static NestedComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.otherThingProvider = OtherThing_Factory.create(PrimitivesModule_ProvideIntFactory.create()); + this.thingProvider = Thing_Factory.create(otherThingProvider); + } + + @Override + public Thing thing() { + return thingProvider.get(); + } + + public static final class Builder { + private PrimitivesModule primitivesModule; + + private Builder() { + } + + public NestedComponent build() { + if (primitivesModule == null) { + this.primitivesModule = new PrimitivesModule(); + } + return new DaggerOuterClassFoo_NestedComponent(this); + } + + public Builder primitivesModule(PrimitivesModule primitivesModule) { + if (primitivesModule == null) { + throw new NullPointerException("primitivesModule"); + } + this.primitivesModule = primitivesModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerSingletonGenericComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerSingletonGenericComponent.java new file mode 100644 index 000000000..f08ad4296 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/DaggerSingletonGenericComponent.java @@ -0,0 +1,49 @@ +package test; + +import dagger.internal.ScopedProvider; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerSingletonGenericComponent implements SingletonGenericComponent { + private Provider<ScopedGeneric<A>> scopedGenericProvider; + private Provider<ScopedGeneric<B>> scopedGenericProvider1; + + private DaggerSingletonGenericComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static SingletonGenericComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.scopedGenericProvider = ScopedProvider.create(ScopedGeneric_Factory.create(A_Factory.create())); + this.scopedGenericProvider1 = ScopedProvider.create(ScopedGeneric_Factory.create(B_Factory.create())); + } + + @Override + public ScopedGeneric<A> scopedGenericA() { + return scopedGenericProvider.get(); + } + + @Override + public ScopedGeneric<B> scopedGenericB() { + return scopedGenericProvider1.get(); + } + + public static final class Builder { + private Builder() { + } + + public SingletonGenericComponent build() { + return new DaggerSingletonGenericComponent(this); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Generic2_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Generic2_Factory.java new file mode 100644 index 000000000..0f5b13dbf --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Generic2_Factory.java @@ -0,0 +1,25 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class Generic2_Factory<T> implements Factory<Generic2<T>> { + private final Provider<T> tProvider; + + public Generic2_Factory(Provider<T> tProvider) { + assert tProvider != null; + this.tProvider = tProvider; + } + + @Override + public Generic2<T> get() { + return new Generic2<T>(tProvider.get()); + } + + public static <T> Factory<Generic2<T>> create(Provider<T> tProvider) { + return new Generic2_Factory<T>(tProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericChild_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericChild_Factory.java new file mode 100644 index 000000000..95500275c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericChild_Factory.java @@ -0,0 +1,27 @@ +package test; + +import dagger.MembersInjector; +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GenericChild_Factory<T> implements Factory<GenericChild<T>> { + private final MembersInjector<GenericChild<T>> membersInjector; + + public GenericChild_Factory(MembersInjector<GenericChild<T>> membersInjector) { + assert membersInjector != null; + this.membersInjector = membersInjector; + } + + @Override + public GenericChild<T> get() { + GenericChild<T> instance = new GenericChild<T>(); + membersInjector.injectMembers(instance); + return instance; + } + + public static <T> Factory<GenericChild<T>> create(MembersInjector<GenericChild<T>> membersInjector) { + return new GenericChild_Factory<T>(membersInjector); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericChild_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericChild_MembersInjector.java new file mode 100644 index 000000000..5b734a587 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericChild_MembersInjector.java @@ -0,0 +1,38 @@ +package test; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GenericChild_MembersInjector<T> implements MembersInjector<GenericChild<T>> { + private final MembersInjector<GenericParent<T, B>> supertypeInjector; + private final Provider<A> aProvider; + private final Provider<T> tProvider; + + public GenericChild_MembersInjector(MembersInjector<GenericParent<T, B>> supertypeInjector, Provider<A> aProvider, Provider<T> tProvider) { + assert supertypeInjector != null; + this.supertypeInjector = supertypeInjector; + assert aProvider != null; + this.aProvider = aProvider; + assert tProvider != null; + this.tProvider = tProvider; + } + + @Override + public void injectMembers(GenericChild<T> instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + supertypeInjector.injectMembers(instance); + instance.a = aProvider.get(); + instance.t = tProvider.get(); + instance.registerA(aProvider.get()); + instance.registerT(tProvider.get()); + } + + public static <T> MembersInjector<GenericChild<T>> create(MembersInjector<GenericParent<T, B>> supertypeInjector, Provider<A> aProvider, Provider<T> tProvider) { + return new GenericChild_MembersInjector<T>(supertypeInjector, aProvider, tProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericDoubleReferences_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericDoubleReferences_Factory.java new file mode 100644 index 000000000..4e763fb25 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericDoubleReferences_Factory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GenericDoubleReferences_Factory<T> implements Factory<GenericDoubleReferences<T>> { + private final Provider<T> tAndT2Provider; + private final Provider<Thing> aAndA2Provider; + + public GenericDoubleReferences_Factory(Provider<T> tAndT2Provider, Provider<Thing> aAndA2Provider) { + assert tAndT2Provider != null; + this.tAndT2Provider = tAndT2Provider; + assert aAndA2Provider != null; + this.aAndA2Provider = aAndA2Provider; + } + + @Override + public GenericDoubleReferences<T> get() { + return new GenericDoubleReferences<T>(tAndT2Provider.get(), aAndA2Provider.get(), tAndT2Provider.get(), aAndA2Provider.get()); + } + + public static <T> Factory<GenericDoubleReferences<T>> create(Provider<T> tAndT2Provider, Provider<Thing> aAndA2Provider) { + return new GenericDoubleReferences_Factory<T>(tAndT2Provider, aAndA2Provider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericNoDeps_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericNoDeps_Factory.java new file mode 100644 index 000000000..97ac2ee80 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericNoDeps_Factory.java @@ -0,0 +1,21 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@SuppressWarnings("rawtypes") +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum GenericNoDeps_Factory implements Factory<GenericNoDeps> { +INSTANCE; + + @Override + public GenericNoDeps get() { + return new GenericNoDeps(); + } + + @SuppressWarnings("unchecked") + public static <T> Factory<GenericNoDeps<T>> create() { + return (Factory) INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericParent_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericParent_Factory.java new file mode 100644 index 000000000..2e2e62c40 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericParent_Factory.java @@ -0,0 +1,27 @@ +package test; + +import dagger.MembersInjector; +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GenericParent_Factory<X, Y> implements Factory<GenericParent<X, Y>> { + private final MembersInjector<GenericParent<X, Y>> membersInjector; + + public GenericParent_Factory(MembersInjector<GenericParent<X, Y>> membersInjector) { + assert membersInjector != null; + this.membersInjector = membersInjector; + } + + @Override + public GenericParent<X, Y> get() { + GenericParent<X, Y> instance = new GenericParent<X, Y>(); + membersInjector.injectMembers(instance); + return instance; + } + + public static <X, Y> Factory<GenericParent<X, Y>> create(MembersInjector<GenericParent<X, Y>> membersInjector) { + return new GenericParent_Factory<X, Y>(membersInjector); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericParent_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericParent_MembersInjector.java new file mode 100644 index 000000000..5b11ab9e8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/GenericParent_MembersInjector.java @@ -0,0 +1,39 @@ +package test; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GenericParent_MembersInjector<X, Y> implements MembersInjector<GenericParent<X, Y>> { + private final Provider<X> xProvider; + private final Provider<Y> yProvider; + private final Provider<B> bProvider; + + public GenericParent_MembersInjector(Provider<X> xProvider, Provider<Y> yProvider, Provider<B> bProvider) { + assert xProvider != null; + this.xProvider = xProvider; + assert yProvider != null; + this.yProvider = yProvider; + assert bProvider != null; + this.bProvider = bProvider; + } + + @Override + public void injectMembers(GenericParent<X, Y> instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.x = xProvider.get(); + instance.y = yProvider.get(); + instance.b = bProvider.get(); + instance.registerX(xProvider.get()); + instance.registerY(yProvider.get()); + instance.registerB(bProvider.get()); + } + + public static <X, Y> MembersInjector<GenericParent<X, Y>> create(Provider<X> xProvider, Provider<Y> yProvider, Provider<B> bProvider) { + return new GenericParent_MembersInjector<X, Y>(xProvider, yProvider, bProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Generic_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Generic_Factory.java new file mode 100644 index 000000000..e06ad23ad --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Generic_Factory.java @@ -0,0 +1,25 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class Generic_Factory<T> implements Factory<Generic<T>> { + private final Provider<T> tProvider; + + public Generic_Factory(Provider<T> tProvider) { + assert tProvider != null; + this.tProvider = tProvider; + } + + @Override + public Generic<T> get() { + return new Generic<T>(tProvider.get()); + } + + public static <T> Factory<Generic<T>> create(Provider<T> tProvider) { + return new Generic_Factory<T>(tProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/InjectedThing_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/InjectedThing_Factory.java new file mode 100644 index 000000000..65a361f27 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/InjectedThing_Factory.java @@ -0,0 +1,83 @@ +package test; + +import dagger.MembersInjector; +import dagger.internal.DoubleCheckLazy; +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class InjectedThing_Factory implements Factory<InjectedThing> { + private final MembersInjector<InjectedThing> membersInjector; + private final Provider<Byte> boxedBypeAndPrimitiveByteAndByteProvider; + private final Provider<Character> boxedCharAndPrimitiveCharAndCharProvider; + private final Provider<Short> boxedShortAndPrimitiveShortAndShortProvider; + private final Provider<Integer> boxedIntAndPrimitiveIntAndIntProvider; + private final Provider<Long> boxedLongAndPrimitiveLongAndLongProvider; + private final Provider<Boolean> boxedBooleanAndPrimitiveBooleanAndBooleanProvider; + private final Provider<Float> boxedFloatAndPrimitiveFloatAndFloatProvider; + private final Provider<Double> boxedDoubleAndPrimitiveDoubleAndDoubleProvider; + private final Provider<byte[]> byteArrayProvider; + private final Provider<char[]> charArrayProvider; + private final Provider<short[]> shortArrayProvider; + private final Provider<int[]> intArrayProvider; + private final Provider<long[]> longArrayProvider; + private final Provider<boolean[]> booleanArrayProvider; + private final Provider<float[]> floatArrayAndLazyProvider; + private final Provider<double[]> doubleArrayProvider; + private final Provider<Thing> thingProvider; + private final MembersInjector<Thing> thingMembersInjector; + + public InjectedThing_Factory(MembersInjector<InjectedThing> membersInjector, Provider<Byte> boxedBypeAndPrimitiveByteAndByteProvider, Provider<Character> boxedCharAndPrimitiveCharAndCharProvider, Provider<Short> boxedShortAndPrimitiveShortAndShortProvider, Provider<Integer> boxedIntAndPrimitiveIntAndIntProvider, Provider<Long> boxedLongAndPrimitiveLongAndLongProvider, Provider<Boolean> boxedBooleanAndPrimitiveBooleanAndBooleanProvider, Provider<Float> boxedFloatAndPrimitiveFloatAndFloatProvider, Provider<Double> boxedDoubleAndPrimitiveDoubleAndDoubleProvider, Provider<byte[]> byteArrayProvider, Provider<char[]> charArrayProvider, Provider<short[]> shortArrayProvider, Provider<int[]> intArrayProvider, Provider<long[]> longArrayProvider, Provider<boolean[]> booleanArrayProvider, Provider<float[]> floatArrayAndLazyProvider, Provider<double[]> doubleArrayProvider, Provider<Thing> thingProvider, MembersInjector<Thing> thingMembersInjector) { + assert membersInjector != null; + this.membersInjector = membersInjector; + assert boxedBypeAndPrimitiveByteAndByteProvider != null; + this.boxedBypeAndPrimitiveByteAndByteProvider = boxedBypeAndPrimitiveByteAndByteProvider; + assert boxedCharAndPrimitiveCharAndCharProvider != null; + this.boxedCharAndPrimitiveCharAndCharProvider = boxedCharAndPrimitiveCharAndCharProvider; + assert boxedShortAndPrimitiveShortAndShortProvider != null; + this.boxedShortAndPrimitiveShortAndShortProvider = boxedShortAndPrimitiveShortAndShortProvider; + assert boxedIntAndPrimitiveIntAndIntProvider != null; + this.boxedIntAndPrimitiveIntAndIntProvider = boxedIntAndPrimitiveIntAndIntProvider; + assert boxedLongAndPrimitiveLongAndLongProvider != null; + this.boxedLongAndPrimitiveLongAndLongProvider = boxedLongAndPrimitiveLongAndLongProvider; + assert boxedBooleanAndPrimitiveBooleanAndBooleanProvider != null; + this.boxedBooleanAndPrimitiveBooleanAndBooleanProvider = boxedBooleanAndPrimitiveBooleanAndBooleanProvider; + assert boxedFloatAndPrimitiveFloatAndFloatProvider != null; + this.boxedFloatAndPrimitiveFloatAndFloatProvider = boxedFloatAndPrimitiveFloatAndFloatProvider; + assert boxedDoubleAndPrimitiveDoubleAndDoubleProvider != null; + this.boxedDoubleAndPrimitiveDoubleAndDoubleProvider = boxedDoubleAndPrimitiveDoubleAndDoubleProvider; + assert byteArrayProvider != null; + this.byteArrayProvider = byteArrayProvider; + assert charArrayProvider != null; + this.charArrayProvider = charArrayProvider; + assert shortArrayProvider != null; + this.shortArrayProvider = shortArrayProvider; + assert intArrayProvider != null; + this.intArrayProvider = intArrayProvider; + assert longArrayProvider != null; + this.longArrayProvider = longArrayProvider; + assert booleanArrayProvider != null; + this.booleanArrayProvider = booleanArrayProvider; + assert floatArrayAndLazyProvider != null; + this.floatArrayAndLazyProvider = floatArrayAndLazyProvider; + assert doubleArrayProvider != null; + this.doubleArrayProvider = doubleArrayProvider; + assert thingProvider != null; + this.thingProvider = thingProvider; + assert thingMembersInjector != null; + this.thingMembersInjector = thingMembersInjector; + } + + @Override + public InjectedThing get() { + InjectedThing instance = new InjectedThing(boxedBypeAndPrimitiveByteAndByteProvider.get(), boxedCharAndPrimitiveCharAndCharProvider.get(), boxedShortAndPrimitiveShortAndShortProvider.get(), boxedIntAndPrimitiveIntAndIntProvider.get(), boxedLongAndPrimitiveLongAndLongProvider.get(), boxedBooleanAndPrimitiveBooleanAndBooleanProvider.get(), boxedFloatAndPrimitiveFloatAndFloatProvider.get(), boxedDoubleAndPrimitiveDoubleAndDoubleProvider.get(), boxedBypeAndPrimitiveByteAndByteProvider, boxedCharAndPrimitiveCharAndCharProvider, boxedShortAndPrimitiveShortAndShortProvider, boxedIntAndPrimitiveIntAndIntProvider, boxedLongAndPrimitiveLongAndLongProvider, boxedBooleanAndPrimitiveBooleanAndBooleanProvider, boxedFloatAndPrimitiveFloatAndFloatProvider, boxedDoubleAndPrimitiveDoubleAndDoubleProvider, DoubleCheckLazy.create(boxedBypeAndPrimitiveByteAndByteProvider), DoubleCheckLazy.create(boxedCharAndPrimitiveCharAndCharProvider), DoubleCheckLazy.create(boxedShortAndPrimitiveShortAndShortProvider), DoubleCheckLazy.create(boxedIntAndPrimitiveIntAndIntProvider), DoubleCheckLazy.create(boxedLongAndPrimitiveLongAndLongProvider), DoubleCheckLazy.create(boxedBooleanAndPrimitiveBooleanAndBooleanProvider), DoubleCheckLazy.create(boxedFloatAndPrimitiveFloatAndFloatProvider), DoubleCheckLazy.create(boxedDoubleAndPrimitiveDoubleAndDoubleProvider), boxedBypeAndPrimitiveByteAndByteProvider.get(), boxedCharAndPrimitiveCharAndCharProvider.get(), boxedShortAndPrimitiveShortAndShortProvider.get(), boxedIntAndPrimitiveIntAndIntProvider.get(), boxedLongAndPrimitiveLongAndLongProvider.get(), boxedBooleanAndPrimitiveBooleanAndBooleanProvider.get(), boxedFloatAndPrimitiveFloatAndFloatProvider.get(), boxedDoubleAndPrimitiveDoubleAndDoubleProvider.get(), byteArrayProvider.get(), charArrayProvider.get(), shortArrayProvider.get(), intArrayProvider.get(), longArrayProvider.get(), booleanArrayProvider.get(), floatArrayAndLazyProvider.get(), doubleArrayProvider.get(), byteArrayProvider, charArrayProvider, shortArrayProvider, intArrayProvider, longArrayProvider, booleanArrayProvider, floatArrayAndLazyProvider, doubleArrayProvider, DoubleCheckLazy.create(byteArrayProvider), DoubleCheckLazy.create(charArrayProvider), DoubleCheckLazy.create(shortArrayProvider), DoubleCheckLazy.create(intArrayProvider), DoubleCheckLazy.create(longArrayProvider), DoubleCheckLazy.create(booleanArrayProvider), DoubleCheckLazy.create(floatArrayAndLazyProvider), DoubleCheckLazy.create(doubleArrayProvider), thingProvider.get(), thingProvider, DoubleCheckLazy.create(thingProvider), thingMembersInjector); + membersInjector.injectMembers(instance); + return instance; + } + + public static Factory<InjectedThing> create(MembersInjector<InjectedThing> membersInjector, Provider<Byte> boxedBypeAndPrimitiveByteAndByteProvider, Provider<Character> boxedCharAndPrimitiveCharAndCharProvider, Provider<Short> boxedShortAndPrimitiveShortAndShortProvider, Provider<Integer> boxedIntAndPrimitiveIntAndIntProvider, Provider<Long> boxedLongAndPrimitiveLongAndLongProvider, Provider<Boolean> boxedBooleanAndPrimitiveBooleanAndBooleanProvider, Provider<Float> boxedFloatAndPrimitiveFloatAndFloatProvider, Provider<Double> boxedDoubleAndPrimitiveDoubleAndDoubleProvider, Provider<byte[]> byteArrayProvider, Provider<char[]> charArrayProvider, Provider<short[]> shortArrayProvider, Provider<int[]> intArrayProvider, Provider<long[]> longArrayProvider, Provider<boolean[]> booleanArrayProvider, Provider<float[]> floatArrayAndLazyProvider, Provider<double[]> doubleArrayProvider, Provider<Thing> thingProvider, MembersInjector<Thing> thingMembersInjector) { + return new InjectedThing_Factory(membersInjector, boxedBypeAndPrimitiveByteAndByteProvider, boxedCharAndPrimitiveCharAndCharProvider, boxedShortAndPrimitiveShortAndShortProvider, boxedIntAndPrimitiveIntAndIntProvider, boxedLongAndPrimitiveLongAndLongProvider, boxedBooleanAndPrimitiveBooleanAndBooleanProvider, boxedFloatAndPrimitiveFloatAndFloatProvider, boxedDoubleAndPrimitiveDoubleAndDoubleProvider, byteArrayProvider, charArrayProvider, shortArrayProvider, intArrayProvider, longArrayProvider, booleanArrayProvider, floatArrayAndLazyProvider, doubleArrayProvider, thingProvider, thingMembersInjector); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/InjectedThing_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/InjectedThing_MembersInjector.java new file mode 100644 index 000000000..00f265d4c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/InjectedThing_MembersInjector.java @@ -0,0 +1,199 @@ +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 InjectedThing_MembersInjector implements MembersInjector<InjectedThing> { + private final Provider<Byte> boxedBypeAndPrimitiveByteAndByteProvider; + private final Provider<Character> boxedCharAndPrimitiveCharAndCharProvider; + private final Provider<Short> boxedShortAndPrimitiveShortAndShortProvider; + private final Provider<Integer> boxedIntAndPrimitiveIntAndIntProvider; + private final Provider<Long> boxedLongAndPrimitiveLongAndLongProvider; + private final Provider<Boolean> boxedBooleanAndPrimitiveBooleanAndBooleanProvider; + private final Provider<Float> boxedFloatAndPrimitiveFloatAndFloatProvider; + private final Provider<Double> boxedDoubleAndPrimitiveDoubleAndDoubleProvider; + private final Provider<byte[]> byteArrayProvider; + private final Provider<char[]> charArrayProvider; + private final Provider<short[]> shortArrayProvider; + private final Provider<int[]> intArrayProvider; + private final Provider<long[]> longArrayProvider; + private final Provider<boolean[]> booleanArrayProvider; + private final Provider<float[]> floatArrayAndLazyProvider; + private final Provider<double[]> doubleArrayProvider; + private final Provider<Thing> thingProvider; + private final MembersInjector<Thing> thingMembersInjector; + + public InjectedThing_MembersInjector(Provider<Byte> boxedBypeAndPrimitiveByteAndByteProvider, Provider<Character> boxedCharAndPrimitiveCharAndCharProvider, Provider<Short> boxedShortAndPrimitiveShortAndShortProvider, Provider<Integer> boxedIntAndPrimitiveIntAndIntProvider, Provider<Long> boxedLongAndPrimitiveLongAndLongProvider, Provider<Boolean> boxedBooleanAndPrimitiveBooleanAndBooleanProvider, Provider<Float> boxedFloatAndPrimitiveFloatAndFloatProvider, Provider<Double> boxedDoubleAndPrimitiveDoubleAndDoubleProvider, Provider<byte[]> byteArrayProvider, Provider<char[]> charArrayProvider, Provider<short[]> shortArrayProvider, Provider<int[]> intArrayProvider, Provider<long[]> longArrayProvider, Provider<boolean[]> booleanArrayProvider, Provider<float[]> floatArrayAndLazyProvider, Provider<double[]> doubleArrayProvider, Provider<Thing> thingProvider, MembersInjector<Thing> thingMembersInjector) { + assert boxedBypeAndPrimitiveByteAndByteProvider != null; + this.boxedBypeAndPrimitiveByteAndByteProvider = boxedBypeAndPrimitiveByteAndByteProvider; + assert boxedCharAndPrimitiveCharAndCharProvider != null; + this.boxedCharAndPrimitiveCharAndCharProvider = boxedCharAndPrimitiveCharAndCharProvider; + assert boxedShortAndPrimitiveShortAndShortProvider != null; + this.boxedShortAndPrimitiveShortAndShortProvider = boxedShortAndPrimitiveShortAndShortProvider; + assert boxedIntAndPrimitiveIntAndIntProvider != null; + this.boxedIntAndPrimitiveIntAndIntProvider = boxedIntAndPrimitiveIntAndIntProvider; + assert boxedLongAndPrimitiveLongAndLongProvider != null; + this.boxedLongAndPrimitiveLongAndLongProvider = boxedLongAndPrimitiveLongAndLongProvider; + assert boxedBooleanAndPrimitiveBooleanAndBooleanProvider != null; + this.boxedBooleanAndPrimitiveBooleanAndBooleanProvider = boxedBooleanAndPrimitiveBooleanAndBooleanProvider; + assert boxedFloatAndPrimitiveFloatAndFloatProvider != null; + this.boxedFloatAndPrimitiveFloatAndFloatProvider = boxedFloatAndPrimitiveFloatAndFloatProvider; + assert boxedDoubleAndPrimitiveDoubleAndDoubleProvider != null; + this.boxedDoubleAndPrimitiveDoubleAndDoubleProvider = boxedDoubleAndPrimitiveDoubleAndDoubleProvider; + assert byteArrayProvider != null; + this.byteArrayProvider = byteArrayProvider; + assert charArrayProvider != null; + this.charArrayProvider = charArrayProvider; + assert shortArrayProvider != null; + this.shortArrayProvider = shortArrayProvider; + assert intArrayProvider != null; + this.intArrayProvider = intArrayProvider; + assert longArrayProvider != null; + this.longArrayProvider = longArrayProvider; + assert booleanArrayProvider != null; + this.booleanArrayProvider = booleanArrayProvider; + assert floatArrayAndLazyProvider != null; + this.floatArrayAndLazyProvider = floatArrayAndLazyProvider; + assert doubleArrayProvider != null; + this.doubleArrayProvider = doubleArrayProvider; + assert thingProvider != null; + this.thingProvider = thingProvider; + assert thingMembersInjector != null; + this.thingMembersInjector = thingMembersInjector; + } + + @Override + public void injectMembers(InjectedThing instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.primitiveByte = boxedBypeAndPrimitiveByteAndByteProvider.get(); + instance.primitiveChar = boxedCharAndPrimitiveCharAndCharProvider.get(); + instance.primitiveShort = boxedShortAndPrimitiveShortAndShortProvider.get(); + instance.primitiveInt = boxedIntAndPrimitiveIntAndIntProvider.get(); + instance.primitiveLong = boxedLongAndPrimitiveLongAndLongProvider.get(); + instance.primitiveBoolean = boxedBooleanAndPrimitiveBooleanAndBooleanProvider.get(); + instance.primitiveFloat = boxedFloatAndPrimitiveFloatAndFloatProvider.get(); + instance.primitiveDouble = boxedDoubleAndPrimitiveDoubleAndDoubleProvider.get(); + instance.byteProvider = boxedBypeAndPrimitiveByteAndByteProvider; + instance.charProvider = boxedCharAndPrimitiveCharAndCharProvider; + instance.shortProvider = boxedShortAndPrimitiveShortAndShortProvider; + instance.intProvider = boxedIntAndPrimitiveIntAndIntProvider; + instance.longProvider = boxedLongAndPrimitiveLongAndLongProvider; + instance.booleanProvider = boxedBooleanAndPrimitiveBooleanAndBooleanProvider; + instance.floatProvider = boxedFloatAndPrimitiveFloatAndFloatProvider; + instance.doubleProvider = boxedDoubleAndPrimitiveDoubleAndDoubleProvider; + instance.lazyByte = DoubleCheckLazy.create(boxedBypeAndPrimitiveByteAndByteProvider); + instance.lazyChar = DoubleCheckLazy.create(boxedCharAndPrimitiveCharAndCharProvider); + instance.lazyShort = DoubleCheckLazy.create(boxedShortAndPrimitiveShortAndShortProvider); + instance.lazyInt = DoubleCheckLazy.create(boxedIntAndPrimitiveIntAndIntProvider); + instance.lazyLong = DoubleCheckLazy.create(boxedLongAndPrimitiveLongAndLongProvider); + instance.lazyBoolean = DoubleCheckLazy.create(boxedBooleanAndPrimitiveBooleanAndBooleanProvider); + instance.lazyFloat = DoubleCheckLazy.create(boxedFloatAndPrimitiveFloatAndFloatProvider); + instance.lazyDouble = DoubleCheckLazy.create(boxedDoubleAndPrimitiveDoubleAndDoubleProvider); + instance.boxedBype = boxedBypeAndPrimitiveByteAndByteProvider.get(); + instance.boxedChar = boxedCharAndPrimitiveCharAndCharProvider.get(); + instance.boxedShort = boxedShortAndPrimitiveShortAndShortProvider.get(); + instance.boxedInt = boxedIntAndPrimitiveIntAndIntProvider.get(); + instance.boxedLong = boxedLongAndPrimitiveLongAndLongProvider.get(); + instance.boxedBoolean = boxedBooleanAndPrimitiveBooleanAndBooleanProvider.get(); + instance.boxedFloat = boxedFloatAndPrimitiveFloatAndFloatProvider.get(); + instance.boxedDouble = boxedDoubleAndPrimitiveDoubleAndDoubleProvider.get(); + instance.byteArray = byteArrayProvider.get(); + instance.charArray = charArrayProvider.get(); + instance.shortArray = shortArrayProvider.get(); + instance.intArray = intArrayProvider.get(); + instance.longArray = longArrayProvider.get(); + instance.booleanArray = booleanArrayProvider.get(); + instance.floatArray = floatArrayAndLazyProvider.get(); + instance.doubleArray = doubleArrayProvider.get(); + instance.byteArrayProvider = byteArrayProvider; + instance.charArrayProvider = charArrayProvider; + instance.shortArrayProvider = shortArrayProvider; + instance.intArrayProvider = intArrayProvider; + instance.longArrayProvider = longArrayProvider; + instance.booleanArrayProvider = booleanArrayProvider; + instance.floatArrayProvider = floatArrayAndLazyProvider; + instance.doubleArrayProvider = doubleArrayProvider; + instance.lazyByteArray = DoubleCheckLazy.create(byteArrayProvider); + instance.lazyCharArray = DoubleCheckLazy.create(charArrayProvider); + instance.lazyShortArray = DoubleCheckLazy.create(shortArrayProvider); + instance.lazyIntArray = DoubleCheckLazy.create(intArrayProvider); + instance.lazyLongArray = DoubleCheckLazy.create(longArrayProvider); + instance.lazyBooleanArray = DoubleCheckLazy.create(booleanArrayProvider); + instance.lazy = DoubleCheckLazy.create(floatArrayAndLazyProvider); + instance.lazyDoubleArray = DoubleCheckLazy.create(doubleArrayProvider); + instance.thing = thingProvider.get(); + instance.thingProvider = thingProvider; + instance.lazyThing = DoubleCheckLazy.create(thingProvider); + instance.thingMembersInjector = thingMembersInjector; + instance.primitiveByte(boxedBypeAndPrimitiveByteAndByteProvider.get()); + instance.primitiveChar(boxedCharAndPrimitiveCharAndCharProvider.get()); + instance.primitiveShort(boxedShortAndPrimitiveShortAndShortProvider.get()); + instance.primitiveInt(boxedIntAndPrimitiveIntAndIntProvider.get()); + instance.primitiveLong(boxedLongAndPrimitiveLongAndLongProvider.get()); + instance.primitiveBoolean(boxedBooleanAndPrimitiveBooleanAndBooleanProvider.get()); + instance.primitiveFloat(boxedFloatAndPrimitiveFloatAndFloatProvider.get()); + instance.primitiveDouble(boxedDoubleAndPrimitiveDoubleAndDoubleProvider.get()); + instance.byteProvider(boxedBypeAndPrimitiveByteAndByteProvider); + instance.charProvider(boxedCharAndPrimitiveCharAndCharProvider); + instance.shortProvider(boxedShortAndPrimitiveShortAndShortProvider); + instance.intProvider(boxedIntAndPrimitiveIntAndIntProvider); + instance.longProvider(boxedLongAndPrimitiveLongAndLongProvider); + instance.booleanProvider(boxedBooleanAndPrimitiveBooleanAndBooleanProvider); + instance.floatProvider(boxedFloatAndPrimitiveFloatAndFloatProvider); + instance.doubleProvider(boxedDoubleAndPrimitiveDoubleAndDoubleProvider); + instance.lazyByte(DoubleCheckLazy.create(boxedBypeAndPrimitiveByteAndByteProvider)); + instance.lazyChar(DoubleCheckLazy.create(boxedCharAndPrimitiveCharAndCharProvider)); + instance.lazyShort(DoubleCheckLazy.create(boxedShortAndPrimitiveShortAndShortProvider)); + instance.lazyInt(DoubleCheckLazy.create(boxedIntAndPrimitiveIntAndIntProvider)); + instance.lazyLong(DoubleCheckLazy.create(boxedLongAndPrimitiveLongAndLongProvider)); + instance.lazyBoolean(DoubleCheckLazy.create(boxedBooleanAndPrimitiveBooleanAndBooleanProvider)); + instance.lazyFloat(DoubleCheckLazy.create(boxedFloatAndPrimitiveFloatAndFloatProvider)); + instance.lazyDouble(DoubleCheckLazy.create(boxedDoubleAndPrimitiveDoubleAndDoubleProvider)); + instance.boxedBype(boxedBypeAndPrimitiveByteAndByteProvider.get()); + instance.boxedChar(boxedCharAndPrimitiveCharAndCharProvider.get()); + instance.boxedShort(boxedShortAndPrimitiveShortAndShortProvider.get()); + instance.boxedInt(boxedIntAndPrimitiveIntAndIntProvider.get()); + instance.boxedLong(boxedLongAndPrimitiveLongAndLongProvider.get()); + instance.boxedBoolean(boxedBooleanAndPrimitiveBooleanAndBooleanProvider.get()); + instance.boxedFloat(boxedFloatAndPrimitiveFloatAndFloatProvider.get()); + instance.boxedDouble(boxedDoubleAndPrimitiveDoubleAndDoubleProvider.get()); + instance.byteArray(byteArrayProvider.get()); + instance.charArray(charArrayProvider.get()); + instance.shortArray(shortArrayProvider.get()); + instance.intArray(intArrayProvider.get()); + instance.longArray(longArrayProvider.get()); + instance.booleanArray(booleanArrayProvider.get()); + instance.floatArray(floatArrayAndLazyProvider.get()); + instance.doubleArray(doubleArrayProvider.get()); + instance.byteArrayProvider(byteArrayProvider); + instance.charArrayProvider(charArrayProvider); + instance.shortArrayProvider(shortArrayProvider); + instance.intArrayProvider(intArrayProvider); + instance.longArrayProvider(longArrayProvider); + instance.booleanArrayProvider(booleanArrayProvider); + instance.floatArrayProvider(floatArrayAndLazyProvider); + instance.doubleArrayProvider(doubleArrayProvider); + instance.lazyByteArray(DoubleCheckLazy.create(byteArrayProvider)); + instance.lazyCharArray(DoubleCheckLazy.create(charArrayProvider)); + instance.lazyShortArray(DoubleCheckLazy.create(shortArrayProvider)); + instance.lazyIntArray(DoubleCheckLazy.create(intArrayProvider)); + instance.lazyLongArray(DoubleCheckLazy.create(longArrayProvider)); + instance.lazyBooleanArray(DoubleCheckLazy.create(booleanArrayProvider)); + instance.lazy(DoubleCheckLazy.create(floatArrayAndLazyProvider)); + instance.lazyDoubleArray(DoubleCheckLazy.create(doubleArrayProvider)); + instance.thing(thingProvider.get()); + instance.thingProvider(thingProvider); + instance.lazyThing(DoubleCheckLazy.create(thingProvider)); + instance.thingMembersInjector(thingMembersInjector); + } + + public static MembersInjector<InjectedThing> create(Provider<Byte> boxedBypeAndPrimitiveByteAndByteProvider, Provider<Character> boxedCharAndPrimitiveCharAndCharProvider, Provider<Short> boxedShortAndPrimitiveShortAndShortProvider, Provider<Integer> boxedIntAndPrimitiveIntAndIntProvider, Provider<Long> boxedLongAndPrimitiveLongAndLongProvider, Provider<Boolean> boxedBooleanAndPrimitiveBooleanAndBooleanProvider, Provider<Float> boxedFloatAndPrimitiveFloatAndFloatProvider, Provider<Double> boxedDoubleAndPrimitiveDoubleAndDoubleProvider, Provider<byte[]> byteArrayProvider, Provider<char[]> charArrayProvider, Provider<short[]> shortArrayProvider, Provider<int[]> intArrayProvider, Provider<long[]> longArrayProvider, Provider<boolean[]> booleanArrayProvider, Provider<float[]> floatArrayAndLazyProvider, Provider<double[]> doubleArrayProvider, Provider<Thing> thingProvider, MembersInjector<Thing> thingMembersInjector) { + return new InjectedThing_MembersInjector(boxedBypeAndPrimitiveByteAndByteProvider, boxedCharAndPrimitiveCharAndCharProvider, boxedShortAndPrimitiveShortAndShortProvider, boxedIntAndPrimitiveIntAndIntProvider, boxedLongAndPrimitiveLongAndLongProvider, boxedBooleanAndPrimitiveBooleanAndBooleanProvider, boxedFloatAndPrimitiveFloatAndFloatProvider, boxedDoubleAndPrimitiveDoubleAndDoubleProvider, byteArrayProvider, charArrayProvider, shortArrayProvider, intArrayProvider, longArrayProvider, booleanArrayProvider, floatArrayAndLazyProvider, doubleArrayProvider, thingProvider, thingMembersInjector); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideBarKeyFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideBarKeyFactory.java new file mode 100644 index 000000000..85e6c03ea --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideBarKeyFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ProvideBarKeyFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ProvideBarKeyFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.provideBarKey(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ProvideBarKeyFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideFiveToSetFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideFiveToSetFactory.java new file mode 100644 index 000000000..5e6eb7465 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideFiveToSetFactory.java @@ -0,0 +1,26 @@ +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 MultibindingModule_ProvideFiveToSetFactory implements Factory<Set<Integer>> { + private final MultibindingModule module; + + public MultibindingModule_ProvideFiveToSetFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Integer> get() { + return Collections.<Integer>singleton(module.provideFiveToSet()); + } + + public static Factory<Set<Integer>> create(MultibindingModule module) { + return new MultibindingModule_ProvideFiveToSetFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideFooKeyFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideFooKeyFactory.java new file mode 100644 index 000000000..fc5765a5a --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideFooKeyFactory.java @@ -0,0 +1,32 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ProvideFooKeyFactory implements Factory<String> { + private final MultibindingModule module; + private final Provider<Double> doubleDependencyProvider; + + public MultibindingModule_ProvideFooKeyFactory(MultibindingModule module, Provider<Double> doubleDependencyProvider) { + assert module != null; + this.module = module; + assert doubleDependencyProvider != null; + this.doubleDependencyProvider = doubleDependencyProvider; + } + + @Override + public String get() { + String provided = module.provideFooKey(doubleDependencyProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module, Provider<Double> doubleDependencyProvider) { + return new MultibindingModule_ProvideFooKeyFactory(module, doubleDependencyProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideMapKeysFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideMapKeysFactory.java new file mode 100644 index 000000000..e77002741 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideMapKeysFactory.java @@ -0,0 +1,34 @@ +package test; + +import dagger.internal.Factory; +import java.util.Map; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ProvideMapKeysFactory implements Factory<Set<String>> { + private final MultibindingModule module; + private final Provider<Map<String, Provider<String>>> mapProvider; + + public MultibindingModule_ProvideMapKeysFactory(MultibindingModule module, Provider<Map<String, Provider<String>>> mapProvider) { + assert module != null; + this.module = module; + assert mapProvider != null; + this.mapProvider = mapProvider; + } + + @Override + public Set<String> get() { + Set<String> provided = module.provideMapKeys(mapProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Set<String>> create(MultibindingModule module, Provider<Map<String, Provider<String>>> mapProvider) { + return new MultibindingModule_ProvideMapKeysFactory(module, mapProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideMapValuesFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideMapValuesFactory.java new file mode 100644 index 000000000..d38ff0177 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideMapValuesFactory.java @@ -0,0 +1,34 @@ +package test; + +import dagger.internal.Factory; +import java.util.Collection; +import java.util.Map; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ProvideMapValuesFactory implements Factory<Collection<String>> { + private final MultibindingModule module; + private final Provider<Map<String, String>> mapProvider; + + public MultibindingModule_ProvideMapValuesFactory(MultibindingModule module, Provider<Map<String, String>> mapProvider) { + assert module != null; + this.module = module; + assert mapProvider != null; + this.mapProvider = mapProvider; + } + + @Override + public Collection<String> get() { + Collection<String> provided = module.provideMapValues(mapProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Collection<String>> create(MultibindingModule module, Provider<Map<String, String>> mapProvider) { + return new MultibindingModule_ProvideMapValuesFactory(module, mapProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideSixToSetFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideSixToSetFactory.java new file mode 100644 index 000000000..ede45a90a --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ProvideSixToSetFactory.java @@ -0,0 +1,26 @@ +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 MultibindingModule_ProvideSixToSetFactory implements Factory<Set<Integer>> { + private final MultibindingModule module; + + public MultibindingModule_ProvideSixToSetFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Integer> get() { + return Collections.<Integer>singleton(module.provideSixToSet()); + } + + public static Factory<Set<Integer>> create(MultibindingModule module) { + return new MultibindingModule_ProvideSixToSetFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100ByteFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100ByteFactory.java new file mode 100644 index 000000000..b56334ca9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100ByteFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueFor100ByteFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueFor100ByteFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueFor100Byte(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueFor100ByteFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100IntFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100IntFactory.java new file mode 100644 index 000000000..bd82beeeb --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100IntFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueFor100IntFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueFor100IntFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueFor100Int(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueFor100IntFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100LongFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100LongFactory.java new file mode 100644 index 000000000..9930a109b --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100LongFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueFor100LongFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueFor100LongFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueFor100Long(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueFor100LongFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100ShortFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100ShortFactory.java new file mode 100644 index 000000000..a4a7e3b11 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueFor100ShortFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueFor100ShortFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueFor100ShortFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueFor100Short(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueFor100ShortFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForAFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForAFactory.java new file mode 100644 index 000000000..622feddd2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForAFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForAFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForAFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForA(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForAFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForClassIntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForClassIntegerFactory.java new file mode 100644 index 000000000..be5081345 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForClassIntegerFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForClassIntegerFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForClassIntegerFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForClassInteger(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForClassIntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForClassLongFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForClassLongFactory.java new file mode 100644 index 000000000..c97cde628 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForClassLongFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForClassLongFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForClassLongFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForClassLong(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForClassLongFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForIntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForIntegerFactory.java new file mode 100644 index 000000000..cbdb2f9d3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForIntegerFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForIntegerFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForIntegerFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForInteger(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForIntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForLongFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForLongFactory.java new file mode 100644 index 000000000..dccab1af3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForLongFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForLongFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForLongFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForLong(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForLongFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNewlineFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNewlineFactory.java new file mode 100644 index 000000000..7b55a13e6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNewlineFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForNewlineFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForNewlineFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForNewline(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForNewlineFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNumberClassBigDecimalFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNumberClassBigDecimalFactory.java new file mode 100644 index 000000000..c85c73af8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNumberClassBigDecimalFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForNumberClassBigDecimalFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForNumberClassBigDecimalFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForNumberClassBigDecimal(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForNumberClassBigDecimalFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNumberClassBigIntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNumberClassBigIntegerFactory.java new file mode 100644 index 000000000..38bf88093 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForNumberClassBigIntegerFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForNumberClassBigIntegerFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForNumberClassBigIntegerFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForNumberClassBigInteger(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForNumberClassBigIntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForTrueFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForTrueFactory.java new file mode 100644 index 000000000..155601d05 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForTrueFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForTrueFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForTrueFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForTrue(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForTrueFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory.java new file mode 100644 index 000000000..bda513b3e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForUnwrappedAnnotationKeyFoo(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForUnwrappedAnnotationKeyFooFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForWrappedAnnotationKeyFooFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForWrappedAnnotationKeyFooFactory.java new file mode 100644 index 000000000..c92074ed5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/MultibindingModule_ValueForWrappedAnnotationKeyFooFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingModule_ValueForWrappedAnnotationKeyFooFactory implements Factory<String> { + private final MultibindingModule module; + + public MultibindingModule_ValueForWrappedAnnotationKeyFooFactory(MultibindingModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.valueForWrappedAnnotationKeyFoo(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(MultibindingModule module) { + return new MultibindingModule_ValueForWrappedAnnotationKeyFooFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_Factory.java new file mode 100644 index 000000000..72282478e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_Factory.java @@ -0,0 +1,25 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class NeedsFactory_Factory implements Factory<NeedsFactory> { + private final Provider<NeedsFactory_SomethingFactory> somethingFactoryProvider; + + public NeedsFactory_Factory(Provider<NeedsFactory_SomethingFactory> somethingFactoryProvider) { + assert somethingFactoryProvider != null; + this.somethingFactoryProvider = somethingFactoryProvider; + } + + @Override + public NeedsFactory get() { + return new NeedsFactory(somethingFactoryProvider.get()); + } + + public static Factory<NeedsFactory> create(Provider<NeedsFactory_SomethingFactory> somethingFactoryProvider) { + return new NeedsFactory_Factory(somethingFactoryProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_SomethingFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_SomethingFactory.java new file mode 100644 index 000000000..e26f39804 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_SomethingFactory.java @@ -0,0 +1,13 @@ +package test; + +import javax.annotation.Generated; +import javax.inject.Inject; +@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") +final class NeedsFactory_SomethingFactory { + @Inject + NeedsFactory_SomethingFactory() { + } + NeedsFactory.Something create() { + return new NeedsFactory.Something(); + } +} diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_SomethingFactory_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_SomethingFactory_Factory.java new file mode 100644 index 000000000..471304a31 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NeedsFactory_SomethingFactory_Factory.java @@ -0,0 +1,19 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum NeedsFactory_SomethingFactory_Factory implements Factory<NeedsFactory_SomethingFactory> { +INSTANCE; + + @Override + public NeedsFactory_SomethingFactory get() { + return new NeedsFactory_SomethingFactory(); + } + + public static Factory<NeedsFactory_SomethingFactory> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NonComponentDependencyComponent$ThingTwo_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NonComponentDependencyComponent$ThingTwo_Factory.java new file mode 100644 index 000000000..d0665558e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/NonComponentDependencyComponent$ThingTwo_Factory.java @@ -0,0 +1,33 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.NonComponentDependencyComponent.ThingComponent; +import test.NonComponentDependencyComponent.ThingTwo; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class NonComponentDependencyComponent$ThingTwo_Factory implements Factory<ThingTwo> { + private final Provider<Thing> thingProvider; + private final Provider<NonComponentDependencyComponent> nonComponentDependencyComponentProvider; + private final Provider<ThingComponent> thingComponentProvider; + + public NonComponentDependencyComponent$ThingTwo_Factory(Provider<Thing> thingProvider, Provider<NonComponentDependencyComponent> nonComponentDependencyComponentProvider, Provider<ThingComponent> thingComponentProvider) { + assert thingProvider != null; + this.thingProvider = thingProvider; + assert nonComponentDependencyComponentProvider != null; + this.nonComponentDependencyComponentProvider = nonComponentDependencyComponentProvider; + assert thingComponentProvider != null; + this.thingComponentProvider = thingComponentProvider; + } + + @Override + public ThingTwo get() { + return new ThingTwo(thingProvider.get(), nonComponentDependencyComponentProvider.get(), thingComponentProvider.get()); + } + + public static Factory<ThingTwo> create(Provider<Thing> thingProvider, Provider<NonComponentDependencyComponent> nonComponentDependencyComponentProvider, Provider<ThingComponent> thingComponentProvider) { + return new NonComponentDependencyComponent$ThingTwo_Factory(thingProvider, nonComponentDependencyComponentProvider, thingComponentProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ParentModule_ProvideIterableOfAWithCFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ParentModule_ProvideIterableOfAWithCFactory.java new file mode 100644 index 000000000..89a013ca2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ParentModule_ProvideIterableOfAWithCFactory.java @@ -0,0 +1,35 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ParentModule_ProvideIterableOfAWithCFactory<A extends Number & Comparable<A>, B, C extends Iterable<A>> implements Factory<Iterable<A>> { + private final ParentModule<A, B, C> module; + private final Provider<A> aProvider; + private final Provider<C> cProvider; + + public ParentModule_ProvideIterableOfAWithCFactory(ParentModule<A, B, C> module, Provider<A> aProvider, Provider<C> cProvider) { + assert module != null; + this.module = module; + assert aProvider != null; + this.aProvider = aProvider; + assert cProvider != null; + this.cProvider = cProvider; + } + + @Override + public Iterable<A> get() { + Iterable<A> provided = module.provideIterableOfAWithC(aProvider.get(), cProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static <A extends Number & Comparable<A>, B, C extends Iterable<A>> Factory<Iterable<A>> create(ParentModule<A, B, C> module, Provider<A> aProvider, Provider<C> cProvider) { + return new ParentModule_ProvideIterableOfAWithCFactory<A, B, C>(module, aProvider, cProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_BoundDoubleArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_BoundDoubleArrayFactory.java new file mode 100644 index 000000000..adc286bff --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_BoundDoubleArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_BoundDoubleArrayFactory implements Factory<double[]> { +INSTANCE; + + @Override + public double[] get() { + double[] provided = PrimitivesModule.boundDoubleArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<double[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_BoundDoubleFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_BoundDoubleFactory.java new file mode 100644 index 000000000..dd6ec9fb8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_BoundDoubleFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_BoundDoubleFactory implements Factory<Double> { +INSTANCE; + + @Override + public Double get() { + Double provided = PrimitivesModule.boundDouble(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Double> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideBooleanArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideBooleanArrayFactory.java new file mode 100644 index 000000000..886a13009 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideBooleanArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideBooleanArrayFactory implements Factory<boolean[]> { +INSTANCE; + + @Override + public boolean[] get() { + boolean[] provided = PrimitivesModule.provideBooleanArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<boolean[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideBooleanFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideBooleanFactory.java new file mode 100644 index 000000000..4f19803a0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideBooleanFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideBooleanFactory implements Factory<Boolean> { +INSTANCE; + + @Override + public Boolean get() { + Boolean provided = PrimitivesModule.provideBoolean(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Boolean> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideByteArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideByteArrayFactory.java new file mode 100644 index 000000000..0b2badc29 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideByteArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideByteArrayFactory implements Factory<byte[]> { +INSTANCE; + + @Override + public byte[] get() { + byte[] provided = PrimitivesModule.provideByteArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<byte[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideByteFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideByteFactory.java new file mode 100644 index 000000000..a3e07e2b6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideByteFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideByteFactory implements Factory<Byte> { +INSTANCE; + + @Override + public Byte get() { + Byte provided = PrimitivesModule.provideByte(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Byte> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideCharArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideCharArrayFactory.java new file mode 100644 index 000000000..91fe5f802 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideCharArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideCharArrayFactory implements Factory<char[]> { +INSTANCE; + + @Override + public char[] get() { + char[] provided = PrimitivesModule.provideCharArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<char[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideCharFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideCharFactory.java new file mode 100644 index 000000000..3bab0df7b --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideCharFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideCharFactory implements Factory<Character> { +INSTANCE; + + @Override + public Character get() { + Character provided = PrimitivesModule.provideChar(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Character> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideFloatArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideFloatArrayFactory.java new file mode 100644 index 000000000..6a0a3d58c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideFloatArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideFloatArrayFactory implements Factory<float[]> { +INSTANCE; + + @Override + public float[] get() { + float[] provided = PrimitivesModule.provideFloatArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<float[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideFloatFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideFloatFactory.java new file mode 100644 index 000000000..c24913b9f --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideFloatFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideFloatFactory implements Factory<Float> { +INSTANCE; + + @Override + public Float get() { + Float provided = PrimitivesModule.provideFloat(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Float> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideIntArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideIntArrayFactory.java new file mode 100644 index 000000000..37e594b63 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideIntArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideIntArrayFactory implements Factory<int[]> { +INSTANCE; + + @Override + public int[] get() { + int[] provided = PrimitivesModule.provideIntArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<int[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideIntFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideIntFactory.java new file mode 100644 index 000000000..8d8f83082 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideIntFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideIntFactory implements Factory<Integer> { +INSTANCE; + + @Override + public Integer get() { + Integer provided = PrimitivesModule.provideInt(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Integer> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideLongArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideLongArrayFactory.java new file mode 100644 index 000000000..190f69b31 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideLongArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideLongArrayFactory implements Factory<long[]> { +INSTANCE; + + @Override + public long[] get() { + long[] provided = PrimitivesModule.provideLongArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<long[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideLongFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideLongFactory.java new file mode 100644 index 000000000..9713a5642 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideLongFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideLongFactory implements Factory<Long> { +INSTANCE; + + @Override + public Long get() { + Long provided = PrimitivesModule.provideLong(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Long> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideShortArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideShortArrayFactory.java new file mode 100644 index 000000000..3d415e48d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideShortArrayFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideShortArrayFactory implements Factory<short[]> { +INSTANCE; + + @Override + public short[] get() { + short[] provided = PrimitivesModule.provideShortArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<short[]> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideShortFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideShortFactory.java new file mode 100644 index 000000000..2a956f1f6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/PrimitivesModule_ProvideShortFactory.java @@ -0,0 +1,23 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PrimitivesModule_ProvideShortFactory implements Factory<Short> { +INSTANCE; + + @Override + public Short get() { + Short provided = PrimitivesModule.provideShort(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Short> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ReferencesGeneric_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ReferencesGeneric_Factory.java new file mode 100644 index 000000000..1378694bf --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ReferencesGeneric_Factory.java @@ -0,0 +1,25 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ReferencesGeneric_Factory implements Factory<ReferencesGeneric> { + private final Provider<Generic<A>> genericAProvider; + + public ReferencesGeneric_Factory(Provider<Generic<A>> genericAProvider) { + assert genericAProvider != null; + this.genericAProvider = genericAProvider; + } + + @Override + public ReferencesGeneric get() { + return new ReferencesGeneric(genericAProvider.get()); + } + + public static Factory<ReferencesGeneric> create(Provider<Generic<A>> genericAProvider) { + return new ReferencesGeneric_Factory(genericAProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ScopedGeneric_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ScopedGeneric_Factory.java new file mode 100644 index 000000000..5a5be87a3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/ScopedGeneric_Factory.java @@ -0,0 +1,25 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ScopedGeneric_Factory<T> implements Factory<ScopedGeneric<T>> { + private final Provider<T> tProvider; + + public ScopedGeneric_Factory(Provider<T> tProvider) { + assert tProvider != null; + this.tProvider = tProvider; + } + + @Override + public ScopedGeneric<T> get() { + return new ScopedGeneric<T>(tProvider.get()); + } + + public static <T> Factory<ScopedGeneric<T>> create(Provider<T> tProvider) { + return new ScopedGeneric_Factory<T>(tProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestStringKey$NestedWrappedKeyCreator.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestStringKey$NestedWrappedKeyCreator.java new file mode 100644 index 000000000..2c73118de --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestStringKey$NestedWrappedKeyCreator.java @@ -0,0 +1,13 @@ +package test; + +import javax.annotation.Generated; +import test.TestStringKey.NestedWrappedKey; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class TestStringKey$NestedWrappedKeyCreator { + @com.google.auto.value.AutoAnnotation + public static NestedWrappedKey createNestedWrappedKey(Class<?> value) { + return new AutoAnnotation_TestStringKey$NestedWrappedKeyCreator_createNestedWrappedKey(value); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestUnwrappedAnnotationKeyCreator.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestUnwrappedAnnotationKeyCreator.java new file mode 100644 index 000000000..629fd91b4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestUnwrappedAnnotationKeyCreator.java @@ -0,0 +1,12 @@ +package test; + +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class TestUnwrappedAnnotationKeyCreator { + @com.google.auto.value.AutoAnnotation + public static TestStringKey createTestStringKey(String value) { + return new AutoAnnotation_TestUnwrappedAnnotationKeyCreator_createTestStringKey(value); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestWrappedAnnotationKeyCreator.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestWrappedAnnotationKeyCreator.java new file mode 100644 index 000000000..a0e1a9b50 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TestWrappedAnnotationKeyCreator.java @@ -0,0 +1,17 @@ +package test; + +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class TestWrappedAnnotationKeyCreator { + @com.google.auto.value.AutoAnnotation + public static TestWrappedAnnotationKey createTestWrappedAnnotationKey(TestStringKey value, int[] integers, TestClassKey[] annotations, Class<? extends Number>[] classes) { + return new AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestWrappedAnnotationKey(value, integers, annotations, classes); + } + + @com.google.auto.value.AutoAnnotation + public static TestStringKey createTestStringKey(String value) { + return new AutoAnnotation_TestWrappedAnnotationKeyCreator_createTestStringKey(value); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Thing_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Thing_Factory.java new file mode 100644 index 000000000..f05092217 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/Thing_Factory.java @@ -0,0 +1,26 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.sub.OtherThing; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class Thing_Factory implements Factory<Thing> { + private final Provider<OtherThing> unusedProvider; + + public Thing_Factory(Provider<OtherThing> unusedProvider) { + assert unusedProvider != null; + this.unusedProvider = unusedProvider; + } + + @Override + public Thing get() { + return new Thing(unusedProvider.get()); + } + + public static Factory<Thing> create(Provider<OtherThing> unusedProvider) { + return new Thing_Factory(unusedProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TypeWithInheritedMembersInjection_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TypeWithInheritedMembersInjection_Factory.java new file mode 100644 index 000000000..8540d629c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/TypeWithInheritedMembersInjection_Factory.java @@ -0,0 +1,27 @@ +package test; + +import dagger.MembersInjector; +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class TypeWithInheritedMembersInjection_Factory implements Factory<TypeWithInheritedMembersInjection> { + private final MembersInjector<TypeWithInheritedMembersInjection> membersInjector; + + public TypeWithInheritedMembersInjection_Factory(MembersInjector<TypeWithInheritedMembersInjection> membersInjector) { + assert membersInjector != null; + this.membersInjector = membersInjector; + } + + @Override + public TypeWithInheritedMembersInjection get() { + TypeWithInheritedMembersInjection instance = new TypeWithInheritedMembersInjection(); + membersInjector.injectMembers(instance); + return instance; + } + + public static Factory<TypeWithInheritedMembersInjection> create(MembersInjector<TypeWithInheritedMembersInjection> membersInjector) { + return new TypeWithInheritedMembersInjection_Factory(membersInjector); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/ByteModule_BFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/ByteModule_BFactory.java new file mode 100644 index 000000000..ca35d3902 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/ByteModule_BFactory.java @@ -0,0 +1,28 @@ +package test.builder; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ByteModule_BFactory implements Factory<Byte> { + private final ByteModule module; + + public ByteModule_BFactory(ByteModule module) { + assert module != null; + this.module = module; + } + + @Override + public Byte get() { + Byte provided = module.b(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Byte> create(ByteModule module) { + return new ByteModule_BFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerDepComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerDepComponent.java new file mode 100644 index 000000000..f5ad9a558 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerDepComponent.java @@ -0,0 +1,28 @@ +package test.builder; + +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerDepComponent implements DepComponent { + private DaggerDepComponent(Builder builder) { + assert builder != null; + } + + public static Builder builder() { + return new Builder(); + } + + public static DepComponent create() { + return builder().build(); + } + + public static final class Builder { + private Builder() { + } + + public DepComponent build() { + return new DaggerDepComponent(this); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerParentComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerParentComponent.java new file mode 100644 index 000000000..42c668a0e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerParentComponent.java @@ -0,0 +1,519 @@ +package test.builder; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.builder.Grandchild; +import test.builder.MiddleChild; +import test.builder.OtherMiddleChild; +import test.builder.TestChildComponentWithBuilderAbstractClass; +import test.builder.TestChildComponentWithBuilderInterface; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerParentComponent implements ParentComponent { + private DaggerParentComponent(Builder builder) { + assert builder != null; + } + + public static Builder builder() { + return new Builder(); + } + + public static ParentComponent create() { + return builder().build(); + } + + @Override + public TestChildComponentWithBuilderAbstractClass.Builder childAbstractClassBuilder() { + return new TestChildComponentWithBuilderAbstractClassBuilder(); + } + + @Override + public TestChildComponentWithBuilderInterface.Builder childInterfaceBuilder() { + return new TestChildComponentWithBuilderInterfaceBuilder(); + } + + @Override + public MiddleChild.Builder middleBuilder() { + return new MiddleChildBuilder(); + } + + @Override + public OtherMiddleChild.Builder otherBuilder() { + return new OtherMiddleChildBuilder(); + } + + public static final class Builder { + private Builder() { + } + + public ParentComponent build() { + return new DaggerParentComponent(this); + } + } + + private final class TestChildComponentWithBuilderAbstractClassImpl implements TestChildComponentWithBuilderAbstractClass { + private Provider<String> stringProvider; + private Provider<Integer> integerProvider; + private Provider<Long> lProvider; + private Provider<Float> fProvider; + private Provider<Double> dProvider; + private Provider<Byte> bProvider; + + private TestChildComponentWithBuilderAbstractClassImpl(TestChildComponentWithBuilderAbstractClassBuilder builder) { + assert builder != null; + initialize(builder); + } + + private void initialize(final TestChildComponentWithBuilderAbstractClassBuilder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + this.lProvider = LongModule_LFactory.create(builder.longModule); + this.fProvider = FloatModule_FFactory.create(builder.floatModule); + this.dProvider = DoubleModule_DFactory.create(builder.doubleModule); + this.bProvider = ByteModule_BFactory.create(builder.byteModule); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public long l() { + return lProvider.get(); + } + + @Override + public float f() { + return fProvider.get(); + } + + @Override + public double d() { + return dProvider.get(); + } + + @Override + public byte b() { + return bProvider.get(); + } + } + + private final class TestChildComponentWithBuilderAbstractClassBuilder extends TestChildComponentWithBuilderAbstractClass.Builder { + private StringModule stringModule; + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + private LongModule longModule; + private ByteModule byteModule; + + + @Override + public TestChildComponentWithBuilderAbstractClass build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + if (longModule == null) { + this.longModule = new LongModule(); + } + if (byteModule == null) { + throw new IllegalStateException("byteModule must be set"); + } + return new TestChildComponentWithBuilderAbstractClassImpl(this); + } + + @Override + public TestChildComponentWithBuilderAbstractClassBuilder setM1(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + + @Override + public TestChildComponentWithBuilderAbstractClassBuilder setM2(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + + @Override + public void setM3(DoubleModule doubleModule) { + if (doubleModule == null) { + throw new NullPointerException("doubleModule"); + } + this.doubleModule = doubleModule; + } + + @Override + public TestChildComponentWithBuilderAbstractClassBuilder set(FloatModule floatModule) { + if (floatModule == null) { + throw new NullPointerException("floatModule"); + } + this.floatModule = floatModule; + return this; + } + + @Override + public void set(ByteModule byteModule) { + if (byteModule == null) { + throw new NullPointerException("byteModule"); + } + this.byteModule = byteModule; + } + } + + private final class TestChildComponentWithBuilderInterfaceImpl implements TestChildComponentWithBuilderInterface { + private Provider<String> stringProvider; + private Provider<Integer> integerProvider; + private Provider<Long> lProvider; + private Provider<Float> fProvider; + private Provider<Double> dProvider; + private Provider<Byte> bProvider; + + private TestChildComponentWithBuilderInterfaceImpl(TestChildComponentWithBuilderInterfaceBuilder builder) { + assert builder != null; + initialize(builder); + } + + private void initialize(final TestChildComponentWithBuilderInterfaceBuilder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + this.lProvider = LongModule_LFactory.create(builder.longModule); + this.fProvider = FloatModule_FFactory.create(builder.floatModule); + this.dProvider = DoubleModule_DFactory.create(builder.doubleModule); + this.bProvider = ByteModule_BFactory.create(builder.byteModule); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public long l() { + return lProvider.get(); + } + + @Override + public float f() { + return fProvider.get(); + } + + @Override + public double d() { + return dProvider.get(); + } + + @Override + public byte b() { + return bProvider.get(); + } + } + + private final class TestChildComponentWithBuilderInterfaceBuilder implements TestChildComponentWithBuilderInterface.Builder { + private StringModule stringModule; + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + private LongModule longModule; + private ByteModule byteModule; + + + @Override + public TestChildComponentWithBuilderInterface build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + if (longModule == null) { + this.longModule = new LongModule(); + } + if (byteModule == null) { + throw new IllegalStateException("byteModule must be set"); + } + return new TestChildComponentWithBuilderInterfaceImpl(this); + } + + @Override + public TestChildComponentWithBuilderInterfaceBuilder setM1(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + + @Override + public TestChildComponentWithBuilderInterfaceBuilder setM2(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + + @Override + public void setM3(DoubleModule doubleModule) { + if (doubleModule == null) { + throw new NullPointerException("doubleModule"); + } + this.doubleModule = doubleModule; + } + + @Override + public TestChildComponentWithBuilderInterfaceBuilder set(FloatModule floatModule) { + if (floatModule == null) { + throw new NullPointerException("floatModule"); + } + this.floatModule = floatModule; + return this; + } + + @Override + public void set(ByteModule byteModule) { + if (byteModule == null) { + throw new NullPointerException("byteModule"); + } + this.byteModule = byteModule; + } + } + + private final class MiddleChildImpl implements MiddleChild { + private Provider<String> stringProvider; + + private MiddleChildImpl(MiddleChildBuilder builder) { + assert builder != null; + initialize(builder); + } + + private void initialize(final MiddleChildBuilder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public Grandchild.Builder grandchildBuilder() { + return new GrandchildBuilder(); + } + + private final class GrandchildImpl implements Grandchild { + private Provider<Integer> integerProvider; + + private GrandchildImpl(GrandchildBuilder builder) { + assert builder != null; + initialize(builder); + } + + private void initialize(final GrandchildBuilder builder) { + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public String s() { + return MiddleChildImpl.this.stringProvider.get(); + } + } + + private final class GrandchildBuilder implements Grandchild.Builder { + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + + + @Override + public Grandchild build() { + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + return new GrandchildImpl(this); + } + + @Override + public GrandchildBuilder set(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + } + } + + private final class MiddleChildBuilder implements MiddleChild.Builder { + private StringModule stringModule; + + + @Override + public MiddleChild build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + return new MiddleChildImpl(this); + } + + @Override + public MiddleChildBuilder set(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + } + + private final class OtherMiddleChildImpl implements OtherMiddleChild { + private Provider<Long> lProvider; + private Provider<String> stringProvider; + + private OtherMiddleChildImpl(OtherMiddleChildBuilder builder) { + assert builder != null; + initialize(builder); + } + + private void initialize(final OtherMiddleChildBuilder builder) { + this.lProvider = LongModule_LFactory.create(builder.longModule); + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + } + + @Override + public long l() { + return lProvider.get(); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public Grandchild.Builder grandchildBuilder() { + return new GrandchildBuilder(); + } + + private final class GrandchildImpl implements Grandchild { + private Provider<Integer> integerProvider; + + private GrandchildImpl(GrandchildBuilder builder) { + assert builder != null; + initialize(builder); + } + + private void initialize(final GrandchildBuilder builder) { + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public String s() { + return OtherMiddleChildImpl.this.stringProvider.get(); + } + } + + private final class GrandchildBuilder implements Grandchild.Builder { + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + + + @Override + public Grandchild build() { + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + return new GrandchildImpl(this); + } + + @Override + public GrandchildBuilder set(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + } + } + + private final class OtherMiddleChildBuilder implements OtherMiddleChild.Builder { + private StringModule stringModule; + private LongModule longModule; + + + @Override + public OtherMiddleChild build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + if (longModule == null) { + this.longModule = new LongModule(); + } + return new OtherMiddleChildImpl(this); + } + + @Override + public OtherMiddleChildBuilder set(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerParentOfGenericComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerParentOfGenericComponent.java new file mode 100644 index 000000000..d9f77a8cc --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerParentOfGenericComponent.java @@ -0,0 +1,104 @@ +package test.builder; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.builder.Grandchild; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerParentOfGenericComponent implements ParentOfGenericComponent { + private Provider<String> stringProvider; + + private DaggerParentOfGenericComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + } + + @Override + public Grandchild.Builder subcomponentBuilder() { + return new GrandchildBuilder(); + } + + public static final class Builder { + private StringModule stringModule; + + private Builder() { + } + + public ParentOfGenericComponent build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + return new DaggerParentOfGenericComponent(this); + } + + public Builder stringModule(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + } + + private final class GrandchildImpl implements Grandchild { + private Provider<Integer> integerProvider; + + private GrandchildImpl(GrandchildBuilder builder) { + assert builder != null; + initialize(builder); + } + + private void initialize(final GrandchildBuilder builder) { + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public String s() { + return DaggerParentOfGenericComponent.this.stringProvider.get(); + } + } + + private final class GrandchildBuilder implements Grandchild.Builder { + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + + + @Override + public Grandchild build() { + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + return new GrandchildImpl(this); + } + + @Override + public GrandchildBuilder set(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithBuilderAbstractClass.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithBuilderAbstractClass.java new file mode 100644 index 000000000..fb9d9b5e4 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithBuilderAbstractClass.java @@ -0,0 +1,124 @@ +package test.builder; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.builder.TestComponentWithBuilderAbstractClass; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerTestComponentWithBuilderAbstractClass extends TestComponentWithBuilderAbstractClass { + private Provider<String> stringProvider; + private Provider<Integer> integerProvider; + private Provider<Long> lProvider; + private Provider<Float> fProvider; + private Provider<Double> dProvider; + + private DaggerTestComponentWithBuilderAbstractClass(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static TestComponentWithBuilderAbstractClass.Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + this.lProvider = LongModule_LFactory.create(builder.longModule); + this.fProvider = FloatModule_FFactory.create(builder.floatModule); + this.dProvider = DoubleModule_DFactory.create(builder.doubleModule); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public long l() { + return lProvider.get(); + } + + @Override + public float f() { + return fProvider.get(); + } + + @Override + public double d() { + return dProvider.get(); + } + + private static final class Builder extends TestComponentWithBuilderAbstractClass.Builder { + private StringModule stringModule; + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + private LongModule longModule; + private DepComponent depComponent; + + + @Override + public TestComponentWithBuilderAbstractClass build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + if (longModule == null) { + this.longModule = new LongModule(); + } + if (depComponent == null) { + throw new IllegalStateException("depComponent must be set"); + } + return new DaggerTestComponentWithBuilderAbstractClass(this); + } + + @Override + public Builder stringModule(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + + @Override + public Builder intModule(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + + @Override + public void doubleModule(DoubleModule doubleModule) { + if (doubleModule == null) { + throw new NullPointerException("doubleModule"); + } + this.doubleModule = doubleModule; + } + + @Override + public void depComponent(DepComponent depComponent) { + if (depComponent == null) { + throw new NullPointerException("depComponent"); + } + this.depComponent = depComponent; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithBuilderInterface.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithBuilderInterface.java new file mode 100644 index 000000000..196019690 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithBuilderInterface.java @@ -0,0 +1,124 @@ +package test.builder; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.builder.TestComponentWithBuilderInterface; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerTestComponentWithBuilderInterface implements TestComponentWithBuilderInterface { + private Provider<String> stringProvider; + private Provider<Integer> integerProvider; + private Provider<Long> lProvider; + private Provider<Float> fProvider; + private Provider<Double> dProvider; + + private DaggerTestComponentWithBuilderInterface(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static TestComponentWithBuilderInterface.Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + this.lProvider = LongModule_LFactory.create(builder.longModule); + this.fProvider = FloatModule_FFactory.create(builder.floatModule); + this.dProvider = DoubleModule_DFactory.create(builder.doubleModule); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public long l() { + return lProvider.get(); + } + + @Override + public float f() { + return fProvider.get(); + } + + @Override + public double d() { + return dProvider.get(); + } + + private static final class Builder implements TestComponentWithBuilderInterface.Builder { + private StringModule stringModule; + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + private LongModule longModule; + private DepComponent depComponent; + + + @Override + public TestComponentWithBuilderInterface build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + if (longModule == null) { + this.longModule = new LongModule(); + } + if (depComponent == null) { + throw new IllegalStateException("depComponent must be set"); + } + return new DaggerTestComponentWithBuilderInterface(this); + } + + @Override + public Builder stringModule(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + + @Override + public Builder intModule(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + + @Override + public void doubleModule(DoubleModule doubleModule) { + if (doubleModule == null) { + throw new NullPointerException("doubleModule"); + } + this.doubleModule = doubleModule; + } + + @Override + public void depComponent(DepComponent depComponent) { + if (depComponent == null) { + throw new NullPointerException("depComponent"); + } + this.depComponent = depComponent; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass.java new file mode 100644 index 000000000..53d87c24d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithGenericBuilderAbstractClass.java @@ -0,0 +1,133 @@ +package test.builder; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.builder.TestComponentWithGenericBuilderAbstractClass; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerTestComponentWithGenericBuilderAbstractClass implements TestComponentWithGenericBuilderAbstractClass { + private Provider<String> stringProvider; + private Provider<Integer> integerProvider; + private Provider<Long> lProvider; + private Provider<Float> fProvider; + private Provider<Double> dProvider; + + private DaggerTestComponentWithGenericBuilderAbstractClass(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static TestComponentWithGenericBuilderAbstractClass.Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + this.lProvider = LongModule_LFactory.create(builder.longModule); + this.fProvider = FloatModule_FFactory.create(builder.floatModule); + this.dProvider = DoubleModule_DFactory.create(builder.doubleModule); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public long l() { + return lProvider.get(); + } + + @Override + public float f() { + return fProvider.get(); + } + + @Override + public double d() { + return dProvider.get(); + } + + private static final class Builder extends TestComponentWithGenericBuilderAbstractClass.Builder { + private StringModule stringModule; + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + private LongModule longModule; + private DepComponent depComponent; + + + @Override + public TestComponentWithGenericBuilderAbstractClass build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + if (longModule == null) { + this.longModule = new LongModule(); + } + if (depComponent == null) { + throw new IllegalStateException("depComponent must be set"); + } + return new DaggerTestComponentWithGenericBuilderAbstractClass(this); + } + + @Override + public Builder setM1(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + + @Override + public Builder setM2(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + + @Override + public void doubleModule(DoubleModule doubleModule) { + if (doubleModule == null) { + throw new NullPointerException("doubleModule"); + } + this.doubleModule = doubleModule; + } + + @Override + public Builder depComponent(FloatModule floatModule) { + if (floatModule == null) { + throw new NullPointerException("floatModule"); + } + this.floatModule = floatModule; + return this; + } + + @Override + public void depComponent(DepComponent depComponent) { + if (depComponent == null) { + throw new NullPointerException("depComponent"); + } + this.depComponent = depComponent; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithGenericBuilderInterface.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithGenericBuilderInterface.java new file mode 100644 index 000000000..89e92ab6f --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DaggerTestComponentWithGenericBuilderInterface.java @@ -0,0 +1,133 @@ +package test.builder; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.builder.TestComponentWithGenericBuilderInterface; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerTestComponentWithGenericBuilderInterface implements TestComponentWithGenericBuilderInterface { + private Provider<String> stringProvider; + private Provider<Integer> integerProvider; + private Provider<Long> lProvider; + private Provider<Float> fProvider; + private Provider<Double> dProvider; + + private DaggerTestComponentWithGenericBuilderInterface(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static TestComponentWithGenericBuilderInterface.Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.stringProvider = StringModule_StringFactory.create(builder.stringModule); + this.integerProvider = IntModuleIncludingDoubleAndFloat_IntegerFactory.create(builder.intModuleIncludingDoubleAndFloat); + this.lProvider = LongModule_LFactory.create(builder.longModule); + this.fProvider = FloatModule_FFactory.create(builder.floatModule); + this.dProvider = DoubleModule_DFactory.create(builder.doubleModule); + } + + @Override + public String s() { + return stringProvider.get(); + } + + @Override + public int i() { + return integerProvider.get(); + } + + @Override + public long l() { + return lProvider.get(); + } + + @Override + public float f() { + return fProvider.get(); + } + + @Override + public double d() { + return dProvider.get(); + } + + private static final class Builder implements TestComponentWithGenericBuilderInterface.Builder { + private StringModule stringModule; + private IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat; + private DoubleModule doubleModule; + private FloatModule floatModule; + private LongModule longModule; + private DepComponent depComponent; + + + @Override + public TestComponentWithGenericBuilderInterface build() { + if (stringModule == null) { + throw new IllegalStateException("stringModule must be set"); + } + if (intModuleIncludingDoubleAndFloat == null) { + throw new IllegalStateException("intModuleIncludingDoubleAndFloat must be set"); + } + if (doubleModule == null) { + this.doubleModule = new DoubleModule(); + } + if (floatModule == null) { + this.floatModule = new FloatModule(); + } + if (longModule == null) { + this.longModule = new LongModule(); + } + if (depComponent == null) { + throw new IllegalStateException("depComponent must be set"); + } + return new DaggerTestComponentWithGenericBuilderInterface(this); + } + + @Override + public Builder setM1(StringModule stringModule) { + if (stringModule == null) { + throw new NullPointerException("stringModule"); + } + this.stringModule = stringModule; + return this; + } + + @Override + public Builder setM2(IntModuleIncludingDoubleAndFloat intModuleIncludingDoubleAndFloat) { + if (intModuleIncludingDoubleAndFloat == null) { + throw new NullPointerException("intModuleIncludingDoubleAndFloat"); + } + this.intModuleIncludingDoubleAndFloat = intModuleIncludingDoubleAndFloat; + return this; + } + + @Override + public void doubleModule(DoubleModule doubleModule) { + if (doubleModule == null) { + throw new NullPointerException("doubleModule"); + } + this.doubleModule = doubleModule; + } + + @Override + public Builder set(FloatModule floatModule) { + if (floatModule == null) { + throw new NullPointerException("floatModule"); + } + this.floatModule = floatModule; + return this; + } + + @Override + public void depComponent(DepComponent depComponent) { + if (depComponent == null) { + throw new NullPointerException("depComponent"); + } + this.depComponent = depComponent; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DoubleModule_DFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DoubleModule_DFactory.java new file mode 100644 index 000000000..e5963cd9e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/DoubleModule_DFactory.java @@ -0,0 +1,28 @@ +package test.builder; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DoubleModule_DFactory implements Factory<Double> { + private final DoubleModule module; + + public DoubleModule_DFactory(DoubleModule module) { + assert module != null; + this.module = module; + } + + @Override + public Double get() { + Double provided = module.d(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Double> create(DoubleModule module) { + return new DoubleModule_DFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/FloatModule_FFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/FloatModule_FFactory.java new file mode 100644 index 000000000..c8ba14cae --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/FloatModule_FFactory.java @@ -0,0 +1,28 @@ +package test.builder; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class FloatModule_FFactory implements Factory<Float> { + private final FloatModule module; + + public FloatModule_FFactory(FloatModule module) { + assert module != null; + this.module = module; + } + + @Override + public Float get() { + Float provided = module.f(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Float> create(FloatModule module) { + return new FloatModule_FFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/IntModuleIncludingDoubleAndFloat_IntegerFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/IntModuleIncludingDoubleAndFloat_IntegerFactory.java new file mode 100644 index 000000000..2775053c9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/IntModuleIncludingDoubleAndFloat_IntegerFactory.java @@ -0,0 +1,28 @@ +package test.builder; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class IntModuleIncludingDoubleAndFloat_IntegerFactory implements Factory<Integer> { + private final IntModuleIncludingDoubleAndFloat module; + + public IntModuleIncludingDoubleAndFloat_IntegerFactory(IntModuleIncludingDoubleAndFloat module) { + assert module != null; + this.module = module; + } + + @Override + public Integer get() { + Integer provided = module.integer(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Integer> create(IntModuleIncludingDoubleAndFloat module) { + return new IntModuleIncludingDoubleAndFloat_IntegerFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/LongModule_LFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/LongModule_LFactory.java new file mode 100644 index 000000000..7f7220aeb --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/LongModule_LFactory.java @@ -0,0 +1,28 @@ +package test.builder; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class LongModule_LFactory implements Factory<Long> { + private final LongModule module; + + public LongModule_LFactory(LongModule module) { + assert module != null; + this.module = module; + } + + @Override + public Long get() { + Long provided = module.l(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Long> create(LongModule module) { + return new LongModule_LFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/StringModule_StringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/StringModule_StringFactory.java new file mode 100644 index 000000000..ca1f00dbc --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/StringModule_StringFactory.java @@ -0,0 +1,28 @@ +package test.builder; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class StringModule_StringFactory implements Factory<String> { + private final StringModule module; + + public StringModule_StringFactory(StringModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.string(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(StringModule module) { + return new StringModule_StringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/ContactDataStore_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/ContactDataStore_MembersInjector.java new file mode 100644 index 000000000..4590a8b01 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/ContactDataStore_MembersInjector.java @@ -0,0 +1,28 @@ +package test.builder.abstractinjectmethod; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ContactDataStore_MembersInjector implements MembersInjector<ContactDataStore> { + private final Provider<RestClient> restClientProvider; + + public ContactDataStore_MembersInjector(Provider<RestClient> restClientProvider) { + assert restClientProvider != null; + this.restClientProvider = restClientProvider; + } + + @Override + public void injectMembers(ContactDataStore instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.setRestClient(restClientProvider.get()); + } + + public static MembersInjector<ContactDataStore> create(Provider<RestClient> restClientProvider) { + return new ContactDataStore_MembersInjector(restClientProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/DaggerApiComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/DaggerApiComponent.java new file mode 100644 index 000000000..4fcb6e752 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/DaggerApiComponent.java @@ -0,0 +1,49 @@ +package test.builder.abstractinjectmethod; + +import dagger.MembersInjector; +import dagger.internal.MembersInjectors; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerApiComponent implements ApiComponent { + private MembersInjector<ContactDataStore> contactDataStoreMembersInjector; + private MembersInjector<CloudContactDataStore> cloudContactDataStoreMembersInjector; + + private DaggerApiComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static ApiComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.contactDataStoreMembersInjector = ContactDataStore_MembersInjector.create(RestClient_Factory.create()); + this.cloudContactDataStoreMembersInjector = MembersInjectors.delegatingTo(contactDataStoreMembersInjector); + } + + @Override + public void inject(ContactDataStore contactDataStore) { + contactDataStoreMembersInjector.injectMembers(contactDataStore); + } + + @Override + public void inject(CloudContactDataStore contactDataStore) { + cloudContactDataStoreMembersInjector.injectMembers(contactDataStore); + } + + public static final class Builder { + private Builder() { + } + + public ApiComponent build() { + return new DaggerApiComponent(this); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/RestClient_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/RestClient_Factory.java new file mode 100644 index 000000000..7e65f4945 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/abstractinjectmethod/RestClient_Factory.java @@ -0,0 +1,19 @@ +package test.builder.abstractinjectmethod; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum RestClient_Factory implements Factory<RestClient> { +INSTANCE; + + @Override + public RestClient get() { + return new RestClient(); + } + + public static Factory<RestClient> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ContactDataStore_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ContactDataStore_MembersInjector.java new file mode 100644 index 000000000..4c28a2c41 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ContactDataStore_MembersInjector.java @@ -0,0 +1,30 @@ +package test.builder.product; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.builder.abstractinjectmethod.ContactDataStore; +import test.builder.abstractinjectmethod.RestClient; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ContactDataStore_MembersInjector implements MembersInjector<ContactDataStore> { + private final Provider<RestClient> restClientProvider; + + public ContactDataStore_MembersInjector(Provider<RestClient> restClientProvider) { + assert restClientProvider != null; + this.restClientProvider = restClientProvider; + } + + @Override + public void injectMembers(ContactDataStore instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.setRestClient(restClientProvider.get()); + } + + public static MembersInjector<ContactDataStore> create(Provider<RestClient> restClientProvider) { + return new ContactDataStore_MembersInjector(restClientProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerApiComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerApiComponent.java new file mode 100644 index 000000000..d214e4645 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerApiComponent.java @@ -0,0 +1,43 @@ +package test.builder.product; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import test.builder.abstractinjectmethod.ApiComponent; +import test.builder.abstractinjectmethod.ContactDataStore; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerApiComponent implements ApiComponent { + private MembersInjector<ContactDataStore> contactDataStoreMembersInjector; + + private DaggerApiComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static ApiComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.contactDataStoreMembersInjector = ContactDataStore_MembersInjector.create(RestClient_Factory.create()); + } + + @Override + public void inject(ContactDataStore contactDataStore) { + contactDataStoreMembersInjector.injectMembers(contactDataStore); + } + + public static final class Builder { + private Builder() { + } + + public ApiComponent build() { + return new DaggerApiComponent(this); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerPackagerOneComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerPackagerOneComponent.java new file mode 100644 index 000000000..5e04ccc81 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerPackagerOneComponent.java @@ -0,0 +1,56 @@ +package test.builder.product; + +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerPackagerOneComponent implements PackagerOneComponent { + private Provider<Product> provideProductProvider; + private Provider<Packager> packagerProvider; + + private DaggerPackagerOneComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static PackagerOneComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideProductProvider = ProductOneModule_ProvideProductFactory.create(builder.productOneModule, ProductOne_Factory.create()); + this.packagerProvider = Packager_Factory.create(provideProductProvider); + } + + @Override + public Packager packager() { + return packagerProvider.get(); + } + + public static final class Builder { + private ProductOneModule productOneModule; + + private Builder() { + } + + public PackagerOneComponent build() { + if (productOneModule == null) { + this.productOneModule = new ProductOneModule(); + } + return new DaggerPackagerOneComponent(this); + } + + public Builder productOneModule(ProductOneModule productOneModule) { + if (productOneModule == null) { + throw new NullPointerException("productOneModule"); + } + this.productOneModule = productOneModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerPackagerTwoComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerPackagerTwoComponent.java new file mode 100644 index 000000000..a691f2399 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/DaggerPackagerTwoComponent.java @@ -0,0 +1,56 @@ +package test.builder.product; + +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerPackagerTwoComponent implements PackagerTwoComponent { + private Provider<Product> provideProductProvider; + private Provider<Packager> packagerProvider; + + private DaggerPackagerTwoComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static PackagerTwoComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideProductProvider = ProductTwoModule_ProvideProductFactory.create(builder.productTwoModule, ProductTwo_Factory.create()); + this.packagerProvider = Packager_Factory.create(provideProductProvider); + } + + @Override + public Packager packager() { + return packagerProvider.get(); + } + + public static final class Builder { + private ProductTwoModule productTwoModule; + + private Builder() { + } + + public PackagerTwoComponent build() { + if (productTwoModule == null) { + this.productTwoModule = new ProductTwoModule(); + } + return new DaggerPackagerTwoComponent(this); + } + + public Builder productTwoModule(ProductTwoModule productTwoModule) { + if (productTwoModule == null) { + throw new NullPointerException("productTwoModule"); + } + this.productTwoModule = productTwoModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/Packager_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/Packager_Factory.java new file mode 100644 index 000000000..8ce215437 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/Packager_Factory.java @@ -0,0 +1,25 @@ +package test.builder.product; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class Packager_Factory implements Factory<Packager> { + private final Provider<Product> productProvider; + + public Packager_Factory(Provider<Product> productProvider) { + assert productProvider != null; + this.productProvider = productProvider; + } + + @Override + public Packager get() { + return new Packager(productProvider.get()); + } + + public static Factory<Packager> create(Provider<Product> productProvider) { + return new Packager_Factory(productProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductOneModule_ProvideProductFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductOneModule_ProvideProductFactory.java new file mode 100644 index 000000000..99578a483 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductOneModule_ProvideProductFactory.java @@ -0,0 +1,32 @@ +package test.builder.product; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ProductOneModule_ProvideProductFactory implements Factory<Product> { + private final ProductOneModule module; + private final Provider<ProductOne> productOneProvider; + + public ProductOneModule_ProvideProductFactory(ProductOneModule module, Provider<ProductOne> productOneProvider) { + assert module != null; + this.module = module; + assert productOneProvider != null; + this.productOneProvider = productOneProvider; + } + + @Override + public Product get() { + Product provided = module.provideProduct(productOneProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Product> create(ProductOneModule module, Provider<ProductOne> productOneProvider) { + return new ProductOneModule_ProvideProductFactory(module, productOneProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductOne_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductOne_Factory.java new file mode 100644 index 000000000..a9d0a0524 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductOne_Factory.java @@ -0,0 +1,19 @@ +package test.builder.product; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum ProductOne_Factory implements Factory<ProductOne> { +INSTANCE; + + @Override + public ProductOne get() { + return new ProductOne(); + } + + public static Factory<ProductOne> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductTwoModule_ProvideProductFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductTwoModule_ProvideProductFactory.java new file mode 100644 index 000000000..131a6199d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductTwoModule_ProvideProductFactory.java @@ -0,0 +1,32 @@ +package test.builder.product; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ProductTwoModule_ProvideProductFactory implements Factory<Product> { + private final ProductTwoModule module; + private final Provider<ProductTwo> productTwoProvider; + + public ProductTwoModule_ProvideProductFactory(ProductTwoModule module, Provider<ProductTwo> productTwoProvider) { + assert module != null; + this.module = module; + assert productTwoProvider != null; + this.productTwoProvider = productTwoProvider; + } + + @Override + public Product get() { + Product provided = module.provideProduct(productTwoProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Product> create(ProductTwoModule module, Provider<ProductTwo> productTwoProvider) { + return new ProductTwoModule_ProvideProductFactory(module, productTwoProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductTwo_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductTwo_Factory.java new file mode 100644 index 000000000..dca46d756 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/ProductTwo_Factory.java @@ -0,0 +1,19 @@ +package test.builder.product; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum ProductTwo_Factory implements Factory<ProductTwo> { +INSTANCE; + + @Override + public ProductTwo get() { + return new ProductTwo(); + } + + public static Factory<ProductTwo> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/RestClient_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/RestClient_Factory.java new file mode 100644 index 000000000..6ce80ced5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/product/RestClient_Factory.java @@ -0,0 +1,20 @@ +package test.builder.product; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import test.builder.abstractinjectmethod.RestClient; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum RestClient_Factory implements Factory<RestClient> { +INSTANCE; + + @Override + public RestClient get() { + return new RestClient(); + } + + public static Factory<RestClient> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/DaggerRootComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/DaggerRootComponent.java new file mode 100644 index 000000000..07e359302 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/DaggerRootComponent.java @@ -0,0 +1,69 @@ +package test.builder.subcomponent; + +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerRootComponent implements RootComponent { + private Provider<String> providesStringProvider; + + private DaggerRootComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.providesStringProvider = ModuleWithParameter_ProvidesStringFactory.create(builder.moduleWithParameter); + } + + @Override + public String string() { + return providesStringProvider.get(); + } + + @Override + public SubComponent newSubComponent() { + return new SubComponentImpl(); + } + + public static final class Builder { + private ModuleWithParameter moduleWithParameter; + + private Builder() { + } + + public RootComponent build() { + if (moduleWithParameter == null) { + throw new IllegalStateException("moduleWithParameter must be set"); + } + return new DaggerRootComponent(this); + } + + public Builder moduleWithParameter(ModuleWithParameter moduleWithParameter) { + if (moduleWithParameter == null) { + throw new NullPointerException("moduleWithParameter"); + } + this.moduleWithParameter = moduleWithParameter; + return this; + } + } + + private final class SubComponentImpl implements SubComponent { + private SubComponentImpl() { + initialize(); + } + + private void initialize() { + } + + @Override + public String string() { + return DaggerRootComponent.this.providesStringProvider.get(); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/DaggerSubComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/DaggerSubComponent.java new file mode 100644 index 000000000..86ae65f4c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/DaggerSubComponent.java @@ -0,0 +1,50 @@ +package test.builder.subcomponent; + +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerSubComponent implements SubComponent { + private Provider<String> providesStringProvider; + + private DaggerSubComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.providesStringProvider = ModuleWithParameter_ProvidesStringFactory.create(builder.moduleWithParameter); + } + + @Override + public String string() { + return providesStringProvider.get(); + } + + public static final class Builder { + private ModuleWithParameter moduleWithParameter; + + private Builder() { + } + + public SubComponent build() { + if (moduleWithParameter == null) { + throw new IllegalStateException("moduleWithParameter must be set"); + } + return new DaggerSubComponent(this); + } + + public Builder moduleWithParameter(ModuleWithParameter moduleWithParameter) { + if (moduleWithParameter == null) { + throw new NullPointerException("moduleWithParameter"); + } + this.moduleWithParameter = moduleWithParameter; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/ModuleWithParameter_ProvidesStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/ModuleWithParameter_ProvidesStringFactory.java new file mode 100644 index 000000000..df0f9b0b1 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/builder/subcomponent/ModuleWithParameter_ProvidesStringFactory.java @@ -0,0 +1,28 @@ +package test.builder.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ModuleWithParameter_ProvidesStringFactory implements Factory<String> { + private final ModuleWithParameter module; + + public ModuleWithParameter_ProvidesStringFactory(ModuleWithParameter module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.providesString(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(ModuleWithParameter module) { + return new ModuleWithParameter_ProvidesStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/DaggerMembersInjectComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/DaggerMembersInjectComponent.java new file mode 100644 index 000000000..c566f6c63 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/DaggerMembersInjectComponent.java @@ -0,0 +1,82 @@ +package test.membersinject; + +import dagger.MembersInjector; +import dagger.internal.MembersInjectors; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerMembersInjectComponent implements MembersInjectComponent { + private Provider<String[]> provideStringArrayProvider; + private MembersInjector<MembersInjectGenericParent<String[]>> membersInjectGenericParentMembersInjector; + private MembersInjector<ChildOfStringArray> childOfStringArrayMembersInjector; + private Provider<MembersInjectGenericParent<String[]>[]> provideFooArrayOfStringArrayProvider; + private MembersInjector<MembersInjectGenericParent<MembersInjectGenericParent<String[]>[]>> membersInjectGenericParentMembersInjector1; + private MembersInjector<ChildOfArrayOfParentOfStringArray> childOfArrayOfParentOfStringArrayMembersInjector; + private Provider<int[]> provideIntArrayProvider; + private MembersInjector<MembersInjectGenericParent<int[]>> membersInjectGenericParentMembersInjector2; + private MembersInjector<ChildOfPrimitiveIntArray> childOfPrimitiveIntArrayMembersInjector; + + private DaggerMembersInjectComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static MembersInjectComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideStringArrayProvider = MembersInjectModule_ProvideStringArrayFactory.create(builder.membersInjectModule); + this.membersInjectGenericParentMembersInjector = MembersInjectGenericParent_MembersInjector.create(provideStringArrayProvider); + this.childOfStringArrayMembersInjector = MembersInjectors.delegatingTo(membersInjectGenericParentMembersInjector); + this.provideFooArrayOfStringArrayProvider = MembersInjectModule_ProvideFooArrayOfStringArrayFactory.create(builder.membersInjectModule); + this.membersInjectGenericParentMembersInjector1 = MembersInjectGenericParent_MembersInjector.create(provideFooArrayOfStringArrayProvider); + this.childOfArrayOfParentOfStringArrayMembersInjector = MembersInjectors.delegatingTo(membersInjectGenericParentMembersInjector1); + this.provideIntArrayProvider = MembersInjectModule_ProvideIntArrayFactory.create(builder.membersInjectModule); + this.membersInjectGenericParentMembersInjector2 = MembersInjectGenericParent_MembersInjector.create(provideIntArrayProvider); + this.childOfPrimitiveIntArrayMembersInjector = MembersInjectors.delegatingTo(membersInjectGenericParentMembersInjector2); + } + + @Override + public void inject(ChildOfStringArray subfoo) { + childOfStringArrayMembersInjector.injectMembers(subfoo); + } + + @Override + public void inject(ChildOfArrayOfParentOfStringArray subfoo) { + childOfArrayOfParentOfStringArrayMembersInjector.injectMembers(subfoo); + } + + @Override + public void inject(ChildOfPrimitiveIntArray subfoo) { + childOfPrimitiveIntArrayMembersInjector.injectMembers(subfoo); + } + + public static final class Builder { + private MembersInjectModule membersInjectModule; + + private Builder() { + } + + public MembersInjectComponent build() { + if (membersInjectModule == null) { + this.membersInjectModule = new MembersInjectModule(); + } + return new DaggerMembersInjectComponent(this); + } + + public Builder membersInjectModule(MembersInjectModule membersInjectModule) { + if (membersInjectModule == null) { + throw new NullPointerException("membersInjectModule"); + } + this.membersInjectModule = membersInjectModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectGenericParent_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectGenericParent_MembersInjector.java new file mode 100644 index 000000000..8b3624a33 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectGenericParent_MembersInjector.java @@ -0,0 +1,28 @@ +package test.membersinject; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MembersInjectGenericParent_MembersInjector<T> implements MembersInjector<MembersInjectGenericParent<T>> { + private final Provider<T> tProvider; + + public MembersInjectGenericParent_MembersInjector(Provider<T> tProvider) { + assert tProvider != null; + this.tProvider = tProvider; + } + + @Override + public void injectMembers(MembersInjectGenericParent<T> instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.t = tProvider.get(); + } + + public static <T> MembersInjector<MembersInjectGenericParent<T>> create(Provider<T> tProvider) { + return new MembersInjectGenericParent_MembersInjector<T>(tProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideFooArrayOfStringArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideFooArrayOfStringArrayFactory.java new file mode 100644 index 000000000..59719859c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideFooArrayOfStringArrayFactory.java @@ -0,0 +1,28 @@ +package test.membersinject; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MembersInjectModule_ProvideFooArrayOfStringArrayFactory implements Factory<MembersInjectGenericParent<String[]>[]> { + private final MembersInjectModule module; + + public MembersInjectModule_ProvideFooArrayOfStringArrayFactory(MembersInjectModule module) { + assert module != null; + this.module = module; + } + + @Override + public MembersInjectGenericParent<String[]>[] get() { + MembersInjectGenericParent<String[]>[] provided = module.provideFooArrayOfStringArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<MembersInjectGenericParent<String[]>[]> create(MembersInjectModule module) { + return new MembersInjectModule_ProvideFooArrayOfStringArrayFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideIntArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideIntArrayFactory.java new file mode 100644 index 000000000..c5fef425b --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideIntArrayFactory.java @@ -0,0 +1,28 @@ +package test.membersinject; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MembersInjectModule_ProvideIntArrayFactory implements Factory<int[]> { + private final MembersInjectModule module; + + public MembersInjectModule_ProvideIntArrayFactory(MembersInjectModule module) { + assert module != null; + this.module = module; + } + + @Override + public int[] get() { + int[] provided = module.provideIntArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<int[]> create(MembersInjectModule module) { + return new MembersInjectModule_ProvideIntArrayFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideStringArrayFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideStringArrayFactory.java new file mode 100644 index 000000000..76b083da2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/membersinject/MembersInjectModule_ProvideStringArrayFactory.java @@ -0,0 +1,28 @@ +package test.membersinject; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MembersInjectModule_ProvideStringArrayFactory implements Factory<String[]> { + private final MembersInjectModule module; + + public MembersInjectModule_ProvideStringArrayFactory(MembersInjectModule module) { + assert module != null; + this.module = module; + } + + @Override + public String[] get() { + String[] provided = module.provideStringArray(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String[]> create(MembersInjectModule module) { + return new MembersInjectModule_ProvideStringArrayFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/DaggerFooComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/DaggerFooComponent.java new file mode 100644 index 000000000..e8a396f73 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/DaggerFooComponent.java @@ -0,0 +1,130 @@ +package test.multipackage; + +import dagger.internal.SetFactory; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.multipackage.a.AModule; +import test.multipackage.a.AModule_ProvideStringFactory; +import test.multipackage.b.BModule; +import test.multipackage.b.BModule_ProvideStringFactory; +import test.multipackage.c.CModule; +import test.multipackage.c.CModule_ProvideStringFactory; +import test.multipackage.d.DModule; +import test.multipackage.d.DModule_ProvideStringFactory; +import test.multipackage.foo.Foo; +import test.multipackage.foo.Foo_Factory; +import test.multipackage.grandsub.FooGrandchildComponent; +import test.multipackage.sub.FooChildComponent; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerFooComponent implements FooComponent { + private Provider<Set<String>> setOfStringContribution1Provider; + private Provider<Set<String>> setOfStringProvider; + + private DaggerFooComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static FooComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.setOfStringContribution1Provider = AModule_ProvideStringFactory.create(builder.aModule); + this.setOfStringProvider = SetFactory.create(setOfStringContribution1Provider); + } + + @Override + public Set<String> setOfString() { + return setOfStringProvider.get(); + } + + @Override + public FooChildComponent fooChildComponent() { + return new FooChildComponentImpl(); + } + + public static final class Builder { + private AModule aModule; + + private Builder() { + } + + public FooComponent build() { + if (aModule == null) { + this.aModule = new AModule(); + } + return new DaggerFooComponent(this); + } + + public Builder aModule(AModule aModule) { + if (aModule == null) { + throw new NullPointerException("aModule"); + } + this.aModule = aModule; + return this; + } + } + + private final class FooChildComponentImpl implements FooChildComponent { + private final BModule bModule; + private final CModule cModule; + private Provider<Set<String>> setOfStringContribution2Provider; + private Provider<Set<String>> setOfStringContribution3Provider; + private Provider<Set<String>> setOfStringProvider; + private Provider<Foo<FooChildComponent>> fooProvider; + + private FooChildComponentImpl() { + this.bModule = new BModule(); + this.cModule = new CModule(); + initialize(); + } + + private void initialize() { + this.setOfStringContribution2Provider = BModule_ProvideStringFactory.create(bModule); + this.setOfStringContribution3Provider = CModule_ProvideStringFactory.create(cModule); + this.setOfStringProvider = SetFactory.create(setOfStringContribution1Provider, setOfStringContribution2Provider, setOfStringContribution3Provider); + this.fooProvider = Foo_Factory.create(setOfStringProvider); + } + + @Override + public Foo<FooChildComponent> foo() { + return fooProvider.get(); + } + + @Override + public FooGrandchildComponent fooGrandchildComponent() { + return new FooGrandchildComponentImpl(); + } + + private final class FooGrandchildComponentImpl implements FooGrandchildComponent { + private final DModule dModule; + private Provider<Set<String>> setOfStringContribution4Provider; + private Provider<Set<String>> setOfStringProvider; + private Provider<Foo<FooGrandchildComponent>> fooProvider; + + private FooGrandchildComponentImpl() { + this.dModule = new DModule(); + initialize(); + } + + private void initialize() { + this.setOfStringContribution4Provider = DModule_ProvideStringFactory.create(dModule); + this.setOfStringProvider = SetFactory.create(setOfStringContribution1Provider, setOfStringContribution2Provider, setOfStringContribution3Provider, setOfStringContribution4Provider); + this.fooProvider = Foo_Factory.create(setOfStringProvider); + } + + @Override + public Foo<FooGrandchildComponent> foo() { + return fooProvider.get(); + } + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/a/AModule_ProvideStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/a/AModule_ProvideStringFactory.java new file mode 100644 index 000000000..eaa2ab79b --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/a/AModule_ProvideStringFactory.java @@ -0,0 +1,26 @@ +package test.multipackage.a; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class AModule_ProvideStringFactory implements Factory<Set<String>> { + private final AModule module; + + public AModule_ProvideStringFactory(AModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<String> get() { + return Collections.<String>singleton(module.provideString()); + } + + public static Factory<Set<String>> create(AModule module) { + return new AModule_ProvideStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/b/BModule_ProvideStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/b/BModule_ProvideStringFactory.java new file mode 100644 index 000000000..5a53cff51 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/b/BModule_ProvideStringFactory.java @@ -0,0 +1,26 @@ +package test.multipackage.b; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class BModule_ProvideStringFactory implements Factory<Set<String>> { + private final BModule module; + + public BModule_ProvideStringFactory(BModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<String> get() { + return Collections.<String>singleton(module.provideString()); + } + + public static Factory<Set<String>> create(BModule module) { + return new BModule_ProvideStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/c/CModule_ProvideStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/c/CModule_ProvideStringFactory.java new file mode 100644 index 000000000..516deaaa6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/c/CModule_ProvideStringFactory.java @@ -0,0 +1,26 @@ +package test.multipackage.c; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class CModule_ProvideStringFactory implements Factory<Set<String>> { + private final CModule module; + + public CModule_ProvideStringFactory(CModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<String> get() { + return Collections.<String>singleton(module.provideString()); + } + + public static Factory<Set<String>> create(CModule module) { + return new CModule_ProvideStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/d/DModule_ProvideStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/d/DModule_ProvideStringFactory.java new file mode 100644 index 000000000..b0abcd366 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/d/DModule_ProvideStringFactory.java @@ -0,0 +1,26 @@ +package test.multipackage.d; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DModule_ProvideStringFactory implements Factory<Set<String>> { + private final DModule module; + + public DModule_ProvideStringFactory(DModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<String> get() { + return Collections.<String>singleton(module.provideString()); + } + + public static Factory<Set<String>> create(DModule module) { + return new DModule_ProvideStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/foo/Foo_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/foo/Foo_Factory.java new file mode 100644 index 000000000..2d4f3c201 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/multipackage/foo/Foo_Factory.java @@ -0,0 +1,26 @@ +package test.multipackage.foo; + +import dagger.internal.Factory; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class Foo_Factory<T> implements Factory<Foo<T>> { + private final Provider<Set<String>> stringsProvider; + + public Foo_Factory(Provider<Set<String>> stringsProvider) { + assert stringsProvider != null; + this.stringsProvider = stringsProvider; + } + + @Override + public Foo<T> get() { + return new Foo<T>(stringsProvider.get()); + } + + public static <T> Factory<Foo<T>> create(Provider<Set<String>> stringsProvider) { + return new Foo_Factory<T>(stringsProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/DaggerNullComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/DaggerNullComponent.java new file mode 100644 index 000000000..e7c7d219a --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/DaggerNullComponent.java @@ -0,0 +1,81 @@ +package test.nullables; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerNullComponent implements NullComponent { + private Provider<String> provideNullableStringProvider; + private Provider<Number> provideNumberProvider; + private MembersInjector<NullFoo> nullFooMembersInjector; + private Provider<NullFoo> nullFooProvider; + + private DaggerNullComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static NullComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideNullableStringProvider = NullModule_ProvideNullableStringFactory.create(builder.nullModule); + this.provideNumberProvider = NullModule_ProvideNumberFactory.create(builder.nullModule); + this.nullFooMembersInjector = NullFoo_MembersInjector.create(provideNullableStringProvider, provideNumberProvider); + this.nullFooProvider = NullFoo_Factory.create(nullFooMembersInjector, provideNullableStringProvider, provideNumberProvider); + } + + @Override + public NullFoo nullFoo() { + return nullFooProvider.get(); + } + + @Override + public String string() { + return provideNullableStringProvider.get(); + } + + @Override + public Provider<String> stringProvider() { + return provideNullableStringProvider; + } + + @Override + public Number number() { + return provideNumberProvider.get(); + } + + @Override + public Provider<Number> numberProvider() { + return provideNumberProvider; + } + + public static final class Builder { + private NullModule nullModule; + + private Builder() { + } + + public NullComponent build() { + if (nullModule == null) { + this.nullModule = new NullModule(); + } + return new DaggerNullComponent(this); + } + + public Builder nullModule(NullModule nullModule) { + if (nullModule == null) { + throw new NullPointerException("nullModule"); + } + this.nullModule = nullModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/DaggerNullComponentWithDependency.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/DaggerNullComponentWithDependency.java new file mode 100644 index 000000000..8c44e045a --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/DaggerNullComponentWithDependency.java @@ -0,0 +1,82 @@ +package test.nullables; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerNullComponentWithDependency implements NullComponentWithDependency { + private Provider<String> stringProvider; + private Provider<Number> numberProvider; + + private DaggerNullComponentWithDependency(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.stringProvider = new Factory<String>() { + private final NullComponent nullComponent = builder.nullComponent; + @Nullable @Override public String get() { + return nullComponent.string(); + } + }; + this.numberProvider = new Factory<Number>() { + private final NullComponent nullComponent = builder.nullComponent; + @Override public Number get() { + Number provided = nullComponent.number(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable component method"); + } + return provided; + } + }; + } + + @Override + public String string() { + return stringProvider.get(); + } + + @Override + public Provider<String> stringProvider() { + return stringProvider; + } + + @Override + public Number number() { + return numberProvider.get(); + } + + @Override + public Provider<Number> numberProvider() { + return numberProvider; + } + + public static final class Builder { + private NullComponent nullComponent; + + private Builder() { + } + + public NullComponentWithDependency build() { + if (nullComponent == null) { + throw new IllegalStateException("nullComponent must be set"); + } + return new DaggerNullComponentWithDependency(this); + } + + public Builder nullComponent(NullComponent nullComponent) { + if (nullComponent == null) { + throw new NullPointerException("nullComponent"); + } + this.nullComponent = nullComponent; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullFoo_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullFoo_Factory.java new file mode 100644 index 000000000..3795e8776 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullFoo_Factory.java @@ -0,0 +1,34 @@ +package test.nullables; + +import dagger.MembersInjector; +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class NullFoo_Factory implements Factory<NullFoo> { + private final MembersInjector<NullFoo> membersInjector; + private final Provider<String> stringProvider; + private final Provider<Number> numberProvider; + + public NullFoo_Factory(MembersInjector<NullFoo> membersInjector, Provider<String> stringProvider, Provider<Number> numberProvider) { + assert membersInjector != null; + this.membersInjector = membersInjector; + assert stringProvider != null; + this.stringProvider = stringProvider; + assert numberProvider != null; + this.numberProvider = numberProvider; + } + + @Override + public NullFoo get() { + NullFoo instance = new NullFoo(stringProvider.get(), stringProvider, numberProvider.get(), numberProvider); + membersInjector.injectMembers(instance); + return instance; + } + + public static Factory<NullFoo> create(MembersInjector<NullFoo> membersInjector, Provider<String> stringProvider, Provider<Number> numberProvider) { + return new NullFoo_Factory(membersInjector, stringProvider, numberProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullFoo_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullFoo_MembersInjector.java new file mode 100644 index 000000000..d16b41c46 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullFoo_MembersInjector.java @@ -0,0 +1,35 @@ +package test.nullables; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class NullFoo_MembersInjector implements MembersInjector<NullFoo> { + private final Provider<String> fieldInjectedStringAndStringProvider; + private final Provider<Number> fieldInjectedNumberAndNumberProvider; + + public NullFoo_MembersInjector(Provider<String> fieldInjectedStringAndStringProvider, Provider<Number> fieldInjectedNumberAndNumberProvider) { + assert fieldInjectedStringAndStringProvider != null; + this.fieldInjectedStringAndStringProvider = fieldInjectedStringAndStringProvider; + assert fieldInjectedNumberAndNumberProvider != null; + this.fieldInjectedNumberAndNumberProvider = fieldInjectedNumberAndNumberProvider; + } + + @Override + public void injectMembers(NullFoo instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.fieldInjectedString = fieldInjectedStringAndStringProvider.get(); + instance.fieldInjectedStringProvider = fieldInjectedStringAndStringProvider; + instance.fieldInjectedNumber = fieldInjectedNumberAndNumberProvider.get(); + instance.fieldInjectedNumberProvider = fieldInjectedNumberAndNumberProvider; + instance.inject(fieldInjectedStringAndStringProvider.get(), fieldInjectedStringAndStringProvider, fieldInjectedNumberAndNumberProvider.get(), fieldInjectedNumberAndNumberProvider); + } + + public static MembersInjector<NullFoo> create(Provider<String> fieldInjectedStringAndStringProvider, Provider<Number> fieldInjectedNumberAndNumberProvider) { + return new NullFoo_MembersInjector(fieldInjectedStringAndStringProvider, fieldInjectedNumberAndNumberProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullModule_ProvideNullableStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullModule_ProvideNullableStringFactory.java new file mode 100644 index 000000000..f853f1df2 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullModule_ProvideNullableStringFactory.java @@ -0,0 +1,25 @@ +package test.nullables; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class NullModule_ProvideNullableStringFactory implements Factory<String> { + private final NullModule module; + + public NullModule_ProvideNullableStringFactory(NullModule module) { + assert module != null; + this.module = module; + } + + @Override + @Nullable + public String get() { + return module.provideNullableString(); + } + + public static Factory<String> create(NullModule module) { + return new NullModule_ProvideNullableStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullModule_ProvideNumberFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullModule_ProvideNumberFactory.java new file mode 100644 index 000000000..84e372788 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/nullables/NullModule_ProvideNumberFactory.java @@ -0,0 +1,28 @@ +package test.nullables; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class NullModule_ProvideNumberFactory implements Factory<Number> { + private final NullModule module; + + public NullModule_ProvideNumberFactory(NullModule module) { + assert module != null; + this.module = module; + } + + @Override + public Number get() { + Number provided = module.provideNumber(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Number> create(NullModule module) { + return new NullModule_ProvideNumberFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/AllStaticModule_ContibuteEmptyIntegerSetFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/AllStaticModule_ContibuteEmptyIntegerSetFactory.java new file mode 100644 index 000000000..506fbe250 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/AllStaticModule_ContibuteEmptyIntegerSetFactory.java @@ -0,0 +1,24 @@ +package test.staticprovides; + +import dagger.internal.Factory; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum AllStaticModule_ContibuteEmptyIntegerSetFactory implements Factory<Set<Integer>> { +INSTANCE; + + @Override + public Set<Integer> get() { + Set<Integer> provided = AllStaticModule.contibuteEmptyIntegerSet(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Set<Integer>> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/AllStaticModule_ContributeStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/AllStaticModule_ContributeStringFactory.java new file mode 100644 index 000000000..9700ff3d5 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/AllStaticModule_ContributeStringFactory.java @@ -0,0 +1,21 @@ +package test.staticprovides; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum AllStaticModule_ContributeStringFactory implements Factory<Set<String>> { +INSTANCE; + + @Override + public Set<String> get() { + return Collections.<String>singleton(AllStaticModule.contributeString()); + } + + public static Factory<Set<String>> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/DaggerStaticTestComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/DaggerStaticTestComponent.java new file mode 100644 index 000000000..8df8c5c50 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/DaggerStaticTestComponent.java @@ -0,0 +1,83 @@ +package test.staticprovides; + +import dagger.internal.SetFactory; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerStaticTestComponent implements StaticTestComponent { + private Provider<Set<String>> setOfStringContribution1Provider; + private Provider<Set<String>> setOfStringContribution2Provider; + private Provider<Set<String>> setOfStringContribution3Provider; + private Provider<Set<String>> setOfStringProvider; + private Provider<Set<Integer>> setOfIntegerContribution1Provider; + private Provider<Set<Integer>> setOfIntegerProvider; + + private DaggerStaticTestComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static StaticTestComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.setOfStringContribution1Provider = AllStaticModule_ContributeStringFactory.create(); + this.setOfStringContribution2Provider = SomeStaticModule_ContributeStringFromAStaticMethodFactory.create(); + this.setOfStringContribution3Provider = SomeStaticModule_ContributeStringFromAnInstanceMethodFactory.create(builder.someStaticModule); + this.setOfStringProvider = SetFactory.create(setOfStringContribution1Provider, setOfStringContribution2Provider, setOfStringContribution3Provider); + this.setOfIntegerContribution1Provider = AllStaticModule_ContibuteEmptyIntegerSetFactory.create(); + this.setOfIntegerProvider = SetFactory.create(setOfIntegerContribution1Provider); + } + + @Override + public Set<String> getMultiboundStrings() { + return setOfStringProvider.get(); + } + + @Override + public Set<Integer> getMultiboundIntegers() { + return setOfIntegerProvider.get(); + } + + public static final class Builder { + private AllStaticModule allStaticModule; + private SomeStaticModule someStaticModule; + + private Builder() { + } + + public StaticTestComponent build() { + if (allStaticModule == null) { + this.allStaticModule = new AllStaticModule(); + } + if (someStaticModule == null) { + this.someStaticModule = new SomeStaticModule(); + } + return new DaggerStaticTestComponent(this); + } + + public Builder allStaticModule(AllStaticModule allStaticModule) { + if (allStaticModule == null) { + throw new NullPointerException("allStaticModule"); + } + this.allStaticModule = allStaticModule; + return this; + } + + public Builder someStaticModule(SomeStaticModule someStaticModule) { + if (someStaticModule == null) { + throw new NullPointerException("someStaticModule"); + } + this.someStaticModule = someStaticModule; + return this; + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/SomeStaticModule_ContributeStringFromAStaticMethodFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/SomeStaticModule_ContributeStringFromAStaticMethodFactory.java new file mode 100644 index 000000000..e4177662d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/SomeStaticModule_ContributeStringFromAStaticMethodFactory.java @@ -0,0 +1,21 @@ +package test.staticprovides; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum SomeStaticModule_ContributeStringFromAStaticMethodFactory implements Factory<Set<String>> { +INSTANCE; + + @Override + public Set<String> get() { + return Collections.<String>singleton(SomeStaticModule.contributeStringFromAStaticMethod()); + } + + public static Factory<Set<String>> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/SomeStaticModule_ContributeStringFromAnInstanceMethodFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/SomeStaticModule_ContributeStringFromAnInstanceMethodFactory.java new file mode 100644 index 000000000..f96be5feb --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/staticprovides/SomeStaticModule_ContributeStringFromAnInstanceMethodFactory.java @@ -0,0 +1,26 @@ +package test.staticprovides; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class SomeStaticModule_ContributeStringFromAnInstanceMethodFactory implements Factory<Set<String>> { + private final SomeStaticModule module; + + public SomeStaticModule_ContributeStringFromAnInstanceMethodFactory(SomeStaticModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<String> get() { + return Collections.<String>singleton(module.contributeStringFromAnInstanceMethod()); + } + + public static Factory<Set<String>> create(SomeStaticModule module) { + return new SomeStaticModule_ContributeStringFromAnInstanceMethodFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeAnIntFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeAnIntFactory.java new file mode 100644 index 000000000..dddf66ec9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeAnIntFactory.java @@ -0,0 +1,30 @@ +package test.sub; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ContributionsModule_ContributeAnIntFactory implements Factory<Set<Integer>> { + private final ContributionsModule module; + private final Provider<Double> doubleDependencyProvider; + + public ContributionsModule_ContributeAnIntFactory(ContributionsModule module, Provider<Double> doubleDependencyProvider) { + assert module != null; + this.module = module; + assert doubleDependencyProvider != null; + this.doubleDependencyProvider = doubleDependencyProvider; + } + + @Override + public Set<Integer> get() { + return Collections.<Integer>singleton(module.contributeAnInt(doubleDependencyProvider.get())); + } + + public static Factory<Set<Integer>> create(ContributionsModule module, Provider<Double> doubleDependencyProvider) { + return new ContributionsModule_ContributeAnIntFactory(module, doubleDependencyProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeAnotherIntFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeAnotherIntFactory.java new file mode 100644 index 000000000..e36e78a01 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeAnotherIntFactory.java @@ -0,0 +1,26 @@ +package test.sub; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ContributionsModule_ContributeAnotherIntFactory implements Factory<Set<Integer>> { + private final ContributionsModule module; + + public ContributionsModule_ContributeAnotherIntFactory(ContributionsModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Integer> get() { + return Collections.<Integer>singleton(module.contributeAnotherInt()); + } + + public static Factory<Set<Integer>> create(ContributionsModule module) { + return new ContributionsModule_ContributeAnotherIntFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeSomeIntsFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeSomeIntsFactory.java new file mode 100644 index 000000000..68e42d694 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/ContributionsModule_ContributeSomeIntsFactory.java @@ -0,0 +1,29 @@ +package test.sub; + +import dagger.internal.Factory; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ContributionsModule_ContributeSomeIntsFactory implements Factory<Set<Integer>> { + private final ContributionsModule module; + + public ContributionsModule_ContributeSomeIntsFactory(ContributionsModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Integer> get() { + Set<Integer> provided = module.contributeSomeInts(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Set<Integer>> create(ContributionsModule module) { + return new ContributionsModule_ContributeSomeIntsFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/DaggerGenericComponent_PackageProxy.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/DaggerGenericComponent_PackageProxy.java new file mode 100644 index 000000000..24f34741e --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/DaggerGenericComponent_PackageProxy.java @@ -0,0 +1,16 @@ +package test.sub; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.Generic; +import test.Generic2; +import test.sub.PackagePrivateContainer.PublicEnclosed; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerGenericComponent_PackageProxy { + public Provider<Generic2<PackagePrivate>> generic2Provider; + public Provider<Generic2<PublicEnclosed>> generic2Provider1; + public Provider<Generic<PackagePrivate>> genericProvider; + public Provider<Generic<PublicEnclosed>> genericProvider1; +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/Exposed_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/Exposed_Factory.java new file mode 100644 index 000000000..404872f0a --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/Exposed_Factory.java @@ -0,0 +1,36 @@ +package test.sub; + +import dagger.MembersInjector; +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.Generic; +import test.sub.PackagePrivateContainer.PublicEnclosed; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class Exposed_Factory implements Factory<Exposed> { + private final MembersInjector<Exposed> membersInjector; + private final Provider<Generic<PackagePrivate>> gppProvider; + private final Provider<Generic<PublicEnclosed>> gppcProvider; + + public Exposed_Factory(MembersInjector<Exposed> membersInjector, Provider<Generic<PackagePrivate>> gppProvider, Provider<Generic<PublicEnclosed>> gppcProvider) { + assert membersInjector != null; + this.membersInjector = membersInjector; + assert gppProvider != null; + this.gppProvider = gppProvider; + assert gppcProvider != null; + this.gppcProvider = gppcProvider; + } + + @Override + public Exposed get() { + Exposed instance = new Exposed(gppProvider.get(), gppcProvider.get()); + membersInjector.injectMembers(instance); + return instance; + } + + public static Factory<Exposed> create(MembersInjector<Exposed> membersInjector, Provider<Generic<PackagePrivate>> gppProvider, Provider<Generic<PublicEnclosed>> gppcProvider) { + return new Exposed_Factory(membersInjector, gppProvider, gppcProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/Exposed_MembersInjector.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/Exposed_MembersInjector.java new file mode 100644 index 000000000..2f94e5a62 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/Exposed_MembersInjector.java @@ -0,0 +1,34 @@ +package test.sub; + +import dagger.MembersInjector; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.Generic2; +import test.sub.PackagePrivateContainer.PublicEnclosed; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class Exposed_MembersInjector implements MembersInjector<Exposed> { + private final Provider<Generic2<PackagePrivate>> gpp2Provider; + private final Provider<Generic2<PublicEnclosed>> gppc2Provider; + + public Exposed_MembersInjector(Provider<Generic2<PackagePrivate>> gpp2Provider, Provider<Generic2<PublicEnclosed>> gppc2Provider) { + assert gpp2Provider != null; + this.gpp2Provider = gpp2Provider; + assert gppc2Provider != null; + this.gppc2Provider = gppc2Provider; + } + + @Override + public void injectMembers(Exposed instance) { + if (instance == null) { + throw new NullPointerException("Cannot inject members into a null reference"); + } + instance.gpp2 = gpp2Provider.get(); + instance.gppc2 = gppc2Provider.get(); + } + + public static MembersInjector<Exposed> create(Provider<Generic2<PackagePrivate>> gpp2Provider, Provider<Generic2<PublicEnclosed>> gppc2Provider) { + return new Exposed_MembersInjector(gpp2Provider, gppc2Provider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/OtherThing_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/OtherThing_Factory.java new file mode 100644 index 000000000..8d844fb07 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/OtherThing_Factory.java @@ -0,0 +1,25 @@ +package test.sub; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class OtherThing_Factory implements Factory<OtherThing> { + private final Provider<Integer> iProvider; + + public OtherThing_Factory(Provider<Integer> iProvider) { + assert iProvider != null; + this.iProvider = iProvider; + } + + @Override + public OtherThing get() { + return new OtherThing(iProvider.get()); + } + + public static Factory<OtherThing> create(Provider<Integer> iProvider) { + return new OtherThing_Factory(iProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PackagePrivateContainer$PublicEnclosed_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PackagePrivateContainer$PublicEnclosed_Factory.java new file mode 100644 index 000000000..44f943e4c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PackagePrivateContainer$PublicEnclosed_Factory.java @@ -0,0 +1,20 @@ +package test.sub; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import test.sub.PackagePrivateContainer.PublicEnclosed; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PackagePrivateContainer$PublicEnclosed_Factory implements Factory<PublicEnclosed> { +INSTANCE; + + @Override + public PublicEnclosed get() { + return new PublicEnclosed(); + } + + public static Factory<PublicEnclosed> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PackagePrivate_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PackagePrivate_Factory.java new file mode 100644 index 000000000..165f4f33a --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PackagePrivate_Factory.java @@ -0,0 +1,19 @@ +package test.sub; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum PackagePrivate_Factory implements Factory<PackagePrivate> { +INSTANCE; + + @Override + public PackagePrivate get() { + return new PackagePrivate(); + } + + public static Factory<PackagePrivate> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PublicSubclass2_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PublicSubclass2_Factory.java new file mode 100644 index 000000000..0c863c6d9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PublicSubclass2_Factory.java @@ -0,0 +1,32 @@ +package test.sub; + +import dagger.MembersInjector; +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.sub.PackagePrivateContainer.PublicEnclosed; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class PublicSubclass2_Factory implements Factory<PublicSubclass2> { + private final MembersInjector<PublicSubclass2> membersInjector; + private final Provider<PublicEnclosed> ppProvider; + + public PublicSubclass2_Factory(MembersInjector<PublicSubclass2> membersInjector, Provider<PublicEnclosed> ppProvider) { + assert membersInjector != null; + this.membersInjector = membersInjector; + assert ppProvider != null; + this.ppProvider = ppProvider; + } + + @Override + public PublicSubclass2 get() { + PublicSubclass2 instance = new PublicSubclass2(ppProvider.get()); + membersInjector.injectMembers(instance); + return instance; + } + + public static Factory<PublicSubclass2> create(MembersInjector<PublicSubclass2> membersInjector, Provider<PublicEnclosed> ppProvider) { + return new PublicSubclass2_Factory(membersInjector, ppProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PublicSubclass_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PublicSubclass_Factory.java new file mode 100644 index 000000000..1f2f5406f --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/sub/PublicSubclass_Factory.java @@ -0,0 +1,31 @@ +package test.sub; + +import dagger.MembersInjector; +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class PublicSubclass_Factory implements Factory<PublicSubclass> { + private final MembersInjector<PublicSubclass> membersInjector; + private final Provider<PackagePrivate> ppProvider; + + public PublicSubclass_Factory(MembersInjector<PublicSubclass> membersInjector, Provider<PackagePrivate> ppProvider) { + assert membersInjector != null; + this.membersInjector = membersInjector; + assert ppProvider != null; + this.ppProvider = ppProvider; + } + + @Override + public PublicSubclass get() { + PublicSubclass instance = new PublicSubclass(ppProvider.get()); + membersInjector.injectMembers(instance); + return instance; + } + + public static Factory<PublicSubclass> create(MembersInjector<PublicSubclass> membersInjector, Provider<PackagePrivate> ppProvider) { + return new PublicSubclass_Factory(membersInjector, ppProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ChildModuleWithState_ProvideIntFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ChildModuleWithState_ProvideIntFactory.java new file mode 100644 index 000000000..150bdc090 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ChildModuleWithState_ProvideIntFactory.java @@ -0,0 +1,28 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ChildModuleWithState_ProvideIntFactory implements Factory<Integer> { + private final ChildModuleWithState module; + + public ChildModuleWithState_ProvideIntFactory(ChildModuleWithState module) { + assert module != null; + this.module = module; + } + + @Override + public Integer get() { + Integer provided = module.provideInt(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Integer> create(ChildModuleWithState module) { + return new ChildModuleWithState_ProvideIntFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ChildModule_ProvideUnscopedObjectFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ChildModule_ProvideUnscopedObjectFactory.java new file mode 100644 index 000000000..67456801a --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ChildModule_ProvideUnscopedObjectFactory.java @@ -0,0 +1,26 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ChildModule_ProvideUnscopedObjectFactory implements Factory<Set<Object>> { + private final ChildModule module; + + public ChildModule_ProvideUnscopedObjectFactory(ChildModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Object> get() { + return Collections.<Object>singleton(module.provideUnscopedObject()); + } + + public static Factory<Set<Object>> create(ChildModule module) { + return new ChildModule_ProvideUnscopedObjectFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/DaggerParentComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/DaggerParentComponent.java new file mode 100644 index 000000000..7169bbf43 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/DaggerParentComponent.java @@ -0,0 +1,274 @@ +package test.subcomponent; + +import dagger.internal.ScopedProvider; +import dagger.internal.SetFactory; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerParentComponent implements ParentComponent { + private Provider<SingletonType> singletonTypeProvider; + private Provider<UnscopedType> unscopedTypeProvider; + private Provider<Set<Object>> setOfObjectContribution1Provider; + private Provider<Set<Object>> setOfObjectContribution2Provider; + private Provider<Set<Object>> setOfObjectProvider; + private Provider<UnscopedType> provideUnscopedTypeBoundAsSingletonProvider; + + private DaggerParentComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static ParentComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.singletonTypeProvider = ScopedProvider.create(SingletonType_Factory.create()); + this.unscopedTypeProvider = UnscopedType_Factory.create(singletonTypeProvider); + this.setOfObjectContribution1Provider = ParentModule_ProvideUnscopedObjectFactory.create(builder.parentModule); + this.setOfObjectContribution2Provider = ScopedProvider.create(ParentModule_ProvideSingletonObjectFactory.create(builder.parentModule)); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider); + this.provideUnscopedTypeBoundAsSingletonProvider = ScopedProvider.create(ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.create(builder.parentModule, unscopedTypeProvider)); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return unscopedTypeProvider; + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public ChildComponent newChildComponent() { + return new ChildComponentImpl(); + } + + @Override + public ChildAbstractClassComponent newChildAbstractClassComponent() { + return new ChildAbstractClassComponentImpl(); + } + + @Override + public ChildComponentRequiringModules newChildComponentRequiringModules(ChildModuleWithParameters cmwp, ChildModuleWithState childModuleWithState) { + return new ChildComponentRequiringModulesImpl(cmwp, childModuleWithState); + } + + public static final class Builder { + private ParentModule parentModule; + + private Builder() { + } + + public ParentComponent build() { + if (parentModule == null) { + this.parentModule = new ParentModule(); + } + return new DaggerParentComponent(this); + } + + public Builder parentModule(ParentModule parentModule) { + if (parentModule == null) { + throw new NullPointerException("parentModule"); + } + this.parentModule = parentModule; + return this; + } + } + + private final class ChildComponentImpl implements ChildComponent { + private final ChildModule childModule; + private Provider<RequiresSingletons> requiresSingletonsProvider; + private Provider<Set<Object>> setOfObjectContribution3Provider; + private Provider<Set<Object>> setOfObjectProvider; + + private ChildComponentImpl() { + this.childModule = new ChildModule(); + initialize(); + } + + private void initialize() { + this.requiresSingletonsProvider = RequiresSingletons_Factory.create(DaggerParentComponent.this.singletonTypeProvider, DaggerParentComponent.this.provideUnscopedTypeBoundAsSingletonProvider); + this.setOfObjectContribution3Provider = ChildModule_ProvideUnscopedObjectFactory.create(childModule); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider, setOfObjectContribution3Provider); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return DaggerParentComponent.this.unscopedTypeProvider; + } + + @Override + public RequiresSingletons requiresSingleton() { + return requiresSingletonsProvider.get(); + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public GrandchildComponent newGrandchildComponent() { + return new GrandchildComponentImpl(); + } + + private final class GrandchildComponentImpl implements GrandchildComponent { + private final GrandchildModule grandchildModule; + private Provider<Set<Object>> setOfObjectContribution4Provider; + private Provider<Set<Object>> setOfObjectProvider; + private Provider<AnInterface> provideAnInterfaceProvider; + private Provider<NeedsAnInterface> provideNeedsAnInterfaceProvider; + + private GrandchildComponentImpl() { + this.grandchildModule = new GrandchildModule(); + initialize(); + } + + private void initialize() { + this.setOfObjectContribution4Provider = GrandchildModule_ProvideUnscopedObjectFactory.create(grandchildModule); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider, setOfObjectContribution3Provider, setOfObjectContribution4Provider); + this.provideAnInterfaceProvider = GrandchildModule_ProvideAnInterfaceFactory.create(grandchildModule, ImplementsAnInterface_Factory.create()); + this.provideNeedsAnInterfaceProvider = GrandchildModule_ProvideNeedsAnInterfaceFactory.create(grandchildModule, provideAnInterfaceProvider); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return DaggerParentComponent.this.unscopedTypeProvider; + } + + @Override + public RequiresSingletons requiresSingleton() { + return ChildComponentImpl.this.requiresSingletonsProvider.get(); + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public NeedsAnInterface needsAnInterface() { + return provideNeedsAnInterfaceProvider.get(); + } + } + } + + private final class ChildAbstractClassComponentImpl extends ChildAbstractClassComponent { + private final ChildModule childModule; + private Provider<RequiresSingletons> requiresSingletonsProvider; + private Provider<Set<Object>> setOfObjectContribution3Provider; + private Provider<Set<Object>> setOfObjectProvider; + + private ChildAbstractClassComponentImpl() { + this.childModule = new ChildModule(); + initialize(); + } + + private void initialize() { + this.requiresSingletonsProvider = RequiresSingletons_Factory.create(DaggerParentComponent.this.singletonTypeProvider, DaggerParentComponent.this.provideUnscopedTypeBoundAsSingletonProvider); + this.setOfObjectContribution3Provider = ChildModule_ProvideUnscopedObjectFactory.create(childModule); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider, setOfObjectContribution3Provider); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return DaggerParentComponent.this.unscopedTypeProvider; + } + + @Override + public RequiresSingletons requiresSingleton() { + return requiresSingletonsProvider.get(); + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public GrandchildComponent newGrandchildComponent() { + return new GrandchildComponentImpl(); + } + + private final class GrandchildComponentImpl implements GrandchildComponent { + private final GrandchildModule grandchildModule; + private Provider<Set<Object>> setOfObjectContribution4Provider; + private Provider<Set<Object>> setOfObjectProvider; + private Provider<AnInterface> provideAnInterfaceProvider; + private Provider<NeedsAnInterface> provideNeedsAnInterfaceProvider; + + private GrandchildComponentImpl() { + this.grandchildModule = new GrandchildModule(); + initialize(); + } + + private void initialize() { + this.setOfObjectContribution4Provider = GrandchildModule_ProvideUnscopedObjectFactory.create(grandchildModule); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider, setOfObjectContribution3Provider, setOfObjectContribution4Provider); + this.provideAnInterfaceProvider = GrandchildModule_ProvideAnInterfaceFactory.create(grandchildModule, ImplementsAnInterface_Factory.create()); + this.provideNeedsAnInterfaceProvider = GrandchildModule_ProvideNeedsAnInterfaceFactory.create(grandchildModule, provideAnInterfaceProvider); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return DaggerParentComponent.this.unscopedTypeProvider; + } + + @Override + public RequiresSingletons requiresSingleton() { + return ChildAbstractClassComponentImpl.this.requiresSingletonsProvider.get(); + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public NeedsAnInterface needsAnInterface() { + return provideNeedsAnInterfaceProvider.get(); + } + } + } + + private final class ChildComponentRequiringModulesImpl implements ChildComponentRequiringModules { + private final ChildModuleWithParameters childModuleWithParameters; + private final ChildModuleWithState childModuleWithState; + private final ChildModule childModule; + private Provider<Integer> provideIntProvider; + private Provider<Set<Object>> setOfObjectProvider; + + private ChildComponentRequiringModulesImpl(ChildModuleWithParameters childModuleWithParameters, ChildModuleWithState childModuleWithState) { + if (childModuleWithParameters == null) { + throw new NullPointerException(); + } + this.childModuleWithParameters = childModuleWithParameters; + if (childModuleWithState == null) { + throw new NullPointerException(); + } + this.childModuleWithState = childModuleWithState; + this.childModule = new ChildModule(); + initialize(); + } + + private void initialize() { + this.provideIntProvider = ChildModuleWithState_ProvideIntFactory.create(childModuleWithState); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider); + } + + @Override + public int getInt() { + return provideIntProvider.get(); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/DaggerParentOfGenericComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/DaggerParentOfGenericComponent.java new file mode 100644 index 000000000..bb1e240b8 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/DaggerParentOfGenericComponent.java @@ -0,0 +1,155 @@ +package test.subcomponent; + +import dagger.internal.ScopedProvider; +import dagger.internal.SetFactory; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerParentOfGenericComponent implements ParentOfGenericComponent { + private Provider<SingletonType> singletonTypeProvider; + private Provider<UnscopedType> unscopedTypeProvider; + private Provider<Set<Object>> setOfObjectContribution1Provider; + private Provider<Set<Object>> setOfObjectContribution2Provider; + private Provider<Set<Object>> setOfObjectProvider; + private Provider<UnscopedType> provideUnscopedTypeBoundAsSingletonProvider; + + private DaggerParentOfGenericComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static ParentOfGenericComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.singletonTypeProvider = ScopedProvider.create(SingletonType_Factory.create()); + this.unscopedTypeProvider = UnscopedType_Factory.create(singletonTypeProvider); + this.setOfObjectContribution1Provider = ParentModule_ProvideUnscopedObjectFactory.create(builder.parentModule); + this.setOfObjectContribution2Provider = ScopedProvider.create(ParentModule_ProvideSingletonObjectFactory.create(builder.parentModule)); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider); + this.provideUnscopedTypeBoundAsSingletonProvider = ScopedProvider.create(ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.create(builder.parentModule, unscopedTypeProvider)); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return unscopedTypeProvider; + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public ChildComponent subcomponent() { + return new ChildComponentImpl(); + } + + public static final class Builder { + private ParentModule parentModule; + + private Builder() { + } + + public ParentOfGenericComponent build() { + if (parentModule == null) { + this.parentModule = new ParentModule(); + } + return new DaggerParentOfGenericComponent(this); + } + + public Builder parentModule(ParentModule parentModule) { + if (parentModule == null) { + throw new NullPointerException("parentModule"); + } + this.parentModule = parentModule; + return this; + } + } + + private final class ChildComponentImpl implements ChildComponent { + private final ChildModule childModule; + private Provider<RequiresSingletons> requiresSingletonsProvider; + private Provider<Set<Object>> setOfObjectContribution3Provider; + private Provider<Set<Object>> setOfObjectProvider; + + private ChildComponentImpl() { + this.childModule = new ChildModule(); + initialize(); + } + + private void initialize() { + this.requiresSingletonsProvider = RequiresSingletons_Factory.create(DaggerParentOfGenericComponent.this.singletonTypeProvider, DaggerParentOfGenericComponent.this.provideUnscopedTypeBoundAsSingletonProvider); + this.setOfObjectContribution3Provider = ChildModule_ProvideUnscopedObjectFactory.create(childModule); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider, setOfObjectContribution3Provider); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return DaggerParentOfGenericComponent.this.unscopedTypeProvider; + } + + @Override + public RequiresSingletons requiresSingleton() { + return requiresSingletonsProvider.get(); + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public GrandchildComponent newGrandchildComponent() { + return new GrandchildComponentImpl(); + } + + private final class GrandchildComponentImpl implements GrandchildComponent { + private final GrandchildModule grandchildModule; + private Provider<Set<Object>> setOfObjectContribution4Provider; + private Provider<Set<Object>> setOfObjectProvider; + private Provider<AnInterface> provideAnInterfaceProvider; + private Provider<NeedsAnInterface> provideNeedsAnInterfaceProvider; + + private GrandchildComponentImpl() { + this.grandchildModule = new GrandchildModule(); + initialize(); + } + + private void initialize() { + this.setOfObjectContribution4Provider = GrandchildModule_ProvideUnscopedObjectFactory.create(grandchildModule); + this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider, setOfObjectContribution2Provider, setOfObjectContribution3Provider, setOfObjectContribution4Provider); + this.provideAnInterfaceProvider = GrandchildModule_ProvideAnInterfaceFactory.create(grandchildModule, ImplementsAnInterface_Factory.create()); + this.provideNeedsAnInterfaceProvider = GrandchildModule_ProvideNeedsAnInterfaceFactory.create(grandchildModule, provideAnInterfaceProvider); + } + + @Override + public Provider<UnscopedType> getUnscopedTypeProvider() { + return DaggerParentOfGenericComponent.this.unscopedTypeProvider; + } + + @Override + public RequiresSingletons requiresSingleton() { + return ChildComponentImpl.this.requiresSingletonsProvider.get(); + } + + @Override + public Set<Object> objectSet() { + return setOfObjectProvider.get(); + } + + @Override + public NeedsAnInterface needsAnInterface() { + return provideNeedsAnInterfaceProvider.get(); + } + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideAnInterfaceFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideAnInterfaceFactory.java new file mode 100644 index 000000000..f2bdbe7e3 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideAnInterfaceFactory.java @@ -0,0 +1,32 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GrandchildModule_ProvideAnInterfaceFactory implements Factory<AnInterface> { + private final GrandchildModule module; + private final Provider<ImplementsAnInterface> implementsAnInterfaceProvider; + + public GrandchildModule_ProvideAnInterfaceFactory(GrandchildModule module, Provider<ImplementsAnInterface> implementsAnInterfaceProvider) { + assert module != null; + this.module = module; + assert implementsAnInterfaceProvider != null; + this.implementsAnInterfaceProvider = implementsAnInterfaceProvider; + } + + @Override + public AnInterface get() { + AnInterface provided = module.provideAnInterface(implementsAnInterfaceProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<AnInterface> create(GrandchildModule module, Provider<ImplementsAnInterface> implementsAnInterfaceProvider) { + return new GrandchildModule_ProvideAnInterfaceFactory(module, implementsAnInterfaceProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideNeedsAnInterfaceFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideNeedsAnInterfaceFactory.java new file mode 100644 index 000000000..25801e090 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideNeedsAnInterfaceFactory.java @@ -0,0 +1,32 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GrandchildModule_ProvideNeedsAnInterfaceFactory implements Factory<NeedsAnInterface> { + private final GrandchildModule module; + private final Provider<AnInterface> anInterfaceProvider; + + public GrandchildModule_ProvideNeedsAnInterfaceFactory(GrandchildModule module, Provider<AnInterface> anInterfaceProvider) { + assert module != null; + this.module = module; + assert anInterfaceProvider != null; + this.anInterfaceProvider = anInterfaceProvider; + } + + @Override + public NeedsAnInterface get() { + NeedsAnInterface provided = module.provideNeedsAnInterface(anInterfaceProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<NeedsAnInterface> create(GrandchildModule module, Provider<AnInterface> anInterfaceProvider) { + return new GrandchildModule_ProvideNeedsAnInterfaceFactory(module, anInterfaceProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideUnscopedObjectFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideUnscopedObjectFactory.java new file mode 100644 index 000000000..677282ff6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/GrandchildModule_ProvideUnscopedObjectFactory.java @@ -0,0 +1,26 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class GrandchildModule_ProvideUnscopedObjectFactory implements Factory<Set<Object>> { + private final GrandchildModule module; + + public GrandchildModule_ProvideUnscopedObjectFactory(GrandchildModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Object> get() { + return Collections.<Object>singleton(module.provideUnscopedObject()); + } + + public static Factory<Set<Object>> create(GrandchildModule module) { + return new GrandchildModule_ProvideUnscopedObjectFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ImplementsAnInterface_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ImplementsAnInterface_Factory.java new file mode 100644 index 000000000..592b8a78d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ImplementsAnInterface_Factory.java @@ -0,0 +1,19 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum ImplementsAnInterface_Factory implements Factory<ImplementsAnInterface> { +INSTANCE; + + @Override + public ImplementsAnInterface get() { + return new ImplementsAnInterface(); + } + + public static Factory<ImplementsAnInterface> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideSingletonObjectFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideSingletonObjectFactory.java new file mode 100644 index 000000000..e59a84002 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideSingletonObjectFactory.java @@ -0,0 +1,26 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ParentModule_ProvideSingletonObjectFactory implements Factory<Set<Object>> { + private final ParentModule module; + + public ParentModule_ProvideSingletonObjectFactory(ParentModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Object> get() { + return Collections.<Object>singleton(module.provideSingletonObject()); + } + + public static Factory<Set<Object>> create(ParentModule module) { + return new ParentModule_ProvideSingletonObjectFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideUnscopedObjectFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideUnscopedObjectFactory.java new file mode 100644 index 000000000..854a59a4d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideUnscopedObjectFactory.java @@ -0,0 +1,26 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ParentModule_ProvideUnscopedObjectFactory implements Factory<Set<Object>> { + private final ParentModule module; + + public ParentModule_ProvideUnscopedObjectFactory(ParentModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<Object> get() { + return Collections.<Object>singleton(module.provideUnscopedObject()); + } + + public static Factory<Set<Object>> create(ParentModule module) { + return new ParentModule_ProvideUnscopedObjectFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.java new file mode 100644 index 000000000..fbe6e4fa9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory.java @@ -0,0 +1,32 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory implements Factory<UnscopedType> { + private final ParentModule module; + private final Provider<UnscopedType> unscopedTypeProvider; + + public ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory(ParentModule module, Provider<UnscopedType> unscopedTypeProvider) { + assert module != null; + this.module = module; + assert unscopedTypeProvider != null; + this.unscopedTypeProvider = unscopedTypeProvider; + } + + @Override + public UnscopedType get() { + UnscopedType provided = module.provideUnscopedTypeBoundAsSingleton(unscopedTypeProvider.get()); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<UnscopedType> create(ParentModule module, Provider<UnscopedType> unscopedTypeProvider) { + return new ParentModule_ProvideUnscopedTypeBoundAsSingletonFactory(module, unscopedTypeProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/RequiresSingletons_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/RequiresSingletons_Factory.java new file mode 100644 index 000000000..fbe397c6c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/RequiresSingletons_Factory.java @@ -0,0 +1,28 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class RequiresSingletons_Factory implements Factory<RequiresSingletons> { + private final Provider<SingletonType> singletonTypeProvider; + private final Provider<UnscopedType> unscopedTypeBoundAsSingletonProvider; + + public RequiresSingletons_Factory(Provider<SingletonType> singletonTypeProvider, Provider<UnscopedType> unscopedTypeBoundAsSingletonProvider) { + assert singletonTypeProvider != null; + this.singletonTypeProvider = singletonTypeProvider; + assert unscopedTypeBoundAsSingletonProvider != null; + this.unscopedTypeBoundAsSingletonProvider = unscopedTypeBoundAsSingletonProvider; + } + + @Override + public RequiresSingletons get() { + return new RequiresSingletons(singletonTypeProvider.get(), unscopedTypeBoundAsSingletonProvider.get()); + } + + public static Factory<RequiresSingletons> create(Provider<SingletonType> singletonTypeProvider, Provider<UnscopedType> unscopedTypeBoundAsSingletonProvider) { + return new RequiresSingletons_Factory(singletonTypeProvider, unscopedTypeBoundAsSingletonProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/SingletonType_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/SingletonType_Factory.java new file mode 100644 index 000000000..1159e38cf --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/SingletonType_Factory.java @@ -0,0 +1,19 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum SingletonType_Factory implements Factory<SingletonType> { +INSTANCE; + + @Override + public SingletonType get() { + return new SingletonType(); + } + + public static Factory<SingletonType> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/UnscopedType_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/UnscopedType_Factory.java new file mode 100644 index 000000000..9b2a57582 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/UnscopedType_Factory.java @@ -0,0 +1,25 @@ +package test.subcomponent; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class UnscopedType_Factory implements Factory<UnscopedType> { + private final Provider<SingletonType> singletonTypeProvider; + + public UnscopedType_Factory(Provider<SingletonType> singletonTypeProvider) { + assert singletonTypeProvider != null; + this.singletonTypeProvider = singletonTypeProvider; + } + + @Override + public UnscopedType get() { + return new UnscopedType(singletonTypeProvider.get()); + } + + public static Factory<UnscopedType> create(Provider<SingletonType> singletonTypeProvider) { + return new UnscopedType_Factory(singletonTypeProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/DaggerParentComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/DaggerParentComponent.java new file mode 100644 index 000000000..1360a9b9c --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/DaggerParentComponent.java @@ -0,0 +1,91 @@ +package test.subcomponent.hiding; + +import javax.annotation.Generated; +import javax.inject.Provider; +import test.subcomponent.hiding.a.CommonModuleName; +import test.subcomponent.hiding.a.CommonModuleName_ProvideStringFactory; +import test.subcomponent.hiding.a.CommonName; +import test.subcomponent.hiding.a.CommonName_Factory; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerParentComponent implements ParentComponent { + private Provider<String> provideStringProvider; + private Provider<CommonName> commonNameProvider; + + private DaggerParentComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static ParentComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideStringProvider = CommonModuleName_ProvideStringFactory.create(builder.commonModuleName); + this.commonNameProvider = CommonName_Factory.create(provideStringProvider); + } + + @Override + public CommonName aCommonName() { + return commonNameProvider.get(); + } + + @Override + public ChildComponent newChildComponent() { + return new ChildComponentImpl(); + } + + public static final class Builder { + private CommonModuleName commonModuleName; + + private Builder() { + } + + public ParentComponent build() { + if (commonModuleName == null) { + this.commonModuleName = new CommonModuleName(); + } + return new DaggerParentComponent(this); + } + + public Builder commonModuleName(CommonModuleName commonModuleName) { + if (commonModuleName == null) { + throw new NullPointerException("commonModuleName"); + } + this.commonModuleName = commonModuleName; + return this; + } + } + + private final class ChildComponentImpl implements ChildComponent { + private final test.subcomponent.hiding.b.CommonModuleName commonModuleName; + private Provider<Integer> provideStringProvider; + private Provider<test.subcomponent.hiding.b.CommonName> commonNameProvider; + + private ChildComponentImpl() { + this.commonModuleName = new test.subcomponent.hiding.b.CommonModuleName(); + initialize(); + } + + private void initialize() { + this.provideStringProvider = test.subcomponent.hiding.b.CommonModuleName_ProvideStringFactory.create(commonModuleName); + this.commonNameProvider = test.subcomponent.hiding.b.CommonName_Factory.create(provideStringProvider); + } + + @Override + public CommonName aCommonName() { + return DaggerParentComponent.this.commonNameProvider.get(); + } + + @Override + public test.subcomponent.hiding.b.CommonName bCommonName() { + return commonNameProvider.get(); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/a/CommonModuleName_ProvideStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/a/CommonModuleName_ProvideStringFactory.java new file mode 100644 index 000000000..c782d1ec6 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/a/CommonModuleName_ProvideStringFactory.java @@ -0,0 +1,28 @@ +package test.subcomponent.hiding.a; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class CommonModuleName_ProvideStringFactory implements Factory<String> { + private final CommonModuleName module; + + public CommonModuleName_ProvideStringFactory(CommonModuleName module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.provideString(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(CommonModuleName module) { + return new CommonModuleName_ProvideStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/a/CommonName_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/a/CommonName_Factory.java new file mode 100644 index 000000000..ffe03c9eb --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/a/CommonName_Factory.java @@ -0,0 +1,25 @@ +package test.subcomponent.hiding.a; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class CommonName_Factory implements Factory<CommonName> { + private final Provider<String> sProvider; + + public CommonName_Factory(Provider<String> sProvider) { + assert sProvider != null; + this.sProvider = sProvider; + } + + @Override + public CommonName get() { + return new CommonName(sProvider.get()); + } + + public static Factory<CommonName> create(Provider<String> sProvider) { + return new CommonName_Factory(sProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/b/CommonModuleName_ProvideStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/b/CommonModuleName_ProvideStringFactory.java new file mode 100644 index 000000000..9eb4d10c0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/b/CommonModuleName_ProvideStringFactory.java @@ -0,0 +1,28 @@ +package test.subcomponent.hiding.b; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class CommonModuleName_ProvideStringFactory implements Factory<Integer> { + private final CommonModuleName module; + + public CommonModuleName_ProvideStringFactory(CommonModuleName module) { + assert module != null; + this.module = module; + } + + @Override + public Integer get() { + Integer provided = module.provideString(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<Integer> create(CommonModuleName module) { + return new CommonModuleName_ProvideStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/b/CommonName_Factory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/b/CommonName_Factory.java new file mode 100644 index 000000000..e05b6f4d9 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/hiding/b/CommonName_Factory.java @@ -0,0 +1,25 @@ +package test.subcomponent.hiding.b; + +import dagger.internal.Factory; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class CommonName_Factory implements Factory<CommonName> { + private final Provider<Integer> iProvider; + + public CommonName_Factory(Provider<Integer> iProvider) { + assert iProvider != null; + this.iProvider = iProvider; + } + + @Override + public CommonName get() { + return new CommonName(iProvider.get()); + } + + public static Factory<CommonName> create(Provider<Integer> iProvider) { + return new CommonName_Factory(iProvider); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/DaggerParentComponent.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/DaggerParentComponent.java new file mode 100644 index 000000000..9b46e3011 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/DaggerParentComponent.java @@ -0,0 +1,102 @@ +package test.subcomponent.repeat; + +import dagger.internal.SetFactory; +import java.util.Set; +import javax.annotation.Generated; +import javax.inject.Provider; +import test.subcomponent.repeat.ParentComponent; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerParentComponent implements ParentComponent { + private Provider<String> provideStringProvider; + private Provider<Set<String>> setOfStringContribution1Provider; + private Provider<Set<String>> setOfStringProvider; + private Provider<OnlyUsedInParent> provideOnlyUsedInParentProvider; + + private DaggerParentComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static ParentComponent.Builder builder() { + return new Builder(); + } + + public static ParentComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideStringProvider = RepeatedModule_ProvideStringFactory.create(builder.repeatedModule); + this.setOfStringContribution1Provider = RepeatedModule_ContributeStringFactory.create(builder.repeatedModule); + this.setOfStringProvider = SetFactory.create(setOfStringContribution1Provider); + this.provideOnlyUsedInParentProvider = RepeatedModule_ProvideOnlyUsedInParentFactory.create(builder.repeatedModule); + } + + @Override + public String getString() { + return provideStringProvider.get(); + } + + @Override + public Set<String> getMultiboundStrings() { + return setOfStringProvider.get(); + } + + @Override + public OnlyUsedInParent getOnlyUsedInParent() { + return provideOnlyUsedInParentProvider.get(); + } + + @Override + public ChildComponent newChildComponent() { + return new ChildComponentImpl(); + } + + private static final class Builder implements ParentComponent.Builder { + private RepeatedModule repeatedModule; + + + @Override + public ParentComponent build() { + if (repeatedModule == null) { + this.repeatedModule = new RepeatedModule(); + } + return new DaggerParentComponent(this); + } + } + + private final class ChildComponentImpl implements ChildComponent { + private final RepeatedModule repeatedModule; + private Provider<Set<String>> setOfStringContribution1Provider; + private Provider<Set<String>> setOfStringProvider; + private Provider<OnlyUsedInChild> provideOnlyUsedInChildProvider; + + private ChildComponentImpl() { + this.repeatedModule = new RepeatedModule(); + initialize(); + } + + private void initialize() { + this.setOfStringContribution1Provider = RepeatedModule_ContributeStringFactory.create(repeatedModule); + this.setOfStringProvider = SetFactory.create(setOfStringContribution1Provider); + this.provideOnlyUsedInChildProvider = RepeatedModule_ProvideOnlyUsedInChildFactory.create(repeatedModule); + } + + @Override + public String getString() { + return DaggerParentComponent.this.provideStringProvider.get(); + } + + @Override + public Set<String> getMultiboundStrings() { + return setOfStringProvider.get(); + } + + @Override + public OnlyUsedInChild getOnlyUsedInChild() { + return provideOnlyUsedInChildProvider.get(); + } + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ContributeStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ContributeStringFactory.java new file mode 100644 index 000000000..63bebf89b --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ContributeStringFactory.java @@ -0,0 +1,26 @@ +package test.subcomponent.repeat; + +import dagger.internal.Factory; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class RepeatedModule_ContributeStringFactory implements Factory<Set<String>> { + private final RepeatedModule module; + + public RepeatedModule_ContributeStringFactory(RepeatedModule module) { + assert module != null; + this.module = module; + } + + @Override + public Set<String> get() { + return Collections.<String>singleton(module.contributeString()); + } + + public static Factory<Set<String>> create(RepeatedModule module) { + return new RepeatedModule_ContributeStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInChildFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInChildFactory.java new file mode 100644 index 000000000..4ac085de0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInChildFactory.java @@ -0,0 +1,28 @@ +package test.subcomponent.repeat; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class RepeatedModule_ProvideOnlyUsedInChildFactory implements Factory<OnlyUsedInChild> { + private final RepeatedModule module; + + public RepeatedModule_ProvideOnlyUsedInChildFactory(RepeatedModule module) { + assert module != null; + this.module = module; + } + + @Override + public OnlyUsedInChild get() { + OnlyUsedInChild provided = module.provideOnlyUsedInChild(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<OnlyUsedInChild> create(RepeatedModule module) { + return new RepeatedModule_ProvideOnlyUsedInChildFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInParentFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInParentFactory.java new file mode 100644 index 000000000..e3fd0ff64 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideOnlyUsedInParentFactory.java @@ -0,0 +1,28 @@ +package test.subcomponent.repeat; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class RepeatedModule_ProvideOnlyUsedInParentFactory implements Factory<OnlyUsedInParent> { + private final RepeatedModule module; + + public RepeatedModule_ProvideOnlyUsedInParentFactory(RepeatedModule module) { + assert module != null; + this.module = module; + } + + @Override + public OnlyUsedInParent get() { + OnlyUsedInParent provided = module.provideOnlyUsedInParent(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<OnlyUsedInParent> create(RepeatedModule module) { + return new RepeatedModule_ProvideOnlyUsedInParentFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideStringFactory.java b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideStringFactory.java new file mode 100644 index 000000000..4ebb69168 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-sources/annotations/test/subcomponent/repeat/RepeatedModule_ProvideStringFactory.java @@ -0,0 +1,28 @@ +package test.subcomponent.repeat; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class RepeatedModule_ProvideStringFactory implements Factory<String> { + private final RepeatedModule module; + + public RepeatedModule_ProvideStringFactory(RepeatedModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.provideString(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(RepeatedModule module) { + return new RepeatedModule_ProvideStringFactory(module); + } +} + diff --git a/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_nestedWrappedKey.java b/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_nestedWrappedKey.java new file mode 100644 index 000000000..f2877eac7 --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_nestedWrappedKey.java @@ -0,0 +1,55 @@ + +package test; + +import javax.annotation.Generated; + +@Generated("com.google.auto.value.processor.AutoAnnotationProcessor") +final class AutoAnnotation_MultibindingTest_nestedWrappedKey implements TestStringKey.NestedWrappedKey { + + private final Class<?> value; + + AutoAnnotation_MultibindingTest_nestedWrappedKey( + Class<?> value) { + if (value == null) { + throw new NullPointerException("Null value"); + } + this.value = value; + } + + @Override + public Class<? extends TestStringKey.NestedWrappedKey> annotationType() { + return TestStringKey.NestedWrappedKey.class; + } + + @Override + public Class<?> value() { + return value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("@test.TestStringKey.NestedWrappedKey("); + sb.append(value); + return sb.append(')').toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestStringKey.NestedWrappedKey) { + TestStringKey.NestedWrappedKey that = (TestStringKey.NestedWrappedKey) o; + return (value.equals(that.value())); + } + return false; + } + + @Override + public int hashCode() { + return + ((127 * 111972721) ^ (value.hashCode())); + // 111972721 is "value".hashCode() + } + +} diff --git a/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_testStringKey.java b/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_testStringKey.java new file mode 100644 index 000000000..37a81ddbd --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_testStringKey.java @@ -0,0 +1,90 @@ + +package test; + +import javax.annotation.Generated; + +@Generated("com.google.auto.value.processor.AutoAnnotationProcessor") +final class AutoAnnotation_MultibindingTest_testStringKey implements TestStringKey { + + private final String value; + + AutoAnnotation_MultibindingTest_testStringKey( + String value) { + if (value == null) { + throw new NullPointerException("Null value"); + } + this.value = value; + } + + @Override + public Class<? extends TestStringKey> annotationType() { + return TestStringKey.class; + } + + @Override + public String value() { + return value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("@test.TestStringKey("); + appendQuoted(sb, value); + return sb.append(')').toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestStringKey) { + TestStringKey that = (TestStringKey) o; + return (value.equals(that.value())); + } + return false; + } + + @Override + public int hashCode() { + return + ((127 * 111972721) ^ (value.hashCode())); + // 111972721 is "value".hashCode() + } + + private static void appendQuoted(StringBuilder sb, String s) { + sb.append('"'); + for (int i = 0; i < s.length(); i++) { + appendEscaped(sb, s.charAt(i)); + } + sb.append('"'); + } + + private static void appendEscaped(StringBuilder sb, char c) { + switch (c) { + case '\\': + case '"': + case '\'': + sb.append('\\').append(c); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + if (c < 0x20) { + sb.append(String.format("\\%03o", (int) c)); + } else if (c < 0x7f || Character.isLetter(c)) { + sb.append(c); + } else { + sb.append(String.format("\\u%04x", (int) c)); + } + break; + } + } +} diff --git a/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_testWrappedAnnotationKey.java b/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_testWrappedAnnotationKey.java new file mode 100644 index 000000000..d3e798d5d --- /dev/null +++ b/compiler/src/it/functional-tests/target/generated-test-sources/test-annotations/test/AutoAnnotation_MultibindingTest_testWrappedAnnotationKey.java @@ -0,0 +1,120 @@ + +package test; + +import java.util.Arrays; +import javax.annotation.Generated; + +@Generated("com.google.auto.value.processor.AutoAnnotationProcessor") +final class AutoAnnotation_MultibindingTest_testWrappedAnnotationKey implements TestWrappedAnnotationKey { + + private final TestStringKey value; + + private final int[] integers; + + private final TestClassKey[] annotations; + + private final Class<? extends Number>[] classes; + + AutoAnnotation_MultibindingTest_testWrappedAnnotationKey( + TestStringKey value, + int[] integers, + TestClassKey[] annotations, + Class<? extends Number>[] classes) { + if (value == null) { + throw new NullPointerException("Null value"); + } + this.value = value; + if (integers == null) { + throw new NullPointerException("Null integers"); + } + this.integers = integers.clone(); + if (annotations == null) { + throw new NullPointerException("Null annotations"); + } + this.annotations = annotations.clone(); + if (classes == null) { + throw new NullPointerException("Null classes"); + } + this.classes = classes.clone(); + } + + @Override + public Class<? extends TestWrappedAnnotationKey> annotationType() { + return TestWrappedAnnotationKey.class; + } + + @Override + public TestStringKey value() { + return value; + } + + @Override + public int[] integers() { + return integers.clone(); + } + + @Override + public TestClassKey[] annotations() { + return annotations.clone(); + } + + @Override + public Class<? extends Number>[] classes() { + return classes.clone(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("@test.TestWrappedAnnotationKey("); + sb.append("value="); + sb.append(value); + sb.append(", "); + sb.append("integers="); + sb.append(Arrays.toString(integers)); + sb.append(", "); + sb.append("annotations="); + sb.append(Arrays.toString(annotations)); + sb.append(", "); + sb.append("classes="); + sb.append(Arrays.toString(classes)); + return sb.append(')').toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestWrappedAnnotationKey) { + TestWrappedAnnotationKey that = (TestWrappedAnnotationKey) o; + return (value.equals(that.value())) + && (Arrays.equals(integers, + (that instanceof AutoAnnotation_MultibindingTest_testWrappedAnnotationKey) + ? ((AutoAnnotation_MultibindingTest_testWrappedAnnotationKey) that).integers + : that.integers())) + && (Arrays.equals(annotations, + (that instanceof AutoAnnotation_MultibindingTest_testWrappedAnnotationKey) + ? ((AutoAnnotation_MultibindingTest_testWrappedAnnotationKey) that).annotations + : that.annotations())) + && (Arrays.equals(classes, + (that instanceof AutoAnnotation_MultibindingTest_testWrappedAnnotationKey) + ? ((AutoAnnotation_MultibindingTest_testWrappedAnnotationKey) that).classes + : that.classes())); + } + return false; + } + + @Override + public int hashCode() { + return + ((127 * 111972721) ^ (value.hashCode())) + + ((127 * 570074869) ^ (Arrays.hashCode(integers))) + + ((127 * -961709276) ^ (Arrays.hashCode(annotations))) + + ((127 * 853620774) ^ (Arrays.hashCode(classes))); + // 111972721 is "value".hashCode() + // 570074869 is "integers".hashCode() + // -961709276 is "annotations".hashCode() + // 853620774 is "classes".hashCode() + } + +} diff --git a/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1$1.class b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1$1.class Binary files differnew file mode 100644 index 000000000..a558550c0 --- /dev/null +++ b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1$1.class diff --git a/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1$2.class b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1$2.class Binary files differnew file mode 100644 index 000000000..899679986 --- /dev/null +++ b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1$2.class diff --git a/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1.class b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1.class Binary files differnew file mode 100644 index 000000000..f6abfe204 --- /dev/null +++ b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest$1.class diff --git a/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest.class b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest.class Binary files differnew file mode 100644 index 000000000..956726c91 --- /dev/null +++ b/compiler/src/it/functional-tests/target/test-classes/test/nullables/NullabilityTest.class diff --git a/compiler/src/it/producers-functional-tests/pom.xml b/compiler/src/it/producers-functional-tests/pom.xml new file mode 100644 index 000000000..bc1c3c7b5 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/pom.xml @@ -0,0 +1,98 @@ +<?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.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>com.google.guava</groupId> + <artifactId>guava</artifactId> + </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/test/DependedComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedComponent.java new file mode 100644 index 000000000..fa392dd8e --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedComponent.java @@ -0,0 +1,25 @@ +/* + * 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.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/test/DependedModule.java b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedModule.java new file mode 100644 index 000000000..604107025 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedModule.java @@ -0,0 +1,27 @@ +/* + * 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; + +@Module +final class DependedModule { + @Provides + String provideGreeting() { + return "Hello world!"; + } +} diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProducerModule.java new file mode 100644 index 000000000..ad0c792a9 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProducerModule.java @@ -0,0 +1,34 @@ +/* + * 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.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/test/DependedProductionComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProductionComponent.java new file mode 100644 index 000000000..57f175812 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProductionComponent.java @@ -0,0 +1,25 @@ +/* + * 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.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/test/DependentComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/test/DependentComponent.java new file mode 100644 index 000000000..4b14f99b5 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/DependentComponent.java @@ -0,0 +1,28 @@ +/* + * 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.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/test/DependentProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/test/DependentProducerModule.java new file mode 100644 index 000000000..234c088d3 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/DependentProducerModule.java @@ -0,0 +1,35 @@ +/* + * 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.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/test/MultibindingComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingComponent.java new file mode 100644 index 000000000..561ad4a89 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingComponent.java @@ -0,0 +1,26 @@ +/* + * 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.common.util.concurrent.ListenableFuture; +import dagger.producers.ProductionComponent; +import java.util.Set; + +@ProductionComponent(modules = MultibindingProducerModule.class) +interface MultibindingComponent { + ListenableFuture<Set<String>> strs(); + ListenableFuture<Integer> strCount(); +} diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingProducerModule.java new file mode 100644 index 000000000..4651afcc6 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingProducerModule.java @@ -0,0 +1,49 @@ +/* +* 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.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 static dagger.producers.Produces.Type.SET; +import static dagger.producers.Produces.Type.SET_VALUES; + +@ProducerModule +final class MultibindingProducerModule { + @Produces(type = SET) ListenableFuture<String> futureStr() { + return Futures.immediateFuture("foo"); + } + + @Produces(type = SET) String str() { + return "bar"; + } + + @Produces(type = SET_VALUES) ListenableFuture<Set<String>> futureStrs() { + return Futures.<Set<String>>immediateFuture(ImmutableSet.of("foo1", "foo2")); + } + + @Produces(type = SET_VALUES) Set<String> strs() { + return ImmutableSet.of("bar1", "bar2"); + } + + @Produces int strCount(Set<String> strs) { + return strs.size(); + } +} diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/Request.java b/compiler/src/it/producers-functional-tests/src/main/java/test/Request.java new file mode 100644 index 000000000..039d0fe55 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/Request.java @@ -0,0 +1,31 @@ +/* + * 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 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/test/Response.java b/compiler/src/it/producers-functional-tests/src/main/java/test/Response.java new file mode 100644 index 000000000..7a46e5b0e --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/Response.java @@ -0,0 +1,28 @@ +/* + * 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; + +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/test/ResponseModule.java b/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseModule.java new file mode 100644 index 000000000..4a83c6c83 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseModule.java @@ -0,0 +1,27 @@ +/* + * 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; + +@Module +final class ResponseModule { + @Provides + static int requestNumber() { + return 5; + } +} diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseProducerModule.java new file mode 100644 index 000000000..f3ad22939 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseProducerModule.java @@ -0,0 +1,34 @@ +/* + * 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.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/test/SimpleComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleComponent.java new file mode 100644 index 000000000..583cd50f0 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleComponent.java @@ -0,0 +1,24 @@ +/* + * 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.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/test/SimpleProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleProducerModule.java new file mode 100644 index 000000000..98f3cfb56 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleProducerModule.java @@ -0,0 +1,219 @@ +/* +* 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.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/test/java/test/DependentTest.java b/compiler/src/it/producers-functional-tests/src/test/java/test/DependentTest.java new file mode 100644 index 000000000..15aa48165 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/test/java/test/DependentTest.java @@ -0,0 +1,74 @@ +/* +* 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.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/test/MultibindingTest.java b/compiler/src/it/producers-functional-tests/src/test/java/test/MultibindingTest.java new file mode 100644 index 000000000..20c86dc52 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/test/java/test/MultibindingTest.java @@ -0,0 +1,35 @@ +/* +* 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.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 MultibindingTest { + @Test public void multibinding() 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); + } +} diff --git a/compiler/src/it/producers-functional-tests/src/test/java/test/ProducerFactoryTest.java b/compiler/src/it/producers-functional-tests/src/test/java/test/ProducerFactoryTest.java new file mode 100644 index 000000000..4c347318a --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/test/java/test/ProducerFactoryTest.java @@ -0,0 +1,195 @@ +/* +* 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 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.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +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.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 java.util.concurrent.ExecutionException; + +@RunWith(JUnit4.class) +public class ProducerFactoryTest { + @Mock private ProductionComponentMonitor componentMonitor; + @Mock private ProducerMonitor monitor; + + @Before + public void setUpMocks() { + MockitoAnnotations.initMocks(this); + when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor); + } + + @Test + public void noArgMethod() throws Exception { + ProducerToken token = ProducerToken.create(SimpleProducerModule_StrFactory.class); + Producer<String> producer = + new SimpleProducerModule_StrFactory( + componentMonitor, MoreExecutors.directExecutor()); + 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( + componentMonitor, MoreExecutors.directExecutor(), 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( + componentMonitor, MoreExecutors.directExecutor(), 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( + componentMonitor, MoreExecutors.directExecutor(), 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( + componentMonitor, MoreExecutors.directExecutor()); + 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 + public void nullComponentMonitor() throws Exception { + Producer<String> producer = + new SimpleProducerModule_StrFactory(null, MoreExecutors.directExecutor()); + assertThat(producer.get().get()).isEqualTo("str"); + verifyZeroInteractions(componentMonitor, monitor); + } + + @Test + public void nullMonitor() throws Exception { + when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(null); + + ProducerToken token = ProducerToken.create(SimpleProducerModule_StrFactory.class); + Producer<String> producer = + new SimpleProducerModule_StrFactory( + componentMonitor, MoreExecutors.directExecutor()); + assertThat(producer.get().get()).isEqualTo("str"); + verify(componentMonitor).producerMonitorFor(token); + verifyZeroInteractions(monitor); + } + + 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/test/SimpleTest.java b/compiler/src/it/producers-functional-tests/src/test/java/test/SimpleTest.java new file mode 100644 index 000000000..e6e73961c --- /dev/null +++ b/compiler/src/it/producers-functional-tests/src/test/java/test/SimpleTest.java @@ -0,0 +1,35 @@ +/* +* 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.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/target/classes/test/DaggerDependedComponent$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent$1.class Binary files differnew file mode 100644 index 000000000..a91fe82ec --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent$Builder.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent$Builder.class Binary files differnew file mode 100644 index 000000000..eb9ca5814 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent$Builder.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent.class Binary files differnew file mode 100644 index 000000000..1f7742d1d --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent$1.class Binary files differnew file mode 100644 index 000000000..410d94316 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent$Builder.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent$Builder.class Binary files differnew file mode 100644 index 000000000..2513bc09b --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent$Builder.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent.class Binary files differnew file mode 100644 index 000000000..acf8d8abf --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependedProductionComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$1.class Binary files differnew file mode 100644 index 000000000..63265be68 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$2.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$2.class Binary files differnew file mode 100644 index 000000000..6ed580933 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$2.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$Builder.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$Builder.class Binary files differnew file mode 100644 index 000000000..e86528462 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent$Builder.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent.class Binary files differnew file mode 100644 index 000000000..a02fb5594 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerDependentComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent$1.class Binary files differnew file mode 100644 index 000000000..ebdc3abfa --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent$Builder.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent$Builder.class Binary files differnew file mode 100644 index 000000000..0d7425a91 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent$Builder.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent.class Binary files differnew file mode 100644 index 000000000..cbb2e35e0 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerMultibindingComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent$1.class Binary files differnew file mode 100644 index 000000000..e55138d76 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent$Builder.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent$Builder.class Binary files differnew file mode 100644 index 000000000..56538412e --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent$Builder.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent.class Binary files differnew file mode 100644 index 000000000..8f486a6ed --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DaggerSimpleComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependedComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependedComponent.class Binary files differnew file mode 100644 index 000000000..6b785c0ff --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependedComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependedModule.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependedModule.class Binary files differnew file mode 100644 index 000000000..6c67e1d7d --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependedModule.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependedModule_ProvideGreetingFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependedModule_ProvideGreetingFactory.class Binary files differnew file mode 100644 index 000000000..d9a8fc543 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependedModule_ProvideGreetingFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule.class Binary files differnew file mode 100644 index 000000000..589230974 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule_ProduceNumberOfGreetingsFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule_ProduceNumberOfGreetingsFactory$1.class Binary files differnew file mode 100644 index 000000000..f1b7f66ba --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule_ProduceNumberOfGreetingsFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule_ProduceNumberOfGreetingsFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule_ProduceNumberOfGreetingsFactory.class Binary files differnew file mode 100644 index 000000000..0d6a5b29b --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProducerModule_ProduceNumberOfGreetingsFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependedProductionComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProductionComponent.class Binary files differnew file mode 100644 index 000000000..8235aae01 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependedProductionComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependentComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependentComponent.class Binary files differnew file mode 100644 index 000000000..a343eb341 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependentComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule.class Binary files differnew file mode 100644 index 000000000..88aa71235 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule_GreetingsFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule_GreetingsFactory$1.class Binary files differnew file mode 100644 index 000000000..daa203c65 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule_GreetingsFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule_GreetingsFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule_GreetingsFactory.class Binary files differnew file mode 100644 index 000000000..81062ffc6 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/DependentProducerModule_GreetingsFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingComponent.class Binary files differnew file mode 100644 index 000000000..029eb4f29 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule.class Binary files differnew file mode 100644 index 000000000..969082eb1 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrFactory$1.class Binary files differnew file mode 100644 index 000000000..76795987d --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrFactory.class Binary files differnew file mode 100644 index 000000000..9d7d69f78 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrsFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrsFactory$1.class Binary files differnew file mode 100644 index 000000000..b39e8a870 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrsFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrsFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrsFactory.class Binary files differnew file mode 100644 index 000000000..d55182cd7 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_FutureStrsFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrCountFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrCountFactory$1.class Binary files differnew file mode 100644 index 000000000..82877e51c --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrCountFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrCountFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrCountFactory.class Binary files differnew file mode 100644 index 000000000..1d8769756 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrCountFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrFactory$1.class Binary files differnew file mode 100644 index 000000000..ac9e2935e --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrFactory.class Binary files differnew file mode 100644 index 000000000..d1ff35e3d --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrsFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrsFactory$1.class Binary files differnew file mode 100644 index 000000000..a68db6060 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrsFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrsFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrsFactory.class Binary files differnew file mode 100644 index 000000000..47c2f6320 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/MultibindingProducerModule_StrsFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/Request.class b/compiler/src/it/producers-functional-tests/target/classes/test/Request.class Binary files differnew file mode 100644 index 000000000..ff235acfd --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/Request.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/Request_Factory.class b/compiler/src/it/producers-functional-tests/target/classes/test/Request_Factory.class Binary files differnew file mode 100644 index 000000000..cca4b9bd2 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/Request_Factory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/Response.class b/compiler/src/it/producers-functional-tests/target/classes/test/Response.class Binary files differnew file mode 100644 index 000000000..230c7e232 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/Response.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule.class b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule.class Binary files differnew file mode 100644 index 000000000..72bf75b4c --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_GreetingFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_GreetingFactory$1.class Binary files differnew file mode 100644 index 000000000..9ced7811b --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_GreetingFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_GreetingFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_GreetingFactory.class Binary files differnew file mode 100644 index 000000000..958e2953c --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_GreetingFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_ResponseFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_ResponseFactory$1.class Binary files differnew file mode 100644 index 000000000..18d2dd382 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_ResponseFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_ResponseFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_ResponseFactory.class Binary files differnew file mode 100644 index 000000000..495d4a790 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/ResponseProducerModule_ResponseFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/SimpleComponent.class b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleComponent.class Binary files differnew file mode 100644 index 000000000..0bb28fee5 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleComponent.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule.class b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule.class Binary files differnew file mode 100644 index 000000000..37cb83eb2 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_LenFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_LenFactory$1.class Binary files differnew file mode 100644 index 000000000..fe2162c7c --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_LenFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_LenFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_LenFactory.class Binary files differnew file mode 100644 index 000000000..225191592 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_LenFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_StrFactory$1.class b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_StrFactory$1.class Binary files differnew file mode 100644 index 000000000..77dc845fa --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_StrFactory$1.class diff --git a/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_StrFactory.class b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_StrFactory.class Binary files differnew file mode 100644 index 000000000..dfd5c8154 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/classes/test/SimpleProducerModule_StrFactory.class diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependedComponent.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependedComponent.java new file mode 100644 index 000000000..7bafd2699 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependedComponent.java @@ -0,0 +1,54 @@ +package test; + +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerDependedComponent implements DependedComponent { + private Provider<String> provideGreetingProvider; + + private DaggerDependedComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static DependedComponent create() { + return builder().build(); + } + + private void initialize(final Builder builder) { + this.provideGreetingProvider = DependedModule_ProvideGreetingFactory.create(builder.dependedModule); + } + + @Override + public String getGreeting() { + return provideGreetingProvider.get(); + } + + public static final class Builder { + private DependedModule dependedModule; + + private Builder() { + } + + public DependedComponent build() { + if (dependedModule == null) { + this.dependedModule = new DependedModule(); + } + return new DaggerDependedComponent(this); + } + + public Builder dependedModule(DependedModule dependedModule) { + if (dependedModule == null) { + throw new NullPointerException("dependedModule"); + } + this.dependedModule = dependedModule; + return this; + } + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependedProductionComponent.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependedProductionComponent.java new file mode 100644 index 000000000..ca5d48b8e --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependedProductionComponent.java @@ -0,0 +1,64 @@ +package test; + +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.Producer; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerDependedProductionComponent implements DependedProductionComponent { + private Producer<Integer> produceNumberOfGreetingsProducer; + + private DaggerDependedProductionComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.produceNumberOfGreetingsProducer = new DependedProducerModule_ProduceNumberOfGreetingsFactory(builder.dependedProducerModule, builder.executor); + } + + @Override + public ListenableFuture<Integer> numGreetings() { + return produceNumberOfGreetingsProducer.get(); + } + + public static final class Builder { + private DependedProducerModule dependedProducerModule; + private Executor executor; + + private Builder() { + } + + public DependedProductionComponent build() { + if (dependedProducerModule == null) { + this.dependedProducerModule = new DependedProducerModule(); + } + if (executor == null) { + throw new IllegalStateException("executor must be set"); + } + return new DaggerDependedProductionComponent(this); + } + + public Builder dependedProducerModule(DependedProducerModule dependedProducerModule) { + if (dependedProducerModule == null) { + throw new NullPointerException("dependedProducerModule"); + } + this.dependedProducerModule = dependedProducerModule; + return this; + } + + public Builder executor(Executor executor) { + if (executor == null) { + throw new NullPointerException("executor"); + } + this.executor = executor; + return this; + } + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependentComponent.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependentComponent.java new file mode 100644 index 000000000..22f46a0bf --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerDependentComponent.java @@ -0,0 +1,110 @@ +package test; + +import com.google.common.util.concurrent.ListenableFuture; +import dagger.internal.Factory; +import dagger.producers.Producer; +import dagger.producers.internal.Producers; +import java.util.List; +import java.util.concurrent.Executor; +import javax.annotation.Generated; +import javax.inject.Provider; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerDependentComponent implements DependentComponent { + private Producer<Integer> numGreetingsProducer; + private Provider<String> getGreetingProvider; + private Producer<List<String>> greetingsProducer; + + private DaggerDependentComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.numGreetingsProducer = new Producer<Integer>() { + private final DependedProductionComponent dependedProductionComponent = builder.dependedProductionComponent; + @Override public ListenableFuture<Integer> get() { + return dependedProductionComponent.numGreetings(); + } + }; + this.getGreetingProvider = new Factory<String>() { + private final DependedComponent dependedComponent = builder.dependedComponent; + @Override public String get() { + String provided = dependedComponent.getGreeting(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable component method"); + } + return provided; + } + }; + this.greetingsProducer = new DependentProducerModule_GreetingsFactory(builder.dependentProducerModule, builder.executor, numGreetingsProducer, Producers.producerFromProvider(getGreetingProvider)); + } + + @Override + public ListenableFuture<List<String>> greetings() { + return greetingsProducer.get(); + } + + public static final class Builder { + private DependentProducerModule dependentProducerModule; + private DependedComponent dependedComponent; + private DependedProductionComponent dependedProductionComponent; + private Executor executor; + + private Builder() { + } + + public DependentComponent build() { + if (dependentProducerModule == null) { + this.dependentProducerModule = new DependentProducerModule(); + } + if (dependedComponent == null) { + throw new IllegalStateException("dependedComponent must be set"); + } + if (dependedProductionComponent == null) { + throw new IllegalStateException("dependedProductionComponent must be set"); + } + if (executor == null) { + throw new IllegalStateException("executor must be set"); + } + return new DaggerDependentComponent(this); + } + + public Builder dependentProducerModule(DependentProducerModule dependentProducerModule) { + if (dependentProducerModule == null) { + throw new NullPointerException("dependentProducerModule"); + } + this.dependentProducerModule = dependentProducerModule; + return this; + } + + public Builder dependedComponent(DependedComponent dependedComponent) { + if (dependedComponent == null) { + throw new NullPointerException("dependedComponent"); + } + this.dependedComponent = dependedComponent; + return this; + } + + public Builder dependedProductionComponent(DependedProductionComponent dependedProductionComponent) { + if (dependedProductionComponent == null) { + throw new NullPointerException("dependedProductionComponent"); + } + this.dependedProductionComponent = dependedProductionComponent; + return this; + } + + public Builder executor(Executor executor) { + if (executor == null) { + throw new NullPointerException("executor"); + } + this.executor = executor; + return this; + } + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerMultibindingComponent.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerMultibindingComponent.java new file mode 100644 index 000000000..fe03735e2 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerMultibindingComponent.java @@ -0,0 +1,81 @@ +package test; + +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.Producer; +import dagger.producers.internal.SetProducer; +import java.util.Set; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerMultibindingComponent implements MultibindingComponent { + private Producer<Set<String>> setOfStringContribution1Producer; + private Producer<Set<String>> setOfStringContribution2Producer; + private Producer<Set<String>> setOfStringContribution3Producer; + private Producer<Set<String>> setOfStringContribution4Producer; + private Producer<Set<String>> setOfStringProducer; + private Producer<Integer> strCountProducer; + + private DaggerMultibindingComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.setOfStringContribution1Producer = new MultibindingProducerModule_FutureStrFactory(builder.multibindingProducerModule, builder.executor); + this.setOfStringContribution2Producer = new MultibindingProducerModule_StrFactory(builder.multibindingProducerModule, builder.executor); + this.setOfStringContribution3Producer = new MultibindingProducerModule_FutureStrsFactory(builder.multibindingProducerModule, builder.executor); + this.setOfStringContribution4Producer = new MultibindingProducerModule_StrsFactory(builder.multibindingProducerModule, builder.executor); + this.setOfStringProducer = SetProducer.create(setOfStringContribution1Producer, setOfStringContribution2Producer, setOfStringContribution3Producer, setOfStringContribution4Producer); + this.strCountProducer = new MultibindingProducerModule_StrCountFactory(builder.multibindingProducerModule, builder.executor, setOfStringProducer); + } + + @Override + public ListenableFuture<Set<String>> strs() { + return setOfStringProducer.get(); + } + + @Override + public ListenableFuture<Integer> strCount() { + return strCountProducer.get(); + } + + public static final class Builder { + private MultibindingProducerModule multibindingProducerModule; + private Executor executor; + + private Builder() { + } + + public MultibindingComponent build() { + if (multibindingProducerModule == null) { + this.multibindingProducerModule = new MultibindingProducerModule(); + } + if (executor == null) { + throw new IllegalStateException("executor must be set"); + } + return new DaggerMultibindingComponent(this); + } + + public Builder multibindingProducerModule(MultibindingProducerModule multibindingProducerModule) { + if (multibindingProducerModule == null) { + throw new NullPointerException("multibindingProducerModule"); + } + this.multibindingProducerModule = multibindingProducerModule; + return this; + } + + public Builder executor(Executor executor) { + if (executor == null) { + throw new NullPointerException("executor"); + } + this.executor = executor; + return this; + } + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerSimpleComponent.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerSimpleComponent.java new file mode 100644 index 000000000..1a5f86f65 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DaggerSimpleComponent.java @@ -0,0 +1,67 @@ +package test; + +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.Producer; +import dagger.producers.internal.Producers; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DaggerSimpleComponent implements SimpleComponent { + private Producer<String> greetingProducer; + private Producer<Response> responseProducer; + + private DaggerSimpleComponent(Builder builder) { + assert builder != null; + initialize(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private void initialize(final Builder builder) { + this.greetingProducer = new ResponseProducerModule_GreetingFactory(builder.responseProducerModule, builder.executor); + this.responseProducer = new ResponseProducerModule_ResponseFactory(builder.responseProducerModule, builder.executor, greetingProducer, Producers.producerFromProvider(Request_Factory.create())); + } + + @Override + public ListenableFuture<Response> response() { + return responseProducer.get(); + } + + public static final class Builder { + private ResponseProducerModule responseProducerModule; + private Executor executor; + + private Builder() { + } + + public SimpleComponent build() { + if (responseProducerModule == null) { + this.responseProducerModule = new ResponseProducerModule(); + } + if (executor == null) { + throw new IllegalStateException("executor must be set"); + } + return new DaggerSimpleComponent(this); + } + + public Builder responseProducerModule(ResponseProducerModule responseProducerModule) { + if (responseProducerModule == null) { + throw new NullPointerException("responseProducerModule"); + } + this.responseProducerModule = responseProducerModule; + return this; + } + + public Builder executor(Executor executor) { + if (executor == null) { + throw new NullPointerException("executor"); + } + this.executor = executor; + return this; + } + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependedModule_ProvideGreetingFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependedModule_ProvideGreetingFactory.java new file mode 100644 index 000000000..ef14f86af --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependedModule_ProvideGreetingFactory.java @@ -0,0 +1,28 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DependedModule_ProvideGreetingFactory implements Factory<String> { + private final DependedModule module; + + public DependedModule_ProvideGreetingFactory(DependedModule module) { + assert module != null; + this.module = module; + } + + @Override + public String get() { + String provided = module.provideGreeting(); + if (provided == null) { + throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); + } + return provided; + } + + public static Factory<String> create(DependedModule module) { + return new DependedModule_ProvideGreetingFactory(module); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependedProducerModule_ProduceNumberOfGreetingsFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependedProducerModule_ProduceNumberOfGreetingsFactory.java new file mode 100644 index 000000000..4bee1017b --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependedProducerModule_ProduceNumberOfGreetingsFactory.java @@ -0,0 +1,32 @@ +package test; + +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.internal.AbstractProducer; +import dagger.producers.internal.Producers; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DependedProducerModule_ProduceNumberOfGreetingsFactory extends AbstractProducer<Integer> { + private final DependedProducerModule module; + private final Executor executor; + + public DependedProducerModule_ProduceNumberOfGreetingsFactory(DependedProducerModule module, Executor executor) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + } + + @Override + protected ListenableFuture<Integer> compute() { + ListenableFuture<Integer> future = Producers.submitToExecutor(new Callable<Integer>() { + @Override public Integer call() { + return module.produceNumberOfGreetings(); + } + }, executor); + return future; + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependentProducerModule_GreetingsFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependentProducerModule_GreetingsFactory.java new file mode 100644 index 000000000..ba4887249 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/DependentProducerModule_GreetingsFactory.java @@ -0,0 +1,42 @@ +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.Producer; +import dagger.producers.internal.AbstractProducer; +import java.util.List; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class DependentProducerModule_GreetingsFactory extends AbstractProducer<List<String>> { + private final DependentProducerModule module; + private final Executor executor; + private final Producer<Integer> numGreetingsProducer; + private final Producer<String> greetingProducer; + + public DependentProducerModule_GreetingsFactory(DependentProducerModule module, Executor executor, Producer<Integer> numGreetingsProducer, Producer<String> greetingProducer) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + assert numGreetingsProducer != null; + this.numGreetingsProducer = numGreetingsProducer; + assert greetingProducer != null; + this.greetingProducer = greetingProducer; + } + + @Override + protected ListenableFuture<List<String>> compute() { + ListenableFuture<Integer> numGreetingsProducerFuture = numGreetingsProducer.get(); + ListenableFuture<String> greetingProducerFuture = greetingProducer.get(); + return Futures.transform(Futures.<Object>allAsList(numGreetingsProducerFuture,greetingProducerFuture), new AsyncFunction<List<Object>, List<String>>() { + @SuppressWarnings("unchecked") // safe by specification + @Override public ListenableFuture<List<String>> apply(List<Object> args) { + return module.greetings((Integer) args.get(0), (String) args.get(1)); + } + }, executor); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_FutureStrFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_FutureStrFactory.java new file mode 100644 index 000000000..1f8e47fc6 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_FutureStrFactory.java @@ -0,0 +1,34 @@ +package test; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.internal.AbstractProducer; +import dagger.producers.internal.Producers; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingProducerModule_FutureStrFactory extends AbstractProducer<Set<String>> { + private final MultibindingProducerModule module; + private final Executor executor; + + public MultibindingProducerModule_FutureStrFactory(MultibindingProducerModule module, Executor executor) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + } + + @Override + protected ListenableFuture<Set<String>> compute() { + ListenableFuture<ListenableFuture<Set<String>>> future = Producers.submitToExecutor(new Callable<ListenableFuture<Set<String>>>() { + @Override public ListenableFuture<Set<String>> call() { + return Producers.createFutureSingletonSet(module.futureStr()); + } + }, executor); + return Futures.dereference(future); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_FutureStrsFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_FutureStrsFactory.java new file mode 100644 index 000000000..8838ea5a4 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_FutureStrsFactory.java @@ -0,0 +1,34 @@ +package test; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.internal.AbstractProducer; +import dagger.producers.internal.Producers; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingProducerModule_FutureStrsFactory extends AbstractProducer<Set<String>> { + private final MultibindingProducerModule module; + private final Executor executor; + + public MultibindingProducerModule_FutureStrsFactory(MultibindingProducerModule module, Executor executor) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + } + + @Override + protected ListenableFuture<Set<String>> compute() { + ListenableFuture<ListenableFuture<Set<String>>> future = Producers.submitToExecutor(new Callable<ListenableFuture<Set<String>>>() { + @Override public ListenableFuture<Set<String>> call() { + return module.futureStrs(); + } + }, executor); + return Futures.dereference(future); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrCountFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrCountFactory.java new file mode 100644 index 000000000..1da88f986 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrCountFactory.java @@ -0,0 +1,37 @@ +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.Producer; +import dagger.producers.internal.AbstractProducer; +import java.util.Set; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingProducerModule_StrCountFactory extends AbstractProducer<Integer> { + private final MultibindingProducerModule module; + private final Executor executor; + private final Producer<Set<String>> strsProducer; + + public MultibindingProducerModule_StrCountFactory(MultibindingProducerModule module, Executor executor, Producer<Set<String>> strsProducer) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + assert strsProducer != null; + this.strsProducer = strsProducer; + } + + @Override + protected ListenableFuture<Integer> compute() { + ListenableFuture<Set<String>> strsProducerFuture = strsProducer.get(); + return Futures.transform(strsProducerFuture, new AsyncFunction<Set<String>, Integer>() { + @Override public ListenableFuture<Integer> apply(Set<String> strs) { + return Futures.immediateFuture(module.strCount(strs)); + } + }, executor); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrFactory.java new file mode 100644 index 000000000..e514ff9b5 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrFactory.java @@ -0,0 +1,34 @@ +package test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.internal.AbstractProducer; +import dagger.producers.internal.Producers; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingProducerModule_StrFactory extends AbstractProducer<Set<String>> { + private final MultibindingProducerModule module; + private final Executor executor; + + public MultibindingProducerModule_StrFactory(MultibindingProducerModule module, Executor executor) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + } + + @Override + protected ListenableFuture<Set<String>> compute() { + ListenableFuture<Set<String>> future = Producers.submitToExecutor(new Callable<Set<String>>() { + @Override public Set<String> call() { + return ImmutableSet.of(module.str()); + } + }, executor); + return future; + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrsFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrsFactory.java new file mode 100644 index 000000000..3bc2c1d83 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/MultibindingProducerModule_StrsFactory.java @@ -0,0 +1,33 @@ +package test; + +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.internal.AbstractProducer; +import dagger.producers.internal.Producers; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class MultibindingProducerModule_StrsFactory extends AbstractProducer<Set<String>> { + private final MultibindingProducerModule module; + private final Executor executor; + + public MultibindingProducerModule_StrsFactory(MultibindingProducerModule module, Executor executor) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + } + + @Override + protected ListenableFuture<Set<String>> compute() { + ListenableFuture<Set<String>> future = Producers.submitToExecutor(new Callable<Set<String>>() { + @Override public Set<String> call() { + return module.strs(); + } + }, executor); + return future; + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/Request_Factory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/Request_Factory.java new file mode 100644 index 000000000..5a5b3c24e --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/Request_Factory.java @@ -0,0 +1,19 @@ +package test; + +import dagger.internal.Factory; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public enum Request_Factory implements Factory<Request> { +INSTANCE; + + @Override + public Request get() { + return new Request(); + } + + public static Factory<Request> create() { + return INSTANCE; + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/ResponseProducerModule_GreetingFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/ResponseProducerModule_GreetingFactory.java new file mode 100644 index 000000000..8e0d8bd78 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/ResponseProducerModule_GreetingFactory.java @@ -0,0 +1,33 @@ +package test; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.internal.AbstractProducer; +import dagger.producers.internal.Producers; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ResponseProducerModule_GreetingFactory extends AbstractProducer<String> { + private final ResponseProducerModule module; + private final Executor executor; + + public ResponseProducerModule_GreetingFactory(ResponseProducerModule module, Executor executor) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + } + + @Override + protected ListenableFuture<String> compute() { + ListenableFuture<ListenableFuture<String>> future = Producers.submitToExecutor(new Callable<ListenableFuture<String>>() { + @Override public ListenableFuture<String> call() { + return module.greeting(); + } + }, executor); + return Futures.dereference(future); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/ResponseProducerModule_ResponseFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/ResponseProducerModule_ResponseFactory.java new file mode 100644 index 000000000..2d65f3890 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/ResponseProducerModule_ResponseFactory.java @@ -0,0 +1,42 @@ +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.Producer; +import dagger.producers.internal.AbstractProducer; +import java.util.List; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class ResponseProducerModule_ResponseFactory extends AbstractProducer<Response> { + private final ResponseProducerModule module; + private final Executor executor; + private final Producer<String> greetingProducer; + private final Producer<Request> requestProducer; + + public ResponseProducerModule_ResponseFactory(ResponseProducerModule module, Executor executor, Producer<String> greetingProducer, Producer<Request> requestProducer) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + assert greetingProducer != null; + this.greetingProducer = greetingProducer; + assert requestProducer != null; + this.requestProducer = requestProducer; + } + + @Override + protected ListenableFuture<Response> compute() { + ListenableFuture<String> greetingProducerFuture = greetingProducer.get(); + ListenableFuture<Request> requestProducerFuture = requestProducer.get(); + return Futures.transform(Futures.<Object>allAsList(greetingProducerFuture,requestProducerFuture), new AsyncFunction<List<Object>, Response>() { + @SuppressWarnings("unchecked") // safe by specification + @Override public ListenableFuture<Response> apply(List<Object> args) { + return Futures.immediateFuture(module.response((String) args.get(0), (Request) args.get(1))); + } + }, executor); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/SimpleProducerModule_LenFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/SimpleProducerModule_LenFactory.java new file mode 100644 index 000000000..0d26ff842 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/SimpleProducerModule_LenFactory.java @@ -0,0 +1,36 @@ +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.Producer; +import dagger.producers.internal.AbstractProducer; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class SimpleProducerModule_LenFactory extends AbstractProducer<Integer> { + private final SimpleProducerModule module; + private final Executor executor; + private final Producer<String> strProducer; + + public SimpleProducerModule_LenFactory(SimpleProducerModule module, Executor executor, Producer<String> strProducer) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + assert strProducer != null; + this.strProducer = strProducer; + } + + @Override + protected ListenableFuture<Integer> compute() { + ListenableFuture<String> strProducerFuture = strProducer.get(); + return Futures.transform(strProducerFuture, new AsyncFunction<String, Integer>() { + @Override public ListenableFuture<Integer> apply(String str) { + return Futures.immediateFuture(module.len(str)); + } + }, executor); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/SimpleProducerModule_StrFactory.java b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/SimpleProducerModule_StrFactory.java new file mode 100644 index 000000000..d38e99c3f --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/generated-sources/annotations/test/SimpleProducerModule_StrFactory.java @@ -0,0 +1,33 @@ +package test; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.internal.AbstractProducer; +import dagger.producers.internal.Producers; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; + +@Generated("dagger.internal.codegen.ComponentProcessor") +public final class SimpleProducerModule_StrFactory extends AbstractProducer<String> { + private final SimpleProducerModule module; + private final Executor executor; + + public SimpleProducerModule_StrFactory(SimpleProducerModule module, Executor executor) { + assert module != null; + this.module = module; + assert executor != null; + this.executor = executor; + } + + @Override + protected ListenableFuture<String> compute() { + ListenableFuture<ListenableFuture<String>> future = Producers.submitToExecutor(new Callable<ListenableFuture<String>>() { + @Override public ListenableFuture<String> call() { + return module.str(); + } + }, executor); + return Futures.dereference(future); + } +} + diff --git a/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest$1.class b/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest$1.class Binary files differnew file mode 100644 index 000000000..ff19f2a48 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest$1.class diff --git a/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest$2.class b/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest$2.class Binary files differnew file mode 100644 index 000000000..f3a1d6008 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest$2.class diff --git a/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest.class b/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest.class Binary files differnew file mode 100644 index 000000000..c7c6daa67 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/test-classes/test/DependentTest.class diff --git a/compiler/src/it/producers-functional-tests/target/test-classes/test/MultibindingTest.class b/compiler/src/it/producers-functional-tests/target/test-classes/test/MultibindingTest.class Binary files differnew file mode 100644 index 000000000..cbf125c2d --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/test-classes/test/MultibindingTest.class diff --git a/compiler/src/it/producers-functional-tests/target/test-classes/test/ProducerFactoryTest$1.class b/compiler/src/it/producers-functional-tests/target/test-classes/test/ProducerFactoryTest$1.class Binary files differnew file mode 100644 index 000000000..7daeead5d --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/test-classes/test/ProducerFactoryTest$1.class diff --git a/compiler/src/it/producers-functional-tests/target/test-classes/test/ProducerFactoryTest.class b/compiler/src/it/producers-functional-tests/target/test-classes/test/ProducerFactoryTest.class Binary files differnew file mode 100644 index 000000000..96ee02f11 --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/test-classes/test/ProducerFactoryTest.class diff --git a/compiler/src/it/producers-functional-tests/target/test-classes/test/SimpleTest.class b/compiler/src/it/producers-functional-tests/target/test-classes/test/SimpleTest.class Binary files differnew file mode 100644 index 000000000..d00fb7c0d --- /dev/null +++ b/compiler/src/it/producers-functional-tests/target/test-classes/test/SimpleTest.class diff --git a/compiler/src/main/java/dagger/internal/codegen/AbstractComponentProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/AbstractComponentProcessingStep.java new file mode 100644 index 000000000..578fb5ffa --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/AbstractComponentProcessingStep.java @@ -0,0 +1,117 @@ +/* + * 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 new file mode 100644 index 000000000..e0425c925 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java @@ -0,0 +1,1150 @@ +/* + * 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.base.Predicates; +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.ComponentGenerator.ProxyClassAndField; +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.TypeWriter; +import dagger.internal.codegen.writer.VoidName; +import dagger.producers.Producer; +import dagger.producers.internal.Producers; +import dagger.producers.internal.SetProducer; +import java.util.Collection; +import java.util.EnumSet; +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.annotation.Generated; +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.Modifier; +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.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.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.ProvisionBinding.FactoryCreationStrategy.ENUM_INSTANCE; +import static dagger.internal.codegen.ProvisionBinding.Kind.PROVISION; +import static dagger.internal.codegen.SourceFiles.factoryNameForProductionBinding; +import static dagger.internal.codegen.SourceFiles.factoryNameForProvisionBinding; +import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement; +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.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<String, ProxyClassAndField> packageProxies = new HashMap<>(); + private final Map<BindingKey, InitializationState> initializationStates = new HashMap<>(); + protected ClassWriter componentWriter; + private ImmutableMap<BindingKey, MemberSelect> memberSelectSnippets; + private ImmutableMap<ContributionBinding, MemberSelect> multibindingContributionSnippets; + private ImmutableSet<BindingKey> enumBindingKeys; + 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); + } + + 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() { + Map<BindingKey, MemberSelect> memberSelectSnippetsBuilder = Maps.newHashMap(); + Map<ContributionBinding, MemberSelect> multibindingContributionSnippetsBuilder = + Maps.newHashMap(); + ImmutableSet.Builder<BindingKey> enumBindingKeysBuilder = ImmutableSet.builder(); + + for (ResolvedBindings resolvedBindings : graph.resolvedBindings().values()) { + addField( + memberSelectSnippetsBuilder, + multibindingContributionSnippetsBuilder, + enumBindingKeysBuilder, + resolvedBindings); + } + + memberSelectSnippets = ImmutableMap.copyOf(memberSelectSnippetsBuilder); + multibindingContributionSnippets = ImmutableMap.copyOf(multibindingContributionSnippetsBuilder); + enumBindingKeys = enumBindingKeysBuilder.build(); + } + + private void addField( + Map<BindingKey, MemberSelect> memberSelectSnippetsBuilder, + Map<ContributionBinding, MemberSelect> multibindingContributionSnippetsBuilder, + ImmutableSet.Builder<BindingKey> enumBindingKeysBuilder, + ResolvedBindings resolvedBindings) { + BindingKey bindingKey = resolvedBindings.bindingKey(); + + // No field needed for unique contributions inherited from the parent. + if (resolvedBindings.isUniqueContribution() && resolvedBindings.ownedBindings().isEmpty()) { + return; + } + + // No field needed for bindings with no dependencies or state. + Optional<MemberSelect> staticMemberSelect = staticMemberSelect(resolvedBindings); + if (staticMemberSelect.isPresent()) { + // TODO(gak): refactor to use enumBindingKeys throughout the generator + enumBindingKeysBuilder.add(bindingKey); + memberSelectSnippetsBuilder.put(bindingKey, staticMemberSelect.get()); + return; + } + + String bindingPackage = bindingPackageFor(resolvedBindings.bindings()).or(name.packageName()); + + final Optional<String> proxySelector; + final TypeWriter classWithFields; + final Set<Modifier> fieldModifiers; + + if (bindingPackage.equals(name.packageName())) { + // no proxy + proxySelector = Optional.absent(); + // component gets the fields + classWithFields = componentWriter; + // private fields + fieldModifiers = EnumSet.of(PRIVATE); + } else { + // get or create the proxy + ProxyClassAndField proxyClassAndField = packageProxies.get(bindingPackage); + if (proxyClassAndField == null) { + JavaWriter proxyJavaWriter = JavaWriter.inPackage(bindingPackage); + javaWriters.add(proxyJavaWriter); + ClassWriter proxyWriter = proxyJavaWriter.addClass(name.simpleName() + "_PackageProxy"); + proxyWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getCanonicalName()); + proxyWriter.addModifiers(PUBLIC, FINAL); + // create the field for the proxy in the component + FieldWriter proxyFieldWriter = + componentWriter.addField( + proxyWriter.name(), bindingPackage.replace('.', '_') + "_Proxy"); + proxyFieldWriter.addModifiers(PRIVATE, FINAL); + proxyFieldWriter.setInitializer("new %s()", proxyWriter.name()); + proxyClassAndField = ProxyClassAndField.create(proxyWriter, proxyFieldWriter); + packageProxies.put(bindingPackage, proxyClassAndField); + } + // add the field for the member select + proxySelector = Optional.of(proxyClassAndField.proxyFieldWriter().name()); + // proxy gets the fields + classWithFields = proxyClassAndField.proxyWriter(); + // public fields in the proxy + fieldModifiers = EnumSet.of(PUBLIC); + } + + if (bindingKey.kind().equals(BindingKey.Kind.CONTRIBUTION)) { + ImmutableSet<? extends ContributionBinding> contributionBindings = + resolvedBindings.contributionBindings(); + if (ContributionBinding.bindingTypeFor(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.ownedBindings().contains(contributionBinding)) { + FrameworkField contributionBindingField = + FrameworkField.createForSyntheticContributionBinding( + bindingKey, contributionNumber, contributionBinding); + FieldWriter contributionField = + classWithFields.addField( + contributionBindingField.frameworkType(), contributionBindingField.name()); + contributionField.addModifiers(fieldModifiers); + + ImmutableList<String> contributionSelectTokens = + new ImmutableList.Builder<String>() + .addAll(proxySelector.asSet()) + .add(contributionField.name()) + .build(); + multibindingContributionSnippetsBuilder.put( + contributionBinding, + MemberSelect.instanceSelect(name, memberSelectSnippet(contributionSelectTokens))); + } + } + } + } + } + + FrameworkField bindingField = FrameworkField.createForResolvedBindings(resolvedBindings); + FieldWriter frameworkField = + classWithFields.addField(bindingField.frameworkType(), bindingField.name()); + frameworkField.addModifiers(fieldModifiers); + + ImmutableList<String> memberSelectTokens = + new ImmutableList.Builder<String>() + .addAll(proxySelector.asSet()) + .add(frameworkField.name()) + .build(); + memberSelectSnippetsBuilder.put( + bindingKey, + MemberSelect.instanceSelect(name, Snippet.memberSelectSnippet(memberSelectTokens))); + } + + /** + * If {@code resolvedBindings} is an unscoped provision binding with no factory arguments or a + * no-op members injection binding, then we do'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) { + if (resolvedBindings.bindings().size() != 1) { + return Optional.absent(); + } + switch (resolvedBindings.bindingKey().kind()) { + case CONTRIBUTION: + ContributionBinding contributionBinding = + getOnlyElement(resolvedBindings.contributionBindings()); + if (contributionBinding.bindingType().isMultibinding() + || !(contributionBinding instanceof ProvisionBinding)) { + return Optional.absent(); + } + ProvisionBinding provisionBinding = (ProvisionBinding) contributionBinding; + if (provisionBinding.factoryCreationStrategy().equals(ENUM_INSTANCE) + && !provisionBinding.scope().isPresent()) { + return Optional.of( + staticSelect( + factoryNameForProvisionBinding(provisionBinding), Snippet.format("create()"))); + } + break; + + case MEMBERS_INJECTION: + if (getOnlyElement(resolvedBindings.membersInjectionBindings()) + .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(); + switch (interfaceRequest.kind()) { + case MEMBERS_INJECTOR: + Snippet membersInjectorSelect = getMemberSelectSnippet(bindingKey); + List<? extends VariableElement> parameters = requestElement.getParameters(); + if (parameters.isEmpty()) { + // we're returning the framework type + interfaceMethod.body().addSnippet("return %s;", membersInjectorSelect); + } 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);", + // In this case we know we won't need the cast because we're never going to + // pass the reference to anything. + membersInjectorSelect, + parameterName); + if (!requestType.getReturnType().getKind().equals(VOID)) { + interfaceMethod.body().addSnippet("return %s;", parameterName); + } + } + break; + case INSTANCE: + if (enumBindingKeys.contains(bindingKey) + && 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, getMemberSelectSnippet(bindingKey)); + 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( + getMemberSelectSnippet(bindingKey), 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 void initializeFrameworkTypes() { + List<List<BindingKey>> partitions = + Lists.partition(graph.resolvedBindings().keySet().asList(), 100); + for (int i = 0; i < partitions.size(); i++) { + MethodWriter initializeMethod = + componentWriter.addMethod(VoidName.VOID, "initialize" + ((i == 0) ? "" : i)); + initializeMethod.body(); + 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()); + } + for (BindingKey bindingKey : partitions.get(i)) { + ResolvedBindings resolvedBindings = graph.resolvedBindings().get(bindingKey); + switch (bindingKey.kind()) { + case CONTRIBUTION: + ImmutableSet<? extends ContributionBinding> bindings = + resolvedBindings.contributionBindings(); + + switch (ContributionBinding.bindingTypeFor(bindings)) { + case SET: + boolean hasOnlyProvisions = + Iterables.all(bindings, Predicates.instanceOf(ProvisionBinding.class)); + ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder(); + for (ContributionBinding binding : bindings) { + 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)) { + Snippet initializeSnippet = initializeFactoryForContributionBinding(binding); + initializeMethod.body().addSnippet("this.%s = %s;", snippet, initializeSnippet); + } + parameterSnippets.add(snippet); + } + Snippet initializeSetSnippet = + Snippet.format( + "%s.create(%s)", + hasOnlyProvisions + ? ClassName.fromClass(SetFactory.class) + : ClassName.fromClass(SetProducer.class), + Snippet.makeParametersSnippet(parameterSnippets.build())); + initializeMember(initializeMethod, bindingKey, initializeSetSnippet); + break; + case MAP: + if (Sets.filter(bindings, Predicates.instanceOf(ProductionBinding.class)) + .isEmpty()) { + @SuppressWarnings("unchecked") // checked by the instanceof filter above + ImmutableSet<ProvisionBinding> provisionBindings = + (ImmutableSet<ProvisionBinding>) bindings; + for (ProvisionBinding provisionBinding : provisionBindings) { + Optional<MemberSelect> multibindingContributionSnippet = + getMultibindingContributionSnippet(provisionBinding); + if (!isMapWithNonProvidedValues(provisionBinding.key().type()) + && multibindingContributionSnippet.isPresent() + && multibindingContributionSnippet.get().owningClass().equals(name)) { + initializeMethod + .body() + .addSnippet( + "this.%s = %s;", + multibindingContributionSnippet.get().getSnippetFor(name), + initializeFactoryForProvisionBinding(provisionBinding)); + } + } + initializeMember( + initializeMethod, bindingKey, initializeMapBinding(provisionBindings)); + } else { + // TODO(beder): Implement producer map bindings. + throw new IllegalStateException("producer map bindings not implemented yet"); + } + break; + case UNIQUE: + if (!resolvedBindings.ownedContributionBindings().isEmpty()) { + ContributionBinding binding = Iterables.getOnlyElement(bindings); + if (binding instanceof ProvisionBinding) { + ProvisionBinding provisionBinding = (ProvisionBinding) binding; + if (!provisionBinding.factoryCreationStrategy().equals(ENUM_INSTANCE) + || provisionBinding.scope().isPresent()) { + initializeDelegateFactories(binding, initializeMethod); + initializeMember( + initializeMethod, + bindingKey, + initializeFactoryForProvisionBinding(provisionBinding)); + } + } else if (binding instanceof ProductionBinding) { + ProductionBinding productionBinding = (ProductionBinding) binding; + initializeMember( + initializeMethod, + bindingKey, + initializeFactoryForProductionBinding(productionBinding)); + } else { + throw new AssertionError(); + } + } + break; + default: + throw new IllegalStateException(); + } + break; + case MEMBERS_INJECTION: + MembersInjectionBinding binding = + Iterables.getOnlyElement(resolvedBindings.membersInjectionBindings()); + if (!binding.injectionStrategy().equals(MembersInjectionBinding.Strategy.NO_OP)) { + initializeDelegateFactories(binding, initializeMethod); + initializeMember( + initializeMethod, bindingKey, initializeMembersInjectorForBinding(binding)); + } + break; + default: + throw new AssertionError(); + } + } + } + } + + private void initializeDelegateFactories(Binding binding, MethodWriter initializeMethod) { + 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)) { + initializeMethod + .body() + .addSnippet( + "this.%s = new %s();", + getMemberSelectSnippet(dependencyKey), + ClassName.fromClass(DelegateFactory.class)); + setInitializationState(dependencyKey, DELEGATED); + } + } + } + + private void initializeMember( + MethodWriter initializeMethod, BindingKey bindingKey, Snippet initializationSnippet) { + Snippet memberSelect = getMemberSelectSnippet(bindingKey); + Snippet delegateFactoryVariable = delegateFactoryVariableSnippet(bindingKey); + if (getInitializationState(bindingKey).equals(DELEGATED)) { + initializeMethod + .body() + .addSnippet( + "%1$s %2$s = (%1$s) %3$s;", + ClassName.fromClass(DelegateFactory.class), + delegateFactoryVariable, + memberSelect); + } + initializeMethod.body().addSnippet("this.%s = %s;", memberSelect, initializationSnippet); + if (getInitializationState(bindingKey).equals(DELEGATED)) { + initializeMethod + .body() + .addSnippet("%s.setDelegatedProvider(%s);", delegateFactoryVariable, memberSelect); + } + setInitializationState(bindingKey, INITIALIZED); + } + + private Snippet delegateFactoryVariableSnippet(BindingKey key) { + return Snippet.format("%sDelegate", getMemberSelectSnippet(key).toString().replace('.', '_')); + } + + private Snippet initializeFactoryForContributionBinding(ContributionBinding binding) { + if (binding instanceof ProvisionBinding) { + return initializeFactoryForProvisionBinding((ProvisionBinding) binding); + } else if (binding instanceof ProductionBinding) { + return initializeFactoryForProductionBinding((ProductionBinding) binding); + } else { + throw new AssertionError(); + } + } + + private Snippet initializeFactoryForProvisionBinding(ProvisionBinding 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()); + if (binding.nullableType().isPresent() + || nullableValidationType.equals(Diagnostic.Kind.WARNING)) { + Snippet nullableSnippet = + binding.nullableType().isPresent() + ? Snippet.format("@%s ", TypeNames.forTypeMirror(binding.nullableType().get())) + : Snippet.format(""); + return Snippet.format( + Joiner.on('\n') + .join( + "new %1$s<%2$s>() {", + " private final %6$s %7$s = %3$s;", + " %5$s@Override public %2$s get() {", + " return %7$s.%4$s();", + " }", + "}"), + /* 1 */ ClassName.fromClass(Factory.class), + /* 2 */ bindingKeyTypeName, + /* 3 */ getComponentContributionSnippet(bindingTypeElement), + /* 4 */ binding.bindingElement().getSimpleName().toString(), + /* 5 */ nullableSnippet, + /* 6 */ TypeNames.forTypeMirror(bindingTypeElement.asType()), + /* 7 */ simpleVariableName(bindingTypeElement)); + } else { + // 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); + return Snippet.format( + Joiner.on('\n') + .join( + "new %1$s<%2$s>() {", + " private final %6$s %7$s = %3$s;", + " @Override public %2$s get() {", + " %2$s provided = %7$s.%4$s();", + " if (provided == null) {", + " throw new NullPointerException(%5$s);", + " }", + " return provided;", + " }", + "}"), + /* 1 */ ClassName.fromClass(Factory.class), + /* 2 */ bindingKeyTypeName, + /* 3 */ getComponentContributionSnippet(bindingTypeElement), + /* 4 */ binding.bindingElement().getSimpleName().toString(), + /* 5 */ failMsg, + /* 6 */ TypeNames.forTypeMirror(bindingTypeElement.asType()), + /* 7 */ simpleVariableName(bindingTypeElement)); + } + 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)", + factoryNameForProvisionBinding(binding), + Snippet.makeParametersSnippet(parameters)); + return binding.scope().isPresent() + ? Snippet.format( + "%s.create(%s)", ClassName.fromClass(ScopedProvider.class), factorySnippet) + : factorySnippet; + default: + throw new AssertionError(); + } + } + + private Snippet initializeFactoryForProductionBinding(ProductionBinding binding) { + switch (binding.bindingKind()) { + 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.dependencies().size() + 3); + // TODO(beder): Pass the actual ProductionComponentMonitor. + parameters.add(Snippet.format("null")); + 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)", + factoryNameForProductionBinding(binding), + Snippet.makeParametersSnippet(parameters)); + default: + throw new AssertionError(); + } + } + + 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.dependencies()).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<ProvisionBinding> bindings) { + // Get type information from the first binding. + ProvisionBinding 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 (ProvisionBinding binding : bindings) { + snippets.add( + Snippet.format( + " .put(%s, %s)", + getMapKeySnippet(binding.bindingElement()), + getMultibindingContributionSnippet(binding).get().getSnippetFor(name))); + } + + snippets.add(Snippet.format(" .build()")); + + return Snippet.join(Joiner.on('\n'), 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 new file mode 100644 index 000000000..29f17b3c5 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/Binding.java @@ -0,0 +1,206 @@ +/* + * 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.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import java.util.List; +import java.util.Set; +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 { + 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(); + + /** 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(); + + // 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 new file mode 100644 index 000000000..f170f3988 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java @@ -0,0 +1,642 @@ +/* + * 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.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.ComponentDescriptor.ComponentMethodDescriptor; +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.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 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.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<? extends ContributionBinding>>() { + @Override + public Set<? extends 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 DependencyRequest.Factory dependencyRequestFactory; + private final ProvisionBinding.Factory provisionBindingFactory; + private final ProductionBinding.Factory productionBindingFactory; + + Factory(Elements elements, + InjectBindingRegistry injectBindingRegistry, + Key.Factory keyFactory, + DependencyRequest.Factory dependencyRequestFactory, + ProvisionBinding.Factory provisionBindingFactory, + ProductionBinding.Factory productionBindingFactory) { + this.elements = elements; + this.injectBindingRegistry = injectBindingRegistry; + this.keyFactory = keyFactory; + this.dependencyRequestFactory = dependencyRequestFactory; + 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<ProvisionBinding> explicitProvisionBindingsBuilder = + ImmutableSet.builder(); + ImmutableSet.Builder<ProductionBinding> explicitProductionBindingsBuilder = + ImmutableSet.builder(); + + // binding for the component itself + TypeElement componentDefinitionType = componentDescriptor.componentDefinitionType(); + ProvisionBinding componentBinding = + provisionBindingFactory.forComponent(componentDefinitionType); + explicitProvisionBindingsBuilder.add(componentBinding); + + // 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) { + explicitProvisionBindingsBuilder.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)) { + if (componentDescriptor.kind().equals(PRODUCTION_COMPONENT) + && isComponentProductionMethod(elements, method)) { + explicitProductionBindingsBuilder.add( + productionBindingFactory.forComponentMethod(method)); + } else { + explicitProvisionBindingsBuilder.add( + provisionBindingFactory.forComponentMethod(method)); + } + } + } + } + + // Collect transitive module bindings. + for (ModuleDescriptor moduleDescriptor : componentDescriptor.transitiveModules()) { + for (ContributionBinding binding : moduleDescriptor.bindings()) { + if (binding instanceof ProvisionBinding) { + explicitProvisionBindingsBuilder.add((ProvisionBinding) binding); + } + if (binding instanceof ProductionBinding) { + explicitProductionBindingsBuilder.add((ProductionBinding) binding); + } + } + } + + Resolver requestResolver = + new Resolver( + parentResolver, + componentDescriptor, + explicitBindingsByKey(explicitProvisionBindingsBuilder.build()), + explicitBindingsByKey(explicitProductionBindingsBuilder.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, ProvisionBinding> explicitProvisionBindings; + final ImmutableSet<ProvisionBinding> explicitProvisionBindingsSet; + final ImmutableSetMultimap<Key, ProductionBinding> explicitProductionBindings; + 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, ProvisionBinding> explicitProvisionBindings, + ImmutableSetMultimap<Key, ProductionBinding> explicitProductionBindings) { + assert parentResolver != null; + this.parentResolver = parentResolver; + assert componentDescriptor != null; + this.componentDescriptor = componentDescriptor; + assert explicitProvisionBindings != null; + this.explicitProvisionBindings = explicitProvisionBindings; + this.explicitProvisionBindingsSet = ImmutableSet.copyOf(explicitProvisionBindings.values()); + assert explicitProductionBindings != null; + this.explicitProductionBindings = explicitProductionBindings; + this.resolvedBindings = Maps.newLinkedHashMap(); + } + + /** + * Looks up the bindings associated with a given dependency request and returns them. In the + * event that the binding is owned by a parent component it will trigger resolution in that + * component's resolver but will return an {@link Optional#absent} value. + */ + ResolvedBindings lookUpBindings(DependencyRequest request) { + BindingKey bindingKey = request.bindingKey(); + switch (bindingKey.kind()) { + case CONTRIBUTION: + // First, check for explicit keys (those from modules and components) + ImmutableSet<ProvisionBinding> explicitProvisionBindingsForKey = + getExplicitProvisionBindings(bindingKey.key()); + ImmutableSet<ProductionBinding> explicitProductionBindingsForKey = + getExplicitProductionBindings(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<ProvisionBinding> explicitMapProvisionBindings = ImmutableSet.of(); + if (mapProviderKey.isPresent()) { + explicitMapProvisionBindings = getExplicitProvisionBindings(mapProviderKey.get()); + } + + Optional<Key> mapProducerKey = keyFactory.implicitMapProducerKeyFrom(bindingKey.key()); + ImmutableSet<ProductionBinding> explicitMapProductionBindings = ImmutableSet.of(); + if (mapProducerKey.isPresent()) { + explicitMapProductionBindings = getExplicitProductionBindings(mapProducerKey.get()); + } + + if (!explicitProvisionBindingsForKey.isEmpty() + || !explicitProductionBindingsForKey.isEmpty()) { + // we have some explicit binding for this key, so we collect all explicit implicit map + // bindings that might conflict with this and let the validator sort it out + ImmutableSet.Builder<ContributionBinding> ownedBindings = ImmutableSet.builder(); + ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding> + inheritedBindings = ImmutableSetMultimap.builder(); + for (ProvisionBinding provisionBinding : + Sets.union(explicitProvisionBindingsForKey, explicitMapProvisionBindings)) { + if (isResolvedInParent(request, provisionBinding) + && !shouldOwnParentBinding(request, provisionBinding)) { + inheritedBindings.put( + getOwningResolver(provisionBinding).get().componentDescriptor, + provisionBinding); + } else { + ownedBindings.add(provisionBinding); + } + } + return ResolvedBindings.create(bindingKey, + componentDescriptor, + ownedBindings + .addAll(explicitProductionBindingsForKey) + .addAll(explicitMapProductionBindings) + .build(), + inheritedBindings.build()); + } else { + if (!explicitMapProductionBindings.isEmpty()) { + // if we have any explicit Map<K, Producer<V>> bindings, then this Map<K, V> binding + // must be considered an implicit ProductionBinding + DependencyRequest implicitRequest = + dependencyRequestFactory.forImplicitMapBinding(request, mapProducerKey.get()); + return ResolvedBindings.create( + bindingKey, + componentDescriptor, + productionBindingFactory.forImplicitMapBinding(request, implicitRequest)); + } else if (!explicitMapProvisionBindings.isEmpty()) { + // if there are Map<K, Provider<V>> bindings, then it'll be an implicit + // ProvisionBinding + DependencyRequest implicitRequest = + dependencyRequestFactory.forImplicitMapBinding(request, mapProviderKey.get()); + return ResolvedBindings.create( + bindingKey, + componentDescriptor, + provisionBindingFactory.forImplicitMapBinding(request, implicitRequest)); + } else { + // no explicit binding, look it up. + Optional<ProvisionBinding> provisionBinding = + injectBindingRegistry.getOrFindProvisionBinding(bindingKey.key()); + if (provisionBinding.isPresent()) { + if (isResolvedInParent(request, provisionBinding.get()) + && !shouldOwnParentBinding(request, provisionBinding.get())) { + return ResolvedBindings.create( + bindingKey, + componentDescriptor, + ImmutableSet.<Binding>of(), + ImmutableSetMultimap.of( + getOwningResolver(provisionBinding.get()).get().componentDescriptor, + provisionBinding.get())); + } + } + return ResolvedBindings.create( + bindingKey, + componentDescriptor, + provisionBinding.asSet(), + ImmutableSetMultimap.<ComponentDescriptor, Binding>of()); + } + } + case MEMBERS_INJECTION: + // no explicit deps for members injection, so just look it up + return ResolvedBindings.create( + bindingKey, + componentDescriptor, + rollUpMembersInjectionBindings(bindingKey.key())); + default: + throw new AssertionError(); + } + } + + /** + * Returns {@code true} if {@code provisionBinding} is owned by a parent resolver. If so, + * calls {@link #resolve(DependencyRequest) resolve(request)} on that resolver. + */ + private boolean isResolvedInParent( + DependencyRequest request, ProvisionBinding provisionBinding) { + Optional<Resolver> owningResolver = getOwningResolver(provisionBinding); + if (owningResolver.isPresent() && !owningResolver.get().equals(this)) { + owningResolver.get().resolve(request); + return true; + } else { + return false; + } + } + + /** + * Returns {@code true} if {@code provisionBinding}, 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, ProvisionBinding provisionBinding) { + return !isScoped(provisionBinding) + && 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(ProvisionBinding provisionBinding) { + for (Resolver requestResolver : getResolverLineage().reverse()) { + if (requestResolver.explicitProvisionBindingsSet.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<ProvisionBinding> getExplicitProvisionBindings(Key requestKey) { + ImmutableSet.Builder<ProvisionBinding> explicitBindingsForKey = ImmutableSet.builder(); + for (Resolver resolver : getResolverLineage()) { + explicitBindingsForKey.addAll(resolver.explicitProvisionBindings.get(requestKey)); + } + return explicitBindingsForKey.build(); + } + + private ImmutableSet<ProductionBinding> getExplicitProductionBindings(Key requestKey) { + ImmutableSet.Builder<ProductionBinding> explicitBindingsForKey = ImmutableSet.builder(); + for (Resolver resolver : getResolverLineage()) { + explicitBindingsForKey.addAll(resolver.explicitProductionBindings.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 (!isScoped(binding) && !(binding instanceof ProductionBinding)) { + 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 !explicitProvisionBindings.get(resolvedBindings.bindingKey().key()).isEmpty() + || !explicitProductionBindings.get(resolvedBindings.bindingKey().key()).isEmpty(); + } + + private boolean isScoped(Binding binding) { + if (binding instanceof ProvisionBinding) { + ProvisionBinding provisionBinding = (ProvisionBinding) binding; + return provisionBinding.scope().isPresent(); + } + return false; + } + + 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 new file mode 100644 index 000000000..3af59d61b --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java @@ -0,0 +1,1165 @@ +/* + * 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.BindingType; +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.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.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static com.google.common.base.Verify.verify; +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 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.NULLABLE_TO_NON_NULLABLE; +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.stripCommonTypePrefixes; +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 ProvisionBindingFormatter provisionBindingFormatter; + private final ProductionBindingFormatter productionBindingFormatter; + private final MethodSignatureFormatter methodSignatureFormatter; + private final DependencyRequestFormatter dependencyRequestFormatter; + private final KeyFormatter keyFormatter; + + BindingGraphValidator( + Types types, + InjectBindingRegistry injectBindingRegistry, + ValidationType scopeCycleValidationType, + Diagnostic.Kind nullableValidationType, + ProvisionBindingFormatter provisionBindingFormatter, + ProductionBindingFormatter productionBindingFormatter, + MethodSignatureFormatter methodSignatureFormatter, + DependencyRequestFormatter dependencyRequestFormatter, + KeyFormatter keyFormatter) { + this.types = types; + this.injectBindingRegistry = injectBindingRegistry; + this.scopeCycleValidationType = scopeCycleValidationType; + this.nullableValidationType = nullableValidationType; + this.provisionBindingFormatter = provisionBindingFormatter; + this.productionBindingFormatter = productionBindingFormatter; + 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 (BindingGraph subgraph : subject.subgraphs().values()) { + Validation subgraphValidation = + new Validation(topLevelGraph, subgraph); + subgraphValidation.validateSubgraph(); + reportBuilder.addSubreport(subgraphValidation.buildReport()); + } + } + + /** + * 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; + } + + ImmutableSet.Builder<ProvisionBinding> provisionBindingsBuilder = + ImmutableSet.builder(); + ImmutableSet.Builder<ProductionBinding> productionBindingsBuilder = + ImmutableSet.builder(); + ImmutableSet.Builder<MembersInjectionBinding> membersInjectionBindingsBuilder = + ImmutableSet.builder(); + for (Binding binding : resolvedBinding.bindings()) { + if (binding instanceof ProvisionBinding) { + provisionBindingsBuilder.add((ProvisionBinding) binding); + } + if (binding instanceof ProductionBinding) { + productionBindingsBuilder.add((ProductionBinding) binding); + } + if (binding instanceof MembersInjectionBinding) { + membersInjectionBindingsBuilder.add((MembersInjectionBinding) binding); + } + } + ImmutableSet<ProvisionBinding> provisionBindings = provisionBindingsBuilder.build(); + ImmutableSet<ProductionBinding> productionBindings = productionBindingsBuilder.build(); + ImmutableSet<MembersInjectionBinding> membersInjectionBindings = + membersInjectionBindingsBuilder.build(); + + switch (resolvedBinding.bindingKey().kind()) { + case CONTRIBUTION: + if (!membersInjectionBindings.isEmpty()) { + throw new IllegalArgumentException( + "contribution binding keys should never have members injection bindings"); + } + Set<ContributionBinding> combined = Sets.union(provisionBindings, productionBindings); + if (!validateNullability(path.peek().request(), combined)) { + return false; + } + if (!productionBindings.isEmpty() && doesPathRequireProvisionOnly(path)) { + reportProviderMayNotDependOnProducer(path); + return false; + } + if (combined.size() <= 1) { + return true; + } + ImmutableListMultimap<BindingType, ContributionBinding> bindingsByType = + ContributionBinding.bindingTypesFor(combined); + if (bindingsByType.keySet().size() > 1) { + reportMultipleBindingTypes(path); + return false; + } + switch (getOnlyElement(bindingsByType.keySet())) { + case UNIQUE: + reportDuplicateBindings(path); + return false; + case MAP: + boolean duplicateMapKeys = hasDuplicateMapKeys(path, combined); + boolean inconsistentMapKeyAnnotationTypes = + hasInconsistentMapKeyAnnotationTypes(path, combined); + return !duplicateMapKeys && !inconsistentMapKeyAnnotationTypes; + case SET: + break; + default: + throw new AssertionError(); + } + break; + case MEMBERS_INJECTION: + if (!provisionBindings.isEmpty() || !productionBindings.isEmpty()) { + throw new IllegalArgumentException( + "members injection binding keys should never have contribution bindings"); + } + if (membersInjectionBindings.size() > 1) { + reportDuplicateBindings(path); + return false; + } + if (membersInjectionBindings.size() == 1) { + MembersInjectionBinding binding = getOnlyElement(membersInjectionBindings); + if (!validateMembersInjectionBinding(binding, path)) { + return false; + } + } + break; + 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) { + boolean valid = true; + if (!request.isNullable()) { + String typeName = null; + for (ContributionBinding binding : bindings) { + if (binding.nullableType().isPresent()) { + String methodSignature; + if (binding instanceof ProvisionBinding) { + ProvisionBinding provisionBinding = (ProvisionBinding) binding; + methodSignature = provisionBindingFormatter.format(provisionBinding); + } else { + ProductionBinding productionBinding = (ProductionBinding) binding; + methodSignature = productionBindingFormatter.format(productionBinding); + } + // 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 ouf the request in that case, otherwise the error + // message is kind of useless. + if (typeName == null) { + typeName = TypeNames.forTypeMirror(request.key().type()).toString(); + } + reportBuilder.addItem( + String.format(NULLABLE_TO_NON_NULLABLE, typeName, methodSignature) + + "\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> mapBindings) { + ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding> + mapBindingsByAnnotationType = indexMapBindingsByAnnotationType(mapBindings); + 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( + MembersInjectionBinding 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()) { + if (contributionBinding instanceof ProvisionBinding) { + ProvisionBinding provisionBinding = (ProvisionBinding) contributionBinding; + Scope bindingScope = provisionBinding.scope(); + if (bindingScope.isPresent() && !componentScope.equals(bindingScope)) { + // 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 (provisionBinding.bindingKind()) { + case PROVISION: + ExecutableElement provisionMethod = + MoreElements.asExecutable(provisionBinding.bindingElement()); + incompatiblyScopedMethodsBuilder.add( + methodSignatureFormatter.format(provisionMethod)); + break; + case INJECTION: + incompatiblyScopedMethodsBuilder.add(bindingScope.getReadableSource() + + " class " + provisionBinding.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<ProvisionBinding> 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(); + TypeMirror type = key.type(); + String typeName = TypeNames.forTypeMirror(type).toString(); + 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, typeName)); + 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 (Binding binding : Iterables.limit(resolvedBinding.bindings(), DUPLICATE_SIZE_LIMIT)) { + builder.append('\n').append(INDENT).append(formatBinding(binding)); + } + int numberOfOtherBindings = resolvedBinding.bindings().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<BindingType, ContributionBinding> bindingsByType = + ContributionBinding.<ContributionBinding>bindingTypesFor( + resolvedBinding.contributionBindings()); + for (BindingType 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); + if (binding instanceof ProvisionBinding) { + builder.append(provisionBindingFormatter.format((ProvisionBinding) binding)); + } else if (binding instanceof ProductionBinding) { + builder.append(productionBindingFormatter.format((ProductionBinding) binding)); + } + builder.append('\n'); + } + } + reportBuilder.addError(builder.toString(), path.getLast().request().requestElement()); + } + + private void reportDuplicateMapKeys( + Deque<ResolvedRequest> path, Collection<? extends 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 + ImmutableSet<ProvisionBinding> dependentProvisions = provisionsDependingOnLatestRequest(path); + return !dependentProvisions.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<ProvisionBinding> provisionsDependingOnLatestRequest( + Deque<ResolvedRequest> path) { + Iterator<ResolvedRequest> iterator = path.iterator(); + final DependencyRequest request = iterator.next().request(); + ResolvedRequest previousResolvedRequest = iterator.next(); + @SuppressWarnings("unchecked") // validated by instanceof below + ImmutableSet<ProvisionBinding> bindings = (ImmutableSet<ProvisionBinding>) FluentIterable + .from(previousResolvedRequest.binding().bindings()) + .filter(new Predicate<Binding>() { + @Override public boolean apply(Binding binding) { + return binding instanceof ProvisionBinding + && binding.implicitDependencies().contains(request); + } + }).toSet(); + return bindings; + } + + private String formatBindingType(BindingType 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 formatBinding(Binding binding) { + // TODO(beder): Refactor the formatters so we don't need these instanceof checks. + if (binding instanceof ProvisionBinding) { + return provisionBindingFormatter.format((ProvisionBinding) binding); + } else if (binding instanceof ProductionBinding) { + return productionBindingFormatter.format((ProductionBinding) binding); + } else { + throw new IllegalArgumentException( + "Expected either a ProvisionBinding or a ProductionBinding, not " + binding); + } + } + + private String formatRootRequestKey(Deque<ResolvedRequest> path) { + return keyFormatter.format(path.peek().request().key()); + } + + private void appendBindings( + StringBuilder builder, Collection<? extends Binding> bindings, int indentLevel) { + for (Binding binding : Iterables.limit(bindings, DUPLICATE_SIZE_LIMIT)) { + builder.append('\n'); + for (int i = 0; i < indentLevel; i++) { + builder.append(INDENT); + } + builder.append(formatBinding(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.create(bindingKey, + graph.componentDescriptor(), + ImmutableSet.<Binding>of(), + ImmutableSetMultimap.<ComponentDescriptor, Binding>of()) + : 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 new file mode 100644 index 000000000..cd29d8d69 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/BindingKey.java @@ -0,0 +1,40 @@ +/* + * 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 new file mode 100644 index 000000000..ba96ebfd3 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/BuilderValidator.java @@ -0,0 +1,203 @@ +/* + * 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 new file mode 100644 index 000000000..7a5c13520 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java @@ -0,0 +1,450 @@ +/* + * 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.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, null, 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(); + } + + 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))); + } + + 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())); + } + } + + 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 new file mode 100644 index 000000000..bb75e14bd --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java @@ -0,0 +1,137 @@ +/* + * 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 ProxyClassAndField { + abstract ClassWriter proxyWriter(); + abstract FieldWriter proxyFieldWriter(); + + static ProxyClassAndField create(ClassWriter proxyWriter, FieldWriter proxyFieldWriter) { + return new AutoValue_ComponentGenerator_ProxyClassAndField(proxyWriter, proxyFieldWriter); + } + } + + @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 new file mode 100644 index 000000000..8fb4191a5 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ComponentHierarchyValidator.java @@ -0,0 +1,115 @@ +/* + * 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 new file mode 100644 index 000000000..39b21ca32 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ComponentProcessingStep.java @@ -0,0 +1,173 @@ +/* + * 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 new file mode 100644 index 000000000..1e96a534a --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java @@ -0,0 +1,289 @@ +/* + * 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); + ProvisionBindingFormatter provisionBindingFormatter = + new ProvisionBindingFormatter(methodSignatureFormatter); + ProductionBindingFormatter productionBindingFormatter = + new ProductionBindingFormatter(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(); + + 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); + + DependencyRequest.Factory dependencyRequestFactory = new DependencyRequest.Factory(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, + dependencyRequestFactory, + provisionBindingFactory, + productionBindingFactory); + + MapKeyGenerator mapKeyGenerator = new MapKeyGenerator(filer); + ComponentHierarchyValidator componentHierarchyValidator = new ComponentHierarchyValidator(); + BindingGraphValidator bindingGraphValidator = + new BindingGraphValidator( + types, + injectBindingRegistry, + scopeValidationType(processingEnv), + nullableDiagnosticType, + provisionBindingFormatter, + productionBindingFormatter, + methodSignatureFormatter, + dependencyRequestFormatter, + keyFormatter); + + return ImmutableList.<ProcessingStep>of( + new MapKeyProcessingStep(messager, types, mapKeyValidator, mapKeyGenerator), + new InjectProcessingStep( + messager, + injectConstructorValidator, + injectFieldValidator, + injectMethodValidator, + provisionBindingFactory, + membersInjectionBindingFactory, + injectBindingRegistry), + 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, + 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 new file mode 100644 index 000000000..58fe1ca0c --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java @@ -0,0 +1,370 @@ +/* + * 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.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.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 com.google.common.collect.Sets.SetView; +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 dagger.internal.codegen.Util.componentCanMakeNewInstances; +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 + ImmutableSet<TypeElement> transitiveModules = + getTransitiveModules(types, elements, moduleTypes); + + ImmutableSet<TypeElement> requiredModules = + FluentIterable.from(transitiveModules) + .filter(new Predicate<TypeElement>() { + @Override public boolean apply(TypeElement input) { + return !componentCanMakeNewInstances(input); + } + }) + .toSet(); + + 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); + } + } + + SetView<TypeElement> missingModules = + Sets.difference(requiredModules, ImmutableSet.copyOf(variableTypes)); + if (!missingModules.isEmpty()) { + builder.addError( + String.format( + "%s requires modules which have no visible default constructors. " + + "Add the following modules as parameters to this method: %s", + MoreTypes.asTypeElement(returnType).getQualifiedName(), + Joiner.on(", ").join(missingModules)), + method); + } + } + + 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 new file mode 100644 index 000000000..5221cf0a2 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ComponentWriter.java @@ -0,0 +1,118 @@ +/* + * 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 new file mode 100644 index 000000000..50e343543 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java @@ -0,0 +1,241 @@ +/* + * 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 new file mode 100644 index 000000000..a0a4dc148 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java @@ -0,0 +1,160 @@ +/* + * 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.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 dagger.MapKey; +import java.util.EnumSet; +import java.util.Set; +import javax.inject.Provider; +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; + +/** + * 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 { + static enum BindingType { + /** Represents map bindings. */ + MAP, + /** Represents set bindings. */ + SET, + /** Represents a valid non-collection binding. */ + UNIQUE; + + boolean isMultibinding() { + return !this.equals(UNIQUE); + } + } + + abstract BindingType bindingType(); + + /** 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. + */ + // TODO(user): Remove the SYNTHETIC enums from ProvisionBinding and ProductionBinding and make + // this field the source of truth for synthetic bindings. + abstract boolean isSyntheticBinding(); + + /** + * Returns the framework class associated with this binding, e.g., {@link Provider} for a + * ProvisionBinding. + */ + abstract Class<?> frameworkClass(); + + /** + * Returns the set of {@link BindingType} enum values implied by a given + * {@link ContributionBinding} collection. + */ + static <B extends ContributionBinding> ImmutableListMultimap<BindingType, B> bindingTypesFor( + Iterable<? extends B> bindings) { + ImmutableListMultimap.Builder<BindingType, B> builder = + ImmutableListMultimap.builder(); + builder.orderKeysBy(Ordering.<BindingType>natural()); + for (B binding : bindings) { + builder.put(binding.bindingType(), binding); + } + return builder.build(); + } + + /** + * Returns a single {@code BindingsType} represented by a given collection of + * {@code ContributionBindings} or throws an IllegalArgumentException if the given bindings + * are not all of one type. + */ + static BindingType bindingTypeFor(Iterable<? extends ContributionBinding> bindings) { + checkNotNull(bindings); + checkArgument(!Iterables.isEmpty(bindings), "no bindings"); + Set<BindingType> types = EnumSet.noneOf(BindingType.class); + for (ContributionBinding binding : bindings) { + types.add(binding.bindingType()); + } + if (types.size() > 1) { + throw new IllegalArgumentException( + String.format(ErrorMessages.MULTIPLE_BINDING_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/DependencyRequest.java b/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java new file mode 100644 index 000000000..5af9a82c5 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java @@ -0,0 +1,318 @@ +/* + * 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 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.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 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; + +/** + * 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 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(); + + /** + * 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 Key.Factory keyFactory; + + Factory(Key.Factory keyFactory) { + 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 DependencyRequest for implictMapBinding, this request's key will be + * {@code Map<K, Provider<V>>}, this DependencyRequest is depended by the DependencyRequest + * whose key is {@code Map<K, V>} + */ + DependencyRequest forImplicitMapBinding(DependencyRequest delegatingRequest, Key delegateKey) { + checkNotNull(delegatingRequest); + return new AutoValue_DependencyRequest(Kind.PROVIDER, delegateKey, + delegatingRequest.requestElement(), + delegatingRequest.enclosingType(), + false /* doesn't allow null */); + } + + DependencyRequest forRequiredVariable(VariableElement variableElement) { + checkNotNull(variableElement); + TypeMirror type = variableElement.asType(); + Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement); + return newDependencyRequest(variableElement, type, qualifier, + getEnclosingType(variableElement)); + } + + DependencyRequest forRequiredResolvedVariable(DeclaredType container, + VariableElement variableElement, + TypeMirror resolvedType) { + checkNotNull(variableElement); + checkNotNull(resolvedType); + Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement); + return newDependencyRequest(variableElement, resolvedType, qualifier, container); + } + + DependencyRequest forComponentProvisionMethod(ExecutableElement provisionMethod, + ExecutableType provisionMethodType) { + checkNotNull(provisionMethod); + checkNotNull(provisionMethodType); + checkArgument(provisionMethod.getParameters().isEmpty(), + "Component provision methods must be empty: " + provisionMethod); + Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(provisionMethod); + return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier, + getEnclosingType(provisionMethod)); + } + + 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 */); + } else { + return newDependencyRequest(productionMethod, type, qualifier, container); + } + } + + 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 */); + } else { + return new AutoValue_DependencyRequest(Kind.MEMBERS_INJECTOR, + keyFactory.forMembersInjectedType( + Iterables.getOnlyElement(membersInjectionMethodType.getParameterTypes())), + membersInjectionMethod, + getEnclosingType(membersInjectionMethod), + false /* doesn't allow null */); + } + } + + DependencyRequest forMembersInjectedType(DeclaredType type) { + return new AutoValue_DependencyRequest(Kind.MEMBERS_INJECTOR, + keyFactory.forMembersInjectedType(type), + type.asElement(), + type, + false /* doesn't allow null */); + } + + private DependencyRequest newDependencyRequest(Element requestElement, + TypeMirror type, Optional<AnnotationMirror> qualifier, DeclaredType container) { + 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); + } + + @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 new file mode 100644 index 000000000..0e5f1f240 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/DependencyRequestFormatter.java @@ -0,0 +1,151 @@ +/* + * 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 new file mode 100644 index 000000000..1dc48fc8b --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/DependencyRequestMapper.java @@ -0,0 +1,97 @@ +/* + * 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 new file mode 100644 index 000000000..1643adbc4 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java @@ -0,0 +1,64 @@ +/* + * 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) { + 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 new file mode 100644 index 000000000..c025eb48a --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java @@ -0,0 +1,425 @@ +/* + * 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_BINDING_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 final String NULLABLE_TO_NON_NULLABLE = + "%s is not nullable, but is being provided by %s"; + + 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; + 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"; + } + } + + /** + * 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 new file mode 100644 index 000000000..c7eb6326f --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java @@ -0,0 +1,278 @@ +/* + * 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.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD; +import static dagger.internal.codegen.ProvisionBinding.Kind.PROVISION; +import static dagger.internal.codegen.SourceFiles.factoryNameForProvisionBinding; +import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement; +import static dagger.internal.codegen.SourceFiles.parameterizedFactoryNameForProvisionBinding; +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 factoryNameForProvisionBinding(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.memberInjectionRequest().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);", + parameterizedFactoryNameForProvisionBinding(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.memberInjectionRequest().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 new file mode 100644 index 000000000..880b78705 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/Formatter.java @@ -0,0 +1,48 @@ +/* + * 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 new file mode 100644 index 000000000..b92cef796 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java @@ -0,0 +1,169 @@ +/* + * 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 com.google.common.collect.Iterables; +import dagger.MembersInjector; +import dagger.internal.codegen.ContributionBinding.BindingType; +import dagger.internal.codegen.writer.ClassName; +import dagger.internal.codegen.writer.ParameterizedTypeName; +import dagger.internal.codegen.writer.TypeNames; +import dagger.producers.Producer; +import javax.inject.Provider; +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; + +/** + * 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( + BindingKey bindingKey, int contributionNumber, ContributionBinding contributionBinding) { + switch (contributionBinding.bindingType()) { + case MAP: + return createForMapBindingContribution( + contributionBinding.frameworkClass(), + BindingKey.create(bindingKey.kind(), contributionBinding.key()), + KeyVariableNamer.INSTANCE.apply(bindingKey.key()) + + "Contribution" + contributionNumber); + case SET: + return createWithTypeFromKey( + contributionBinding.frameworkClass(), + bindingKey, + KeyVariableNamer.INSTANCE.apply(bindingKey.key()) + + "Contribution" + contributionNumber); + case UNIQUE: + return createWithTypeFromKey( + contributionBinding.frameworkClass(), + bindingKey, + KeyVariableNamer.INSTANCE.apply(bindingKey.key()) + + "Contribution" + contributionNumber); + default: + throw new AssertionError(); + } + } + + static FrameworkField createForResolvedBindings(ResolvedBindings resolvedBindings) { + BindingKey bindingKey = resolvedBindings.bindingKey(); + switch (bindingKey.kind()) { + case CONTRIBUTION: + ImmutableSet<? extends ContributionBinding> contributionBindings = + resolvedBindings.contributionBindings(); + BindingType bindingsType = ProvisionBinding.bindingTypeFor(contributionBindings); + switch (bindingsType) { + case SET: + case MAP: + return createWithTypeFromKey( + FrameworkField.frameworkClassForResolvedBindings(resolvedBindings), + bindingKey, + KeyVariableNamer.INSTANCE.apply(bindingKey.key())); + case UNIQUE: + ContributionBinding binding = Iterables.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, + Iterables.getOnlyElement(resolvedBindings.bindings()) + .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: + for (ContributionBinding binding : resolvedBindings.contributionBindings()) { + if (binding instanceof ProductionBinding) { + return Producer.class; + } + } + return Provider.class; + 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 new file mode 100644 index 000000000..b8e302e9a --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java @@ -0,0 +1,262 @@ +/* + * 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.membersInjectorNameForType; + +/** + * 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 pretends a binding is generated without actually generating it. */ + B pretendBindingGenerated(B binding, ClassName factoryName) { + tryToCacheBinding(binding); + if (shouldGenerateBinding(binding, factoryName)) { + materializedBindingKeys.add(binding.key()); + } + return binding; + } + + /** 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 = SourceFiles.factoryNameForProvisionBinding(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 = membersInjectorNameForType(binding.bindingTypeElement()); + if (binding.injectionSites().isEmpty()) { + // empty members injection bindings are special and don't need source files. + // so, we just pretend + membersInjectionBindings.pretendBindingGenerated(binding, membersInjectorName); + if (binding.hasNonDefaultTypeParameters()) { + membersInjectionBindings.pretendBindingGenerated( + membersInjectionBindingFactory.unresolve(binding), membersInjectorName); + } + } else { + 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 new file mode 100644 index 000000000..11cd066fc --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/InjectConstructorValidator.java @@ -0,0 +1,112 @@ +/* + * 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 new file mode 100644 index 000000000..91143de6e --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java @@ -0,0 +1,76 @@ +/* + * 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 new file mode 100644 index 000000000..a716b7d81 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/InjectMethodValidator.java @@ -0,0 +1,83 @@ +/* + * 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 new file mode 100644 index 000000000..61b19c96c --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java @@ -0,0 +1,146 @@ +/* + * 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) { + // 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)) { + 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); + } + + for (DeclaredType injectedType : membersInjectedTypes.build()) { + injectBindingRegistry.registerBinding(membersInjectionBindingFactory.forInjectedType( + injectedType, Optional.<TypeMirror>absent())); + } + + for (ProvisionBinding binding : provisions.build()) { + injectBindingRegistry.registerBinding(binding); + } + return ImmutableSet.of(); + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectionAnnotations.java b/compiler/src/main/java/dagger/internal/codegen/InjectionAnnotations.java new file mode 100644 index 000000000..b3b245dfb --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/InjectionAnnotations.java @@ -0,0 +1,72 @@ +/* + * 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 new file mode 100644 index 000000000..c14cc22c4 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/Key.java @@ -0,0 +1,354 @@ +/* + * 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.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.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 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)) { + DeclaredType 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(); + } + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java b/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java new file mode 100644 index 000000000..d2e62fcab --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java @@ -0,0 +1,35 @@ +/* + * 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()); // TODO(cgruber): Use AnnotationMirrorFormatter. + 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 new file mode 100644 index 000000000..5fe12b102 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/KeyVariableNamer.java @@ -0,0 +1,66 @@ +/* + * 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 new file mode 100644 index 000000000..8d72e5e48 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MapKeyGenerator.java @@ -0,0 +1,176 @@ +/* + * 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 new file mode 100644 index 000000000..c4a264aa6 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MapKeyProcessingStep.java @@ -0,0 +1,98 @@ +/* + * 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 new file mode 100644 index 000000000..d6aa2f21c --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java @@ -0,0 +1,52 @@ +/* + * 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 {@link 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 new file mode 100644 index 000000000..4d79a28c8 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MapKeys.java @@ -0,0 +1,335 @@ +/* + * 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 com.google.common.collect.Iterables; +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 new file mode 100644 index 000000000..87b2ab187 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java @@ -0,0 +1,314 @@ +/* + * 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()); + } + + @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 new file mode 100644 index 000000000..f7fcdee9d --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java @@ -0,0 +1,398 @@ +/* + * 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.parameterizedMembersInjectorNameForMembersInjectionBinding; +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) { + 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);", + parameterizedMembersInjectorNameForMembersInjectionBinding(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 new file mode 100644 index 000000000..447ed24ef --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MethodSignature.java @@ -0,0 +1,33 @@ +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 new file mode 100644 index 000000000..078977e16 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MethodSignatureFormatter.java @@ -0,0 +1,115 @@ +/* + * 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 new file mode 100644 index 000000000..dcabab52d --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java @@ -0,0 +1,55 @@ +/* + * 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.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 new file mode 100644 index 000000000..f5e33b59d --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java @@ -0,0 +1,122 @@ +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<? extends 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 new file mode 100644 index 000000000..1afda7d90 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ModuleProcessingStep.java @@ -0,0 +1,136 @@ +/* + * 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 new file mode 100644 index 000000000..8b0c9a217 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ModuleValidator.java @@ -0,0 +1,338 @@ +/* + * 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/ProducerFactoryGenerator.java b/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java new file mode 100644 index 000000000..9ae154886 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java @@ -0,0 +1,434 @@ +/* + * 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.ProducerToken; +import dagger.producers.monitoring.ProductionComponentMonitor; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import javax.annotation.Generated; +import javax.annotation.Nullable; +import javax.annotation.processing.Filer; +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; + +import static dagger.internal.codegen.SourceFiles.factoryNameForProductionBinding; +import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement; +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 factoryNameForProductionBinding(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); + + constructorWriter + .addParameter(ProductionComponentMonitor.class, "componentMonitor") + .annotate(Nullable.class); + constructorWriter + .body() + .addSnippet( + "super(%s.producerMonitorFor(componentMonitor, %s.create(%s.class)));", + ClassName.fromClass(Producers.class), + 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 getMethodWriter = factoryWriter.addMethod(futureTypeName, "compute"); + getMethodWriter.annotate(Override.class); + getMethodWriter.addModifiers(PROTECTED); + + final 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.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(ProductionBinding.Kind.FUTURE_PRODUCTION); + ImmutableList<DependencyRequest> asyncDependencies = FluentIterable + .from(binding.dependencies()) + .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); + getMethodWriter.body().addSnippet("%s %sFuture = %s;", + futureType, + name, + dependency.kind().equals(DependencyRequest.Kind.PRODUCED) + ? Snippet.format("%s.createFutureProduced(%s)", + ClassName.fromClass(Producers.class), futureAccess) + : futureAccess); + } + + if (asyncDependencies.isEmpty()) { + ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder(); + for (DependencyRequest dependency : binding.dependencies()) { + parameterSnippets.add(frameworkTypeUsageStatement( + Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind())); + } + final boolean wrapWithFuture = false; // since submitToExecutor will create the future + Snippet invocationSnippet = + getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build()); + TypeName callableReturnType = returnsFuture ? futureTypeName : providedTypeName; + Snippet throwsClause = getThrowsClause(binding.thrownTypes()); + Snippet callableSnippet = + Snippet.format( + Joiner.on('\n') + .join( + "new %1$s<%2$s>() {", + " @Override public %2$s call() %3$s{", + " %4$s", + " }", + "}"), + ClassName.fromClass(Callable.class), + callableReturnType, + throwsClause, + invocationSnippet); + getMethodWriter + .body() + .addSnippet( + "%s future = %s.submitToExecutor(%s, executor);", + ParameterizedTypeName.create( + ClassName.fromClass(ListenableFuture.class), callableReturnType), + ClassName.fromClass(Producers.class), + callableSnippet); + getMethodWriter + .body() + .addSnippet( + "return %s;", + returnsFuture + ? Snippet.format("%s.dereference(future)", ClassName.fromClass(Futures.class)) + : "future"); + } else { + final Snippet futureSnippet; + final Snippet transformSnippet; + if (asyncDependencies.size() == 1) { + DependencyRequest asyncDependency = Iterables.getOnlyElement(asyncDependencies); + futureSnippet = Snippet.format("%s", + fields.get(asyncDependency.bindingKey()).name() + "Future"); + String argName = asyncDependency.requestElement().getSimpleName().toString(); + 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", argName)); + } else { + parameterSnippets.add(frameworkTypeUsageStatement( + Snippet.format(fields.get(dependency.bindingKey()).name()), + dependency.kind())); + } + } + boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future + Snippet invocationSnippet = + getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build()); + Snippet throwsClause = getThrowsClause(binding.thrownTypes()); + transformSnippet = + Snippet.format( + Joiner.on('\n') + .join( + "new %1$s<%2$s, %3$s>() {", + " @Override public %4$s apply(%2$s %5$s) %6$s{", + " %7$s", + " }", + "}"), + ClassName.fromClass(AsyncFunction.class), + asyncDependencyType(asyncDependency), + providedTypeName, + futureTypeName, + argName, + throwsClause, + invocationSnippet); + } else { + futureSnippet = Snippet.format("%s.<%s>allAsList(%s)", + ClassName.fromClass(Futures.class), + ClassName.fromClass(Object.class), + Joiner.on(",").join(FluentIterable + .from(asyncDependencies) + .transform(DependencyRequest.BINDING_KEY_FUNCTION) + .transform(new Function<BindingKey, String>() { + @Override public String apply(BindingKey dependencyBindingKey) { + return fields.get(dependencyBindingKey).name() + "Future"; + } + }))); + ImmutableList<Snippet> parameterSnippets = getParameterSnippets(binding, fields, "args"); + boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future + Snippet invocationSnippet = + getInvocationSnippet(wrapWithFuture, binding, parameterSnippets); + ParameterizedTypeName listOfObject = + ParameterizedTypeName.create( + ClassName.fromClass(List.class), ClassName.fromClass(Object.class)); + Snippet throwsClause = getThrowsClause(binding.thrownTypes()); + transformSnippet = + Snippet.format( + Joiner.on('\n') + .join( + "new %1$s<%2$s, %3$s>() {", + " @SuppressWarnings(\"unchecked\") // safe by specification", + " @Override public %4$s apply(%2$s args) %5$s{", + " %6$s", + " }", + "}"), + ClassName.fromClass(AsyncFunction.class), + listOfObject, + providedTypeName, + futureTypeName, + throwsClause, + invocationSnippet); + } + getMethodWriter.body().addSnippet("return %s.%s(%s, %s, executor);", + ClassName.fromClass(Futures.class), + "transform", + futureSnippet, + transformSnippet); + } + + // TODO(gak): write a sensible toString + return ImmutableSet.of(writer); + } + + private boolean isAsyncDependency(DependencyRequest dependency) { + switch (dependency.kind()) { + case INSTANCE: + case PRODUCED: + return true; + default: + return false; + } + } + + private 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 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(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("if (monitor != null) { monitor.methodStarting(); }")); + + final Snippet valueSnippet; + if (binding.productionType().equals(Produces.Type.SET)) { + if (binding.bindingKind().equals(ProductionBinding.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( + "if (monitor != null) { monitor.methodStarting(); }", + "try {", + " return %s;", + "} finally {", + " if (monitor != null) { 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 new file mode 100644 index 000000000..cc167e599 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProducerModuleProcessingStep.java @@ -0,0 +1,141 @@ +/* + * 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 new file mode 100644 index 000000000..b0b4df1cc --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProducesMethodValidator.java @@ -0,0 +1,206 @@ +/* + * 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 new file mode 100644 index 000000000..38d45e6a2 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java @@ -0,0 +1,188 @@ +/* + * 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.util.concurrent.ListenableFuture; +import dagger.producers.Producer; +import dagger.producers.Produces; +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 + ImmutableSet<DependencyRequest> implicitDependencies() { + return dependencies(); + } + + enum Kind { + /** Represents a binding configured by {@link Produces} that doesn't return a future. */ + IMMEDIATE, + /** Represents a binding configured by {@link Produces} that returns a future. */ + FUTURE_PRODUCTION, + /** + * Represents a binding that is not explicitly tied to code, but generated implicitly by the + * framework. + */ + SYNTHETIC_PRODUCTION, + /** + * Represents a binding from a production method on a component dependency that returns a + * future. Methods that return immediate values are considered provision bindings. + */ + COMPONENT_PRODUCTION, + } + + /** + * The type of binding (whether the {@link Produces} method returns a future). For the particular + * type of production, use {@link #productionType}. + */ + abstract Kind bindingKind(); + + /** 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(); + + @Override + BindingType bindingType() { + switch (productionType()) { + case SET: + case SET_VALUES: + return BindingType.SET; + case MAP: + return BindingType.MAP; + case UNIQUE: + return BindingType.UNIQUE; + default: + throw new IllegalStateException("Unknown production type: " + productionType()); + } + } + + @Override + boolean isSyntheticBinding() { + return bindingKind().equals(Kind.SYNTHETIC_PRODUCTION); + } + + @Override + Class<?> frameworkClass() { + return Producer.class; + } + + 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()); + 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)), + kind, + producesAnnotation.type(), + ImmutableList.copyOf(producesMethod.getThrownTypes())); + } + + ProductionBinding forImplicitMapBinding(DependencyRequest explicitRequest, + DependencyRequest implicitRequest) { + checkNotNull(explicitRequest); + checkNotNull(implicitRequest); + ImmutableSet<DependencyRequest> dependencies = ImmutableSet.of(implicitRequest); + return new AutoValue_ProductionBinding( + explicitRequest.key(), + implicitRequest.requestElement(), + dependencies, + findBindingPackage(explicitRequest.key()), + false, + Optional.<DeclaredType>absent(), + Optional.<TypeElement>absent(), + Kind.SYNTHETIC_PRODUCTION, + Produces.Type.MAP, + ImmutableList.<TypeMirror>of()); + } + + 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(), + Kind.COMPONENT_PRODUCTION, + Produces.Type.UNIQUE, + ImmutableList.copyOf(componentMethod.getThrownTypes())); + } + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionBindingFormatter.java b/compiler/src/main/java/dagger/internal/codegen/ProductionBindingFormatter.java new file mode 100644 index 000000000..e7e7e778a --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProductionBindingFormatter.java @@ -0,0 +1,49 @@ +/* + * 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 ProductionBinding} into a {@link String} suitable for use in error messages. + * + * @author Jesse Beder + * @since 2.0 + */ +final class ProductionBindingFormatter extends Formatter<ProductionBinding> { + private final MethodSignatureFormatter methodSignatureFormatter; + + ProductionBindingFormatter(MethodSignatureFormatter methodSignatureFormatter) { + this.methodSignatureFormatter = methodSignatureFormatter; + } + + @Override public String format(ProductionBinding binding) { + switch (binding.bindingKind()) { + case IMMEDIATE: + case FUTURE_PRODUCTION: + return methodSignatureFormatter.format(asExecutable(binding.bindingElement()), + Optional.of(asDeclared(binding.contributedBy().get().asType()))); + case COMPONENT_PRODUCTION: + return methodSignatureFormatter.format(asExecutable(binding.bindingElement())); + default: + throw new UnsupportedOperationException( + "Not yet supporting " + binding.bindingKind() + " binding types."); + } + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java new file mode 100644 index 000000000..56f8ccb17 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java @@ -0,0 +1,75 @@ +/* + * 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.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; + +/** + * 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 ComponentElementValidator componentElementValidator; + + ProductionComponentProcessingStep( + Messager messager, + final ProductionComponentValidator componentValidator, + ComponentHierarchyValidator componentHierarchyValidator, + BindingGraphValidator bindingGraphValidator, + ComponentDescriptor.Factory componentDescriptorFactory, + BindingGraph.Factory bindingGraphFactory, + ComponentGenerator componentGenerator) { + super( + ProductionComponent.class, + messager, + componentHierarchyValidator, + bindingGraphValidator, + componentDescriptorFactory, + bindingGraphFactory, + componentGenerator); + this.componentElementValidator = + new ComponentElementValidator() { + @Override + boolean validateComponent(TypeElement componentTypeElement, Messager messager) { + ValidationReport<TypeElement> validationReport = + componentValidator.validate(componentTypeElement); + validationReport.printMessagesTo(messager); + return validationReport.isClean(); + } + }; + } + + @Override + public Set<Class<? extends Annotation>> annotations() { + return ImmutableSet.<Class<? extends Annotation>>of(ProductionComponent.class); + } + + @Override + protected ComponentElementValidator componentElementValidator( + SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + return componentElementValidator; + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentValidator.java b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentValidator.java new file mode 100644 index 000000000..2e2291d34 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentValidator.java @@ -0,0 +1,85 @@ +/* + * 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 new file mode 100644 index 000000000..e9c8b1629 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProvidesMethodValidator.java @@ -0,0 +1,185 @@ +/* + * 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 new file mode 100644 index 000000000..8a3c203c8 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java @@ -0,0 +1,323 @@ +/* + * 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 java.util.Set; +import javax.inject.Inject; +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.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.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.ProvisionBinding.Kind.INJECTION; +import static dagger.internal.codegen.ProvisionBinding.Kind.PROVISION; +import static javax.lang.model.element.ElementKind.CONSTRUCTOR; +import static javax.lang.model.element.ElementKind.FIELD; +import static javax.lang.model.element.ElementKind.METHOD; +import static javax.lang.model.element.Modifier.STATIC; + +/** + * 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 + Set<DependencyRequest> implicitDependencies() { + // Optimization: If we don't need the memberInjectionRequest, don't create more objects. + if (!memberInjectionRequest().isPresent()) { + return dependencies(); + } else { + // Optimization: Avoid creating an ImmutableSet+Builder just to union two things together. + return Sets.union(memberInjectionRequest().asSet(), dependencies()); + } + } + + enum Kind { + /** Represents an {@link Inject} binding. */ + INJECTION, + /** Represents a binding configured by {@link Provides}. */ + PROVISION, + /** + * Represents a binding that is not explicitly tied to code, but generated implicitly by the + * framework. + */ + SYNTHETIC_PROVISON, + /** Represents the implicit binding to the component. */ + COMPONENT, + /** Represents a binding from a provision method on a component dependency. */ + COMPONENT_PROVISION, + } + + /** + * The type of binding ({@link Inject} or {@link Provides}). For the particular type of provision, + * use {@link #provisionType}. + */ + abstract Kind bindingKind(); + + /** Returns provision type that was used to bind the key. */ + abstract Provides.Type provisionType(); + + /** + * The scope of the provider. + */ + abstract Scope scope(); + + /** If this provision requires members injection, this will be the corresponding request. */ + abstract Optional<DependencyRequest> memberInjectionRequest(); + + @Override + BindingType bindingType() { + switch (provisionType()) { + case SET: + case SET_VALUES: + return BindingType.SET; + case MAP: + return BindingType.MAP; + case UNIQUE: + return BindingType.UNIQUE; + default: + throw new IllegalStateException("Unknown provision type: " + provisionType()); + } + } + + @Override + boolean isSyntheticBinding() { + return bindingKind().equals(Kind.SYNTHETIC_PROVISON); + } + + @Override + Class<?> frameworkClass() { + return Provider.class; + } + + enum FactoryCreationStrategy { + ENUM_INSTANCE, + CLASS_CONSTRUCTOR, + } + + FactoryCreationStrategy factoryCreationStrategy() { + if (bindingKind().equals(INJECTION) && implicitDependencies().isEmpty()) { + return FactoryCreationStrategy.ENUM_INSTANCE; + } + if (bindingKind().equals(PROVISION) + && implicitDependencies().isEmpty() + && bindingElement().getModifiers().contains(STATIC)) { + return FactoryCreationStrategy.ENUM_INSTANCE; + } + return FactoryCreationStrategy.CLASS_CONSTRUCTOR; + } + + 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(), + Kind.INJECTION, + Provides.Type.UNIQUE, + scope, + membersInjectionRequest); + } + + 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)), + Kind.PROVISION, + providesAnnotation.type(), + scope, + Optional.<DependencyRequest>absent()); + } + + ProvisionBinding forImplicitMapBinding(DependencyRequest explicitRequest, + DependencyRequest implicitRequest) { + checkNotNull(explicitRequest); + checkNotNull(implicitRequest); + ImmutableSet<DependencyRequest> dependencies = ImmutableSet.of(implicitRequest); + Scope scope = Scope.scopeOf(implicitRequest.requestElement()); + return new AutoValue_ProvisionBinding( + explicitRequest.key(), + implicitRequest.requestElement(), + dependencies, + findBindingPackage(explicitRequest.key()), + false /* no non-default parameter types */, + Optional.<DeclaredType>absent(), + Optional.<TypeElement>absent(), + Kind.SYNTHETIC_PROVISON, + Provides.Type.MAP, + scope, + Optional.<DependencyRequest>absent()); + } + + 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(), + Kind.COMPONENT, + Provides.Type.UNIQUE, + Scope.unscoped(), + Optional.<DependencyRequest>absent()); + } + + 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(), + Kind.COMPONENT_PROVISION, + Provides.Type.UNIQUE, + scope, + Optional.<DependencyRequest>absent()); + } + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/ProvisionBindingFormatter.java b/compiler/src/main/java/dagger/internal/codegen/ProvisionBindingFormatter.java new file mode 100644 index 000000000..92d031042 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ProvisionBindingFormatter.java @@ -0,0 +1,48 @@ +/* + * 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 ProvisionBinding} into a {@link String} suitable for use in error messages. + * + * @author Christian Gruber + * @since 2.0 + */ +final class ProvisionBindingFormatter extends Formatter<ProvisionBinding> { + private final MethodSignatureFormatter methodSignatureFormatter; + + ProvisionBindingFormatter(MethodSignatureFormatter methodSignatureFormatter) { + this.methodSignatureFormatter = methodSignatureFormatter; + } + + @Override public String format(ProvisionBinding binding) { + switch (binding.bindingKind()) { + case PROVISION: + return methodSignatureFormatter.format(asExecutable(binding.bindingElement()), + Optional.of(asDeclared(binding.contributedBy().get().asType()))); + case COMPONENT_PROVISION: + return methodSignatureFormatter.format(asExecutable(binding.bindingElement())); + default: + throw new UnsupportedOperationException( + "Not yet supporting " + binding.bindingKind() + " binding types."); + } + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java b/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java new file mode 100644 index 000000000..7ef4deb92 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java @@ -0,0 +1,125 @@ +/* + * 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.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.Multimap; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkState; +import static dagger.internal.codegen.ContributionBinding.bindingTypeFor; + +/** + * The collection of bindings that have been resolved for a binding key. + * + * @author Gregory Kick + */ +@AutoValue +abstract class ResolvedBindings { + abstract BindingKey bindingKey(); + abstract ComponentDescriptor owningComponent(); + abstract ImmutableSet<? extends Binding> ownedBindings(); + abstract ImmutableSetMultimap<ComponentDescriptor, ? extends Binding> inheritedBindings(); + + static ResolvedBindings create( + BindingKey bindingKey, + ComponentDescriptor owningComponent, + Set<? extends Binding> ownedBindings, + Multimap<ComponentDescriptor, ? extends Binding> inheritedBindings) { + return new AutoValue_ResolvedBindings( + bindingKey, + owningComponent, + ImmutableSet.copyOf(ownedBindings), + ImmutableSetMultimap.copyOf(inheritedBindings)); + } + + static ResolvedBindings create( + BindingKey bindingKey, + ComponentDescriptor owningComponent, + Binding... ownedBindings) { + return new AutoValue_ResolvedBindings( + bindingKey, + owningComponent, + ImmutableSet.copyOf(ownedBindings), + ImmutableSetMultimap.<ComponentDescriptor, Binding>of()); + } + + ImmutableSet<? extends Binding> bindings() { + return new ImmutableSet.Builder<Binding>() + .addAll(ownedBindings()) + .addAll(inheritedBindings().values()) + .build(); + } + + @SuppressWarnings("unchecked") // checked by validator + ImmutableSet<? extends ContributionBinding> ownedContributionBindings() { + checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)); + return (ImmutableSet<? extends ContributionBinding>) ownedBindings(); + } + + @SuppressWarnings("unchecked") // checked by validator + ImmutableSet<? extends ContributionBinding> contributionBindings() { + checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)); + return new ImmutableSet.Builder<ContributionBinding>() + .addAll((Iterable<? extends ContributionBinding>) ownedBindings()) + .addAll((Iterable<? extends ContributionBinding>) inheritedBindings().values()) + .build(); + } + + @SuppressWarnings("unchecked") // checked by validator + ImmutableSet<? extends MembersInjectionBinding> membersInjectionBindings() { + checkState(bindingKey().kind().equals(BindingKey.Kind.MEMBERS_INJECTION)); + return new ImmutableSet.Builder<MembersInjectionBinding>() + .addAll((Iterable<? extends MembersInjectionBinding>) ownedBindings()) + .addAll((Iterable<? extends MembersInjectionBinding>) inheritedBindings().values()) + .build(); + } + + /** + * Returns a {@code ResolvedBindings} with the same {@link #bindingKey()} and {@link #bindings()} + * as this one, but no {@link #ownedBindings()}. + */ + ResolvedBindings asInheritedIn(ComponentDescriptor owningComponent) { + return ResolvedBindings.create( + bindingKey(), + owningComponent, + ImmutableSet.<Binding>of(), + new ImmutableSetMultimap.Builder<ComponentDescriptor, Binding>() + .putAll(inheritedBindings()) + .putAll(owningComponent, ownedBindings()) + .build()); + } + + /** + * {@code true} if this is a multibindings contribution. + */ + boolean isMultibindings() { + return bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION) + && !contributionBindings().isEmpty() + && bindingTypeFor(contributionBindings()).isMultibinding(); + } + + /** + * {@code true} if this is a unique contribution binding. + */ + boolean isUniqueContribution() { + return bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION) + && !contributionBindings().isEmpty() + && !bindingTypeFor(contributionBindings()).isMultibinding(); + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/Scope.java b/compiler/src/main/java/dagger/internal/codegen/Scope.java new file mode 100644 index 000000000..bcb009d1e --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/Scope.java @@ -0,0 +1,145 @@ +/* + * 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 new file mode 100644 index 000000000..c2620981a --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerationException.java @@ -0,0 +1,81 @@ +/* + * 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 new file mode 100644 index 000000000..4b6efc0ea --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/SourceFileGenerator.java @@ -0,0 +1,100 @@ +/* + * 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 new file mode 100644 index 000000000..890d3553b --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java @@ -0,0 +1,295 @@ +/* + * 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.ContributionBinding.BindingType; +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.List; +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; + +/** + * 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(); + } + } + + static ClassName factoryNameForProvisionBinding(ProvisionBinding binding) { + TypeElement enclosingTypeElement = binding.bindingTypeElement(); + ClassName enclosingClassName = ClassName.fromTypeElement(enclosingTypeElement); + switch (binding.bindingKind()) { + case INJECTION: + case PROVISION: + return enclosingClassName.topLevelClassName().peerNamed( + enclosingClassName.classFileName() + "_" + factoryPrefix(binding) + "Factory"); + case SYNTHETIC_PROVISON: + throw new IllegalArgumentException(); + default: + throw new AssertionError(); + } + } + + /** + * Returns the factory name parameterized with the ProvisionBinding's parameters (if necessary). + */ + static TypeName parameterizedFactoryNameForProvisionBinding( + ProvisionBinding binding) { + ClassName factoryName = factoryNameForProvisionBinding(binding); + List<TypeName> parameters = ImmutableList.of(); + if (binding.bindingType().equals(BindingType.UNIQUE)) { + switch(binding.bindingKind()) { + case INJECTION: + TypeName bindingName = TypeNames.forTypeMirror(binding.key().type()); + // If the binding is parameterized, parameterize the factory. + if (bindingName instanceof ParameterizedTypeName) { + parameters = ((ParameterizedTypeName) bindingName).parameters(); + } + 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>. + if (!binding.bindingTypeElement().getTypeParameters().isEmpty()) { + parameters = ((ParameterizedTypeName) TypeNames.forTypeMirror( + binding.bindingTypeElement().asType())).parameters(); + } + break; + default: // fall through. + } + } + return parameters.isEmpty() ? factoryName + : ParameterizedTypeName.create(factoryName, parameters); + } + + static ClassName factoryNameForProductionBinding(ProductionBinding binding) { + TypeElement enclosingTypeElement = binding.bindingTypeElement(); + ClassName enclosingClassName = ClassName.fromTypeElement(enclosingTypeElement); + switch (binding.bindingKind()) { + case IMMEDIATE: + case FUTURE_PRODUCTION: + return enclosingClassName.topLevelClassName().peerNamed( + enclosingClassName.classFileName() + "_" + factoryPrefix(binding) + "Factory"); + default: + throw new AssertionError(); + } + } + + /** + * Returns the members injector's name parameterized with the binding's parameters (if necessary). + */ + static TypeName parameterizedMembersInjectorNameForMembersInjectionBinding( + MembersInjectionBinding binding) { + ClassName factoryName = membersInjectorNameForType(binding.bindingElement()); + TypeName bindingName = TypeNames.forTypeMirror(binding.key().type()); + // If the binding is parameterized, parameterize the MembersInjector. + if (bindingName instanceof ParameterizedTypeName) { + return ParameterizedTypeName.create(factoryName, + ((ParameterizedTypeName) bindingName).parameters()); + } + return factoryName; + } + + static ClassName membersInjectorNameForType(TypeElement typeElement) { + ClassName injectedClassName = ClassName.fromTypeElement(typeElement); + return injectedClassName + .topLevelClassName() + .peerNamed(injectedClassName.classFileName() + "_MembersInjector"); + } + + private static String factoryPrefix(ProvisionBinding binding) { + switch (binding.bindingKind()) { + case INJECTION: + return ""; + case PROVISION: + return CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, + ((ExecutableElement) binding.bindingElement()).getSimpleName().toString()); + default: + throw new IllegalArgumentException(); + } + } + + private static String factoryPrefix(ProductionBinding binding) { + switch (binding.bindingKind()) { + 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 new file mode 100644 index 000000000..8cb31b92d --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java @@ -0,0 +1,214 @@ +/* + * 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.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +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 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 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())); + } + } + + ImmutableSet<TypeElement> uninitializedModules = + FluentIterable.from(graph.componentDescriptor().transitiveModules()) + .transform(ModuleDescriptor.getModuleElement()) + .filter(Predicates.not(Predicates.in(componentContributionFields.keySet()))) + .toSet(); + + 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 new file mode 100644 index 000000000..faa0459c4 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/Util.java @@ -0,0 +1,180 @@ +/* + * 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 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.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 Map<K, Provider<V>>} if the map + * includes such a construction + */ + public static DeclaredType getProvidedValueTypeOfMap(DeclaredType mapType) { + checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType); + return asDeclared(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 DeclaredType getValueTypeOfMap(DeclaredType mapType) { + checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType); + List<? extends TypeMirror> mapArgs = mapType.getTypeArguments(); + return asDeclared(mapArgs.get(1)); + } + + /** + * Returns the key type for a {@link Map} type like Map<K, Provider<V>>} + */ + public static DeclaredType getKeyTypeOfMap(DeclaredType mapType) { + checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType); + List<? extends TypeMirror> mapArgs = mapType.getTypeArguments(); + return MoreTypes.asDeclared(mapArgs.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)); + } + + /** + * 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 new file mode 100644 index 000000000..e17406755 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ValidationReport.java @@ -0,0 +1,223 @@ +/* + * 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 new file mode 100644 index 000000000..d60207241 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/ValidationType.java @@ -0,0 +1,40 @@ +/* +* 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 new file mode 100644 index 000000000..b2cb45134 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java @@ -0,0 +1,70 @@ +/* + * 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 com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +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 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))); + } + + @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 ImmutableSet.of(annotationName); + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/ArrayTypeName.java b/compiler/src/main/java/dagger/internal/codegen/writer/ArrayTypeName.java new file mode 100644 index 000000000..e7960629e --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/ArrayTypeName.java @@ -0,0 +1,53 @@ +/* + * 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 new file mode 100644 index 000000000..c00dd5f07 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/BlockWriter.java @@ -0,0 +1,66 @@ +/* + * 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 new file mode 100644 index 000000000..5b7776853 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java @@ -0,0 +1,273 @@ +/* + * 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)); + checkArgument(Ascii.isUpperCase(memberClassName.charAt(0))); + 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)); + checkArgument(Ascii.isUpperCase(peerClassName.charAt(0))); + return new ClassName(packageName(), enclosingSimpleNames(), peerClassName); + } + + 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 new file mode 100644 index 000000000..edaba3a2d --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/ClassWriter.java @@ -0,0 +1,153 @@ +/* + * 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 new file mode 100644 index 000000000..387c1ddf1 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/ConstructorWriter.java @@ -0,0 +1,96 @@ +/* + * 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 new file mode 100644 index 000000000..4ab017d79 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/EnumWriter.java @@ -0,0 +1,174 @@ +/* + * 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 new file mode 100644 index 000000000..b45e5d9ae --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/FieldWriter.java @@ -0,0 +1,66 @@ +/* + * 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 new file mode 100644 index 000000000..e463ea2cb --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/HasClassReferences.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..a6909ed10 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/HasTypeName.java @@ -0,0 +1,20 @@ +/* + * 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 new file mode 100644 index 000000000..d96f8a3a2 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/IndentingAppendable.java @@ -0,0 +1,92 @@ +/* + * 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 new file mode 100644 index 000000000..ffcfc7562 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/InterfaceWriter.java @@ -0,0 +1,87 @@ +/* + * 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 new file mode 100644 index 000000000..5977371ac --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/JavaWriter.java @@ -0,0 +1,260 @@ +/* + * 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 new file mode 100644 index 000000000..eb4ff8d51 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/MethodWriter.java @@ -0,0 +1,121 @@ +/* + * 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 new file mode 100644 index 000000000..bb4c6ffd9 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/Modifiable.java @@ -0,0 +1,68 @@ +/* + * 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 new file mode 100644 index 000000000..0d3588b77 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/NullName.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..e46a96186 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/ParameterizedTypeName.java @@ -0,0 +1,107 @@ +/* + * 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 new file mode 100644 index 000000000..94961dd7a --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/PrimitiveName.java @@ -0,0 +1,92 @@ +/* + * 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 new file mode 100644 index 000000000..2d11f60f7 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java @@ -0,0 +1,186 @@ +/* + * 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 final class Snippet implements HasClassReferences, Writable { + private final String format; + private final ImmutableSet<TypeName> types; + private final ImmutableList<Object> args; + + private Snippet(String format, ImmutableSet<TypeName> types, ImmutableList<Object> args) { + this.format = format; + this.types = types; + this.args = args; + } + + public String format() { + return format; + } + + public ImmutableList<Object> args() { + return args; + } + + public ImmutableSet<TypeName> types() { + return types; + } + + @Override + public String toString() { + return Writables.writeToString(this); + } + + @Override + public Set<ClassName> referencedClasses() { + return FluentIterable.from(types) + .transformAndConcat(new Function<TypeName, Set<ClassName>>() { + @Override + public Set<ClassName> apply(TypeName input) { + return input.referencedClasses(); + } + }) + .toSet(); + } + + @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, formattedArgsBuilder.build().toArray(new Object[0])); + + 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 Snippet(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) { + Iterator<Snippet> iterator = parameterSnippets.iterator(); + StringBuilder stringBuilder = new StringBuilder(); + ImmutableSet.Builder<TypeName> typesBuilder = ImmutableSet.builder(); + ImmutableList.Builder<Object> argsBuilder = ImmutableList.builder(); + if (iterator.hasNext()) { + Snippet firstSnippet = iterator.next(); + stringBuilder.append(firstSnippet.format()); + typesBuilder.addAll(firstSnippet.types()); + argsBuilder.addAll(firstSnippet.args()); + } + while (iterator.hasNext()) { + Snippet nextSnippet = iterator.next(); + stringBuilder.append(", ").append(nextSnippet.format()); + typesBuilder.addAll(nextSnippet.types()); + argsBuilder.addAll(nextSnippet.args()); + } + return new Snippet(stringBuilder.toString(), typesBuilder.build(), argsBuilder.build()); + } + + /** + * A snippet that concatenates its arguments. + */ + public static Snippet concat(Iterable<Snippet> snippets) { + return join(Joiner.on(""), snippets); + } + + /** + * A snippet that joins its arguments with {@code joiner}. + */ + public static Snippet join(Joiner joiner, Iterable<Snippet> snippets) { + FluentIterable<Snippet> fluentSnippets = FluentIterable.from(snippets); + return new Snippet( + joiner + .appendTo( + new StringBuilder(), + fluentSnippets.transform( + new Function<Snippet, String>() { + @Override + public String apply(Snippet snippet) { + return snippet.format; + } + })) + .toString(), + fluentSnippets + .transformAndConcat( + new Function<Snippet, ImmutableSet<TypeName>>() { + @Override + public ImmutableSet<TypeName> apply(Snippet snippet) { + return snippet.types; + } + }) + .toSet(), + fluentSnippets + .transformAndConcat( + new Function<Snippet, ImmutableList<Object>>() { + @Override + public ImmutableList<Object> apply(Snippet snippet) { + return snippet.args; + } + }) + .toList()); + } +} diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/StringLiteral.java b/compiler/src/main/java/dagger/internal/codegen/writer/StringLiteral.java new file mode 100644 index 000000000..2d059f9af --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/StringLiteral.java @@ -0,0 +1,105 @@ +/* + * 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 new file mode 100644 index 000000000..e0daf5312 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/TypeName.java @@ -0,0 +1,19 @@ +/* + * 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 new file mode 100644 index 000000000..4bc234739 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/TypeNames.java @@ -0,0 +1,108 @@ +/* + * 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 new file mode 100644 index 000000000..c6ee533ca --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/TypeVariableName.java @@ -0,0 +1,115 @@ +/* + * 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 new file mode 100644 index 000000000..b13d0831e --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/TypeWriter.java @@ -0,0 +1,108 @@ +/* + * 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 new file mode 100644 index 000000000..58ee1e494 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/VariableWriter.java @@ -0,0 +1,58 @@ +/* + * 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 new file mode 100644 index 000000000..f82a4cab4 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/VoidName.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..7756f9311 --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/WildcardName.java @@ -0,0 +1,67 @@ +/* + * 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 new file mode 100644 index 000000000..9a88f433d --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/Writable.java @@ -0,0 +1,28 @@ +/* + * 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 new file mode 100644 index 000000000..0186cbfcf --- /dev/null +++ b/compiler/src/main/java/dagger/internal/codegen/writer/Writables.java @@ -0,0 +1,83 @@ +/* + * 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 new file mode 100644 index 000000000..eaaa595d3 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/BindingFieldTest.java @@ -0,0 +1,94 @@ +/* + * 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 new file mode 100644 index 000000000..20bafff65 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java @@ -0,0 +1,938 @@ +/* + * 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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 new file mode 100644 index 000000000..94e48e2c8 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java @@ -0,0 +1,2016 @@ +/* + * 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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 new file mode 100644 index 000000000..7766f244a --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java @@ -0,0 +1,147 @@ +/* + * 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(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 new file mode 100644 index 000000000..141d5c452 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ErrorMessagesTest.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..e207fe0ee --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/GraphValidationScopingTest.java @@ -0,0 +1,348 @@ +/* + * 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 new file mode 100644 index 000000000..36303f1e9 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java @@ -0,0 +1,1123 @@ +/* + * 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.NULLABLE_TO_NON_NULLABLE; +import static java.util.Arrays.asList; + +@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;", + "", + "final class TestClass {", + " interface A {}", + "", + " @Component()", + " interface AComponent {", + " A getA();", + " }", + "}"); + 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(10); + } + + @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(String.format(NULLABLE_TO_NON_NULLABLE, "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(String.format(NULLABLE_TO_NON_NULLABLE, "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(String.format(NULLABLE_TO_NON_NULLABLE, "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(String.format(NULLABLE_TO_NON_NULLABLE, "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/InjectConstructorFactoryGeneratorTest.java b/compiler/src/test/java/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java new file mode 100644 index 000000000..ca0494e47 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java @@ -0,0 +1,1128 @@ +/* + * 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 new file mode 100644 index 000000000..c1d622dfd --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/KeyTest.java @@ -0,0 +1,281 @@ +/* + * 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 new file mode 100644 index 000000000..5f488c814 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java @@ -0,0 +1,901 @@ +/* + * 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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();", + " }", + "", + " 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 new file mode 100644 index 000000000..bc8a2660e --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java @@ -0,0 +1,473 @@ +/* + * 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();", + " }", + "", + " 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();", + " }", + "", + " 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 new file mode 100644 index 000000000..7925bd222 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java @@ -0,0 +1,875 @@ +/* + * 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; + +@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();", + " }", + "", + " 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();", + " }", + "", + " 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); + } +} diff --git a/compiler/src/test/java/dagger/internal/codegen/MethodSignatureFormatterTest.java b/compiler/src/test/java/dagger/internal/codegen/MethodSignatureFormatterTest.java new file mode 100644 index 000000000..45791c75c --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/MethodSignatureFormatterTest.java @@ -0,0 +1,66 @@ +/* + * 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 new file mode 100644 index 000000000..0ae01b40c --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java @@ -0,0 +1,163 @@ +/* + * 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.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; + +@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 new file mode 100644 index 000000000..72248e0c7 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ModuleFactoryGeneratorTest.java @@ -0,0 +1,1070 @@ +/* + * 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 new file mode 100644 index 000000000..27e720d01 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/MultipleRequestTest.java @@ -0,0 +1,119 @@ +/* + * 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/PackageProxyTest.java b/compiler/src/test/java/dagger/internal/codegen/PackageProxyTest.java new file mode 100644 index 000000000..8df80d19b --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/PackageProxyTest.java @@ -0,0 +1,242 @@ +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 PackageProxyTest { + @Test public void basicPackageProxy() { + 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.DaggerTestComponent_PackageProxy;", + "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 {", + " private final DaggerTestComponent_PackageProxy foreign_Proxy =", + " new DaggerTestComponent_PackageProxy();", + " 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();", + " }", + "", + " private void initialize(final Builder builder) {", + " this.foreign_Proxy.nonPublicClass1Provider =", + " NonPublicClass1_Factory.create(NoDepClass_Factory.create());", + " this.foreign_Proxy.nonPublicClass2Provider =", + " NonPublicClass2_Factory.create(NoDepClass_Factory.create());", + " this.publicClassProvider = PublicClass_Factory.create(", + " foreign_Proxy.nonPublicClass1Provider,", + " foreign_Proxy.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);", + " }", + " }", + "}"); + assert_().about(javaSources()) + .that(ImmutableList.of( + noDepClassFile, + publicClassFile, + nonPublicClass1File, + nonPublicClass2File, + componentFile)) + .processedWith(new ComponentProcessor()) + .compilesWithoutError() + .and().generatesSources(generatedComponent); + } + + @Test public void memberInjectionPackageProxy() { + 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();", + " }", + "", + " 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);", + " }", + " }", + "}"); + assert_().about(javaSources()) + .that(ImmutableList.of( + noDepClassFile, + aClassFile, + bClassFile, + cClassFile, + dClassFile, + componentFile)) + .processedWith(new ComponentProcessor()) + .compilesWithoutError() + .and().generatesSources(generatedComponent); + } +} diff --git a/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java b/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java new file mode 100644 index 000000000..0041f5d89 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java @@ -0,0 +1,564 @@ +/* + * 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.Futures;", + "import com.google.common.util.concurrent.ListenableFuture;", + "import dagger.producers.internal.AbstractProducer;", + "import dagger.producers.internal.Producers;", + "import dagger.producers.monitoring.ProducerToken;", + "import dagger.producers.monitoring.ProductionComponentMonitor;", + "import java.util.concurrent.Callable;", + "import java.util.concurrent.Executor;", + "import javax.annotation.Generated;", + "import javax.annotation.Nullable;", + "", + "@Generated(\"dagger.internal.codegen.ComponentProcessor\")", + "public final class TestModule_ProduceStringFactory extends AbstractProducer<String> {", + " private final TestModule module;", + " private final Executor executor;", + "", + " public TestModule_ProduceStringFactory(", + " @Nullable ProductionComponentMonitor componentMonitor,", + " TestModule module,", + " Executor executor) {", + " super(", + " Producers.producerMonitorFor(", + " componentMonitor,", + " ProducerToken.create(TestModule_ProduceStringFactory.class)));", + " assert module != null;", + " this.module = module;", + " assert executor != null;", + " this.executor = executor;", + " }", + "", + " @Override protected ListenableFuture<String> compute() {", + " ListenableFuture<ListenableFuture<String>> future = Producers.submitToExecutor(", + " new Callable<ListenableFuture<String>>() {", + " @Override public ListenableFuture<String> call() {", + " if (monitor != null) {", + " monitor.methodStarting();", + " }", + " try {", + " return module.produceString();", + " } finally {", + " if (monitor != null) {", + " monitor.methodFinished();", + " }", + " }", + " }", + " }, executor);", + " return Futures.dereference(future);", + " }", + "}"); + 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 new file mode 100644 index 000000000..92bbb752c --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java @@ -0,0 +1,223 @@ +/* + * 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.producers.Producer;", + "import dagger.producers.internal.Producers;", + "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<B> bProvider;", + " private Producer<A> aProducer;", + "", + " private DaggerTestClass_SimpleComponent(Builder builder) {", + " assert builder != null;", + " initialize(builder);", + " }", + "", + " public static Builder builder() {", + " return new Builder();", + " }", + "", + " private void initialize(final Builder builder) {", + " this.bProvider = TestClass$BModule_BFactory.create(", + " builder.bModule, TestClass$C_Factory.create());", + " this.aProducer = new TestClass$AModule_AFactory(", + " null,", + " builder.aModule,", + " builder.executor,", + " Producers.producerFromProvider(bProvider));", + " }", + "", + " @Override", + " public ListenableFuture<A> a() {", + " return aProducer.get();", + " }", + "", + " public static final class Builder {", + " private BModule bModule;", + " private AModule aModule;", + " private Executor executor;", + "", + " private Builder() {", + " }", + "", + " public SimpleComponent build() {", + " 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 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 new file mode 100644 index 000000000..9f577b722 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java @@ -0,0 +1,164 @@ +/* + * 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); + } +} diff --git a/compiler/src/test/java/dagger/internal/codegen/RepeatedModuleValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/RepeatedModuleValidationTest.java new file mode 100644 index 000000000..6bf0c9ee0 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/RepeatedModuleValidationTest.java @@ -0,0 +1,121 @@ +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 new file mode 100644 index 000000000..6311a90ca --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/SubcomponentBuilderValidationTest.java @@ -0,0 +1,742 @@ +/* + * 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 new file mode 100644 index 000000000..05ef5f37a --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java @@ -0,0 +1,440 @@ +/* + * 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 {}"); + JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters", + "package test;", + "", + "import dagger.Module;", + "", + "@Module", + "final class ModuleWithParameters {", + " ModuleWithParameters(Object whatever) {}", + "}"); + 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();", + " }", + "", + " 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 MembersInjector<Dep1> dep1MembersInjector;", + " private MembersInjector<Dep2> dep2MembersInjector;", + " ", + " private ChildComponentImpl() { ", + " this.childModule = new ChildModule();", + " initialize();", + " }", + " ", + " 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);", + " this.dep1MembersInjector = Dep1_MembersInjector.create();", + " this.dep2MembersInjector = Dep2_MembersInjector.create();", + " }", + " ", + " @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 new file mode 100644 index 000000000..d7f445125 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/ValidationReportTest.java @@ -0,0 +1,129 @@ +/* + * 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 new file mode 100644 index 000000000..eff01b849 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/writer/ClassNameTest.java @@ -0,0 +1,128 @@ +/* + * 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 new file mode 100644 index 000000000..e775f7487 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/writer/JavaWriterTest.java @@ -0,0 +1,33 @@ +/* + * 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 new file mode 100644 index 000000000..ec82e9605 --- /dev/null +++ b/compiler/src/test/java/dagger/internal/codegen/writer/TypeNamesTest.java @@ -0,0 +1,106 @@ +/* + * 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 new file mode 100644 index 000000000..40545039c --- /dev/null +++ b/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java @@ -0,0 +1,142 @@ +/** + * 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();", + " }", + "", + " 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 new file mode 100644 index 000000000..eab9dd8b5 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,95 @@ +<?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 new file mode 100644 index 000000000..7d724017e --- /dev/null +++ b/core/src/main/java/dagger/Component.java @@ -0,0 +1,258 @@ +/* + * 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 new file mode 100644 index 000000000..e04cc03e3 --- /dev/null +++ b/core/src/main/java/dagger/Lazy.java @@ -0,0 +1,155 @@ +/* + * 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 new file mode 100644 index 000000000..106c00183 --- /dev/null +++ b/core/src/main/java/dagger/MapKey.java @@ -0,0 +1,114 @@ +/* + * 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 new file mode 100644 index 000000000..d0de7f396 --- /dev/null +++ b/core/src/main/java/dagger/MembersInjector.java @@ -0,0 +1,44 @@ +/* + * 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 new file mode 100644 index 000000000..05f0f3a6a --- /dev/null +++ b/core/src/main/java/dagger/Module.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 000000000..741a54a2d --- /dev/null +++ b/core/src/main/java/dagger/Provides.java @@ -0,0 +1,73 @@ +/* + * 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 new file mode 100644 index 000000000..0ef47011f --- /dev/null +++ b/core/src/main/java/dagger/Subcomponent.java @@ -0,0 +1,51 @@ +/* + * 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.Target; + +import static java.lang.annotation.ElementType.TYPE; + +/** + * 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 + */ +@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 new file mode 100644 index 000000000..a0a82c659 --- /dev/null +++ b/core/src/main/java/dagger/internal/Beta.java @@ -0,0 +1,33 @@ +/* + * 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 new file mode 100644 index 000000000..55f26ebab --- /dev/null +++ b/core/src/main/java/dagger/internal/Collections.java @@ -0,0 +1,58 @@ +/* + * 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 new file mode 100644 index 000000000..d1e864d17 --- /dev/null +++ b/core/src/main/java/dagger/internal/DelegateFactory.java @@ -0,0 +1,46 @@ +/* + * 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 new file mode 100644 index 000000000..d0f102896 --- /dev/null +++ b/core/src/main/java/dagger/internal/DoubleCheckLazy.java @@ -0,0 +1,72 @@ +/* + * 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 new file mode 100644 index 000000000..3e2774cec --- /dev/null +++ b/core/src/main/java/dagger/internal/Factory.java @@ -0,0 +1,37 @@ +/* + * 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 new file mode 100644 index 000000000..59b1fcb8b --- /dev/null +++ b/core/src/main/java/dagger/internal/InstanceFactory.java @@ -0,0 +1,48 @@ +/* + * 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 new file mode 100644 index 000000000..4dac1262b --- /dev/null +++ b/core/src/main/java/dagger/internal/MapFactory.java @@ -0,0 +1,60 @@ +/* + * 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 new file mode 100644 index 000000000..00c0fd33a --- /dev/null +++ b/core/src/main/java/dagger/internal/MapProviderFactory.java @@ -0,0 +1,90 @@ +/* + * 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 new file mode 100644 index 000000000..ee4c7b4cf --- /dev/null +++ b/core/src/main/java/dagger/internal/MembersInjectors.java @@ -0,0 +1,63 @@ +/* + * 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 new file mode 100644 index 000000000..b25db380a --- /dev/null +++ b/core/src/main/java/dagger/internal/ScopedProvider.java @@ -0,0 +1,61 @@ +/* + * 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 new file mode 100644 index 000000000..9b73e79db --- /dev/null +++ b/core/src/main/java/dagger/internal/SetFactory.java @@ -0,0 +1,122 @@ +/* + * 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 new file mode 100644 index 000000000..21497c681 --- /dev/null +++ b/core/src/main/java/dagger/mapkeys/ClassKey.java @@ -0,0 +1,35 @@ +/* + * 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 new file mode 100644 index 000000000..011b49fe8 --- /dev/null +++ b/core/src/main/java/dagger/mapkeys/IntKey.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..183b74d02 --- /dev/null +++ b/core/src/main/java/dagger/mapkeys/LongKey.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..7455a9bc5 --- /dev/null +++ b/core/src/main/java/dagger/mapkeys/StringKey.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..e5cc67f39 --- /dev/null +++ b/core/src/main/java/dagger/package-info.java @@ -0,0 +1,34 @@ +/* + * 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 new file mode 100644 index 000000000..579e0405f --- /dev/null +++ b/core/src/test/java/dagger/internal/DoubleCheckLazyTest.java @@ -0,0 +1,100 @@ +/* + * 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 new file mode 100644 index 000000000..acaf20d79 --- /dev/null +++ b/core/src/test/java/dagger/internal/InstanceFactoryTest.java @@ -0,0 +1,42 @@ +/* + * 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 new file mode 100644 index 000000000..b4496e926 --- /dev/null +++ b/core/src/test/java/dagger/internal/MapProviderFactoryTest.java @@ -0,0 +1,86 @@ +/* + * 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 new file mode 100644 index 000000000..84b02c510 --- /dev/null +++ b/core/src/test/java/dagger/internal/ScopedProviderTest.java @@ -0,0 +1,65 @@ +/* + * 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 new file mode 100644 index 000000000..04b982281 --- /dev/null +++ b/core/src/test/java/dagger/internal/SetFactoryTest.java @@ -0,0 +1,134 @@ +/* + * 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 new file mode 100755 index 000000000..1fde1bd57 --- /dev/null +++ b/deploy_website.sh @@ -0,0 +1,49 @@ +#!/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/AndroidManifest.xml b/examples/android-activity-graphs/AndroidManifest.xml new file mode 100644 index 000000000..234406dfa --- /dev/null +++ b/examples/android-activity-graphs/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?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/README.md b/examples/android-activity-graphs/README.md new file mode 100644 index 000000000..ac3680bb5 --- /dev/null +++ b/examples/android-activity-graphs/README.md @@ -0,0 +1,24 @@ +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 new file mode 100644 index 000000000..53de228ff --- /dev/null +++ b/examples/android-activity-graphs/pom.xml @@ -0,0 +1,63 @@ +<?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> + <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.jayway.maven.plugins.android.generation2</groupId> + <artifactId>android-maven-plugin</artifactId> + <extensions>true</extensions> + </plugin> + </plugins> + </build> +</project> 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 new file mode 100644 index 000000000..430838e83 --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/AbstractActivityComponent.java @@ -0,0 +1,30 @@ +/* + * 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 new file mode 100644 index 000000000..cf5462e50 --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ActivityModule.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..04c206225 --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ApplicationComponent.java @@ -0,0 +1,35 @@ +/* + * 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 new file mode 100644 index 000000000..72057332c --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplication.java @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 000000000..070d2c79d --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplicationModule.java @@ -0,0 +1,48 @@ +/* + * 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 new file mode 100644 index 000000000..d54b193e3 --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/PerActivity.java @@ -0,0 +1,31 @@ +/* + * 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 new file mode 100644 index 000000000..c416c75b3 --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/ActivityTitleController.java @@ -0,0 +1,41 @@ +/* + * 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 new file mode 100644 index 000000000..1f3bb7002 --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeActivity.java @@ -0,0 +1,51 @@ +/* + * 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 new file mode 100644 index 000000000..84d2a427c --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeComponent.java @@ -0,0 +1,29 @@ +/* + * 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 new file mode 100644 index 000000000..1df2724cb --- /dev/null +++ b/examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeFragment.java @@ -0,0 +1,51 @@ +/* + * 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-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/BuildConfig.java b/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/BuildConfig.java new file mode 100644 index 000000000..173698284 --- /dev/null +++ b/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/BuildConfig.java @@ -0,0 +1,8 @@ +/*___Generated_by_IDEA___*/ + +package com.example.dagger.activitygraphs; + +/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */ +public final class BuildConfig { + public final static boolean DEBUG = Boolean.parseBoolean(null); +}
\ No newline at end of file diff --git a/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/Manifest.java b/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/Manifest.java new file mode 100644 index 000000000..d0a612287 --- /dev/null +++ b/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/Manifest.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.example.dagger.activitygraphs; + +/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */ +public final class Manifest { +}
\ No newline at end of file diff --git a/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/R.java b/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/R.java new file mode 100644 index 000000000..09fa17967 --- /dev/null +++ b/examples/android-activity-graphs/target/generated-sources/r/com/example/dagger/activitygraphs/R.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.example.dagger.activitygraphs; + +/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */ +public final class R { +}
\ No newline at end of file diff --git a/examples/android-simple/AndroidManifest.xml b/examples/android-simple/AndroidManifest.xml new file mode 100644 index 000000000..53c83bfd3 --- /dev/null +++ b/examples/android-simple/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?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/README.md b/examples/android-simple/README.md new file mode 100644 index 000000000..944d01533 --- /dev/null +++ b/examples/android-simple/README.md @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..332aeb66b --- /dev/null +++ b/examples/android-simple/pom.xml @@ -0,0 +1,59 @@ +<?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> + <optional>true</optional> + </dependency> + + <dependency> + <groupId>com.google.android</groupId> + <artifactId>android</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>com.jayway.maven.plugins.android.generation2</groupId> + <artifactId>android-maven-plugin</artifactId> + <extensions>true</extensions> + </plugin> + </plugins> + </build> +</project> 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 new file mode 100644 index 000000000..18184d164 --- /dev/null +++ b/examples/android-simple/src/main/java/com/example/dagger/simple/AndroidModule.java @@ -0,0 +1,49 @@ +/* + * 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 new file mode 100644 index 000000000..aa09f2d0a --- /dev/null +++ b/examples/android-simple/src/main/java/com/example/dagger/simple/DemoActivity.java @@ -0,0 +1,27 @@ +/* + * 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 new file mode 100644 index 000000000..55402c654 --- /dev/null +++ b/examples/android-simple/src/main/java/com/example/dagger/simple/DemoApplication.java @@ -0,0 +1,52 @@ +/* + * 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 new file mode 100644 index 000000000..84d224740 --- /dev/null +++ b/examples/android-simple/src/main/java/com/example/dagger/simple/ForApplication.java @@ -0,0 +1,25 @@ +/* + * 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 new file mode 100644 index 000000000..7e33b8eec --- /dev/null +++ b/examples/android-simple/src/main/java/com/example/dagger/simple/ui/HomeActivity.java @@ -0,0 +1,35 @@ +/* + * 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/android-simple/target/generated-sources/r/com/example/dagger/simple/BuildConfig.java b/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/BuildConfig.java new file mode 100644 index 000000000..53ff15459 --- /dev/null +++ b/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/BuildConfig.java @@ -0,0 +1,8 @@ +/*___Generated_by_IDEA___*/ + +package com.example.dagger.simple; + +/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */ +public final class BuildConfig { + public final static boolean DEBUG = Boolean.parseBoolean(null); +}
\ No newline at end of file diff --git a/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/Manifest.java b/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/Manifest.java new file mode 100644 index 000000000..d7e5050d1 --- /dev/null +++ b/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/Manifest.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.example.dagger.simple; + +/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */ +public final class Manifest { +}
\ No newline at end of file diff --git a/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/R.java b/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/R.java new file mode 100644 index 000000000..a42b98ee8 --- /dev/null +++ b/examples/android-simple/target/generated-sources/r/com/example/dagger/simple/R.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.example.dagger.simple; + +/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */ +public final class R { +}
\ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml new file mode 100644 index 000000000..708635f1a --- /dev/null +++ b/examples/pom.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<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>com.google.dagger.example</groupId> + <artifactId>dagger-example-parent</artifactId> + <packaging>pom</packaging> + <name>Examples</name> + + <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> + </dependency> + <dependency> + <groupId>com.google.android</groupId> + <artifactId>support-v4</artifactId> + <version>r7</version> + </dependency> + </dependencies> + </dependencyManagement> + + <build> + <pluginManagement> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + </configuration> + </plugin> + <plugin> + <groupId>com.jayway.maven.plugins.android.generation2</groupId> + <artifactId>android-maven-plugin</artifactId> + <version>3.8.2</version> + <configuration> + <sdk> + <platform>16</platform> + </sdk> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> +</project> diff --git a/examples/simple/pom.xml b/examples/simple/pom.xml new file mode 100644 index 000000000..0be10b86d --- /dev/null +++ b/examples/simple/pom.xml @@ -0,0 +1,42 @@ +<?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.example</groupId> + <artifactId>dagger-example-parent</artifactId> + <version>2.1-SNAPSHOT</version> + </parent> + + <artifactId>simple</artifactId> + <name>Examples: Simple</name> + + <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> + <optional>true</optional> + </dependency> + </dependencies> +</project> diff --git a/examples/simple/src/main/java/coffee/CoffeeApp.java b/examples/simple/src/main/java/coffee/CoffeeApp.java new file mode 100644 index 000000000..b0a93ec3c --- /dev/null +++ b/examples/simple/src/main/java/coffee/CoffeeApp.java @@ -0,0 +1,17 @@ +package coffee; + +import dagger.Component; +import javax.inject.Singleton; + +public class CoffeeApp { + @Singleton + @Component(modules = { DripCoffeeModule.class }) + public interface Coffee { + CoffeeMaker maker(); + } + + public static void main(String[] args) { + Coffee coffee = DaggerCoffeeApp_Coffee.builder().build(); + coffee.maker().brew(); + } +} diff --git a/examples/simple/src/main/java/coffee/CoffeeMaker.java b/examples/simple/src/main/java/coffee/CoffeeMaker.java new file mode 100644 index 000000000..6410336d0 --- /dev/null +++ b/examples/simple/src/main/java/coffee/CoffeeMaker.java @@ -0,0 +1,21 @@ +package coffee; + +import dagger.Lazy; +import javax.inject.Inject; + +class CoffeeMaker { + private final Lazy<Heater> heater; // Create a possibly costly heater only when we use it. + private final Pump pump; + + @Inject CoffeeMaker(Lazy<Heater> heater, Pump pump) { + this.heater = heater; + this.pump = pump; + } + + public void brew() { + heater.get().on(); + pump.pump(); + System.out.println(" [_]P coffee! [_]P "); + heater.get().off(); + } +} diff --git a/examples/simple/src/main/java/coffee/DripCoffeeModule.java b/examples/simple/src/main/java/coffee/DripCoffeeModule.java new file mode 100644 index 000000000..e50d249e1 --- /dev/null +++ b/examples/simple/src/main/java/coffee/DripCoffeeModule.java @@ -0,0 +1,12 @@ +package coffee; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; + +@Module(includes = PumpModule.class) +class DripCoffeeModule { + @Provides @Singleton Heater provideHeater() { + return new ElectricHeater(); + } +} diff --git a/examples/simple/src/main/java/coffee/ElectricHeater.java b/examples/simple/src/main/java/coffee/ElectricHeater.java new file mode 100644 index 000000000..fbab399f2 --- /dev/null +++ b/examples/simple/src/main/java/coffee/ElectricHeater.java @@ -0,0 +1,18 @@ +package coffee; + +class ElectricHeater implements Heater { + boolean heating; + + @Override public void on() { + System.out.println("~ ~ ~ heating ~ ~ ~"); + this.heating = true; + } + + @Override public void off() { + this.heating = false; + } + + @Override public boolean isHot() { + return heating; + } +} diff --git a/examples/simple/src/main/java/coffee/Heater.java b/examples/simple/src/main/java/coffee/Heater.java new file mode 100644 index 000000000..b5ddb6b88 --- /dev/null +++ b/examples/simple/src/main/java/coffee/Heater.java @@ -0,0 +1,7 @@ +package coffee; + +interface Heater { + void on(); + void off(); + boolean isHot(); +} diff --git a/examples/simple/src/main/java/coffee/Pump.java b/examples/simple/src/main/java/coffee/Pump.java new file mode 100644 index 000000000..e39434913 --- /dev/null +++ b/examples/simple/src/main/java/coffee/Pump.java @@ -0,0 +1,5 @@ +package coffee; + +interface Pump { + void pump(); +} diff --git a/examples/simple/src/main/java/coffee/PumpModule.java b/examples/simple/src/main/java/coffee/PumpModule.java new file mode 100644 index 000000000..338ad33c2 --- /dev/null +++ b/examples/simple/src/main/java/coffee/PumpModule.java @@ -0,0 +1,11 @@ +package coffee; + +import dagger.Module; +import dagger.Provides; + +@Module +class PumpModule { + @Provides Pump providePump(Thermosiphon pump) { + return pump; + } +} diff --git a/examples/simple/src/main/java/coffee/Thermosiphon.java b/examples/simple/src/main/java/coffee/Thermosiphon.java new file mode 100644 index 000000000..c9f9828b0 --- /dev/null +++ b/examples/simple/src/main/java/coffee/Thermosiphon.java @@ -0,0 +1,18 @@ +package coffee; + +import javax.inject.Inject; + +class Thermosiphon implements Pump { + private final Heater heater; + + @Inject + Thermosiphon(Heater heater) { + this.heater = heater; + } + + @Override public void pump() { + if (heater.isHot()) { + System.out.println("=> => pumping => =>"); + } + } +} diff --git a/lib/auto-common-1.0-20151022.071545-39-sources.jar b/lib/auto-common-1.0-20151022.071545-39-sources.jar Binary files differnew file mode 100644 index 000000000..66b78b08d --- /dev/null +++ b/lib/auto-common-1.0-20151022.071545-39-sources.jar diff --git a/lib/auto-common-1.0-20151022.071545-39.jar b/lib/auto-common-1.0-20151022.071545-39.jar Binary files differnew file mode 100644 index 000000000..8967dead1 --- /dev/null +++ b/lib/auto-common-1.0-20151022.071545-39.jar diff --git a/lib/auto-common-1.0-20151022.071545-39.jar.txt b/lib/auto-common-1.0-20151022.071545-39.jar.txt new file mode 100644 index 000000000..892eaed8f --- /dev/null +++ b/lib/auto-common-1.0-20151022.071545-39.jar.txt @@ -0,0 +1,203 @@ +Google Gson
+
+ 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 2008-2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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-20150915.183854-35-sources.jar b/lib/auto-factory-1.0-20150915.183854-35-sources.jar Binary files differnew file mode 100644 index 000000000..5c124070f --- /dev/null +++ b/lib/auto-factory-1.0-20150915.183854-35-sources.jar diff --git a/lib/auto-factory-1.0-20150915.183854-35.jar b/lib/auto-factory-1.0-20150915.183854-35.jar Binary files differnew file mode 100644 index 000000000..09f408b77 --- /dev/null +++ b/lib/auto-factory-1.0-20150915.183854-35.jar diff --git a/lib/auto-factory-1.0-20150915.183854-35.jar.txt b/lib/auto-factory-1.0-20150915.183854-35.jar.txt new file mode 100644 index 000000000..892eaed8f --- /dev/null +++ b/lib/auto-factory-1.0-20150915.183854-35.jar.txt @@ -0,0 +1,203 @@ +Google Gson
+
+ 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 2008-2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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-rc2-sources.jar b/lib/auto-service-1.0-rc2-sources.jar Binary files differnew file mode 100644 index 000000000..cd8f687b3 --- /dev/null +++ b/lib/auto-service-1.0-rc2-sources.jar diff --git a/lib/auto-service-1.0-rc2.jar b/lib/auto-service-1.0-rc2.jar Binary files differnew file mode 100644 index 000000000..ea8fb68a6 --- /dev/null +++ b/lib/auto-service-1.0-rc2.jar diff --git a/lib/auto-service-1.0-rc2.jar.txt b/lib/auto-service-1.0-rc2.jar.txt new file mode 100644 index 000000000..892eaed8f --- /dev/null +++ b/lib/auto-service-1.0-rc2.jar.txt @@ -0,0 +1,203 @@ +Google Gson
+
+ 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 2008-2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.0-sources.jar b/lib/auto-value-1.0-sources.jar Binary files differnew file mode 100644 index 000000000..86df40d0e --- /dev/null +++ b/lib/auto-value-1.0-sources.jar diff --git a/lib/auto-value-1.0.jar b/lib/auto-value-1.0.jar Binary files differnew file mode 100644 index 000000000..3b8893f7e --- /dev/null +++ b/lib/auto-value-1.0.jar diff --git a/lib/auto-value-1.0.jar.txt b/lib/auto-value-1.0.jar.txt new file mode 100644 index 000000000..892eaed8f --- /dev/null +++ b/lib/auto-value-1.0.jar.txt @@ -0,0 +1,203 @@ +Google Gson
+
+ 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 2008-2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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-0.1-20151017.042846-2.jar b/lib/google-java-format-0.1-20151017.042846-2.jar Binary files differnew file mode 100644 index 000000000..964760f98 --- /dev/null +++ b/lib/google-java-format-0.1-20151017.042846-2.jar 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 Binary files differnew file mode 100644 index 000000000..eb542743c --- /dev/null +++ b/lib/google-java-format-0.1-20151017.042846-2.jar.txt diff --git a/lib/javax-inject-src.jar b/lib/javax-inject-src.jar Binary files differnew file mode 100644 index 000000000..a8a5aa70f --- /dev/null +++ b/lib/javax-inject-src.jar diff --git a/lib/javax-inject.jar b/lib/javax-inject.jar Binary files differnew file mode 100644 index 000000000..4c86c52d2 --- /dev/null +++ b/lib/javax-inject.jar diff --git a/lib/javax-inject.jar.txt b/lib/javax-inject.jar.txt new file mode 100644 index 000000000..892eaed8f --- /dev/null +++ b/lib/javax-inject.jar.txt @@ -0,0 +1,203 @@ +Google Gson
+
+ 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 2008-2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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/pom.xml b/pom.xml new file mode 100644 index 000000000..d584d49d0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,229 @@ +<?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> + <javawriter.version>2.5.0</javawriter.version> + <auto.common.version>1.0-SNAPSHOT</auto.common.version> + <auto.factory.version>1.0-SNAPSHOT</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> + + <!-- Test Dependencies --> + <compile-testing.version>0.7</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>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.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 new file mode 100644 index 000000000..1ee555b97 --- /dev/null +++ b/producers/pom.xml @@ -0,0 +1,58 @@ +<?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.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>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 new file mode 100644 index 000000000..7edfee314 --- /dev/null +++ b/producers/src/main/java/dagger/producers/Produced.java @@ -0,0 +1,133 @@ +/* + * 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(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(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 new file mode 100644 index 000000000..eb159bbf6 --- /dev/null +++ b/producers/src/main/java/dagger/producers/Producer.java @@ -0,0 +1,104 @@ +/* + * 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 new file mode 100644 index 000000000..714e3fa3e --- /dev/null +++ b/producers/src/main/java/dagger/producers/ProducerModule.java @@ -0,0 +1,40 @@ +/* + * 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 new file mode 100644 index 000000000..1b57bc39d --- /dev/null +++ b/producers/src/main/java/dagger/producers/Produces.java @@ -0,0 +1,70 @@ +/* + * 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 new file mode 100644 index 000000000..a6009c5b3 --- /dev/null +++ b/producers/src/main/java/dagger/producers/ProductionComponent.java @@ -0,0 +1,83 @@ +/* + * 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 {}; +} diff --git a/producers/src/main/java/dagger/producers/internal/AbstractProducer.java b/producers/src/main/java/dagger/producers/internal/AbstractProducer.java new file mode 100644 index 000000000..7843b2094 --- /dev/null +++ b/producers/src/main/java/dagger/producers/internal/AbstractProducer.java @@ -0,0 +1,78 @@ +/* + * 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.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import dagger.producers.Producer; +import dagger.producers.monitoring.ProducerMonitor; +import javax.annotation.Nullable; + +/** + * 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> { + @Nullable protected final ProducerMonitor monitor; + private volatile ListenableFuture<T> instance = null; + + protected AbstractProducer() { + this(null); + } + + protected AbstractProducer(@Nullable ProducerMonitor monitor) { + this.monitor = monitor; + } + + /** Computes this producer's future, which is then cached in {@link #get}. */ + protected abstract ListenableFuture<T> compute(); + + @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) { + instance = result = compute(); + if (result == null) { + throw new NullPointerException("compute returned null"); + } + if (monitor != null) { + Futures.addCallback( + result, + new FutureCallback<T>() { + @Override + public void onSuccess(T value) { + monitor.succeeded(value); + } + + @Override + public void onFailure(Throwable t) { + monitor.failed(t); + } + }); + } + } + } + } + return result; + } +} diff --git a/producers/src/main/java/dagger/producers/internal/Producers.java b/producers/src/main/java/dagger/producers/internal/Producers.java new file mode 100644 index 000000000..6cd06d40e --- /dev/null +++ b/producers/src/main/java/dagger/producers/internal/Producers.java @@ -0,0 +1,130 @@ +/* + * 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 com.google.common.util.concurrent.ListenableFutureTask; +import dagger.producers.Produced; +import dagger.producers.Producer; +import dagger.producers.monitoring.ProducerMonitor; +import dagger.producers.monitoring.ProducerToken; +import dagger.producers.monitoring.ProductionComponentMonitor; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; +import javax.annotation.Nullable; +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) { + 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); + } + }); + } + + /** + * Submits a callable to an executor, returning the future representing the task. This mirrors + * {@link com.google.common.util.concurrent.ListeningExecutorService#submit}, but only requires an + * {@link Executor}. + * + * @throws RejectedExecutionException if this task cannot be accepted for execution. + */ + public static <T> ListenableFuture<T> submitToExecutor(Callable<T> callable, Executor executor) { + ListenableFutureTask<T> future = ListenableFutureTask.create(callable); + executor.execute(future); + return future; + } + + /** + * 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() { + return Futures.immediateFuture(provider.get()); + } + }; + } + + /** Lifts {@link ProductionComponentMonitor#producerMonitorFor} to nullable types. */ + @Nullable + public static ProducerMonitor producerMonitorFor( + @Nullable ProductionComponentMonitor componentMonitor, ProducerToken token) { + if (componentMonitor != null) { + return componentMonitor.producerMonitorFor(token); + } + return null; + } + + private Producers() {} +} diff --git a/producers/src/main/java/dagger/producers/internal/SetProducer.java b/producers/src/main/java/dagger/producers/internal/SetProducer.java new file mode 100644 index 000000000..ba3312882 --- /dev/null +++ b/producers/src/main/java/dagger/producers/internal/SetProducer.java @@ -0,0 +1,85 @@ +/* + * 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 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. + */ + public static <T> Producer<Set<T>> create( + @SuppressWarnings("unchecked") 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() { + 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 new file mode 100644 index 000000000..8e4e9b28b --- /dev/null +++ b/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java @@ -0,0 +1,77 @@ +/* + * 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; + +/** + * 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 is: + * <ul> + * <li>{@link #methodStarting} + * <li>The method is called + * <li>{@link #methodFinished} + * <li>If the method returns a value, then: + * <ul> + * <li>{#succeeded} if the method returned normally; or + * <li>{#failed} if the method threw an exception. + * </ul> + * <li>If the method returns a future, then: + * <ul> + * <li>{#succeeded} if the method returned normally, and the future succeeded; or + * <li>{#failed} if the method threw an exception, or returned normally and the future failed. + * </ul> + * </ul> + * + * <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 + * {@code methodWillStart} 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, the {@code methodFinished} calls will be in the + * reverse order from the {@link #methodWillStart} calls. + */ + public void methodFinished() {} + + /** + * Called when the producer’s future has completed successfully with a value. + * + * <p>When multiple monitors are installed, the {@code futureSucceeded} calls will be in the + * reverse order from the {@link #methodWillStart} calls. + */ + public void succeeded(Object o) {} + + /** + * Called when the producer's future has failed with an exception. + * + * <p>When multiple monitors are installed, the {@code futureFailed} calls will be in the reverse + * order from the {@link #methodWillStart} calls. + */ + public void failed(Throwable t) {} +} diff --git a/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java b/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java new file mode 100644 index 000000000..126a40d4e --- /dev/null +++ b/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java @@ -0,0 +1,62 @@ +/* + * 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 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 new file mode 100644 index 000000000..1a62dfa1f --- /dev/null +++ b/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java @@ -0,0 +1,53 @@ +/* + * 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; + +/** + * 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/package-info.java b/producers/src/main/java/dagger/producers/monitoring/package-info.java new file mode 100644 index 000000000..d10408706 --- /dev/null +++ b/producers/src/main/java/dagger/producers/monitoring/package-info.java @@ -0,0 +1,23 @@ +/* + * 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 new file mode 100644 index 000000000..165e7301c --- /dev/null +++ b/producers/src/test/java/dagger/producers/ProducedTest.java @@ -0,0 +1,60 @@ +/* + * 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 new file mode 100644 index 000000000..15a64bd1f --- /dev/null +++ b/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java @@ -0,0 +1,127 @@ +/* + * 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 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.Mock; +import org.mockito.MockitoAnnotations; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Tests {@link AbstractProducer}. + */ +@RunWith(JUnit4.class) +public class AbstractProducerTest { + @Mock private ProducerMonitor monitor; + + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void get_nullPointerException() { + Producer<Object> producer = new DelegateProducer<>(monitor, null); + try { + producer.get(); + fail(); + } catch (NullPointerException expected) { + } + } + + @Test public void get() throws Exception { + Producer<Integer> producer = + new AbstractProducer<Integer>(monitor) { + 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); + } + + @Test + public void monitor_success() throws Exception { + SettableFuture<Integer> delegateFuture = SettableFuture.create(); + Producer<Integer> producer = new DelegateProducer<>(monitor, delegateFuture); + + ListenableFuture<Integer> future = producer.get(); + assertThat(future.isDone()).isFalse(); + verifyZeroInteractions(monitor); + 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<>(monitor, delegateFuture); + + ListenableFuture<Integer> future = producer.get(); + assertThat(future.isDone()).isFalse(); + verifyZeroInteractions(monitor); + 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 + public void monitor_null() throws Exception { + Producer<Integer> producer = new DelegateProducer<>(null, Futures.immediateFuture(42)); + assertThat(producer.get().get()).isEqualTo(42); + } + + static final class DelegateProducer<T> extends AbstractProducer<T> { + private final ListenableFuture<T> delegate; + + DelegateProducer(ProducerMonitor monitor, ListenableFuture<T> delegate) { + super(monitor); + this.delegate = delegate; + } + + @Override + public ListenableFuture<T> compute() { + return delegate; + } + } +} diff --git a/producers/src/test/java/dagger/producers/internal/ProducersTest.java b/producers/src/test/java/dagger/producers/internal/ProducersTest.java new file mode 100644 index 000000000..43564089f --- /dev/null +++ b/producers/src/test/java/dagger/producers/internal/ProducersTest.java @@ -0,0 +1,123 @@ +/* + * 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.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; +import dagger.producers.Produced; +import dagger.producers.Producer; +import java.util.Set; +import java.util.concurrent.Callable; +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 submitToExecutor() throws Exception { + ListenableFuture<Integer> future = Producers.submitToExecutor(new Callable<Integer>() { + @Override public Integer call() { + return 42; + } + }, MoreExecutors.directExecutor()); + assertThat(future.isDone()).isTrue(); + assertThat(future.get()).isEqualTo(42); + } + + @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/SetProducerTest.java b/producers/src/test/java/dagger/producers/internal/SetProducerTest.java new file mode 100644 index 000000000..1f8ff7c3a --- /dev/null +++ b/producers/src/test/java/dagger/producers/internal/SetProducerTest.java @@ -0,0 +1,82 @@ +/* + * 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.Futures; +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( + new ImmediateProducer<Set<Integer>>(ImmutableSet.of(1, 2)), + new ImmediateProducer<Set<Integer>>(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( + new ImmediateProducer<Set<Integer>>(ImmutableSet.of(1, 2)), + new ImmediateProducer<Set<Integer>>(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( + new ImmediateProducer<Set<Integer>>(ImmutableSet.of(1, 2)), + new ImmediateProducer<Set<Integer>>( + Collections.<Integer>singleton(null))); + ListenableFuture<Set<Integer>> future = producer.get(); + try { + future.get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(NullPointerException.class); + } + } + + private static final class ImmediateProducer<T> implements Producer<T> { + private final T value; + + ImmediateProducer(T value) { + this.value = value; + } + + @Override public ListenableFuture<T> get() { + return Futures.immediateFuture(value); + } + } +} diff --git a/util/generate-latest-docs.sh b/util/generate-latest-docs.sh new file mode 100755 index 000000000..b68df0c4a --- /dev/null +++ b/util/generate-latest-docs.sh @@ -0,0 +1,25 @@ +# see http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ for details + +if [ "$TRAVIS_REPO_SLUG" == "google/dagger" ] && \ + [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \ + [ "$TRAVIS_PULL_REQUEST" == "false" ] && \ + [ "$TRAVIS_BRANCH" == "master" ]; then + echo -e "Publishing javadoc...\n" + mvn javadoc:aggregate -P!examples + TARGET="$(pwd)/target" + + 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 + mkdir -p api + mv ${TARGET}/site/apidocs api/latest + git add -f api/latest + git commit -m "Lastest 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" +fi diff --git a/util/mvn-deploy.sh b/util/mvn-deploy.sh new file mode 100755 index 000000000..ec4b7a0d2 --- /dev/null +++ b/util/mvn-deploy.sh @@ -0,0 +1,21 @@ +#!/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 new file mode 100755 index 000000000..be27cb6e0 --- /dev/null +++ b/util/publish-snapshot-on-commit.sh @@ -0,0 +1,12 @@ +# see https://coderwall.com/p/9b_lfq + +if [ "$TRAVIS_REPO_SLUG" == "google/dagger" ] && \ + [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \ + [ "$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 + + echo -e "Published maven snapshot" +fi diff --git a/util/settings.xml b/util/settings.xml new file mode 100644 index 000000000..91f444b22 --- /dev/null +++ b/util/settings.xml @@ -0,0 +1,9 @@ +<settings> + <servers> + <server> + <id>sonatype-nexus-snapshots</id> + <username>${env.CI_DEPLOY_USERNAME}</username> + <password>${env.CI_DEPLOY_PASSWORD}</password> + </server> + </servers> +</settings> |