Repository: square/dagger Branch: master Commit: 0fa73e0ee070 Files: 202 Total size: 601.2 KB Directory structure: gitextract_cz8_4_6g/ ├── .buildscript/ │ ├── deploy_snapshot.sh │ └── settings.xml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── checkstyle.xml ├── compiler/ │ ├── pom.xml │ └── src/ │ ├── it/ │ │ ├── default-package-injected-type/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── TestApp.java │ │ ├── extension-graph/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── TestApp.java │ │ ├── extension-graph-setvalues/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── TestApp.java │ │ ├── final-field-inject/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── include-non-module/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── TestApp.java │ │ ├── inject-on-class/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── inject-parameterized-type/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── TestApp.java │ │ ├── inner-classes-complaint-injection/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── method-injection/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── missing-at-inject-constructor/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── module-type-validation/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestModule.java │ │ │ └── verify.bsh │ │ ├── multiple-modules-setvalues/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── TestApp.java │ │ ├── multiple-provides-methods/ │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── multiple-qualifiers/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── private-inject/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ ├── TestApp.java │ │ │ │ └── TestFoo.java │ │ │ └── verify.bsh │ │ ├── provide-provider-or-lazy/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestModule.java │ │ │ └── verify.bsh │ │ ├── provides-method-not-in-module/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── provides-method-with-throws-clause/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── qualifiers-on-invalid-elements-errors/ │ │ │ ├── invoker.properties │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── qualifiers-on-invalid-elements-warnings/ │ │ │ ├── pom.xml │ │ │ ├── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── test/ │ │ │ │ └── TestApp.java │ │ │ └── verify.bsh │ │ ├── same-provides-method-name/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── TestApp.java │ │ ├── static-injected-binding-doesnt-fail-providers/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── Test.java │ │ ├── uninjectable-supertype/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── test/ │ │ │ └── TestApp.java │ │ └── valid-use-of-qualifiers/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── test/ │ │ └── TestApp.java │ ├── main/ │ │ ├── java/ │ │ │ └── dagger/ │ │ │ └── internal/ │ │ │ └── codegen/ │ │ │ ├── AdapterJavadocs.java │ │ │ ├── GeneratorKeys.java │ │ │ ├── GraphAnalysisErrorHandler.java │ │ │ ├── GraphAnalysisInjectBinding.java │ │ │ ├── GraphAnalysisLoader.java │ │ │ ├── GraphAnalysisProcessor.java │ │ │ ├── GraphAnalysisStaticInjection.java │ │ │ ├── GraphVisualizer.java │ │ │ ├── GraphVizWriter.java │ │ │ ├── InjectAdapterProcessor.java │ │ │ ├── ModuleAdapterProcessor.java │ │ │ ├── Util.java │ │ │ └── ValidationProcessor.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── javax.annotation.processing.Processor │ └── test/ │ └── java/ │ └── dagger/ │ ├── internal/ │ │ └── codegen/ │ │ ├── DotWriterTest.java │ │ ├── GraphAnalysisLoaderTest.java │ │ └── GraphVisualizerTest.java │ ├── testing/ │ │ └── it/ │ │ └── BuildLogValidator.java │ └── tests/ │ └── integration/ │ ├── ProcessorTestUtils.java │ ├── codegen/ │ │ ├── GenericInjectAdapterGenerationTest.java │ │ ├── InjectAdapterGenerationTest.java │ │ └── ModuleAdapterGenerationTest.java │ ├── operation/ │ │ ├── FailureModeErrorsTest.java │ │ ├── PrimitiveInjectionTest.java │ │ └── SimpleInjectionTest.java │ └── validation/ │ ├── CyclicDependencyTest.java │ ├── CyclicModuleIncludesTest.java │ ├── GeneratedTypesNotReadyTest.java │ ├── LibraryModuleTest.java │ ├── ScopeAnnotationUseTest.java │ └── SimpleMissingDependencyTest.java ├── core/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── dagger/ │ │ ├── Lazy.java │ │ ├── MembersInjector.java │ │ ├── Module.java │ │ ├── ObjectGraph.java │ │ ├── Provides.java │ │ └── internal/ │ │ ├── ArrayQueue.java │ │ ├── Binding.java │ │ ├── BindingsGroup.java │ │ ├── BuiltInBinding.java │ │ ├── FailoverLoader.java │ │ ├── Keys.java │ │ ├── LazyBinding.java │ │ ├── Linker.java │ │ ├── Loader.java │ │ ├── Memoizer.java │ │ ├── ModuleAdapter.java │ │ ├── Modules.java │ │ ├── ProblemDetector.java │ │ ├── ProvidesBinding.java │ │ ├── SetBinding.java │ │ ├── StaticInjection.java │ │ ├── ThrowingErrorHandler.java │ │ └── loaders/ │ │ ├── GeneratedAdapters.java │ │ ├── ReflectiveAtInjectBinding.java │ │ └── ReflectiveStaticInjection.java │ └── test/ │ └── java/ │ └── dagger/ │ ├── ExtensionTest.java │ ├── ExtensionWithSetBindingsTest.java │ ├── ExtensionWithStateTest.java │ ├── InjectStaticsTest.java │ ├── InjectionOfLazyTest.java │ ├── InjectionTest.java │ ├── LazyInjectionTest.java │ ├── MembersInjectorTest.java │ ├── ModuleTest.java │ ├── ProblemDetectorTest.java │ ├── SetBindingTest.java │ ├── ThreadSafetyTest.java │ ├── UnusedProviderTest.java │ └── internal/ │ ├── FailoverLoaderTest.java │ ├── KeysTest.java │ ├── SingletonBindingTest.java │ ├── TestingLoader.java │ └── TestingModuleAdapter.java ├── deploy_website.sh ├── examples/ │ ├── android-activity-graphs/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── dagger/ │ │ │ └── activitygraphs/ │ │ │ ├── ActivityModule.java │ │ │ ├── AndroidModule.java │ │ │ ├── DemoApplication.java │ │ │ ├── DemoBaseActivity.java │ │ │ ├── DemoBaseFragment.java │ │ │ ├── ForActivity.java │ │ │ ├── ForApplication.java │ │ │ └── ui/ │ │ │ ├── ActivityTitleController.java │ │ │ ├── HomeActivity.java │ │ │ └── HomeFragment.java │ │ └── res/ │ │ └── values/ │ │ └── strings.xml │ ├── android-simple/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── dagger/ │ │ │ └── simple/ │ │ │ ├── AndroidModule.java │ │ │ ├── DemoApplication.java │ │ │ ├── DemoBaseActivity.java │ │ │ ├── DemoModule.java │ │ │ ├── ForApplication.java │ │ │ └── ui/ │ │ │ └── HomeActivity.java │ │ └── res/ │ │ └── values/ │ │ └── strings.xml │ ├── pom.xml │ └── simple/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── coffee/ │ │ ├── CoffeeApp.java │ │ ├── CoffeeMaker.java │ │ ├── DripCoffeeModule.java │ │ ├── ElectricHeater.java │ │ ├── Heater.java │ │ ├── Pump.java │ │ ├── PumpModule.java │ │ └── Thermosiphon.java │ └── test/ │ └── java/ │ └── coffee/ │ └── CoffeeMakerTest.java ├── pom.xml └── website/ ├── index.html └── static/ ├── app-theme.css ├── app.css └── prettify.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .buildscript/deploy_snapshot.sh ================================================ #!/bin/bash # # Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo. # # Adapted from https://coderwall.com/p/9b_lfq and # http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ SLUG="square/dagger" JDK="oraclejdk7" # Dagger integration tests fail on JDK 8 :( BRANCH="master" set -e if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'." elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'." elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then echo "Skipping snapshot deployment: was pull request." elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." else echo "Deploying snapshot..." mvn clean source:jar javadoc:jar deploy --settings=".buildscript/settings.xml" -Dmaven.test.skip=true -Dinvoker.skip=true echo "Snapshot deployed!" fi ================================================ FILE: .buildscript/settings.xml ================================================ sonatype-nexus-snapshots ${env.CI_DEPLOY_USERNAME} ${env.CI_DEPLOY_PASSWORD} ================================================ FILE: .gitignore ================================================ .classpath .factorypath .project .settings eclipsebin bin gen build out lib target pom.xml.* release.properties .idea *.iml classes obj .DS_Store ================================================ FILE: .travis.yml ================================================ language: android android: components: - build-tools-20.0.0 - android-16 licenses: - android-sdk-license-5be876d5 jdk: - oraclejdk7 # - oraclejdk8 Dagger integration tests fail with JDK 8 :( install: mvn install clean --fail-never --quiet -DskipTests=true -Dinvoker.skip=true script: mvn verify after_success: - .buildscript/deploy_snapshot.sh env: global: - secure: "MhR0Wr+bSbdyO6a6CM2MVHLwmFZoi8ZWWncMCNSi8/xdRoykpC1HkwJjQK+HJv7j3VNbVTPNJ/yTpzJsL4JV9aTF/S28mka8GmHSmQSeQzTuNMqnE30GDbhS3S73azGHvC9/wjh1mAA0Gz/zUX/rzCYvDVZ/DmK1HppomN+P32A=" - secure: "fnHK/ei7tdcUDlQZcXWVPRgXoIFv6h0TWSzz4spgNtFlYqa47Qr4HQOLyEpWRqZHjgfR5eXD+CCI049Z73cg5oVOp1krV0aGX/wHc5lDMJuCrBZ/YoWZnDygiPzM4CvrpxRE7DjqzC0InVdbEbuECiFsI2WotLdQ6efgaxjpZCk=" branches: except: - gh-pages notifications: email: false sudo: false cache: directories: - $HOME/.m2 ================================================ FILE: CHANGELOG.md ================================================ Change Log ========== Version 1.2.5 *(2016-05-09)* ---------------------------- * Fix: Correctly emit generated code for binding parameterized types. Version 1.2.4 *(2016-05-03)* ---------------------------- * Fix: Restore static injection support to work correctly. Version 1.2.3 *(2016-05-02)* ---------------------------- * Fix: Correct detection of module base classes. This previously erroneously failed compilation on modules which extended from `Object` but were not detected as such. * Fix: Allow the use of dollar signs in processed class names. * Fix: Remove the need for `javac` to generate synthetic accessor methods for internal classes. * Fix: Error when duplicate classes are listed in `injects=` or `includes=` lists. Version 1.2.2 *(2014-07-21)* ---------------------------- * Update JavaWriter to 2.5.0. This fixes incorrectly compressing fully-qualified class names in child packages of `java.lang` (e.g., `java.lang.ref.WeakReference`). Version 1.2.1 *(2014-02-16)* ---------------------------- * Restore Java 5 compatibility. * New: Improve performance of `.plus()` with large volumes of set bindings. * Fix: Do not mask underlying exception message from binding problems when constructing a graph. 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. ================================================ FILE: CONTRIBUTING.md ================================================ 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 `/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 ================================================ FILE: LICENSE.txt ================================================ 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. ================================================ FILE: README.md ================================================ Dagger 1 ======== A fast dependency injector for Android and Java. Deprecated – Please upgrade to Dagger 2 --------------------------------------- Square's Dagger 1.x is deprecated in favor of [Google's Dagger 2](https://github.com/google/dagger). Please see [the migration guide](https://google.github.io/dagger/dagger-1-migration.html) for help with the upgrade. Download Dagger 1 ----------------- You will need to include the `dagger-${dagger.version}.jar` in your application's runtime. In order to activate code generation you will need to include `dagger-compiler-${dagger.version}.jar` in your build at compile time. In a Maven project, one would include the runtime in the dependencies section of your `pom.xml` (replacing `${dagger.version}` with the appropriate current release), and the `dagger-compiler` artifact as an "optional" or "provided" dependency: ```xml com.squareup.dagger dagger ${dagger.version} com.squareup.dagger dagger-compiler ${dagger.version} true ``` You can also find downloadable .jars on Maven Central. You'll need [Dagger][dl-dagger], [JavaPoet][dl-javapoet], and [javax.inject][dl-inject]. Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. License ------- Copyright 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. [1]: http://square.github.com/dagger/ [dl-dagger]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.squareup.dagger%22%20a%3A%22dagger%22 [dl-javapoet]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.squareup%22%20a%3A%22javapoet%22 [dl-inject]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.inject%22%20a%3A%22javax.inject%22 [snap]: https://oss.sonatype.org/content/repositories/snapshots/ ================================================ FILE: checkstyle.xml ================================================ ================================================ FILE: compiler/pom.xml ================================================ 4.0.0 com.squareup.dagger dagger-parent 1.2.6-SNAPSHOT ../pom.xml dagger-compiler Dagger Compiler Tools to generate Dagger injection and module adapters from annotated code and validate them. ${project.groupId} dagger ${project.version} com.squareup javapoet com.google.guava guava junit junit test org.mockito mockito-core test ${project.groupId} dagger ${project.version} test tests com.google.testing.compile compile-testing test com.google.truth truth test org.apache.maven.plugins maven-compiler-plugin default-compile compile -proc:none default-test-compile testCompile dagger.internal.codegen.ValidationProcessor dagger.internal.codegen.InjectAdapterProcessor dagger.internal.codegen.ModuleAdapterProcessor dagger.internal.codegen.GraphAnalysisProcessor org.apache.maven.plugins maven-invoker-plugin true ${project.build.directory}/it */pom.xml ${project.build.directory}/local-repo verify ${project.version} ${project.groupId} integration-test install run org.apache.maven.plugins maven-assembly-plugin jar-with-dependencies package single ================================================ FILE: compiler/src/it/default-package-injected-type/pom.xml ================================================ 4.0.0 com.example.dagger.tests default-package-injected-type HEAD-SNAPSHOT Default Package Injected Type @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/default-package-injected-type/src/main/java/TestApp.java ================================================ /* * 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. */ import dagger.ObjectGraph; import dagger.Module; import javax.inject.Inject; import javax.inject.Singleton; class TestApp implements Runnable { @Inject A a; @Override public void run() { a.doit(); } public static void main(String[] args) { ObjectGraph.create(new TestModule()).get(TestApp.class).run(); } @Module(injects = { TestApp.class }) static class TestModule {} @Singleton static class A { @Inject A() {} public void doit() {}; } } ================================================ FILE: compiler/src/it/extension-graph/pom.xml ================================================ 4.0.0 com.example.dagger.tests extension-graph HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/extension-graph/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.ObjectGraph; import dagger.Module; import javax.inject.Inject; import javax.inject.Singleton; class TestApp implements Runnable { @Inject C c; @Override public void run() { c.doit(); } public static void main(String[] args) { ObjectGraph root = ObjectGraph.create(new RootModule()); ObjectGraph extension = root.plus(new ExtensionModule()); extension.get(TestApp.class).run(); } @Module(injects = { A.class, B.class }) static class RootModule { } @Module(addsTo=RootModule.class, injects = { C.class, TestApp.class }) static class ExtensionModule { } @Singleton static class A { @Inject A() {} } static class B { @Inject A a; @Inject B() {} } static class C { @Inject A a; @Inject B b; @Inject C() {} public void doit() {}; } } ================================================ FILE: compiler/src/it/extension-graph-setvalues/pom.xml ================================================ 4.0.0 com.example.dagger.tests extension-graph-setvalues HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/extension-graph-setvalues/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.ObjectGraph; import dagger.Module; import dagger.Provides; import java.util.HashSet; import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; import static dagger.Provides.Type.SET; import static dagger.Provides.Type.SET_VALUES; /** * Contributions to {@code SET_VALUES} binding do not affect Set of providers. */ class TestApp implements Runnable { @Inject Set> providers; @Inject Set strings; @Override public void run() { System.out.println(strings); } public static void main(String[] args) { ObjectGraph root = ObjectGraph.create(new RootModule()); ObjectGraph extension = root.plus(new ExtensionModule()); extension.get(TestApp.class).run(); } @Module(injects = TestApp.class) static class RootModule { @Provides Set> providers() { return new HashSet>(); } @Provides(type = SET_VALUES) Set strings() { return new HashSet(); } } @Module(addsTo = RootModule.class, injects = TestApp.class) static class ExtensionModule { @Provides(type = SET) String addToSet() { return "contributed"; } } } ================================================ FILE: compiler/src/it/final-field-inject/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/final-field-inject/pom.xml ================================================ 4.0.0 com.example.dagger.tests final-field-inject HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/final-field-inject/src/main/java/test/TestApp.java ================================================ /* * 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 test; import javax.inject.Inject; class TestApp { @Inject final Object nope; } ================================================ FILE: compiler/src/it/final-field-inject/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Can't inject a final field: test.TestApp.nope"}); ================================================ FILE: compiler/src/it/include-non-module/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/include-non-module/pom.xml ================================================ 4.0.0 com.example.dagger.tests include-non-module HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/include-non-module/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; import java.lang.String; class TestApp { public static void main(String[] args) { TestApp app = ObjectGraph.create(new TestModule()).get(TestApp.class); } @Inject String s; @Module( injects = TestApp.class, includes = TestApp.class) static class TestModule { @Provides String provideString() { return "a"; } } } ================================================ FILE: compiler/src/it/inject-on-class/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/inject-on-class/pom.xml ================================================ 4.0.0 com.example.dagger.tests method-injection HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/inject-on-class/src/main/java/test/TestApp.java ================================================ /* * 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 test; import javax.inject.Inject; @Inject class TestApp { } ================================================ FILE: compiler/src/it/inject-on-class/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "@Inject is not valid on a class: test.TestApp"}); ================================================ FILE: compiler/src/it/inject-parameterized-type/pom.xml ================================================ 4.0.0 com.example.dagger.tests inject-parameterized-type HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/inject-parameterized-type/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; class TestApp { public static void main(String[] args) { Subtype subtype = ObjectGraph.create(new TestModule()).get(Subtype.class); } static class Supertype { @Inject String s; } static class Subtype extends Supertype { } @Module(injects = Subtype.class) static class TestModule { @Provides String provideString() { return "a"; } } } ================================================ FILE: compiler/src/it/inner-classes-complaint-injection/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/inner-classes-complaint-injection/pom.xml ================================================ 4.0.0 com.example.dagger.tests inner-classes-complaint-injection HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/inner-classes-complaint-injection/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; import java.lang.Override; class TestApp { class Foo { @Inject public Foo() {} } } ================================================ FILE: compiler/src/it/inner-classes-complaint-injection/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Can't inject a non-static inner class: test.TestApp.Foo"}); ================================================ FILE: compiler/src/it/method-injection/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/method-injection/pom.xml ================================================ 4.0.0 com.example.dagger.tests method-injection HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/method-injection/src/main/java/test/TestApp.java ================================================ /* * 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 test; import javax.inject.Inject; class TestApp { @Inject public void doThings(Object things) { } } ================================================ FILE: compiler/src/it/method-injection/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Method injection is not supported: test.TestApp.doThings"}); ================================================ FILE: compiler/src/it/missing-at-inject-constructor/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/missing-at-inject-constructor/pom.xml ================================================ 4.0.0 com.example.dagger.tests missing-at-inject-constructor HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/missing-at-inject-constructor/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.ObjectGraph; import javax.inject.Inject; class TestApp implements Runnable { @Inject Dependency dep; @Override public void run() { dep.doit(); } public static void main(String[] args) { ObjectGraph.create(new TestModule()).get(TestApp.class).run(); } static class Dependency { // missing @Inject Dependency() {} public void doit() { throw AssertionError(); }; } @Module(injects = TestApp.class) static class TestModule { /* missing */ // @Provides Dependency a() { return new Dependency(); } } } ================================================ FILE: compiler/src/it/missing-at-inject-constructor/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "No injectable members on test.TestApp.Dependency.", "required by test.TestApp for test.TestApp.TestModule"}); ================================================ FILE: compiler/src/it/module-type-validation/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/module-type-validation/pom.xml ================================================ 4.0.0 com.example.dagger.tests module-type-validation HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/module-type-validation/src/main/java/test/TestModule.java ================================================ /* * 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 test; import dagger.Module; @Module class ThreadModule extends Thread { } @Module enum EnumModule { } @Module interface InterfaceModule { } ================================================ FILE: compiler/src/it/module-type-validation/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Modules must not extend from other classes: test.ThreadModule"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Modules must be classes: test.EnumModule"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Modules must be classes: test.InterfaceModule"}); ================================================ FILE: compiler/src/it/multiple-modules-setvalues/pom.xml ================================================ 4.0.0 com.example.dagger.tests multiple-modules-setvalues HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/multiple-modules-setvalues/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.ObjectGraph; import dagger.Module; import dagger.Provides; import java.util.HashSet; import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; import static dagger.Provides.Type.SET; import static dagger.Provides.Type.SET_VALUES; /** * Contributions to {@code SET_VALUES} binding do not affect Set of providers. */ class TestApp implements Runnable { @Inject Set> providers; @Inject Set strings; @Override public void run() { System.out.println(strings); } public static void main(String[] args) { ObjectGraph root = ObjectGraph.create(new RootModule(), new ContributingModule()); root.get(TestApp.class).run(); } @Module(injects = TestApp.class) static class RootModule { @Provides Set> providers() { return new HashSet>(); } @Provides(type = SET_VALUES) Set strings() { return new HashSet(); } } @Module(injects = TestApp.class, complete = false) static class ContributingModule { @Provides(type = SET) String addToSet() { return "contributed"; } } } ================================================ FILE: compiler/src/it/multiple-provides-methods/pom.xml ================================================ 4.0.0 com.example.dagger.tests multiple-provides-methods HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/multiple-provides-methods/src/main/java/test/TestApp.java ================================================ /* * Copyright (C) 2013 Square, Inc. * Copyright (C) 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR 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 javax.inject.Inject; class TestApp { static class NotInjectable { } static class InjectableSubclass extends NotInjectable { @Inject String string; @Inject Integer integer; } @Module(injects = InjectableSubclass.class) static class TestModule { @Provides String string() { return "string"; } @Provides Integer integer() { return 5; } } } ================================================ FILE: compiler/src/it/multiple-provides-methods/verify.bsh ================================================ import java.io.File; File classes = new File(basedir, "target/classes/test/"); File moduleAdapter = new File(classes, "TestApp$TestModule$$ModuleAdapter.class"); if (!moduleAdapter.exists()) throw new Exception("No binding generated for module"); File integerBinding = new File(classes, "TestApp$TestModule$$ModuleAdapter$IntegerProvidesAdapter.class"); if (!integerBinding.exists()) throw new Exception("No binding generated for integer()"); File stringBinding = new File(classes, "TestApp$TestModule$$ModuleAdapter$StringProvidesAdapter.class"); if (!stringBinding.exists()) throw new Exception("No binding generated for string()"); ================================================ FILE: compiler/src/it/multiple-qualifiers/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/multiple-qualifiers/pom.xml ================================================ 4.0.0 com.example.dagger.tests multiple-qualifiers HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/multiple-qualifiers/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.Provides; import java.lang.annotation.Retention; import javax.inject.Inject; import javax.inject.Singleton; import javax.inject.Qualifier; import static java.lang.annotation.RetentionPolicy.RUNTIME; class TestApp { static class TestClass1 { @Inject @MyQualifier1 @MyQualifier2 String field; } static class TestClass2 { String string; public TestClass2(@MyQualifier1 @MyQualifier2 String constructorParam) { this.string = string; } } @Module(injects = TestClass1.class) static class TestModule { @MyQualifier1 @MyQualifier2 @Provides String providesString() { return "string"; } } @Qualifier @Retention(value = RUNTIME) @interface MyQualifier1 {} @Qualifier @Retention(value = RUNTIME) @interface MyQualifier2 {} } ================================================ FILE: compiler/src/it/multiple-qualifiers/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Only one qualifier annotation is allowed per element: test.TestApp.TestClass1.field"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Only one qualifier annotation is allowed per element: constructorParam"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Only one qualifier annotation is allowed per element: test.TestApp.TestModule.providesString()"}); ================================================ FILE: compiler/src/it/private-inject/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/private-inject/pom.xml ================================================ 4.0.0 com.example.dagger.tests private-inject HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/private-inject/src/main/java/test/TestApp.java ================================================ /* * 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 test; import javax.inject.Inject; class TestApp { @Inject private Object nope; } ================================================ FILE: compiler/src/it/private-inject/src/main/java/test/TestFoo.java ================================================ /* * 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 test; import javax.inject.Inject; class TestFoo { @Inject private TestFoo() { } } ================================================ FILE: compiler/src/it/private-inject/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Can't inject a private field: test.TestApp.nope"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Can't inject a private constructor: test.TestFoo.TestFoo()"}); ================================================ FILE: compiler/src/it/provide-provider-or-lazy/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/provide-provider-or-lazy/pom.xml ================================================ 4.0.0 com.example.dagger.tests provide-provider-or-lazy HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/provide-provider-or-lazy/src/main/java/test/TestModule.java ================================================ /* * 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 test; import dagger.Lazy; import dagger.Module; import dagger.Provides; import javax.inject.Provider; @Module class TestModule { @Provides Provider provideProvider() { return null; } @Provides Provider provideRawProvider() { return null; } @Provides Lazy provideLazy() { return null; } @Provides Lazy provideRawLazy() { return null; } } ================================================ FILE: compiler/src/it/provide-provider-or-lazy/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "@Provides method must not return javax.inject.Provider directly: test.TestModule.provideProvider"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "@Provides method must not return javax.inject.Provider directly: test.TestModule.provideRawProvider"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "@Provides method must not return dagger.Lazy directly: test.TestModule.provideLazy"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "@Provides method must not return dagger.Lazy directly: test.TestModule.provideRawLazy"}); ================================================ FILE: compiler/src/it/provides-method-not-in-module/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/provides-method-not-in-module/pom.xml ================================================ 4.0.0 com.example.dagger.tests provides-method-not-in-module HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/provides-method-not-in-module/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Provides; class TestApp { @Provides String providesString() { return "string"; } } ================================================ FILE: compiler/src/it/provides-method-not-in-module/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "@Provides methods must be declared in modules: test.TestApp.providesString()"}); ================================================ FILE: compiler/src/it/provides-method-with-throws-clause/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/provides-method-with-throws-clause/pom.xml ================================================ 4.0.0 com.example.dagger.tests provides-method-with-throws-clause HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/provides-method-with-throws-clause/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; import java.io.IOException; import java.lang.Override; class TestApp implements Runnable { @Inject String string; @Override public void run() { // Yay! \o/ } public static void main(String[] args) { ObjectGraph.create(new TestModule()).get(TestApp.class).run(); } @Module(injects = TestApp.class) static class TestModule { @Provides String string() throws IOException { return "string"; } } } ================================================ FILE: compiler/src/it/provides-method-with-throws-clause/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "@Provides methods must not have a throws clause"}); ================================================ FILE: compiler/src/it/qualifiers-on-invalid-elements-errors/invoker.properties ================================================ invoker.buildResult=failure ================================================ FILE: compiler/src/it/qualifiers-on-invalid-elements-errors/pom.xml ================================================ 4.0.0 com.example.dagger.tests qualifiers-on-invalid-elements-errors HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 true ================================================ FILE: compiler/src/it/qualifiers-on-invalid-elements-errors/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.Provides; import java.lang.annotation.Retention; import javax.inject.Inject; import javax.inject.Qualifier; import static java.lang.annotation.RetentionPolicy.RUNTIME; class TestApp { @MyQualifier static class TestClass1 { @MyQualifier // qualifier on non-injectable constructor public TestClass1(String constructorParam) {} } static class TestClass2 { String string; @Inject @MyQualifier // qualifier on injectable constructor public TestClass2(String injectableConstructorParam) { this.string = string; } } @Qualifier @Retention(value = RUNTIME) @interface MyQualifier {} } ================================================ FILE: compiler/src/it/qualifiers-on-invalid-elements-errors/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Qualifier annotations are only allowed on fields, methods, and parameters: test.TestApp.TestClass1"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Qualifier annotations are only allowed on fields, methods, and parameters: test.TestApp.TestClass1.TestClass1(java.lang.String)"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Qualifier annotations are only allowed on fields, methods, and parameters: test.TestApp.TestClass2.TestClass2(java.lang.String)"}); ================================================ FILE: compiler/src/it/qualifiers-on-invalid-elements-warnings/pom.xml ================================================ 4.0.0 com.example.dagger.tests qualifiers-on-invalid-elements-warnings HEAD-SNAPSHOT @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 true ================================================ FILE: compiler/src/it/qualifiers-on-invalid-elements-warnings/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.Provides; import java.lang.annotation.Retention; import javax.inject.Inject; import javax.inject.Qualifier; import static java.lang.annotation.RetentionPolicy.RUNTIME; class TestApp { static class TestClass { @MyQualifier int field1; // non-injectable field @SuppressWarnings("some string other than 'qualifiers'") @MyQualifier int field2; @SuppressWarnings("qualifiers") @MyQualifier int fieldWithWarningSuppressed1; @SuppressWarnings({"foo", "qualifiers", "bar"}) @MyQualifier int fieldWithWarningSuppressed2; // qualifier on non-injectable constructor parameter public TestClass(@MyQualifier String constructorParam) {} @MyQualifier void nonProvidesMethod(@MyQualifier String methodParam) {} } @Qualifier @Retention(value = RUNTIME) @interface MyQualifier {} } ================================================ FILE: compiler/src/it/qualifiers-on-invalid-elements-warnings/verify.bsh ================================================ import dagger.testing.it.BuildLogValidator; import java.io.File; File buildLog = new File(basedir, "build.log"); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Dagger will ignore qualifier annotations on fields that are not annotated with @Inject: test.TestApp.TestClass.field1"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Dagger will ignore qualifier annotations on fields that are not annotated with @Inject: test.TestApp.TestClass.field2"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Dagger will ignore qualifier annotations on methods that are not @Provides methods: test.TestApp.TestClass.nonProvidesMethod(java.lang.String)"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Dagger will ignore qualifier annotations on parameters that are not @Inject constructor parameters or @Provides method parameters: methodParam"}); new BuildLogValidator().assertHasText(buildLog, new String[]{ "Dagger will ignore qualifier annotations on parameters that are not @Inject constructor parameters or @Provides method parameters: constructorParam"}); new BuildLogValidator().assertDoesNotHaveText(buildLog, new String[]{ "Dagger will ignore qualifier annotations on fields that are not annotated with @Inject: test.TestApp.TestClass.fieldWithWarningSuppressed1"}); new BuildLogValidator().assertDoesNotHaveText(buildLog, new String[]{ "Dagger will ignore qualifier annotations on fields that are not annotated with @Inject: test.TestApp.TestClass.fieldWithWarningSuppressed2"}); ================================================ FILE: compiler/src/it/same-provides-method-name/pom.xml ================================================ 4.0.0 com.example.dagger.tests same-provides-method-name HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/same-provides-method-name/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; import java.lang.Override; class TestApp implements Runnable { @Inject Foo foo; @Override public void run() { // Yay! \o/ } public static void main(String[] args) { ObjectGraph.create(new TestModule()).get(TestApp.class).run(); } static class Foo { } static class MyFoo extends Foo { } @Module(injects = TestApp.class) static class TestModule { @Provides Foo providesFoo(MyFoo foo) { return foo; } @Provides MyFoo providesFoo() { return new MyFoo(); } } } ================================================ FILE: compiler/src/it/static-injected-binding-doesnt-fail-providers/pom.xml ================================================ 4.0.0 com.squareup.dagger.tests static-injected-binding-doesnt-fail-providers @dagger.version@ jar Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/static-injected-binding-doesnt-fail-providers/src/main/java/test/Test.java ================================================ /* * 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 test; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; import java.lang.Override; public class Test { public static class InjectsOneField { @Inject static String staticallyInjectedString; } @Module(staticInjections = { InjectsOneField.class }) public static class TestModule { @Provides String string() { return "string"; } } } ================================================ FILE: compiler/src/it/uninjectable-supertype/pom.xml ================================================ 4.0.0 com.example.dagger.tests uninjectable-supertype HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 ================================================ FILE: compiler/src/it/uninjectable-supertype/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.Provides; import javax.inject.Inject; class TestApp { static class NotInjectable { } static class InjectableSubclass extends NotInjectable { @Inject String string; } @Module(injects = InjectableSubclass.class) static class TestModule { @Provides String provideString() { return "string"; } } } ================================================ FILE: compiler/src/it/valid-use-of-qualifiers/pom.xml ================================================ 4.0.0 com.example.dagger.tests valid-use-ofqualifiers HEAD-SNAPSHOT Dagger Integration Test Basic @dagger.groupId@ dagger @dagger.version@ @dagger.groupId@ dagger-compiler @dagger.version@ true maven-compiler-plugin 3.1 1.5 1.5 true ================================================ FILE: compiler/src/it/valid-use-of-qualifiers/src/main/java/test/TestApp.java ================================================ /* * 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 test; import dagger.Module; import dagger.Provides; import java.lang.annotation.Retention; import javax.inject.Inject; import javax.inject.Qualifier; import static java.lang.annotation.RetentionPolicy.RUNTIME; class TestApp { static class TestClass1 { @Inject @MyQualifier1 String field; } static class TestClass2 { String string; @Inject public TestClass2(@MyQualifier1 String constructorParam) { this.string = string; } } @Module(injects = TestClass1.class) static class TestModule { @Provides @MyQualifier1 String providesString(@MyQualifier2 String providesMethodParam) { return providesMethodParam + "foo"; } @Provides @MyQualifier2 String providesString() { return "foo"; } } @Qualifier @Retention(value = RUNTIME) @interface MyQualifier1 {} @Qualifier @Retention(value = RUNTIME) @interface MyQualifier2 {} } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/AdapterJavadocs.java ================================================ /* * 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.internal.codegen; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.TypeName; /** * Utility class providing some commonly used boilerplate between {@code InjectAdapterProcessor} * and {@code ModuleAdapterProcessor}. */ public final class AdapterJavadocs { static final String GENERATED_BY_DAGGER = "Code generated by dagger-compiler. Do not edit."; static final String MEMBERS_INJECT_METHOD = "" + "Injects any {@code @Inject} annotated fields in the given instance,\n" + "satisfying the contract for {@code Provider<$T>}.\n"; static final String GET_METHOD = "" + "Returns the fully provisioned instance satisfying the contract for\n" + "{@code Provider<$T>}.\n"; static final String GET_DEPENDENCIES_METHOD = "" + "Used internally obtain dependency information, such as for cyclical\n" + "graph detection.\n"; static final String ATTACH_METHOD = "" + "Used internally to link bindings/providers together at run time\n" + "according to their dependency graph.\n"; static final String STATIC_INJECT_METHOD = "" + "Performs the injections of dependencies into static fields when requested by\n" + "the {@code $T}.\n"; static final String MODULE_TYPE = "" + "A manager of modules and provides adapters allowing for proper linking and\n" + "instance provision of types served by {@code @$T} methods.\n"; static final String STATIC_INJECTION_TYPE = "" + "A manager for {@code $T}'s injections into static fields.\n"; /** Creates an appropriate javadoc depending on aspects of the type in question. */ static CodeBlock bindingTypeDocs( TypeName type, boolean abstrakt, boolean members, boolean dependent) { CodeBlock.Builder result = CodeBlock.builder() .add("A {@code Binding<$T>} implementation which satisfies\n", type) .add("Dagger's infrastructure requirements including:\n"); if (dependent) { result.add("\n") .add("Owning the dependency links between {@code $T} and its\n", type) .add("dependencies.\n"); } if (!abstrakt) { result.add("\n") .add("Being a {@code Provider<$T>} and handling creation and\n", type) .add("preparation of object instances.\n"); } if (members) { result.add("\n") .add("Being a {@code MembersInjector<$T>} and handling injection\n", type) .add("of annotated fields.\n"); } return result.build(); } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GeneratorKeys.java ================================================ /* * 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.internal.codegen; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Qualifier; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import static dagger.internal.codegen.Util.rawTypeToString; import static dagger.internal.codegen.Util.typeToString; /** * Creates keys using javac's mirror APIs. Unlike {@code Keys}, this class uses * APIs not available on Android. */ final class GeneratorKeys { private static final String SET_PREFIX = Set.class.getCanonicalName() + "<"; private GeneratorKeys() { } /** * Returns the members injector key for the raw type of {@code type}. * Parameterized types are not currently supported for members injection in * generated code. */ public static String rawMembersKey(TypeMirror type) { return "members/" + rawTypeToString(type, '$'); } /** Returns the provider key for {@code type}. */ public static String get(TypeMirror type) { StringBuilder result = new StringBuilder(); typeToString(type, result, '$'); return result.toString(); } /** Returns the provided key for {@code method}. */ public static String get(ExecutableElement method) { StringBuilder result = new StringBuilder(); AnnotationMirror qualifier = getQualifier(method.getAnnotationMirrors()); if (qualifier != null) { qualifierToString(qualifier, result); } typeToString(method.getReturnType(), result, '$'); return result.toString(); } /** Returns the provided key for {@code method} wrapped by {@code Set}. */ public static String getSetKey(ExecutableElement method) { StringBuilder result = new StringBuilder(); AnnotationMirror qualifier = getQualifier(method.getAnnotationMirrors()); if (qualifier != null) { qualifierToString(qualifier, result); } result.append(SET_PREFIX); typeToString(method.getReturnType(), result, '$'); result.append(">"); return result.toString(); } /** Returns the provider key for {@code variable}. */ public static String get(VariableElement variable) { StringBuilder result = new StringBuilder(); AnnotationMirror qualifier = getQualifier(variable.getAnnotationMirrors()); if (qualifier != null) { qualifierToString(qualifier, result); } typeToString(variable.asType(), result, '$'); return result.toString(); } private static void qualifierToString(AnnotationMirror qualifier, StringBuilder result) { // TODO: guarantee that element values are sorted by name (if there are multiple) result.append('@'); typeToString(qualifier.getAnnotationType(), result, '$'); result.append('('); for (Map.Entry entry : qualifier.getElementValues().entrySet()) { result.append(entry.getKey().getSimpleName()); result.append('='); result.append(entry.getValue().getValue()); } result.append(")/"); } /** Does not test for multiple qualifiers. This is tested in {@code ValidationProcessor}. */ private static AnnotationMirror getQualifier( List annotations) { AnnotationMirror qualifier = null; for (AnnotationMirror annotation : annotations) { if (annotation.getAnnotationType().asElement().getAnnotation(Qualifier.class) == null) { continue; } qualifier = annotation; } return qualifier; } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GraphAnalysisErrorHandler.java ================================================ /* * 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.internal.codegen; import dagger.internal.Linker; import java.util.List; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; /** * A {@code Linker.ErrorHandler} which gathers errors and reports them via a processing * environment. */ final class GraphAnalysisErrorHandler implements Linker.ErrorHandler { private final ProcessingEnvironment processingEnv; private final String moduleName; GraphAnalysisErrorHandler(ProcessingEnvironment processingEnv, String moduleName) { this.processingEnv = processingEnv; this.moduleName = moduleName; } @Override public void handleErrors(List errors) { TypeElement module = processingEnv.getElementUtils().getTypeElement(moduleName); for (String error : errors) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, error + " for " + moduleName, module); } } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GraphAnalysisInjectBinding.java ================================================ /* * 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.internal.codegen; import dagger.internal.Binding; import dagger.internal.Linker; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import static dagger.internal.codegen.Util.getApplicationSupertype; /** * A build time binding that injects the constructor and fields of a class. */ final class GraphAnalysisInjectBinding extends Binding { private final TypeElement type; private final List keys; private final Binding[] bindings; private final String supertypeKey; private GraphAnalysisInjectBinding(String provideKey, String membersKey, TypeElement type, List keys, String supertypeKey) { super(provideKey, membersKey, type.getAnnotation(Singleton.class) != null, type.getQualifiedName().toString()); this.type = type; this.keys = keys; this.bindings = new Binding[keys.size()]; this.supertypeKey = supertypeKey; } static GraphAnalysisInjectBinding create(TypeElement type, boolean mustHaveInjections) { List requiredKeys = new ArrayList(); boolean hasInjectConstructor = false; boolean hasNoArgsConstructor = false; for (Element enclosed : type.getEnclosedElements()) { switch (enclosed.getKind()) { case FIELD: if (hasAtInject(enclosed) && !enclosed.getModifiers().contains(Modifier.STATIC)) { // Attach the non-static fields of 'type'. requiredKeys.add(GeneratorKeys.get((VariableElement) enclosed)); } break; case CONSTRUCTOR: ExecutableElement constructor = (ExecutableElement) enclosed; List parameters = constructor.getParameters(); if (hasAtInject(enclosed)) { if (hasAtSingleton(enclosed)) { throw new IllegalArgumentException("Singleton annotations have no effect on " + "constructors. Did you mean to annotate the class? " + type.getQualifiedName().toString()); } if (hasInjectConstructor) { throw new IllegalArgumentException("Too many injectable constructors on " + type.getQualifiedName().toString()); } hasInjectConstructor = true; for (VariableElement parameter : parameters) { requiredKeys.add(GeneratorKeys.get(parameter)); } } else if (parameters.isEmpty()) { hasNoArgsConstructor = true; } break; default: if (hasAtInject(enclosed)) { throw new IllegalArgumentException("Unexpected @Inject annotation on " + enclosed); } } } if (!hasInjectConstructor && requiredKeys.isEmpty() && mustHaveInjections) { throw new IllegalArgumentException("No injectable members on " + type.getQualifiedName().toString() + ". Do you want to add an injectable constructor?"); } // Attach the supertype. TypeMirror supertype = getApplicationSupertype(type); String supertypeKey = supertype != null ? GeneratorKeys.rawMembersKey(supertype) : null; String provideKey = hasInjectConstructor || (hasNoArgsConstructor && !requiredKeys.isEmpty()) ? GeneratorKeys.get(type.asType()) : null; String membersKey = GeneratorKeys.rawMembersKey(type.asType()); return new GraphAnalysisInjectBinding(provideKey, membersKey, type, requiredKeys, supertypeKey); } private static boolean hasAtInject(Element enclosed) { return enclosed.getAnnotation(Inject.class) != null; } private static boolean hasAtSingleton(Element enclosed) { return enclosed.getAnnotation(Singleton.class) != null; } @Override public void attach(Linker linker) { String requiredBy = type.getQualifiedName().toString(); for (int i = 0; i < keys.size(); i++) { bindings[i] = linker.requestBinding(keys.get(i), requiredBy, getClass().getClassLoader()); } if (supertypeKey != null) { // Force the binding lookup. linker.requestBinding(supertypeKey, requiredBy, getClass().getClassLoader(), false, true); } } @Override public Object get() { throw new AssertionError("Compile-time binding should never be called to inject."); } @Override public void injectMembers(Object t) { throw new AssertionError("Compile-time binding should never be called to inject."); } @Override public void getDependencies(Set> get, Set> injectMembers) { Collections.addAll(get, bindings); } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GraphAnalysisLoader.java ================================================ /* * 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.internal.codegen; import com.google.common.annotations.VisibleForTesting; import dagger.internal.Binding; import dagger.internal.Loader; import dagger.internal.ModuleAdapter; import dagger.internal.StaticInjection; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; /** * A {@code Binding.Resolver} suitable for tool use at build time. The bindings created by * this {@code Binding.Resolver} have the correct dependency graph, but do not implement * {@link Binding#get} or {@link Binding#injectMembers} methods. They are only suitable * for graph analysis and error detection. */ public final class GraphAnalysisLoader extends Loader { private final ProcessingEnvironment processingEnv; public GraphAnalysisLoader(ProcessingEnvironment processingEnv) { this.processingEnv = processingEnv; } @Override public Binding getAtInjectBinding( String key, String className, ClassLoader classLoader, boolean mustHaveInjections) { TypeElement type = resolveType(processingEnv.getElementUtils(), className); if (type == null) { // We've encountered a type that the compiler can't introspect. If this // causes problems in practice (due to incremental compiles, etc.) we // should return a new unresolved binding and warn about the possibility // of runtime failures. return null; } if (type.getKind() == ElementKind.INTERFACE) { return null; } return GraphAnalysisInjectBinding.create(type, mustHaveInjections); } /** * Resolves the given class name into a {@link TypeElement}. The class name is a binary name, but * {@link Elements#getTypeElement(CharSequence)} wants a canonical name. So this method searches * the space of possible canonical names, starting with the most likely (since '$' is rarely used * in canonical class names). */ @VisibleForTesting static TypeElement resolveType(Elements elements, String className) { int index = nextDollar(className, className, 0); if (index == -1) { return getTypeElement(elements, className); } // have to test various possibilities of replacing '$' with '.' since '.' in a canonical name // of a nested type is replaced with '$' in the binary name. StringBuilder sb = new StringBuilder(className); return resolveType(elements, className, sb, index); } /** * Recursively explores the space of possible canonical names for a given binary class name. * * @param elements used to resolve a name into a {@link TypeElement} * @param className binary class name * @param sb the current permutation of canonical name to attempt to resolve * @param index the index of a {@code '$'} which may be changed to {@code '.'} in a canonical name */ private static TypeElement resolveType(Elements elements, String className, StringBuilder sb, final int index) { // We assume '$' should be converted to '.'. So we search for classes with dots first. sb.setCharAt(index, '.'); int nextIndex = nextDollar(className, sb, index + 1); TypeElement type = nextIndex == -1 ? getTypeElement(elements, sb) : resolveType(elements, className, sb, nextIndex); if (type != null) { return type; } // if not found, change back to dollar and search. sb.setCharAt(index, '$'); nextIndex = nextDollar(className, sb, index + 1); return nextIndex == -1 ? getTypeElement(elements, sb) : resolveType(elements, className, sb, nextIndex); } /** * Finds the next {@code '$'} in a class name which can be changed to a {@code '.'} when computing * a canonical class name. */ private static int nextDollar(String className, CharSequence current, int searchStart) { while (true) { int index = className.indexOf('$', searchStart); if (index == -1) { return -1; } // We'll never have two dots nor will a type name end or begin with dot. So no need to // consider dollars at the beginning, end, or adjacent to dots. if (index == 0 || index == className.length() - 1 || current.charAt(index - 1) == '.' || current.charAt(index + 1) == '.') { searchStart = index + 1; continue; } return index; } } private static TypeElement getTypeElement(Elements elements, CharSequence className) { try { return elements.getTypeElement(className); } catch (ClassCastException e) { // work-around issue in javac in Java 7 where querying for non-existent type can // throw a ClassCastException // TODO(jh): refer to Oracle Bug ID if/when one is assigned to bug report // (Review ID: JI-9027367) return null; } } @Override public ModuleAdapter getModuleAdapter(Class moduleClass) { throw new UnsupportedOperationException(); } @Override public StaticInjection getStaticInjection(Class injectedClass) { throw new UnsupportedOperationException(); } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GraphAnalysisProcessor.java ================================================ /* * 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.internal.codegen; import dagger.Module; import dagger.Provides; import dagger.internal.Binding; import dagger.internal.Binding.InvalidBindingException; import dagger.internal.BindingsGroup; import dagger.internal.Linker; import dagger.internal.ProblemDetector; import dagger.internal.ProvidesBinding; import dagger.internal.SetBinding; import dagger.internal.codegen.Util.CodeGenerationIncompleteException; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.inject.Singleton; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; 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.FileObject; import javax.tools.JavaFileManager; import javax.tools.StandardLocation; import static dagger.Provides.Type.SET; import static dagger.Provides.Type.SET_VALUES; import static dagger.internal.codegen.Util.className; import static dagger.internal.codegen.Util.getAnnotation; import static dagger.internal.codegen.Util.getPackage; import static dagger.internal.codegen.Util.isInterface; import static java.util.Arrays.asList; /** * Performs full graph analysis on a module. */ @SupportedAnnotationTypes("dagger.Module") public final class GraphAnalysisProcessor extends AbstractProcessor { private static final Set ERROR_NAMES_TO_PROPAGATE = new LinkedHashSet(asList( "com.sun.tools.javac.code.Symbol$CompletionFailure")); private final Set delayedModuleNames = new LinkedHashSet(); @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } /** * Perform full-graph analysis on complete modules. This checks that all of * the module's dependencies are satisfied. */ @Override public boolean process(Set types, RoundEnvironment env) { if (!env.processingOver()) { // Storing module names for later retrieval as the element instance is invalidated across // passes. for (Element e : env.getElementsAnnotatedWith(Module.class)) { if (!(e instanceof TypeElement)) { error("@Module applies to a type, " + e.getSimpleName() + " is a " + e.getKind(), e); continue; } delayedModuleNames.add(((TypeElement) e).getQualifiedName().toString()); } return false; } Set modules = new LinkedHashSet(); for (String moduleName : delayedModuleNames) { modules.add(elements().getTypeElement(moduleName)); } for (Element element : modules) { Map annotation = null; try { annotation = getAnnotation(Module.class, element); } catch (CodeGenerationIncompleteException e) { continue; // skip this element. An up-stream compiler error is in play. } TypeElement moduleType = (TypeElement) element; if (annotation == null) { error("Missing @Module annotation.", moduleType); continue; } if (annotation.get("complete").equals(Boolean.TRUE)) { Map> bindings; try { bindings = processCompleteModule(moduleType, false); new ProblemDetector().detectCircularDependencies(bindings.values()); } catch (ModuleValidationException e) { error("Graph validation failed: " + e.getMessage(), e.source); continue; } catch (InvalidBindingException e) { error("Graph validation failed: " + e.getMessage(), elements().getTypeElement(e.type)); continue; } catch (RuntimeException e) { if (ERROR_NAMES_TO_PROPAGATE.contains(e.getClass().getName())) { throw e; } error("Unknown error " + e.getClass().getName() + " thrown by javac in graph validation: " + e.getMessage(), moduleType); continue; } try { writeDotFile(moduleType, bindings); } catch (IOException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); processingEnv.getMessager() .printMessage(Diagnostic.Kind.WARNING, "Graph visualization failed. Please report this as a bug.\n\n" + sw, moduleType); } } if (annotation.get("library").equals(Boolean.FALSE)) { Map> bindings = processCompleteModule(moduleType, true); try { new ProblemDetector().detectUnusedBinding(bindings.values()); } catch (IllegalStateException e) { error("Graph validation failed: " + e.getMessage(), moduleType); } } } return false; } private void error(String message, Element element) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message, element); } private Map> processCompleteModule(TypeElement rootModule, boolean ignoreCompletenessErrors) { Map allModules = new LinkedHashMap(); collectIncludesRecursively(rootModule, allModules, new LinkedList()); ArrayList staticInjections = new ArrayList(); Linker.ErrorHandler errorHandler = ignoreCompletenessErrors ? Linker.ErrorHandler.NULL : new GraphAnalysisErrorHandler(processingEnv, rootModule.getQualifiedName().toString()); Linker linker = new Linker(null, new GraphAnalysisLoader(processingEnv), errorHandler); // Linker requires synchronization for calls to requestBinding and linkAll. // We know statically that we're single threaded, but we synchronize anyway // to make the linker happy. synchronized (linker) { BindingsGroup baseBindings = new BindingsGroup() { @Override public Binding contributeSetBinding(String key, SetBinding value) { return super.put(key, value); } }; BindingsGroup overrideBindings = new BindingsGroup() { @Override public Binding contributeSetBinding(String key, SetBinding value) { throw new IllegalStateException("Module overrides cannot contribute set bindings."); } }; for (TypeElement module : allModules.values()) { Map annotation = getAnnotation(Module.class, module); boolean overrides = (Boolean) annotation.get("overrides"); boolean library = (Boolean) annotation.get("library"); BindingsGroup addTo = overrides ? overrideBindings : baseBindings; // Gather the injectable types from the annotation. Set injectsProvisionKeys = new LinkedHashSet(); for (Object injectableTypeObject : (Object[]) annotation.get("injects")) { TypeMirror injectableType = (TypeMirror) injectableTypeObject; String providerKey = GeneratorKeys.get(injectableType); injectsProvisionKeys.add(providerKey); String key = isInterface(injectableType) ? providerKey : GeneratorKeys.rawMembersKey(injectableType); linker.requestBinding(key, module.getQualifiedName().toString(), getClass().getClassLoader(), false, true); } // Gather the static injections. for (Object staticInjection : (Object[]) annotation.get("staticInjections")) { TypeMirror staticInjectionTypeMirror = (TypeMirror) staticInjection; Element element = processingEnv.getTypeUtils().asElement(staticInjectionTypeMirror); staticInjections.add(new GraphAnalysisStaticInjection(element)); } // Gather the enclosed @Provides methods. for (Element enclosed : module.getEnclosedElements()) { Provides provides = enclosed.getAnnotation(Provides.class); if (provides == null) { continue; } ExecutableElement providerMethod = (ExecutableElement) enclosed; String key = GeneratorKeys.get(providerMethod); ProvidesBinding binding = new ProviderMethodBinding(key, providerMethod, library); Binding previous = addTo.get(key); if (previous != null) { if ((provides.type() == SET || provides.type() == SET_VALUES) && previous instanceof SetBinding) { // No duplicate bindings error if both bindings are set bindings. } else { String message = "Duplicate bindings for " + key; if (overrides) { message += " in override module(s) - cannot override an override"; } message += ":\n " + previous.requiredBy + "\n " + binding.requiredBy; error(message, providerMethod); } } switch (provides.type()) { case UNIQUE: if (injectsProvisionKeys.contains(binding.provideKey)) { binding.setDependedOn(true); } try { addTo.contributeProvidesBinding(key, binding); } catch (IllegalStateException ise) { throw new ModuleValidationException(ise.getMessage(), providerMethod); } break; case SET: String setKey = GeneratorKeys.getSetKey(providerMethod); SetBinding.add(addTo, setKey, binding); break; case SET_VALUES: SetBinding.add(addTo, key, binding); break; default: throw new AssertionError("Unknown @Provides type " + provides.type()); } } } linker.installBindings(baseBindings); linker.installBindings(overrideBindings); for (GraphAnalysisStaticInjection staticInjection : staticInjections) { staticInjection.attach(linker); } // Link the bindings. This will traverse the dependency graph, and report // errors if any dependencies are missing. return linker.linkAll(); } } private Elements elements() { return processingEnv.getElementUtils(); } void collectIncludesRecursively( TypeElement module, Map result, Deque path) { Map annotation = getAnnotation(Module.class, module); if (annotation == null) { // TODO(tbroyer): pass annotation information throw new ModuleValidationException("No @Module on " + module, module); } // Add the module. String name = module.getQualifiedName().toString(); if (path.contains(name)) { StringBuilder message = new StringBuilder("Module Inclusion Cycle: "); if (path.size() == 1) { message.append(name).append(" includes itself directly."); } else { String current = null; String includer = name; for (int i = 0; path.size() > 0; i++) { current = includer; includer = path.pop(); message.append("\n").append(i).append(". ") .append(current).append(" included by ").append(includer); } message.append("\n0. ").append(name); } throw new ModuleValidationException(message.toString(), module); } result.put(name, module); // Recurse for each included module. Types types = processingEnv.getTypeUtils(); List seedModules = new ArrayList(); seedModules.addAll(Arrays.asList((Object[]) annotation.get("includes"))); if (!annotation.get("addsTo").equals(Void.class)) seedModules.add(annotation.get("addsTo")); for (Object include : seedModules) { if (!(include instanceof TypeMirror)) { // TODO(tbroyer): pass annotation information processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Unexpected value for include: " + include + " in " + module, module); continue; } TypeElement includedModule = (TypeElement) types.asElement((TypeMirror) include); path.push(name); collectIncludesRecursively(includedModule, result, path); path.pop(); } } static class ProviderMethodBinding extends ProvidesBinding { private final ExecutableElement method; private final Binding[] parameters; protected ProviderMethodBinding(String provideKey, ExecutableElement method, boolean library) { super(provideKey, method.getAnnotation(Singleton.class) != null, className(method), method.getSimpleName().toString()); this.method = method; this.parameters = new Binding[method.getParameters().size()]; setLibrary(library); } @Override public void attach(Linker linker) { for (int i = 0; i < method.getParameters().size(); i++) { VariableElement parameter = method.getParameters().get(i); String parameterKey = GeneratorKeys.get(parameter); parameters[i] = linker.requestBinding(parameterKey, method.toString(), getClass().getClassLoader()); } } @Override public Object get() { throw new AssertionError("Compile-time binding should never be called to inject."); } @Override public void injectMembers(Object t) { throw new AssertionError("Compile-time binding should never be called to inject."); } @Override public void getDependencies(Set> get, Set> injectMembers) { Collections.addAll(get, parameters); } @Override public String toString() { return "ProvidesBinding[key=" + provideKey + " method=" + moduleClass + "." + method.getSimpleName() + "()"; } } void writeDotFile(TypeElement module, Map> bindings) throws IOException { JavaFileManager.Location location = StandardLocation.SOURCE_OUTPUT; String path = getPackage(module).getQualifiedName().toString(); String file = module.getQualifiedName().toString().substring(path.length() + 1) + ".dot"; FileObject resource = processingEnv.getFiler().createResource(location, path, file, module); Writer writer = resource.openWriter(); GraphVizWriter dotWriter = new GraphVizWriter(writer); new GraphVisualizer().write(bindings, dotWriter); dotWriter.close(); } static class ModuleValidationException extends IllegalStateException { final Element source; public ModuleValidationException(String message, Element source) { super(message); this.source = source; } } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GraphAnalysisStaticInjection.java ================================================ /* * 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.internal.codegen; import dagger.internal.Linker; import dagger.internal.StaticInjection; import javax.inject.Inject; import javax.lang.model.element.Element; import static dagger.internal.codegen.Util.isStatic; public final class GraphAnalysisStaticInjection extends StaticInjection { private final Element enclosingClass; public GraphAnalysisStaticInjection(Element enclosingClass) { this.enclosingClass = enclosingClass; } @Override public void attach(Linker linker) { for (Element enclosedElement : enclosingClass.getEnclosedElements()) { if (enclosedElement.getKind().isField() && isStatic(enclosedElement)) { Inject injectAnnotation = enclosedElement.getAnnotation(Inject.class); if (injectAnnotation != null) { String key = GeneratorKeys.get(enclosedElement.asType()); linker.requestBinding(key, enclosingClass.toString(), getClass().getClassLoader()); } } } } @Override public void inject() { throw new UnsupportedOperationException(); } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GraphVisualizer.java ================================================ /* * 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.internal.codegen; import dagger.internal.Binding; import java.io.IOException; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Emits an object graph in dot format. */ public final class GraphVisualizer { private static final Pattern KEY_PATTERN = Pattern.compile("" + "(?:@" // Full annotation start. + "(?:[\\w$]+\\.)*" // Annotation package + "([\\w$]+)" // Annotation simple name. Group 1. + "(?:\\(.*\\))?" // Annotation arguments + "/)?" // Full annotation end. + "(?:members/)?" // Members prefix. + "(?:[\\w$]+\\.)*" // Type package. + "([\\w$]+)" // Type simple name. Group 2. + "(\\<[^/]+\\>)?" // Type parameters. Group 3. + "((\\[\\])*)" // Arrays. Group 4. + ""); public void write(Map> bindings, GraphVizWriter writer) throws IOException { Map, String> namesIndex = buildNamesIndex(bindings); writer.beginGraph("concentrate", "true"); for (Map.Entry, String> entry : namesIndex.entrySet()) { Binding sourceBinding = entry.getKey(); String sourceName = entry.getValue(); Set> dependencies = new TreeSet>(new BindingComparator()); sourceBinding.getDependencies(dependencies, dependencies); for (Binding targetBinding : dependencies) { String targetName = namesIndex.get(targetBinding); if (targetName == null) { targetName = "Unbound:" + targetBinding.provideKey; } writer.edge(sourceName, targetName); } } writer.endGraph(); } private Map, String> buildNamesIndex(Map> bindings) { // Optimistically shorten each binding to the class short name; remembering collisions. Map> shortNameToBinding = new TreeMap>(); Set> collisions = new HashSet>(); for (Map.Entry> entry : bindings.entrySet()) { String key = entry.getKey(); Binding binding = entry.getValue(); String shortName = shortName(key); Binding collision = shortNameToBinding.put(shortName, binding); if (collision != null && collision != binding) { collisions.add(binding); collisions.add(collision); } } // Replace collisions with full names. for (Map.Entry> entry : bindings.entrySet()) { Binding binding = entry.getValue(); if (collisions.contains(binding)) { String key = entry.getKey(); String shortName = shortName(key); shortNameToBinding.remove(shortName); shortNameToBinding.put(key, binding); } } // Reverse the map. Map, String> bindingToName = new LinkedHashMap, String>(); for (Map.Entry> entry : shortNameToBinding.entrySet()) { bindingToName.put(entry.getValue(), entry.getKey()); } return bindingToName; } String shortName(String key) { Matcher matcher = KEY_PATTERN.matcher(key); if (!matcher.matches()) throw new IllegalArgumentException("Unexpected key: " + key); StringBuilder result = new StringBuilder(); String annotationSimpleName = matcher.group(1); if (annotationSimpleName != null) { result.append('@').append(annotationSimpleName).append(' '); } String simpleName = matcher.group(2); result.append(simpleName); String typeParameters = matcher.group(3); if (typeParameters != null) { result.append(typeParameters); } String arrays = matcher.group(4); if (arrays != null) { result.append(arrays); } return result.toString(); } /** A Comparator for Bindings so we can insure a consistent ordering of output. */ private static class BindingComparator implements Comparator> { @Override public int compare(Binding left, Binding right) { return getStringForBinding(left).compareTo(getStringForBinding(right)); } private String getStringForBinding(Binding binding) { return binding == null ? "" : binding.toString(); } } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/GraphVizWriter.java ================================================ /** * 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.internal.codegen; import com.squareup.javapoet.CodeBlock; import java.io.Closeable; import java.io.IOException; import java.io.Writer; import java.util.LinkedHashMap; import java.util.Map; /** * Emits dot graphs. */ public final class GraphVizWriter implements Closeable { private static final String INDENT = " "; private final Writer out; private int indent = 0; private int nextName = 1; private final Map generatedNames = new LinkedHashMap(); /** * @param out the stream to which dot data will be written. This should be a * buffered stream. */ public GraphVizWriter(Writer out) { this.out = out; } public void beginGraph(String... attributes) throws IOException { indent(); String type = indent == 0 ? "digraph " : "subgraph "; String name = nextName(indent == 0 ? "G" : "cluster"); out.write(type); out.write(name); out.write(" {\n"); indent++; attributes(attributes); } public void endGraph() throws IOException { indent--; indent(); out.write("}\n"); } public void node(String name, String... attributes) throws IOException { name = nodeName(name); indent(); out.write(name); inlineAttributes(attributes); out.write(";\n"); } public void edge(String source, String target, String... attributes) throws IOException { source = nodeName(source); target = nodeName(target); indent(); out.write(source); out.write(" -> "); out.write(target); inlineAttributes(attributes); out.write(";\n"); } public void nodeDefaults(String... attributes) throws IOException { if (attributes.length == 0) return; indent(); out.write("node"); inlineAttributes(attributes); out.write(";\n"); } public void edgeDefaults(String... attributes) throws IOException { if (attributes.length == 0) return; indent(); out.write("edge"); inlineAttributes(attributes); out.write(";\n"); } private void attributes(String[] attributes) throws IOException { if (attributes.length == 0) return; if (attributes.length % 2 != 0) throw new IllegalArgumentException(); for (int i = 0; i < attributes.length; i += 2) { indent(); out.write(attributes[i]); out.write(" = "); out.write(literal(attributes[i + 1])); out.write(";\n"); } } private void inlineAttributes(String[] attributes) throws IOException { if (attributes.length == 0) return; if (attributes.length % 2 != 0) throw new IllegalArgumentException(); out.write(" ["); for (int i = 0; i < attributes.length; i += 2) { if (i != 0) out.write(";"); out.write(attributes[i]); out.write("="); out.write(literal(attributes[i + 1])); } out.write("]"); } private String nodeName(String name) throws IOException { if (name.matches("\\w+")) return name; String generatedName = generatedNames.get(name); if (generatedName != null) return generatedName; generatedName = nextName("n"); generatedNames.put(name, generatedName); node(generatedName, "label", name); return generatedName; } private String literal(String raw) { if (raw.matches("\\w+")) return raw; return CodeBlock.builder() .add("$S", raw) .build() .toString(); } private void indent() throws IOException { for (int i = 0; i < indent; i++) { out.write(INDENT); } } private String nextName(String prefix) { return prefix + (nextName++); } @Override public void close() throws IOException { out.close(); } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/InjectAdapterProcessor.java ================================================ /* * Copyright (C) 2012 Square, Inc. * Copyright (C) 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; import dagger.ObjectGraph; import dagger.internal.Binding; import dagger.internal.Linker; import dagger.internal.StaticInjection; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.inject.Inject; import javax.inject.Singleton; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import static dagger.internal.codegen.AdapterJavadocs.bindingTypeDocs; import static dagger.internal.codegen.Util.SET_OF_BINDINGS; import static dagger.internal.codegen.Util.adapterName; import static dagger.internal.codegen.Util.bindingOf; import static dagger.internal.codegen.Util.elementToString; import static dagger.internal.codegen.Util.getApplicationSupertype; import static dagger.internal.codegen.Util.getNoArgsConstructor; import static dagger.internal.codegen.Util.getPackage; import static dagger.internal.codegen.Util.injectableType; import static dagger.internal.codegen.Util.isCallableConstructor; import static dagger.internal.codegen.Util.rawTypeToString; import static dagger.internal.loaders.GeneratedAdapters.INJECT_ADAPTER_SUFFIX; import static dagger.internal.loaders.GeneratedAdapters.STATIC_INJECTION_SUFFIX; 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; /** * Generates an implementation of {@link Binding} that injects the * {@literal @}{@code Inject}-annotated members of a class. */ @SupportedAnnotationTypes("javax.inject.Inject") public final class InjectAdapterProcessor extends AbstractProcessor { private final Set remainingTypeNames = new LinkedHashSet(); @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set types, RoundEnvironment env) { remainingTypeNames.addAll(findInjectedClassNames(env)); for (Iterator i = remainingTypeNames.iterator(); i.hasNext();) { InjectedClass injectedClass = createInjectedClass(i.next()); // Verify that we have access to all types to be injected on this pass. boolean missingDependentClasses = !allTypesExist(injectedClass.fields) || (injectedClass.constructor != null && !allTypesExist(injectedClass.constructor .getParameters())) || !allTypesExist(injectedClass.staticFields); if (!missingDependentClasses) { try { generateInjectionsForClass(injectedClass); } catch (IOException e) { error("Code gen failed: " + e, injectedClass.type); } i.remove(); } } if (env.processingOver() && !remainingTypeNames.isEmpty()) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not find injection type required by " + remainingTypeNames); } return false; } private void generateInjectionsForClass(InjectedClass injectedClass) throws IOException { if (injectedClass.constructor != null || !injectedClass.fields.isEmpty()) { generateInjectAdapter(injectedClass.type, injectedClass.constructor, injectedClass.fields); } if (!injectedClass.staticFields.isEmpty()) { generateStaticInjection(injectedClass.type, injectedClass.staticFields); } } /** * Return true if all element types are currently available in this code * generation pass. Unavailable types will be of kind {@link TypeKind#ERROR}. */ private boolean allTypesExist(Collection elements) { for (Element element : elements) { if (element.asType().getKind() == TypeKind.ERROR) { return false; } } return true; } private Set findInjectedClassNames(RoundEnvironment env) { // First gather the set of classes that have @Inject-annotated members. Set injectedTypeNames = new LinkedHashSet(); for (Element element : env.getElementsAnnotatedWith(Inject.class)) { if (!validateInjectable(element)) { continue; } injectedTypeNames.add(rawTypeToString(element.getEnclosingElement().asType(), '.')); } return injectedTypeNames; } private boolean validateInjectable(Element injectable) { Element injectableType = injectable.getEnclosingElement(); if (injectable.getKind() == ElementKind.CLASS) { error("@Inject is not valid on a class: " + elementToString(injectable), injectable); return false; } if (injectable.getKind() == ElementKind.METHOD) { error("Method injection is not supported: " + elementToString(injectable), injectable); return false; } if (injectable.getKind() == ElementKind.FIELD && injectable.getModifiers().contains(FINAL)) { error("Can't inject a final field: " + elementToString(injectable), injectable); return false; } if (injectable.getKind() == ElementKind.FIELD && injectable.getModifiers().contains(PRIVATE)) { error("Can't inject a private field: " + elementToString(injectable), injectable); return false; } if (injectable.getKind() == ElementKind.CONSTRUCTOR && injectable.getModifiers().contains(PRIVATE)) { error("Can't inject a private constructor: " + elementToString(injectable), injectable); return false; } ElementKind elementKind = injectableType.getEnclosingElement().getKind(); boolean isClassOrInterface = elementKind.isClass() || elementKind.isInterface(); boolean isStatic = injectableType.getModifiers().contains(STATIC); if (isClassOrInterface && !isStatic) { error("Can't inject a non-static inner class: " + elementToString(injectable), injectableType); return false; } return true; } /** * @param injectedClassName the name of a class with an @Inject-annotated member. */ private InjectedClass createInjectedClass(String injectedClassName) { TypeElement type = processingEnv.getElementUtils().getTypeElement(injectedClassName); boolean isAbstract = type.getModifiers().contains(ABSTRACT); List staticFields = new ArrayList(); ExecutableElement constructor = null; List fields = new ArrayList(); for (Element member : type.getEnclosedElements()) { if (member.getAnnotation(Inject.class) == null) { continue; } switch (member.getKind()) { case FIELD: if (member.getModifiers().contains(STATIC)) { staticFields.add(member); } else { fields.add(member); } break; case CONSTRUCTOR: if (constructor != null) { // TODO(tbroyer): pass annotation information error("Too many injectable constructors on " + type.getQualifiedName(), member); } else if (isAbstract) { // TODO(tbroyer): pass annotation information error("Abstract class " + type.getQualifiedName() + " must not have an @Inject-annotated constructor.", member); } constructor = (ExecutableElement) member; break; default: // TODO(tbroyer): pass annotation information error("Cannot inject " + elementToString(member), member); break; } } if (constructor == null && !isAbstract) { constructor = getNoArgsConstructor(type); if (constructor != null && !isCallableConstructor(constructor)) { constructor = null; } } return new InjectedClass(type, staticFields, constructor, fields); } /** * Write a companion class for {@code type} that extends {@link Binding}. * * @param constructor the injectable constructor, or null if this binding * supports members injection only. */ private void generateInjectAdapter(TypeElement type, ExecutableElement constructor, List fields) throws IOException { String packageName = getPackage(type).getQualifiedName().toString(); TypeMirror supertype = getApplicationSupertype(type); if (supertype != null) { supertype = processingEnv.getTypeUtils().erasure(supertype); } ClassName injectedClassName = ClassName.get(type); ClassName adapterClassName = adapterName(injectedClassName, INJECT_ADAPTER_SUFFIX); boolean isAbstract = type.getModifiers().contains(ABSTRACT); boolean injectMembers = !fields.isEmpty() || supertype != null; boolean disambiguateFields = !fields.isEmpty() && (constructor != null) && !constructor.getParameters().isEmpty(); boolean dependent = injectMembers || ((constructor != null) && !constructor.getParameters().isEmpty()); TypeSpec.Builder result = TypeSpec.classBuilder(adapterClassName.simpleName()) .addOriginatingElement(type) .addModifiers(PUBLIC, FINAL) .superclass(ParameterizedTypeName.get(ClassName.get(Binding.class), injectedClassName)) .addJavadoc("$L", bindingTypeDocs(injectableType(type.asType()), isAbstract, injectMembers, dependent).toString()); for (Element field : fields) { result.addField(memberBindingField(disambiguateFields, field)); } if (constructor != null) { for (VariableElement parameter : constructor.getParameters()) { result.addField(parameterBindingField(disambiguateFields, parameter)); } } if (supertype != null) { result.addField(supertypeBindingField(supertype)); } result.addMethod(writeInjectAdapterConstructor(constructor, type, injectedClassName)); if (dependent) { result.addMethod(attachMethod( constructor, fields, disambiguateFields, injectedClassName, supertype, true)); result.addMethod(getDependenciesMethod( constructor, fields, disambiguateFields, supertype, true)); } if (constructor != null) { result.addMethod( getMethod(constructor, disambiguateFields, injectMembers, injectedClassName)); } if (injectMembers) { result.addMethod( membersInjectMethod(fields, disambiguateFields, injectedClassName, supertype)); } JavaFile javaFile = JavaFile.builder(packageName, result.build()) .addFileComment(AdapterJavadocs.GENERATED_BY_DAGGER) .build(); javaFile.writeTo(processingEnv.getFiler()); } /** * Write a companion class for {@code type} that extends {@link StaticInjection}. */ private void generateStaticInjection(TypeElement type, List fields) throws IOException { ClassName typeName = ClassName.get(type); ClassName adapterClassName = adapterName(ClassName.get(type), STATIC_INJECTION_SUFFIX); TypeSpec.Builder result = TypeSpec.classBuilder(adapterClassName.simpleName()) .addOriginatingElement(type) .addJavadoc(AdapterJavadocs.STATIC_INJECTION_TYPE, type) .addModifiers(PUBLIC, FINAL) .superclass(StaticInjection.class); for (Element field : fields) { result.addField(memberBindingField(false, field)); } result.addMethod(attachMethod(null, fields, false, typeName, null, true)); result.addMethod(staticInjectMethod(fields, typeName)); String packageName = getPackage(type).getQualifiedName().toString(); JavaFile javaFile = JavaFile.builder(packageName, result.build()) .addFileComment(AdapterJavadocs.GENERATED_BY_DAGGER) .build(); javaFile.writeTo(processingEnv.getFiler()); } private FieldSpec memberBindingField(boolean disambiguateFields, Element field) { return FieldSpec.builder(bindingOf(field.asType()), fieldName(disambiguateFields, field), PRIVATE).build(); } private FieldSpec parameterBindingField(boolean disambiguateFields, VariableElement parameter) { return FieldSpec.builder(bindingOf(parameter.asType()), parameterName(disambiguateFields, parameter), PRIVATE).build(); } private FieldSpec supertypeBindingField(TypeMirror supertype) { return FieldSpec.builder(bindingOf(supertype), "supertype", PRIVATE).build(); } private MethodSpec writeInjectAdapterConstructor(ExecutableElement constructor, TypeElement type, ClassName strippedTypeName) { String key = (constructor != null) ? GeneratorKeys.get(type.asType()) : null; String membersKey = GeneratorKeys.rawMembersKey(type.asType()); boolean singleton = type.getAnnotation(Singleton.class) != null; return MethodSpec.constructorBuilder() .addModifiers(PUBLIC) .addStatement("super($S, $S, $N, $T.class)", key, membersKey, (singleton ? "IS_SINGLETON" : "NOT_SINGLETON"), strippedTypeName) .build(); } private MethodSpec attachMethod(ExecutableElement constructor, List fields, boolean disambiguateFields, ClassName typeName, TypeMirror supertype, boolean extendsBinding) throws IOException { MethodSpec.Builder result = MethodSpec.methodBuilder("attach") .addJavadoc(AdapterJavadocs.ATTACH_METHOD) .addModifiers(PUBLIC) .addParameter(Linker.class, "linker"); if (extendsBinding) { result.addAnnotation(Override.class); } result.addAnnotation(Util.UNCHECKED); if (constructor != null) { for (VariableElement parameter : constructor.getParameters()) { result.addStatement( "$N = ($T) linker.requestBinding($S, $T.class, getClass().getClassLoader())", parameterName(disambiguateFields, parameter), bindingOf(parameter.asType()), GeneratorKeys.get(parameter), typeName); } } for (Element field : fields) { result.addStatement( "$N = ($T) linker.requestBinding($S, $T.class, getClass().getClassLoader())", fieldName(disambiguateFields, field), bindingOf(field.asType()), GeneratorKeys.get((VariableElement) field), typeName); } if (supertype != null) { result.addStatement( "$N = ($T) linker.requestBinding($S, $T.class, getClass().getClassLoader()" + ", false, true)", "supertype", bindingOf(supertype), GeneratorKeys.rawMembersKey(supertype), typeName); } return result.build(); } private MethodSpec getDependenciesMethod(ExecutableElement constructor, List fields, boolean disambiguateFields, TypeMirror supertype, boolean extendsBinding) throws IOException { MethodSpec.Builder result = MethodSpec.methodBuilder("getDependencies") .addJavadoc(AdapterJavadocs.GET_DEPENDENCIES_METHOD) .addModifiers(PUBLIC) .addParameter(SET_OF_BINDINGS, "getBindings") .addParameter(SET_OF_BINDINGS, "injectMembersBindings"); if (extendsBinding) { result.addAnnotation(Override.class); } if (constructor != null) { for (Element parameter : constructor.getParameters()) { result.addStatement("getBindings.add($N)", parameterName(disambiguateFields, parameter)); } } for (Element field : fields) { result.addStatement("injectMembersBindings.add($N)", fieldName(disambiguateFields, field)); } if (supertype != null) { result.addStatement("injectMembersBindings.add($N)", "supertype"); } return result.build(); } private MethodSpec getMethod(ExecutableElement constructor, boolean disambiguateFields, boolean injectMembers, ClassName injectedClassName) { MethodSpec.Builder result = MethodSpec.methodBuilder("get") .addJavadoc(AdapterJavadocs.GET_METHOD, injectedClassName) .addAnnotation(Override.class) .returns(injectedClassName) .addModifiers(PUBLIC); result.addCode("$T result = new $T(", injectedClassName, injectedClassName); boolean first = true; for (VariableElement parameter : constructor.getParameters()) { if (!first) result.addCode(", "); else first = false; result.addCode("$N.get()", parameterName(disambiguateFields, parameter)); } result.addCode(");\n"); if (injectMembers) { result.addStatement("injectMembers(result)"); } result.addStatement("return result"); return result.build(); } private MethodSpec membersInjectMethod(List fields, boolean disambiguateFields, ClassName injectedClassName, TypeMirror supertype) { MethodSpec.Builder result = MethodSpec.methodBuilder("injectMembers") .addJavadoc(AdapterJavadocs.MEMBERS_INJECT_METHOD, injectedClassName) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(injectedClassName, "object"); for (Element field : fields) { result.addStatement("object.$N = $N.get()", field.getSimpleName(), fieldName(disambiguateFields, field)); } if (supertype != null) { result.addStatement("supertype.injectMembers(object)"); } return result.build(); } private MethodSpec staticInjectMethod(List fields, ClassName typeName) { MethodSpec.Builder result = MethodSpec.methodBuilder("inject") .addJavadoc(AdapterJavadocs.STATIC_INJECT_METHOD, ObjectGraph.class) .addAnnotation(Override.class) .addModifiers(PUBLIC); for (Element field : fields) { result.addStatement("$T.$N = $N.get()", typeName, field.getSimpleName().toString(), fieldName(false, field)); } return result.build(); } private String fieldName(boolean disambiguateFields, Element field) { return (disambiguateFields ? "field_" : "") + field.getSimpleName().toString(); } private String parameterName(boolean disambiguateFields, Element parameter) { return (disambiguateFields ? "parameter_" : "") + parameter.getSimpleName().toString(); } private void error(String msg, Element element) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, element); } static class InjectedClass { final TypeElement type; final List staticFields; final ExecutableElement constructor; final List fields; InjectedClass(TypeElement type, List staticFields, ExecutableElement constructor, List fields) { this.type = type; this.staticFields = staticFields; this.constructor = constructor; this.fields = fields; } } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/ModuleAdapterProcessor.java ================================================ /* * 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.internal.codegen; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import dagger.Lazy; import dagger.Module; import dagger.Provides; import dagger.internal.BindingsGroup; import dagger.internal.Linker; import dagger.internal.ModuleAdapter; import dagger.internal.ProvidesBinding; import dagger.internal.SetBinding; import dagger.internal.codegen.Util.CodeGenerationIncompleteException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.inject.Provider; import javax.inject.Singleton; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import static dagger.internal.codegen.AdapterJavadocs.bindingTypeDocs; import static dagger.internal.codegen.Util.ARRAY_OF_CLASS; import static dagger.internal.codegen.Util.bindingOf; import static dagger.internal.codegen.Util.elementToString; import static dagger.internal.codegen.Util.getAnnotation; import static dagger.internal.codegen.Util.getNoArgsConstructor; import static dagger.internal.codegen.Util.isCallableConstructor; import static dagger.internal.codegen.Util.isInterface; import static dagger.internal.codegen.Util.typeToString; import static dagger.internal.loaders.GeneratedAdapters.MODULE_ADAPTER_SUFFIX; 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; /** * Generates an implementation of {@link ModuleAdapter} that includes a binding * for each {@code @Provides} method of a target class. */ @SupportedAnnotationTypes({ "*" }) public final class ModuleAdapterProcessor extends AbstractProcessor { private static final List INVALID_RETURN_TYPES = Arrays.asList(Provider.class.getCanonicalName(), Lazy.class.getCanonicalName()); private final LinkedHashMap> remainingTypes = new LinkedHashMap>(); @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set types, RoundEnvironment env) { remainingTypes.putAll(providerMethodsByClass(env)); for (Iterator i = remainingTypes.keySet().iterator(); i.hasNext();) { String typeName = i.next(); TypeElement type = processingEnv.getElementUtils().getTypeElement(typeName); List providesTypes = remainingTypes.get(typeName); try { // Attempt to get the annotation. If types are missing, this will throw // CodeGenerationIncompleteException. Map parsedAnnotation = getAnnotation(Module.class, type); if (parsedAnnotation == null) { error(type + " has @Provides methods but no @Module annotation", type); continue; } JavaFile javaFile = generateModuleAdapter(type, parsedAnnotation, providesTypes); javaFile.writeTo(processingEnv.getFiler()); } catch (CodeGenerationIncompleteException e) { continue; // A dependent type was not defined, we'll try to catch it on another pass. } catch (IOException e) { error("Code gen failed: " + e, type); } i.remove(); } if (env.processingOver() && remainingTypes.size() > 0) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not find types required by provides methods for " + remainingTypes.keySet()); } return false; // FullGraphProcessor needs an opportunity to process. } private void error(String msg, Element element) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, element); } /** * Returns a map containing all {@code @Provides} methods, indexed by class. */ private Map> providerMethodsByClass(RoundEnvironment env) { Elements elementUtils = processingEnv.getElementUtils(); Types types = processingEnv.getTypeUtils(); Map> result = new HashMap>(); provides: for (Element providerMethod : findProvidesMethods(env)) { switch (providerMethod.getEnclosingElement().getKind()) { case CLASS: break; // valid, move along default: // TODO(tbroyer): pass annotation information error("Unexpected @Provides on " + elementToString(providerMethod), providerMethod); continue; } TypeElement type = (TypeElement) providerMethod.getEnclosingElement(); Set typeModifiers = type.getModifiers(); if (typeModifiers.contains(PRIVATE) || typeModifiers.contains(ABSTRACT)) { error("Classes declaring @Provides methods must not be private or abstract: " + type.getQualifiedName(), type); continue; } Set methodModifiers = providerMethod.getModifiers(); if (methodModifiers.contains(PRIVATE) || methodModifiers.contains(ABSTRACT) || methodModifiers.contains(STATIC)) { error("@Provides methods must not be private, abstract or static: " + type.getQualifiedName() + "." + providerMethod, providerMethod); continue; } ExecutableElement providerMethodAsExecutable = (ExecutableElement) providerMethod; if (!providerMethodAsExecutable.getThrownTypes().isEmpty()) { error("@Provides methods must not have a throws clause: " + type.getQualifiedName() + "." + providerMethod, providerMethod); continue; } // Invalidate return types. TypeMirror returnType = types.erasure(providerMethodAsExecutable.getReturnType()); if (!returnType.getKind().equals(TypeKind.ERROR)) { // Validate if we have a type to validate (a type yet to be generated by other // processors is not "invalid" in this way, so ignore). for (String invalidTypeName : INVALID_RETURN_TYPES) { TypeElement invalidTypeElement = elementUtils.getTypeElement(invalidTypeName); if (invalidTypeElement != null && types.isSameType(returnType, types.erasure(invalidTypeElement.asType()))) { error(String.format("@Provides method must not return %s directly: %s.%s", invalidTypeElement, type.getQualifiedName(), providerMethod), providerMethod); continue provides; // Skip to next provides method. } } } List methods = result.get(type.getQualifiedName().toString()); if (methods == null) { methods = new ArrayList(); result.put(type.getQualifiedName().toString(), methods); } methods.add(providerMethodAsExecutable); } TypeMirror objectType = elementUtils.getTypeElement("java.lang.Object").asType(); // Catch any stray modules without @Provides since their injectable types // should still be registered and a ModuleAdapter should still be written. for (Element module : env.getElementsAnnotatedWith(Module.class)) { if (!module.getKind().equals(ElementKind.CLASS)) { error("Modules must be classes: " + elementToString(module), module); continue; } TypeElement moduleType = (TypeElement) module; // Verify that all modules do not extend from non-Object types. if (!types.isSameType(moduleType.getSuperclass(), objectType)) { error("Modules must not extend from other classes: " + elementToString(module), module); } String moduleName = moduleType.getQualifiedName().toString(); if (result.containsKey(moduleName)) continue; result.put(moduleName, new ArrayList()); } return result; } private Set findProvidesMethods(RoundEnvironment env) { Set result = new LinkedHashSet(); result.addAll(env.getElementsAnnotatedWith(Provides.class)); return result; } /** * Write a companion class for {@code type} that implements {@link * ModuleAdapter} to expose its provider methods. */ private JavaFile generateModuleAdapter(TypeElement type, Map module, List providerMethods) { Object[] staticInjections = (Object[]) module.get("staticInjections"); Object[] injects = (Object[]) module.get("injects"); Object[] includes = (Object[]) module.get("includes"); boolean overrides = (Boolean) module.get("overrides"); boolean complete = (Boolean) module.get("complete"); boolean library = (Boolean) module.get("library"); List duplicateInjects = extractDuplicates(injects); if (!duplicateInjects.isEmpty()) { error("'injects' list contains duplicate entries: " + duplicateInjects, type); } List duplicateIncludes = extractDuplicates(includes); if (!duplicateIncludes.isEmpty()) { error("'includes' list contains duplicate entries: " + duplicateIncludes, type); } ClassName moduleClassName = ClassName.get(type); ClassName adapterClassName = Util.adapterName(moduleClassName, MODULE_ADAPTER_SUFFIX); TypeSpec.Builder adapterBuilder = TypeSpec.classBuilder(adapterClassName.simpleName()) .addOriginatingElement(type) .addJavadoc(AdapterJavadocs.MODULE_TYPE, Provides.class) .superclass(ParameterizedTypeName.get(ClassName.get(ModuleAdapter.class), moduleClassName)) .addModifiers(PUBLIC, FINAL); adapterBuilder.addField(FieldSpec.builder(String[].class, "INJECTS") .addModifiers(PRIVATE, STATIC, FINAL) .initializer("$L", injectsInitializer(injects)) .build()); adapterBuilder.addField(FieldSpec.builder(ARRAY_OF_CLASS, "STATIC_INJECTIONS") .addModifiers(PRIVATE, STATIC, FINAL) .initializer("$L", staticInjectionsInitializer(staticInjections)) .build()); adapterBuilder.addField(FieldSpec.builder(ARRAY_OF_CLASS, "INCLUDES") .addModifiers(PRIVATE, STATIC, FINAL) .initializer("$L", includesInitializer(type, includes)) .build()); adapterBuilder.addMethod(MethodSpec.constructorBuilder() .addModifiers(PUBLIC) .addStatement("super($T.class, INJECTS, STATIC_INJECTIONS, $L /*overrides*/, " + "INCLUDES, $L /*complete*/, $L /*library*/)", type.asType(), overrides, complete, library) .build()); ExecutableElement noArgsConstructor = getNoArgsConstructor(type); if (noArgsConstructor != null && isCallableConstructor(noArgsConstructor)) { adapterBuilder.addMethod(MethodSpec.methodBuilder("newModule") .addAnnotation(Override.class) .addModifiers(PUBLIC) .returns(moduleClassName) .addStatement("return new $T()", type.asType()) .build()); } // Caches. Map methodToClassName = new LinkedHashMap(); Map methodNameToNextId = new LinkedHashMap(); if (!providerMethods.isEmpty()) { MethodSpec.Builder getBindings = MethodSpec.methodBuilder("getBindings") .addJavadoc(AdapterJavadocs.GET_DEPENDENCIES_METHOD) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(BindingsGroup.class, "bindings") .addParameter(moduleClassName, "module"); for (ExecutableElement providerMethod : providerMethods) { Provides provides = providerMethod.getAnnotation(Provides.class); switch (provides.type()) { case UNIQUE: { getBindings.addStatement("bindings.contributeProvidesBinding($S, new $T(module))", GeneratorKeys.get(providerMethod), bindingClassName(adapterClassName, providerMethod, methodToClassName, methodNameToNextId)); break; } case SET: { getBindings.addStatement("$T.add(bindings, $S, new $T(module))", SetBinding.class, GeneratorKeys.getSetKey(providerMethod), bindingClassName(adapterClassName, providerMethod, methodToClassName, methodNameToNextId)); break; } case SET_VALUES: { getBindings.addStatement("$T.add(bindings, $S, new $T(module))", SetBinding.class, GeneratorKeys.get(providerMethod), bindingClassName(adapterClassName, providerMethod, methodToClassName, methodNameToNextId)); break; } default: throw new AssertionError("Unknown @Provides type " + provides.type()); } } adapterBuilder.addMethod(getBindings.build()); } for (ExecutableElement providerMethod : providerMethods) { adapterBuilder.addType(generateProvidesAdapter(moduleClassName, adapterClassName, providerMethod, methodToClassName, methodNameToNextId, library)); } return JavaFile.builder(adapterClassName.packageName(), adapterBuilder.build()) .addFileComment(AdapterJavadocs.GENERATED_BY_DAGGER) .build(); } private static List extractDuplicates(Object[] items) { List itemsList = Arrays.asList(items); List duplicateItems = new ArrayList(itemsList); for (Object item : new LinkedHashSet(itemsList)) { duplicateItems.remove(item); // Not using removeAll since we only want one element removed. } return duplicateItems; } private CodeBlock injectsInitializer(Object[] injects) { CodeBlock.Builder result = CodeBlock.builder() .add("{ "); for (Object injectableType : injects) { TypeMirror typeMirror = (TypeMirror) injectableType; String key = isInterface(typeMirror) ? GeneratorKeys.get(typeMirror) : GeneratorKeys.rawMembersKey(typeMirror); result.add("$S, ", key); } result.add("}"); return result.build(); } private CodeBlock staticInjectionsInitializer(Object[] staticInjections) { CodeBlock.Builder result = CodeBlock.builder() .add("{ "); for (Object staticInjection : staticInjections) { result.add("$T.class, ", staticInjection); } result.add("}"); return result.build(); } private CodeBlock includesInitializer(TypeElement type, Object[] includes) { CodeBlock.Builder result = CodeBlock.builder(); result.add("{ "); for (Object include : includes) { if (!(include instanceof TypeMirror)) { // TODO(tbroyer): pass annotation information processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Unexpected value: " + include + " in includes of " + type, type); continue; } TypeMirror typeMirror = (TypeMirror) include; result.add("$T.class, ", typeMirror); } result.add("}"); return result.build(); } private ClassName bindingClassName(ClassName adapterName, ExecutableElement providerMethod, Map methodToClassName, Map methodNameToNextId) { ClassName className = methodToClassName.get(providerMethod); if (className != null) return className; String methodName = providerMethod.getSimpleName().toString(); String suffix = ""; AtomicInteger id = methodNameToNextId.get(methodName); if (id == null) { methodNameToNextId.put(methodName, new AtomicInteger(2)); } else { suffix = id.toString(); id.incrementAndGet(); } String uppercaseMethodName = Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1); className = adapterName.nestedClass(uppercaseMethodName + "ProvidesAdapter" + suffix); methodToClassName.put(providerMethod, className); return className; } private TypeSpec generateProvidesAdapter(ClassName moduleClassName, ClassName adapterName, ExecutableElement providerMethod, Map methodToClassName, Map methodNameToNextId, boolean library) { String methodName = providerMethod.getSimpleName().toString(); TypeMirror moduleType = providerMethod.getEnclosingElement().asType(); ClassName className = bindingClassName( adapterName, providerMethod, methodToClassName, methodNameToNextId); TypeName returnType = Util.injectableType(providerMethod.getReturnType()); List parameters = providerMethod.getParameters(); boolean dependent = !parameters.isEmpty(); TypeSpec.Builder result = TypeSpec.classBuilder(className.simpleName()) .addJavadoc("$L", bindingTypeDocs(returnType, false, false, dependent)) .addModifiers(PUBLIC, STATIC, FINAL) .superclass(ParameterizedTypeName.get(ClassName.get(ProvidesBinding.class), returnType)); result.addField(moduleClassName, "module", PRIVATE, FINAL); for (Element parameter : parameters) { result.addField(bindingOf(parameter.asType()), parameterName(parameter), PRIVATE); } boolean singleton = providerMethod.getAnnotation(Singleton.class) != null; String key = GeneratorKeys.get(providerMethod); result.addMethod(MethodSpec.constructorBuilder() .addModifiers(PUBLIC) .addParameter(moduleClassName, "module") .addStatement("super($S, $L, $S, $S)", key, (singleton ? "IS_SINGLETON" : "NOT_SINGLETON"), typeToString(moduleType), methodName) .addStatement("this.module = module") .addStatement("setLibrary($L)", library) .build()); if (dependent) { MethodSpec.Builder attachBuilder = MethodSpec.methodBuilder("attach") .addJavadoc(AdapterJavadocs.ATTACH_METHOD) .addAnnotation(Override.class) .addAnnotation(Util.UNCHECKED) .addModifiers(PUBLIC) .addParameter(Linker.class, "linker"); for (VariableElement parameter : parameters) { String parameterKey = GeneratorKeys.get(parameter); attachBuilder.addStatement( "$N = ($T) linker.requestBinding($S, $T.class, getClass().getClassLoader())", parameterName(parameter), bindingOf(parameter.asType()), parameterKey, moduleClassName); } result.addMethod(attachBuilder.build()); MethodSpec.Builder getDependenciesBuilder = MethodSpec.methodBuilder("getDependencies") .addJavadoc(AdapterJavadocs.GET_DEPENDENCIES_METHOD) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(Util.SET_OF_BINDINGS, "getBindings") .addParameter(Util.SET_OF_BINDINGS, "injectMembersBindings"); for (Element parameter : parameters) { getDependenciesBuilder.addStatement("getBindings.add($N)", parameterName(parameter)); } result.addMethod(getDependenciesBuilder.build()); } MethodSpec.Builder getBuilder = MethodSpec.methodBuilder("get") .addJavadoc(AdapterJavadocs.GET_METHOD, returnType) .addAnnotation(Override.class) .addModifiers(PUBLIC) .returns(returnType) .addCode("return module.$N(", methodName); boolean first = true; for (Element parameter : parameters) { if (!first) getBuilder.addCode(", "); getBuilder.addCode("$N.get()", parameterName(parameter)); first = false; } getBuilder.addCode(");\n"); result.addMethod(getBuilder.build()); return result.build(); } private String parameterName(Element parameter) { if (parameter.getSimpleName().contentEquals("module")) { return "parameter_" + parameter.getSimpleName().toString(); } return parameter.getSimpleName().toString(); } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/Util.java ================================================ /* * 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.common.base.Joiner; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ArrayTypeName; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.WildcardTypeName; import dagger.internal.Binding; import dagger.internal.Keys; import java.lang.reflect.Method; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; 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.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ErrorType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.SimpleAnnotationValueVisitor6; import javax.lang.model.util.SimpleTypeVisitor6; /** * Utilities for handling types in annotation processors */ final class Util { // Binding. public static final TypeName BINDING_OF_ANY = ParameterizedTypeName.get( ClassName.get(Binding.class), WildcardTypeName.subtypeOf(Object.class)); // Set>. public static final TypeName SET_OF_BINDINGS = ParameterizedTypeName.get( ClassName.get(Set.class), BINDING_OF_ANY); // Class[]. public static final TypeName ARRAY_OF_CLASS = ArrayTypeName.of(ParameterizedTypeName.get( ClassName.get(Class.class), WildcardTypeName.subtypeOf(Object.class))); // @SuppressWarnings("unchecked") public static final AnnotationSpec UNCHECKED = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "unchecked") .build(); private Util() { } public static PackageElement getPackage(Element type) { while (type.getKind() != ElementKind.PACKAGE) { type = type.getEnclosingElement(); } return (PackageElement) type; } /** * Returns the supertype, or {@code null} if the supertype is a platform * class. This is intended for annotation processors that assume platform * classes will never be annotated with application annotations. */ public static TypeMirror getApplicationSupertype(TypeElement type) { TypeMirror supertype = type.getSuperclass(); return Keys.isPlatformType(supertype.toString()) ? null : supertype; } /** Returns a class name to complement {@code type}. */ public static ClassName adapterName(ClassName type, String suffix) { return ClassName.get(type.packageName(), Joiner.on('$').join(type.simpleNames()) + suffix); } /** Returns a string for {@code type}. Primitive types are always boxed. */ public static String typeToString(TypeMirror type) { StringBuilder result = new StringBuilder(); typeToString(type, result, '.'); return result.toString(); } /** Returns a string for the raw type of {@code type}. Primitive types are always boxed. */ public static String rawTypeToString(TypeMirror type, char innerClassSeparator) { if (!(type instanceof DeclaredType)) { throw new IllegalArgumentException("Unexpected type: " + type); } StringBuilder result = new StringBuilder(); DeclaredType declaredType = (DeclaredType) type; rawTypeToString(result, (TypeElement) declaredType.asElement(), innerClassSeparator); return result.toString(); } /** * Appends a string for {@code type} to {@code result}. Primitive types are * always boxed. * * @param innerClassSeparator either '.' or '$', which will appear in a * class name like "java.lang.Map.Entry" or "java.lang.Map$Entry". * Use '.' for references to existing types in code. Use '$' to define new * class names and for strings that will be used by runtime reflection. */ public static void typeToString(final TypeMirror type, final StringBuilder result, final char innerClassSeparator) { type.accept(new SimpleTypeVisitor6() { @Override public Void visitDeclared(DeclaredType declaredType, Void v) { TypeElement typeElement = (TypeElement) declaredType.asElement(); rawTypeToString(result, typeElement, innerClassSeparator); List typeArguments = declaredType.getTypeArguments(); if (!typeArguments.isEmpty()) { result.append("<"); for (int i = 0; i < typeArguments.size(); i++) { if (i != 0) { result.append(", "); } typeToString(typeArguments.get(i), result, innerClassSeparator); } result.append(">"); } return null; } @Override public Void visitPrimitive(PrimitiveType primitiveType, Void v) { result.append(box((PrimitiveType) type)); return null; } @Override public Void visitArray(ArrayType arrayType, Void v) { TypeMirror type = arrayType.getComponentType(); if (type instanceof PrimitiveType) { result.append(type.toString()); // Don't box, since this is an array. } else { typeToString(arrayType.getComponentType(), result, innerClassSeparator); } result.append("[]"); return null; } @Override public Void visitTypeVariable(TypeVariable typeVariable, Void v) { result.append(typeVariable.asElement().getSimpleName()); return null; } @Override public Void visitError(ErrorType errorType, Void v) { // Error type found, a type may not yet have been generated, but we need the type // so we can generate the correct code in anticipation of the type being available // to the compiler. // Paramterized types which don't exist are returned as an error type whose name is "" if ("".equals(errorType.toString())) { throw new CodeGenerationIncompleteException( "Type reported as is likely a not-yet generated parameterized type."); } // TODO(cgruber): Figure out a strategy for non-FQCN cases. result.append(errorType.toString()); return null; } @Override protected Void defaultAction(TypeMirror typeMirror, Void v) { throw new UnsupportedOperationException( "Unexpected TypeKind " + typeMirror.getKind() + " for " + typeMirror); } }, null); } /** Returns a string for {@code type}. Primitive types are always boxed. */ public static TypeName injectableType(TypeMirror type) { return type.accept(new SimpleTypeVisitor6() { @Override public TypeName visitPrimitive(PrimitiveType primitiveType, Void v) { return box(primitiveType); } @Override public TypeName visitError(ErrorType errorType, Void v) { // Error type found, a type may not yet have been generated, but we need the type // so we can generate the correct code in anticipation of the type being available // to the compiler. // Paramterized types which don't exist are returned as an error type whose name is "" if ("".equals(errorType.toString())) { throw new CodeGenerationIncompleteException( "Type reported as is likely a not-yet generated parameterized type."); } return ClassName.bestGuess(errorType.toString()); } @Override protected TypeName defaultAction(TypeMirror typeMirror, Void v) { return TypeName.get(typeMirror); } }, null); } private static final AnnotationValueVisitor VALUE_EXTRACTOR = new SimpleAnnotationValueVisitor6() { @Override public Object visitString(String s, Void p) { if ("".equals(s)) { throw new CodeGenerationIncompleteException("Unknown type returned as ."); } else if ("".equals(s)) { throw new CodeGenerationIncompleteException("Unknown type returned as ."); } return s; } @Override public Object visitType(TypeMirror t, Void p) { return t; } @Override protected Object defaultAction(Object o, Void v) { return o; } @Override public Object visitArray(List values, Void v) { Object[] result = new Object[values.size()]; for (int i = 0; i < values.size(); i++) { result[i] = values.get(i).accept(this, null); } return result; } }; /** * Returns the annotation on {@code element} formatted as a Map. This returns * a Map rather than an instance of the annotation interface to work-around * the fact that Class and Class[] fields won't work at code generation time. * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5089128 */ public static Map getAnnotation(Class annotationType, Element element) { for (AnnotationMirror annotation : element.getAnnotationMirrors()) { if (!rawTypeToString(annotation.getAnnotationType(), '$') .equals(annotationType.getName())) { continue; } Map result = new LinkedHashMap(); for (Method m : annotationType.getMethods()) { result.put(m.getName(), m.getDefaultValue()); } for (Map.Entry e : annotation.getElementValues().entrySet()) { String name = e.getKey().getSimpleName().toString(); Object value = e.getValue().accept(VALUE_EXTRACTOR, null); Object defaultValue = result.get(name); if (!lenientIsInstance(defaultValue.getClass(), value)) { throw new IllegalStateException(String.format( "Value of %s.%s is a %s but expected a %s\n value: %s", annotationType, name, value.getClass().getName(), defaultValue.getClass().getName(), value instanceof Object[] ? Arrays.toString((Object[]) value) : value)); } result.put(name, value); } return result; } return null; // Annotation not found. } /** * Returns true if {@code value} can be assigned to {@code expectedClass}. * Like {@link Class#isInstance} but more lenient for {@code Class} values. */ private static boolean lenientIsInstance(Class expectedClass, Object value) { if (expectedClass.isArray()) { Class componentType = expectedClass.getComponentType(); if (!(value instanceof Object[])) { return false; } for (Object element : (Object[]) value) { if (!lenientIsInstance(componentType, element)) return false; } return true; } else if (expectedClass == Class.class) { return value instanceof TypeMirror; } else { return expectedClass == value.getClass(); } } // TODO(sgoldfed): better format for other types of elements? static String elementToString(Element element) { switch (element.getKind()) { case FIELD: // fall through case CONSTRUCTOR: // fall through case METHOD: return element.getEnclosingElement() + "." + element; default: return element.toString(); } } static void rawTypeToString(StringBuilder result, TypeElement type, char innerClassSeparator) { String packageName = getPackage(type).getQualifiedName().toString(); String qualifiedName = type.getQualifiedName().toString(); if (packageName.isEmpty()) { result.append(qualifiedName.replace('.', innerClassSeparator)); } else { result.append(packageName); result.append('.'); result.append( qualifiedName.substring(packageName.length() + 1).replace('.', innerClassSeparator)); } } static TypeName box(PrimitiveType primitiveType) { switch (primitiveType.getKind()) { case BYTE: return ClassName.get(Byte.class); case SHORT: return ClassName.get(Short.class); case INT: return ClassName.get(Integer.class); case LONG: return ClassName.get(Long.class); case FLOAT: return ClassName.get(Float.class); case DOUBLE: return ClassName.get(Double.class); case BOOLEAN: return ClassName.get(Boolean.class); case CHAR: return ClassName.get(Character.class); case VOID: return ClassName.get(Void.class); default: throw new AssertionError(); } } /** * Returns the no-args constructor for {@code type}, or null if no such * constructor exists. */ public static ExecutableElement getNoArgsConstructor(TypeElement type) { for (Element enclosed : type.getEnclosedElements()) { if (enclosed.getKind() != ElementKind.CONSTRUCTOR) { continue; } ExecutableElement constructor = (ExecutableElement) enclosed; if (constructor.getParameters().isEmpty()) { return constructor; } } return null; } /** * Returns true if generated code can invoke {@code constructor}. That is, if * the constructor is non-private and its enclosing class is either a * top-level class or a static nested class. */ public static boolean isCallableConstructor(ExecutableElement constructor) { if (constructor.getModifiers().contains(Modifier.PRIVATE)) { return false; } TypeElement type = (TypeElement) constructor.getEnclosingElement(); return type.getEnclosingElement().getKind() == ElementKind.PACKAGE || type.getModifiers().contains(Modifier.STATIC); } /** * Returns a user-presentable string like {@code coffee.CoffeeModule}. */ public static String className(ExecutableElement method) { return ((TypeElement) method.getEnclosingElement()).getQualifiedName().toString(); } public static boolean isInterface(TypeMirror typeMirror) { return typeMirror instanceof DeclaredType && ((DeclaredType) typeMirror).asElement().getKind() == ElementKind.INTERFACE; } static boolean isStatic(Element element) { for (Modifier modifier : element.getModifiers()) { if (modifier.equals(Modifier.STATIC)) { return true; } } return false; } static ParameterizedTypeName bindingOf(TypeMirror type) { return ParameterizedTypeName.get(ClassName.get(Binding.class), injectableType(type)); } /** * An exception thrown when a type is not extant (returns as an error type), * usually as a result of another processor not having yet generated its types upon * which a dagger-annotated type depends. */ final static class CodeGenerationIncompleteException extends IllegalStateException { public CodeGenerationIncompleteException(String s) { super(s); } } } ================================================ FILE: compiler/src/main/java/dagger/internal/codegen/ValidationProcessor.java ================================================ /* * 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 dagger.Module; import dagger.Provides; import dagger.internal.codegen.Util.CodeGenerationIncompleteException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.inject.Inject; import javax.inject.Qualifier; import javax.inject.Scope; import javax.lang.model.SourceVersion; 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.tools.Diagnostic; import static dagger.internal.codegen.Util.elementToString; import static javax.lang.model.element.ElementKind.CONSTRUCTOR; import static javax.lang.model.element.ElementKind.METHOD; import static javax.lang.model.element.Modifier.ABSTRACT; /** * Checks for errors that are not directly related to modules and * {@code @Inject} annotated elements. * *

Warnings for invalid use of qualifier annotations can be suppressed * with @SuppressWarnings("qualifiers") * *

Warnings for invalid use of scoping annotations can be suppressed * with @SuppressWarnings("scoping") */ @SupportedAnnotationTypes({ "*" }) public final class ValidationProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set types, RoundEnvironment env) { List allElements = new ArrayList(); Map parametersToTheirMethods = new LinkedHashMap(); getAllElements(env, allElements, parametersToTheirMethods); for (Element element : allElements) { try { validateProvides(element); } catch (CodeGenerationIncompleteException e) { continue; // Upstream compiler issue in play. Ignore this element. } validateScoping(element); validateQualifiers(element, parametersToTheirMethods); } return false; } private void validateProvides(Element element) { if (element.getAnnotation(Provides.class) != null && Util.getAnnotation(Module.class, element.getEnclosingElement()) == null) { error("@Provides methods must be declared in modules: " + elementToString(element), element); } } private void validateQualifiers(Element element, Map parametersToTheirMethods) { boolean suppressWarnings = element.getAnnotation(SuppressWarnings.class) != null && Arrays.asList( element.getAnnotation(SuppressWarnings.class).value()).contains("qualifiers"); int numberOfQualifiersOnElement = 0; for (AnnotationMirror annotation : element.getAnnotationMirrors()) { if (annotation.getAnnotationType().asElement().getAnnotation(Qualifier.class) == null) { continue; } switch (element.getKind()) { case FIELD: numberOfQualifiersOnElement++; if (element.getAnnotation(Inject.class) == null && !suppressWarnings) { warning("Dagger will ignore qualifier annotations on fields that are not " + "annotated with @Inject: " + elementToString(element), element); } break; case METHOD: numberOfQualifiersOnElement++; if (!isProvidesMethod(element) && !suppressWarnings) { warning("Dagger will ignore qualifier annotations on methods that are not " + "@Provides methods: " + elementToString(element), element); } break; case PARAMETER: numberOfQualifiersOnElement++; if (!isInjectableConstructorParameter(element, parametersToTheirMethods) && !isProvidesMethodParameter(element, parametersToTheirMethods) && !suppressWarnings) { warning("Dagger will ignore qualifier annotations on parameters that are not " + "@Inject constructor parameters or @Provides method parameters: " + elementToString(element), element); } break; default: error("Qualifier annotations are only allowed on fields, methods, and parameters: " + elementToString(element), element); } } if (numberOfQualifiersOnElement > 1) { error("Only one qualifier annotation is allowed per element: " + elementToString(element), element); } } private void validateScoping(Element element) { boolean suppressWarnings = element.getAnnotation(SuppressWarnings.class) != null && Arrays.asList( element.getAnnotation(SuppressWarnings.class).value()).contains("scoping"); int numberOfScopingAnnotationsOnElement = 0; for (AnnotationMirror annotation : element.getAnnotationMirrors()) { if (annotation.getAnnotationType().asElement().getAnnotation(Scope.class) == null) { continue; } switch (element.getKind()) { case METHOD: numberOfScopingAnnotationsOnElement++; if (!isProvidesMethod(element) && !suppressWarnings) { warning("Dagger will ignore scoping annotations on methods that are not " + "@Provides methods: " + elementToString(element), element); } break; case CLASS: if (!element.getModifiers().contains(ABSTRACT)) { numberOfScopingAnnotationsOnElement++; break; } // fall through if abstract default: error("Scoping annotations are only allowed on concrete types and @Provides methods: " + elementToString(element), element); } } if (numberOfScopingAnnotationsOnElement > 1) { error("Only one scoping annotation is allowed per element: " + elementToString(element), element); } } private void getAllElements( RoundEnvironment env, List result, Map parametersToTheirMethods) { for (Element element : env.getRootElements()) { addAllEnclosed(element, result, parametersToTheirMethods); } } private void addAllEnclosed( Element element, List result, Map parametersToTheirMethods) { result.add(element); for (Element enclosed : element.getEnclosedElements()) { addAllEnclosed(enclosed, result, parametersToTheirMethods); if (enclosed.getKind() == METHOD || enclosed.getKind() == CONSTRUCTOR) { for (Element parameter : ((ExecutableElement) enclosed).getParameters()) { result.add(parameter); parametersToTheirMethods.put(parameter, enclosed); } } } } private boolean isProvidesMethod(Element element) { return element.getKind() == METHOD && element.getAnnotation(Provides.class) != null; } /** * @param parameter an {@code Element} whose {@code Kind} is parameter. The {@code Kind} is not * tested here. */ private boolean isProvidesMethodParameter( Element parameter, Map parametersToTheirMethods) { return parametersToTheirMethods.get(parameter).getAnnotation(Provides.class) != null; } /** * @param parameter an {@code Element} whose {@code Kind} is parameter. The {@code Kind} is not * tested here. */ private boolean isInjectableConstructorParameter( Element parameter, Map parametersToTheirMethods) { return parametersToTheirMethods.get(parameter).getKind() == CONSTRUCTOR && parametersToTheirMethods.get(parameter).getAnnotation(Inject.class) != null; } private void error(String msg, Element element) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, element); } private void warning(String msg, Element element) { processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg, element); } } ================================================ FILE: compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor ================================================ dagger.internal.codegen.ValidationProcessor dagger.internal.codegen.InjectAdapterProcessor dagger.internal.codegen.ModuleAdapterProcessor dagger.internal.codegen.GraphAnalysisProcessor ================================================ FILE: compiler/src/test/java/dagger/internal/codegen/DotWriterTest.java ================================================ /** * 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.internal.codegen; import java.io.IOException; import java.io.StringWriter; 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 final class DotWriterTest { private final StringWriter stringWriter = new StringWriter(); private final GraphVizWriter dotWriter = new GraphVizWriter(stringWriter); @Test public void graphWithAttributes() throws IOException { dotWriter.beginGraph(); dotWriter.edge("CoffeeMaker", "Heater", "style", "dotted", "color", "red"); dotWriter.edge("CoffeeMaker", "Pump"); dotWriter.node("CoffeeMaker", "shape", "box"); dotWriter.endGraph(); assertGraph("" + "digraph G1 {\n" + " CoffeeMaker -> Heater [style=dotted;color=red];\n" + " CoffeeMaker -> Pump;\n" + " CoffeeMaker [shape=box];\n" + "}\n"); } @Test public void subgraph() throws IOException { dotWriter.beginGraph("label", "10\" tall"); dotWriter.beginGraph("style", "filled", "color", "lightgrey"); dotWriter.edge("ElectricHeater", "Heater"); dotWriter.endGraph(); dotWriter.edge("CoffeeMaker", "Heater"); dotWriter.edge("CoffeeMaker", "Pump"); dotWriter.endGraph(); assertGraph("" + "digraph G1 {\n" + " label = \"10\\\" tall\";\n" + " subgraph cluster2 {\n" + " style = filled;\n" + " color = lightgrey;\n" + " ElectricHeater -> Heater;\n" + " }\n" + " CoffeeMaker -> Heater;\n" + " CoffeeMaker -> Pump;\n" + "}\n"); } @Test public void defaultAttributes() throws IOException { dotWriter.beginGraph(); dotWriter.nodeDefaults("color", "red"); dotWriter.edgeDefaults("style", "dotted"); dotWriter.edge("CoffeeMaker", "Heater"); dotWriter.endGraph(); assertGraph("" + "digraph G1 {\n" + " node [color=red];\n" + " edge [style=dotted];\n" + " CoffeeMaker -> Heater;\n" + "}\n"); } @Test public void invalidNodeNames() throws IOException { dotWriter.beginGraph(); dotWriter.edge("a.b", "a c"); dotWriter.edge("a c", "a_d"); dotWriter.endGraph(); assertGraph("" + "digraph G1 {\n" + " n2 [label=\"a.b\"];\n" + " n3 [label=\"a c\"];\n" + " n2 -> n3;\n" + " n3 -> a_d;\n" + "}\n"); } private void assertGraph(String expected) { assertThat(stringWriter.toString()).isEqualTo(expected); } } ================================================ FILE: compiler/src/test/java/dagger/internal/codegen/GraphAnalysisLoaderTest.java ================================================ /* * Copyright (C) 2015 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.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(JUnit4.class) public class GraphAnalysisLoaderTest { @Test public void resolveType() { final List resolveAttempts = new ArrayList(); Elements elements = mock(Elements.class); when(elements.getTypeElement(any(CharSequence.class))).then(new Answer() { @Override public TypeElement answer(InvocationOnMock invocationOnMock) throws Throwable { resolveAttempts.add(invocationOnMock.getArguments()[0].toString()); return null; } }); assertNull(GraphAnalysisLoader.resolveType(elements, "blah.blah.Foo$Bar$Baz")); List expectedAttempts = ImmutableList.builder() .add("blah.blah.Foo.Bar.Baz") .add("blah.blah.Foo.Bar$Baz") .add("blah.blah.Foo$Bar.Baz") .add("blah.blah.Foo$Bar$Baz") .build(); assertEquals(expectedAttempts, resolveAttempts); resolveAttempts.clear(); assertNull(GraphAnalysisLoader.resolveType(elements, "$$Foo$$Bar$$Baz$$")); expectedAttempts = ImmutableList.builder() .add("$.Foo.$Bar.$Baz.$") .add("$.Foo.$Bar.$Baz$$") .add("$.Foo.$Bar$.Baz.$") .add("$.Foo.$Bar$.Baz$$") .add("$.Foo.$Bar$$Baz.$") .add("$.Foo.$Bar$$Baz$$") .add("$.Foo$.Bar.$Baz.$") .add("$.Foo$.Bar.$Baz$$") .add("$.Foo$.Bar$.Baz.$") .add("$.Foo$.Bar$.Baz$$") .add("$.Foo$.Bar$$Baz.$") .add("$.Foo$.Bar$$Baz$$") .add("$.Foo$$Bar.$Baz.$") .add("$.Foo$$Bar.$Baz$$") .add("$.Foo$$Bar$.Baz.$") .add("$.Foo$$Bar$.Baz$$") .add("$.Foo$$Bar$$Baz.$") .add("$.Foo$$Bar$$Baz$$") .add("$$Foo.$Bar.$Baz.$") .add("$$Foo.$Bar.$Baz$$") .add("$$Foo.$Bar$.Baz.$") .add("$$Foo.$Bar$.Baz$$") .add("$$Foo.$Bar$$Baz.$") .add("$$Foo.$Bar$$Baz$$") .add("$$Foo$.Bar.$Baz.$") .add("$$Foo$.Bar.$Baz$$") .add("$$Foo$.Bar$.Baz.$") .add("$$Foo$.Bar$.Baz$$") .add("$$Foo$.Bar$$Baz.$") .add("$$Foo$.Bar$$Baz$$") .add("$$Foo$$Bar.$Baz.$") .add("$$Foo$$Bar.$Baz$$") .add("$$Foo$$Bar$.Baz.$") .add("$$Foo$$Bar$.Baz$$") .add("$$Foo$$Bar$$Baz.$") .add("$$Foo$$Bar$$Baz$$") .build(); assertEquals(expectedAttempts, resolveAttempts); Mockito.validateMockitoUsage(); } } ================================================ FILE: compiler/src/test/java/dagger/internal/codegen/GraphVisualizerTest.java ================================================ /** * 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.internal.codegen; import dagger.internal.Keys; import java.lang.reflect.Field; import java.util.Map; import java.util.Set; import javax.inject.Named; 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 final class GraphVisualizerTest { private final GraphVisualizer graphVisualizer = new GraphVisualizer(); String simpleKey; @Test public void testSimpleKey() throws Exception { String key = fieldKey("simpleKey"); assertThat(graphVisualizer.shortName(key)).isEqualTo("String"); } @SuppressWarnings("qualifiers") @Named String annotatedKey; @Test public void testAnnotatedKey() throws Exception { String key = fieldKey("annotatedKey"); assertThat(graphVisualizer.shortName(key)).isEqualTo("@Named String"); } @SuppressWarnings("qualifiers") @Named("/@<>[]()") String annotatedKeyWithParameters; @Test public void testAnnotatedKeyWithParameters() throws Exception { String key = fieldKey("annotatedKeyWithParameters"); // We intentionally omit parameters on annotated keys! assertThat(graphVisualizer.shortName(key)).isEqualTo("@Named String"); } String[][] arrayKey; @Test public void testArrayKey() throws Exception { String key = fieldKey("arrayKey"); assertThat(graphVisualizer.shortName(key)).isEqualTo("String[][]"); } Map> typeParameterKey; @Test public void testTypeParameterKey() throws Exception { String key = fieldKey("typeParameterKey"); assertThat(graphVisualizer.shortName(key)) .isEqualTo("Map>"); } @SuppressWarnings("qualifiers") @Named("/@<>[]()") Map>[] everythingKey; @Test public void testEverythingKey() throws Exception { String key = fieldKey("everythingKey"); assertThat(graphVisualizer.shortName(key)) .isEqualTo("@Named Map>[]"); } @Test public void testMembersKey() throws Exception { String key = Keys.getMembersKey(String.class); assertThat(graphVisualizer.shortName(key)).isEqualTo("String"); } private String fieldKey(String fieldName) throws NoSuchFieldException { Field field = GraphVisualizerTest.class.getDeclaredField(fieldName); return Keys.get(field.getGenericType(), field.getAnnotations(), field); } } ================================================ FILE: compiler/src/test/java/dagger/testing/it/BuildLogValidator.java ================================================ /* * 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.testing.it; import java.io.File; import java.io.FileInputStream; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; public class BuildLogValidator { /** * Processes a log file, ensuring it has all the provided strings within it. * * @param buildLogfile a log file to be searched * @param expectedStrings the strings that must be present in the log file for it to be valid */ public void assertHasText(File buildLogfile, String ... expectedStrings) throws Throwable { String buildOutput = getBuildOutput(buildLogfile); StringBuilder sb = new StringBuilder("Build output did not contain expected error text:"); boolean missing = false; for (String expected : expectedStrings) { if (!buildOutput.contains(expected)) { missing = true; sb.append("\n \"").append(expected).append("\""); } } if (missing) { appendBuildStatus(sb, buildOutput); throw new Exception(sb.toString()); } } /** * Processes a log file, ensuring it does not contain any of the provided strings within it. * * @param buildLogfile a log file to be searched * @param unexpectedStrings the strings that must not be present in the log file for it to be * valid */ public void assertDoesNotHaveText(File buildLogfile, String... unexpectedStrings) throws Throwable { String buildOutput = getBuildOutput(buildLogfile); StringBuilder sb = new StringBuilder("Build output contained unexpected text:"); boolean found = false; for (String unexpected : unexpectedStrings) { if (buildOutput.contains(unexpected)) { found = true; sb.append("\n \"").append(unexpected).append("\""); } } if (found) { appendBuildStatus(sb, buildOutput); throw new Exception(sb.toString()); } } private String getBuildOutput(File buildLogfile) throws Throwable { String buildOutput; FileInputStream stream = new FileInputStream(buildLogfile); try { FileChannel fc = stream.getChannel(); MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); buildOutput = Charset.defaultCharset().decode(buf).toString(); } finally { stream.close(); } if (buildOutput == null) { throw new Exception("Could not read build output"); } return buildOutput; } private void appendBuildStatus(StringBuilder sb, String buildOutput) { sb.append("\n\nBuild Output:\n\n"); boolean containsError = false; for(String line : buildOutput.split("\n")) { if (line.contains("[ERROR]")) { containsError = true; sb.append("\n ").append(line); } } if (!containsError) { sb.append("\nTEST BUILD SUCCEEDED.\n"); } } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/ProcessorTestUtils.java ================================================ /* * 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; import dagger.internal.codegen.GraphAnalysisProcessor; import dagger.internal.codegen.InjectAdapterProcessor; import dagger.internal.codegen.ModuleAdapterProcessor; import dagger.internal.codegen.ValidationProcessor; import java.util.Arrays; import javax.annotation.processing.Processor; /** * Internal test utilities. */ public class ProcessorTestUtils { public static Iterable daggerProcessors() { return Arrays.asList( new InjectAdapterProcessor(), new ModuleAdapterProcessor(), new GraphAnalysisProcessor(), new ValidationProcessor()); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/codegen/GenericInjectAdapterGenerationTest.java ================================================ /* * Copyright (C) 2013 Google Inc. * Copyright (C) 2016 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.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; import static dagger.tests.integration.ProcessorTestUtils.daggerProcessors; @RunWith(JUnit4.class) public final class GenericInjectAdapterGenerationTest { @Test public void basicInjectAdapter() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Basic", "" + "import dagger.Module;\n" + "import javax.inject.Inject;\n" + "class Basic {\n" + " static class Simple {\n" + " @Inject Simple() { }\n" + " }\n" + " static class A { }\n" + " static class B extends A {\n" + " @Inject Simple simple;\n" + " }\n" + " static class C extends B { \n" + " @Inject C() { }\n" + " }\n" + " @Module(injects = { C.class })\n" + " static class AModule { }\n" + "}\n" ); JavaFileObject expectedInjectAdapterC = JavaFileObjects.forSourceString("Basic$B$$InjectAdapter", "" + "import dagger.internal.Binding;\n" + "import dagger.internal.Linker;\n" + "import java.lang.Override;\n" + "import java.lang.SuppressWarnings;\n" + "import java.util.Set;\n" + "public final class Basic$B$$InjectAdapter extends Binding {\n" + " private Binding simple;\n" + " private Binding supertype;\n" + " public Basic$B$$InjectAdapter() {\n" + " super(\"Basic$B\", \"members/Basic$B\", NOT_SINGLETON, Basic.B.class);\n" + " }\n" + " @Override\n" + " @SuppressWarnings(\"unchecked\")\n" + " public void attach(Linker linker) {\n" + " simple = (Binding) linker.requestBinding(\"Basic$Simple\", Basic.B.class, getClass().getClassLoader());\n" + " supertype = (Binding) linker.requestBinding(\"members/Basic$A\", Basic.B.class, getClass().getClassLoader(), false, true);\n" + " }\n" + " @Override\n" + " public void getDependencies(Set> getBindings, Set> injectMembersBindings) {\n" + " injectMembersBindings.add(simple);\n" + " injectMembersBindings.add(supertype);\n" + " }\n" + " @Override\n" + " public Basic.B get() {\n" + " Basic.B result = new Basic.B();\n" + " injectMembers(result);\n" + " return result;\n" + " }\n" + " @Override\n" + " public void injectMembers(Basic.B object) {\n" + " object.simple = simple.get();\n" + " supertype.injectMembers(object);\n" + " }\n" + "}" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError() .and() .generatesSources(expectedInjectAdapterC); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/codegen/InjectAdapterGenerationTest.java ================================================ /* * 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.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; import static dagger.tests.integration.ProcessorTestUtils.daggerProcessors; @RunWith(JUnit4.class) public final class InjectAdapterGenerationTest { @Test public void basicInjectAdapter() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Basic", "" + "import dagger.Module;\n" + "import javax.inject.Inject;\n" + "class Basic {\n" + " static class A { @Inject A() { } }\n" + " static class Foo$Bar {\n" + " @Inject Foo$Bar() { }\n" + " static class Baz { @Inject Baz() { } }\n" + " }\n" + " @Module(injects = { A.class, Foo$Bar.class, Foo$Bar.Baz.class })\n" + " static class AModule { }\n" + "}\n" ); JavaFileObject expectedModuleAdapter = JavaFileObjects.forSourceString("Basic$AModule$$ModuleAdapter", "" + "import dagger.internal.ModuleAdapter;\n" + "import java.lang.Class;\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "public final class Basic$AModule$$ModuleAdapter\n" + " extends ModuleAdapter {\n" + " private static final String[] INJECTS = {\n" + " \"members/Basic$A\", \"members/Basic$Foo$Bar\", \"members/Basic$Foo$Bar$Baz\"};\n" + " private static final Class[] STATIC_INJECTIONS = {};\n" + " private static final Class[] INCLUDES = {};\n" + " public Basic$AModule$$ModuleAdapter() {\n" + " super(Basic.AModule.class, INJECTS, STATIC_INJECTIONS, false, INCLUDES,\n" + " true, false);\n" + " }\n" + " @Override public Basic.AModule newModule() {\n" + " return new Basic.AModule();\n" + " }\n" +"}\n" ); JavaFileObject expectedInjectAdapterA = JavaFileObjects.forSourceString("Basic$A$$InjectAdapter", "" + "import dagger.internal.Binding;\n" + "import java.lang.Override;\n" + "public final class Basic$A$$InjectAdapter\n" + " extends Binding {\n" + " public Basic$A$$InjectAdapter() {\n" + " super(\"Basic$A\", \"members/Basic$A\", NOT_SINGLETON, Basic.A.class);\n" + " }\n" + " @Override public Basic.A get() {\n" + " Basic.A result = new Basic.A();\n" + " return result;\n" + " }\n" + "}\n" ); JavaFileObject expectedInjectAdapterFooBar = JavaFileObjects.forSourceString("Basic$Foo$Bar$$InjectAdapter", "" + "import dagger.internal.Binding;\n" + "import java.lang.Override;\n" + "public final class Basic$Foo$Bar$$InjectAdapter\n" + " extends Binding {\n" + " public Basic$Foo$Bar$$InjectAdapter() {\n" + " super(\"Basic$Foo$Bar\", \"members/Basic$Foo$Bar\",\n" + " NOT_SINGLETON, Basic.Foo$Bar.class);\n" + " }\n" + " @Override public Basic.Foo$Bar get() {\n" + " Basic.Foo$Bar result = new Basic.Foo$Bar();\n" + " return result;\n" + " }\n" + "}\n" ); JavaFileObject expectedInjectAdapterFooBarBaz = JavaFileObjects.forSourceString("Basic$Foo$Bar$Baz$$InjectAdapter", "" + "import dagger.internal.Binding;\n" + "import java.lang.Override;\n" + "public final class Basic$Foo$Bar$Baz$$InjectAdapter\n" + " extends Binding {\n" + " public Basic$Foo$Bar$Baz$$InjectAdapter() {\n" + " super(\"Basic$Foo$Bar$Baz\", \"members/Basic$Foo$Bar$Baz\",\n" + " NOT_SINGLETON, Basic.Foo$Bar.Baz.class);\n" + " }\n" + " @Override public Basic.Foo$Bar.Baz get() {\n" + " Basic.Foo$Bar.Baz result = new Basic.Foo$Bar.Baz();\n" + " return result;\n" + " }\n" + "}\n" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError() .and() .generatesSources(expectedModuleAdapter, expectedInjectAdapterA, expectedInjectAdapterFooBar, expectedInjectAdapterFooBarBaz); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/codegen/ModuleAdapterGenerationTest.java ================================================ /* * 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.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; import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; import static dagger.tests.integration.ProcessorTestUtils.daggerProcessors; import static java.util.Arrays.asList; @RunWith(JUnit4.class) public final class ModuleAdapterGenerationTest { /** * Shows current behavior for a {@link dagger.Provides provides method} * used to supply an injected ctor parameter. * *

    *
  • {@code ProvidesAdapter} invokes the module's provides method on * {@code get}
  • *
  • On {@code getBindings}, the above is newed up and linked to its type * key. *
  • {@code InjectAdapter} contains a field for the parameter binding, * referenced in {@code getDependencies} and set on {@code attach}
  • *
  • On {@code get}, the injected constructor is called with the value of * {@link dagger.internal.Binding#get}
  • *
*/ @Test public void providerForCtorInjection() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Field", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Inject;\n" + "class Field {\n" + " static class A { final String name; @Inject A(String name) { this.name = name; }}\n" + " @Module(injects = { A.class, String.class })\n" + " static class AModule { @Provides String name() { return \"foo\"; }}\n" + "}\n" ); JavaFileObject expectedModuleAdapter = JavaFileObjects.forSourceString("Field$AModule$$ModuleAdapter", "" + "import dagger.internal.BindingsGroup;\n" + "import dagger.internal.ModuleAdapter;\n" + "import dagger.internal.ProvidesBinding;\n" + "import java.lang.Class;\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "public final class Field$AModule$$ModuleAdapter\n" + " extends ModuleAdapter {\n" + " private static final String[] INJECTS = \n" + " {\"members/Field$A\", \"members/java.lang.String\"};\n" + " private static final Class[] STATIC_INJECTIONS = {};\n" + " private static final Class[] INCLUDES = {};\n" + " public Field$AModule$$ModuleAdapter() {\n" + " super(Field.AModule.class, INJECTS, STATIC_INJECTIONS, false, INCLUDES, true, false);\n" + " }\n" + " @Override public Field.AModule newModule() {\n" + " return new Field.AModule();\n" + " }\n" + " @Override public void getBindings(BindingsGroup bindings, Field.AModule module) {\n" + " bindings.contributeProvidesBinding(\"java.lang.String\",\n" + " new NameProvidesAdapter(module));\n" // eager new! + " }\n" + " public static final class NameProvidesAdapter\n" // corresponds to method name + " extends ProvidesBinding {\n" + " private final Field.AModule module;\n" + " public NameProvidesAdapter(Field.AModule module) {\n" + " super(\"java.lang.String\", NOT_SINGLETON, \"Field.AModule\", \"name\");\n" + " this.module = module;\n" + " setLibrary(false);\n" + " }\n" + " @Override public String get() {\n" + " return module.name();\n" // corresponds to @Provides method + " }\n" + " }\n" + "}\n" ); JavaFileObject expectedInjectAdapter = JavaFileObjects.forSourceString("Field$A$$InjectAdapter", "" + "import dagger.internal.Binding;\n" + "import dagger.internal.Linker;\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "import java.lang.SuppressWarnings;\n" + "import java.util.Set;\n" + "public final class Field$A$$InjectAdapter\n" + " extends Binding {\n" + " private Binding name;\n" // for ctor + " public Field$A$$InjectAdapter() {\n" + " super(\"Field$A\", \"members/Field$A\", NOT_SINGLETON, Field.A.class);\n" + " }\n" + " @Override @SuppressWarnings(\"unchecked\")\n" + " public void attach(Linker linker) {\n" + " name = (Binding)linker.requestBinding(\n" // binding key is not a class + " \"java.lang.String\", Field.A.class, getClass().getClassLoader());\n" + " }\n" + " @Override public void getDependencies(\n" + " Set> getBindings, Set> injectMembersBindings) {\n" + " getBindings.add(name);\n" // name is added to dependencies + " }\n" + " @Override public Field.A get() {\n" + " Field.A result = new Field.A(name.get());\n" // adds ctor param + " return result;\n" + " }\n" + "}\n" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError() .and() .generatesSources(expectedModuleAdapter, expectedInjectAdapter); } @Test public void injectsMembersInjectedAndProvidedAndConstructedTypes() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Field", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Inject;\n" + "class Field {\n" + " static class A { final String name; @Inject A(String name) { this.name = name; }}\n" + " static class B { @Inject String name; }\n" + " @Module(injects = { A.class, String.class, B.class })\n" + " static class AModule { @Provides String name() { return \"foo\"; }}\n" + "}\n" ); JavaFileObject expectedModuleAdapter = JavaFileObjects.forSourceString("Field$AModule$$ModuleAdapter", "" + "import dagger.internal.BindingsGroup;\n" + "import dagger.internal.ModuleAdapter;\n" + "import dagger.internal.ProvidesBinding;\n" + "import java.lang.Class;\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "public final class Field$AModule$$ModuleAdapter extends ModuleAdapter {\n" + " private static final String[] INJECTS = \n" + " {\"members/Field$A\", \"members/java.lang.String\", \"members/Field$B\"};\n" + " private static final Class[] STATIC_INJECTIONS = {};\n" + " private static final Class[] INCLUDES = {};\n" + " public Field$AModule$$ModuleAdapter() {\n" + " super(Field.AModule.class, INJECTS, STATIC_INJECTIONS, false, INCLUDES, true, false);\n" + " }\n" + " @Override public Field.AModule newModule() {\n" + " return new Field.AModule();\n" + " }\n" + " @Override public void getBindings(BindingsGroup bindings, Field.AModule module) {\n" + " bindings.contributeProvidesBinding(\"java.lang.String\",\n" + " new NameProvidesAdapter(module));\n" // eager new! + " }\n" + " public static final class NameProvidesAdapter\n" // corresponds to method name + " extends ProvidesBinding {\n" + " private final Field.AModule module;\n" + " public NameProvidesAdapter(Field.AModule module) {\n" + " super(\"java.lang.String\", NOT_SINGLETON, \"Field.AModule\", \"name\");\n" + " this.module = module;\n" + " setLibrary(false);\n" + " }\n" + " @Override public String get() {\n" + " return module.name();\n" // corresponds to @Provides method + " }\n" + " }\n" + "}\n" ); JavaFileObject expectedInjectAdapterA = JavaFileObjects.forSourceString("Field$A$$InjectAdapter", "" + "import dagger.internal.Binding;\n" + "import dagger.internal.Linker;\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "import java.lang.SuppressWarnings;\n" + "import java.util.Set;\n" + "public final class Field$A$$InjectAdapter\n" + " extends Binding {\n" + " private Binding name;\n" // For Constructor. + " public Field$A$$InjectAdapter() {\n" + " super(\"Field$A\", \"members/Field$A\", NOT_SINGLETON, Field.A.class);\n" + " }\n" + " @Override @SuppressWarnings(\"unchecked\")\n" + " public void attach(Linker linker) {\n" + " name = (Binding)linker.requestBinding(\n" + " \"java.lang.String\", Field.A.class, getClass().getClassLoader());\n" + " }\n" + " @Override public void getDependencies(\n" + " Set> getBindings, Set> injectMembersBindings) {\n" + " getBindings.add(name);\n" // Name is added to dependencies. + " }\n" + " @Override public Field.A get() {\n" + " Field.A result = new Field.A(name.get());\n" // Adds constructor parameter. + " return result;\n" + " }\n" + "}\n" ); JavaFileObject expectedInjectAdapterB = JavaFileObjects.forSourceString("Field$B$$InjectAdapter", "" + "import dagger.internal.Binding;\n" + "import dagger.internal.Linker;\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "import java.lang.SuppressWarnings;\n" + "import java.util.Set;\n" + "public final class Field$B$$InjectAdapter\n" + " extends Binding {\n" + " private Binding name;\n" // For field. + " public Field$B$$InjectAdapter() {\n" + " super(\"Field$B\", \"members/Field$B\", NOT_SINGLETON, Field.B.class);\n" + " }\n" + " @Override @SuppressWarnings(\"unchecked\")\n" + " public void attach(Linker linker) {\n" + " name = (Binding)linker.requestBinding(\n" + " \"java.lang.String\", Field.B.class, getClass().getClassLoader());\n" + " }\n" + " @Override public void getDependencies(\n" + " Set> getBindings, Set> injectMembersBindings) {\n" + " injectMembersBindings.add(name);\n" // Name is added to dependencies. + " }\n" + " @Override public Field.B get() {\n" + " Field.B result = new Field.B();\n" + " injectMembers(result);\n" + " return result;\n" + " }\n" + " @Override public void injectMembers(Field.B object) {\n" + " object.name = name.get();\n" // Inject field. + " }\n" + "}\n" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError() .and() .generatesSources(expectedModuleAdapter, expectedInjectAdapterA, expectedInjectAdapterB); } @Test public void providesHasParameterNamedModule() { JavaFileObject a = JavaFileObjects.forSourceString("A", "" + "import javax.inject.Inject;\n" + "class A {\n" + " @Inject A(){ }\n" + "}\n" ); JavaFileObject b = JavaFileObjects.forSourceString("B", "" + "import javax.inject.Inject;\n" + "class B {\n" + " @Inject B(){ }\n" + "}\n" ); JavaFileObject module = JavaFileObjects.forSourceString("BModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Inject;\n" + "@Module(injects = B.class)\n" + "class BModule {\n" + " @Provides B b(A module) {\n" + " return new B();\n" + " }\n" + "}\n" ); assertAbout(javaSources()) .that(asList(a, b, module)) .processedWith(daggerProcessors()) .compilesWithoutError(); } @Test public void duplicateInjectsFails() { JavaFileObject module = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Inject;\n" + "class A {}\n" + "@Module(injects = { A.class, A.class })\n" + "class BModule { }\n" ); assertAbout(javaSource()) .that(module) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining("'injects' list contains duplicate entries: [A]") .in(module).onLine(6); } @Test public void duplicateIncludesFails() { JavaFileObject module = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Inject;\n" + "@Module\n" + "class AModule {}\n" + "@Module(includes = { AModule.class, AModule.class })\n" + "class BModule { }\n" ); assertAbout(javaSource()) .that(module) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining("'includes' list contains duplicate entries: [AModule]") .in(module).onLine(7); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/operation/FailureModeErrorsTest.java ================================================ /** * Copyright (C) 2014 Google, Inc. * 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.tests.integration.operation; import dagger.Module; import dagger.ObjectGraph; import javax.inject.Inject; import javax.inject.Qualifier; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.fail; @RunWith(JUnit4.class) public final class FailureModeErrorsTest { @Module static class CompleteModule {} static class ArrayFoo { @Inject ArrayFoo(String[] ignored) {} } @Module(injects = ArrayFoo.class, complete = false) static class ArrayFooModule {} @Test public void failOnMissingModule_arrayorgenerics() { // Generics here are crazy to try to test for, but this code path is legit regardless. try { ObjectGraph.create(new CompleteModule(), new ArrayFooModule()).get(ArrayFoo.class); fail("Should have thrown."); } catch (IllegalStateException e) { assertThat(e.getMessage()).contains( "java.lang.String[] is a generic class or an array and can only be bound with " + "concrete type parameter(s) in a @Provides method. required by class " + "dagger.tests.integration.operation.FailureModeErrorsTest$ArrayFoo"); } } @Qualifier @interface MyFoo {} static class QualifyingFoo { @Inject QualifyingFoo(@MyFoo String ignored) {} } @Module(injects = QualifyingFoo.class, complete = false) static class QualifyingFooModule {} @Test public void failOnMissingModule_qualified() { try { ObjectGraph.create(new CompleteModule(), new QualifyingFooModule()).get(QualifyingFoo.class); fail("Should have thrown."); } catch (IllegalStateException e) { assertThat(e.getMessage()).contains( "@dagger.tests.integration.operation.FailureModeErrorsTest$MyFoo()/java.lang.String " + "is a @Qualifier-annotated type and must be bound by a @Provides method. required by " + "class dagger.tests.integration.operation.FailureModeErrorsTest$QualifyingFoo"); } } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java ================================================ /** * 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 dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; 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 final class PrimitiveInjectionTest { static class ArrayInjectable { @Inject byte[] byteArray; @Inject int[] integerArray; @Inject boolean[] booleanArray; @Inject char[] charArray; @Inject long[] longArray; @Inject float[] floatArray; @Inject double[] doubleArray; } @Module(injects = ArrayInjectable.class) static class PrimitiveArrayModule { @Provides byte[] byteArray() { return new byte[] { Byte.MAX_VALUE }; } @Provides int[] provideInt() { return new int[] { Integer.MAX_VALUE }; } @Provides boolean[] provideBoolean() { return new boolean[] { true }; } @Provides long[] provideLong() { return new long[] { Long.MAX_VALUE }; } @Provides char[] provideChar() { return new char[] { Character.MAX_VALUE }; } @Provides float[] provideFloat() { return new float[] { Float.MAX_VALUE }; } @Provides double[] provideDouble() { return new double[] { Double.MAX_VALUE }; } } @Test public void primitiveArrayTypesAllInjected() { ArrayInjectable result = ObjectGraph.create(PrimitiveArrayModule.class) .get(ArrayInjectable.class); assertThat(result).isNotNull(); assertThat(result.byteArray).isEqualTo(new byte[] { Byte.MAX_VALUE }); assertThat(result.integerArray).isEqualTo(new int[] { Integer.MAX_VALUE }); assertThat(result.booleanArray).isEqualTo(new boolean[] { true }); assertThat(result.charArray).isEqualTo(new char[] { Character.MAX_VALUE }); assertThat(result.longArray).isEqualTo(new long[] { Long.MAX_VALUE }); assertThat(result.floatArray).hasValuesWithin(0).of(new float[] { Float.MAX_VALUE }); assertThat(result.doubleArray).hasValuesWithin(0).of(new double[] { Double.MAX_VALUE }); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/operation/SimpleInjectionTest.java ================================================ /** * 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 dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; 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 final class SimpleInjectionTest { static abstract class AbstractFoo { @Inject String blah; } static class Foo extends AbstractFoo { } @Module(injects = Foo.class) static class FooModule { @Provides String string() { return "blah"; } } @Module(injects = Foo.class) static class ProvidingFooModule { @Provides String string() { return "blah"; } @Provides Foo foo(String blah) { Foo foo = new Foo(); foo.blah = blah; return foo; } } @Test public void memberInject_WithoutProvidesMethod() { Foo foo = new Foo(); ObjectGraph.create(FooModule.class).inject(foo); assertThat(foo.blah).isEqualTo("blah"); } @Test public void membersInject_WithProvidesMethod() { Foo foo = new Foo(); ObjectGraph.create(ProvidingFooModule.class).inject(foo); assertThat(foo.blah).isEqualTo("blah"); } @Test public void get_WithProvidesMethod() { Foo foo = ObjectGraph.create(ProvidingFooModule.class).get(Foo.class); assertThat(foo.blah).isEqualTo("blah"); } static class Bar { } @Module(injects = Bar.class) static class BarModule { } @Test public void membersInject_WithNonInjectable() { Bar bar = new Bar(); ObjectGraph.create(BarModule.class).inject(bar); } @Module(injects = Bar.class) static class ProvidingBarModule { @Provides public Bar bar() { return new Bar(); } } @Test public void membersInject_WithProvidedNonInjectable() { Bar bar = ObjectGraph.create(ProvidingBarModule.class).get(Bar.class); assertThat(bar).isNotNull(); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/validation/CyclicDependencyTest.java ================================================ /** * 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.validation; 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 dagger.tests.integration.ProcessorTestUtils.daggerProcessors; @RunWith(JUnit4.class) public class CyclicDependencyTest { @Test public void cyclicDepsWithInjectables() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("CyclicDeps", "" + "import dagger.Module;\n" + "import javax.inject.Inject;\n" + "class CyclicDeps {\n" + " static class Foo {\n" + " @Inject Foo(Bar b) { }\n" + " }\n" + " static class Bar {\n" + " @Inject Bar(Blah b) { }\n" + " }\n" + " static class Blah {\n" + " @Inject Blah(Foo f) { }\n" + " }\n" + " static class EntryPoint {\n" + " @Inject Foo f;\n" + " }\n" + " @Module(injects = EntryPoint.class)\n" + " static class TestModule { }\n" + "}\n" ); assertAbout(javaSource()).that(sourceFile).processedWith(daggerProcessors()).failsToCompile() .withErrorContaining("Dependency cycle:").in(sourceFile).onLine(17); } @Test public void cyclicDepsWithProvidesMethods() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("CyclicDeps", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "class CyclicDeps {\n" + " static class A { }\n" + " static class B { }\n" + " static class C { }\n" + " static class D { }\n" + " @Module(injects = D.class)\n" + " static class CyclicModule {\n" + " @Provides A a(D d) { return null; }\n" + " @Provides B b(A a) { return null; }\n" + " @Provides C c(B b) { return null; }\n" + " @Provides D d(C c) { return null; }\n" + " }\n" + "}\n" ); assertAbout(javaSource()).that(sourceFile).processedWith(daggerProcessors()).failsToCompile() .withErrorContaining("Dependency cycle:").in(sourceFile).onLine(9); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/validation/CyclicModuleIncludesTest.java ================================================ /** * 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.validation; 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 dagger.tests.integration.ProcessorTestUtils.daggerProcessors; @RunWith(JUnit4.class) public class CyclicModuleIncludesTest { private final JavaFileObject javaFile = JavaFileObjects.forSourceString("CyclicModules", "" + "import dagger.Module;\n" + "class CyclicModules {\n" + " @Module(includes = SelfReferencingModule.class)\n" + " static class SelfReferencingModule { }\n" + " @Module(includes = Spock.class)\n" + " static class Rock {}\n" + " @Module(includes = Rock.class)\n" + " static class Paper {}\n" + " @Module(includes = Paper.class)\n" + " static class Scissors {}\n" + " @Module(includes = Scissors.class)\n" + " static class Lizard {}\n" + " @Module(includes = Lizard.class)\n" + " static class Spock {}\n" + "}" ); @Test public void cyclicModuleSelfIncludes() { assertAbout(javaSource()) .that(javaFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining("CyclicModules.SelfReferencingModule includes itself directly") .in(javaFile).onLine(4); } @Test public void cyclicModuleIncludes_full_cycle() { assertAbout(javaSource()) .that(javaFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining("0. CyclicModules.Rock included by CyclicModules.Paper") .in(javaFile).onLine(6).and() .withErrorContaining("1. CyclicModules.Paper included by CyclicModules.Scissors") .in(javaFile).onLine(6).and() .withErrorContaining("2. CyclicModules.Scissors included by CyclicModules.Lizard") .in(javaFile).onLine(6).and() .withErrorContaining("3. CyclicModules.Lizard included by CyclicModules.Spock") .in(javaFile).onLine(6).and() .withErrorContaining("4. CyclicModules.Spock included by CyclicModules.Rock") .in(javaFile).onLine(6); } @Test public void cyclicModuleIncludes_initial_inclusion() { assertAbout(javaSource()) .that(javaFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining("0. CyclicModules.Rock included by CyclicModules.Paper") .in(javaFile).onLine(6).and() .withErrorContaining("0. CyclicModules.Paper included by CyclicModules.Scissors") .in(javaFile).onLine(8).and() .withErrorContaining("0. CyclicModules.Scissors included by CyclicModules.Lizard") .in(javaFile).onLine(10).and() .withErrorContaining("0. CyclicModules.Lizard included by CyclicModules.Spock") .in(javaFile).onLine(12).and() .withErrorContaining("0. CyclicModules.Spock included by CyclicModules.Rock") .in(javaFile).onLine(14); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/validation/GeneratedTypesNotReadyTest.java ================================================ /** * 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.validation; import com.google.common.base.Joiner; 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.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; 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.collect.Iterables.concat; import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; import static dagger.tests.integration.ProcessorTestUtils.daggerProcessors; import static java.util.Arrays.asList; import static com.google.common.truth.Truth.assertAbout; /** * Tests that the annotation processor(s) will properly handle the case where * code they are processing and depending on is generated by other processors * in the environment, and so the types they need may not exist yet. */ @RunWith(JUnit4.class) public class GeneratedTypesNotReadyTest { private final JavaFileObject foo = JavaFileObjects.forSourceString("Foo", "" + "package myPackage;\n" + "public interface Foo {}\n" ); private final JavaFileObject main = JavaFileObjects.forSourceString("Main", "" + "import javax.inject.Inject;\n" + "import myPackage.Foo;\n" + "class Main {\n" + " @Inject Foo f;\n" + "}\n" ); @Test public void withstandsMissingTypeReferencedInInjects() { // TODO(cgruber): remove Foo (interface) from this when injects= analysis is fixed. JavaFileObject module = JavaFileObjects.forSourceString("FooModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import myPackage.Foo;\n" + "@Module(injects = { Main.class, myPackage.FooImpl.class })\n" + "class FooModule {\n" + " @Provides Foo provideFoo(myPackage.FooImpl impl) {\n" + " return impl;\n" + " }\n" + "}\n" ); assertAbout(javaSources()) .that(asList(foo, main, module)) .processedWith(concat(asList(new FooImplGenerator()), daggerProcessors())) .compilesWithoutError(); } @Test public void withstandsMissingTypeReferencedInsideModule() { JavaFileObject module = JavaFileObjects.forSourceString("FooModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import myPackage.Foo;\n" + "@Module(injects = { Main.class })\n" + "class FooModule {\n" + " @Provides Foo provideFoo(myPackage.FooImpl impl) {\n" + " return impl;\n" + " }\n" + "}\n" ); assertAbout(javaSources()) .that(asList(foo, module, main)) .processedWith(concat(daggerProcessors(), asList(new FooImplGenerator()))) .compilesWithoutError(); } @Test public void withstandsMissingTypeReferencedByProvidesReturnType() { JavaFileObject main = JavaFileObjects.forSourceString("Main", "" + "import javax.inject.Inject;\n" + "class Main {\n" + " @Inject myPackage.FooImpl f;\n" + "}\n" ); JavaFileObject module = JavaFileObjects.forSourceString("FooModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "@Module(injects = { Main.class })\n" + "class FooModule {\n" + " @Provides myPackage.FooImpl provideFoo() {\n" + " return new myPackage.FooImpl();\n" + " }\n" + "}\n" ); assertAbout(javaSources()) .that(asList(foo, module, main)) .processedWith(concat(daggerProcessors(), asList(new FooImplGenerator()))) .compilesWithoutError(); } @Test public void failsWhenMissingGenericTypeReferencedByProvidesReturnType() { JavaFileObject main = JavaFileObjects.forSourceString("Main", Joiner.on("\n").join( "import javax.inject.Inject;", "class Main {", " @Inject myPackage.FooImpl2 f;", "}")); JavaFileObject module = JavaFileObjects.forSourceString("FooModule", Joiner.on("\n").join( "import dagger.Module;", "import dagger.Provides;", "@Module(injects = { Main.class })", "class FooModule {", " @Provides myPackage.FooImpl2 provideFoo() {", " return new myPackage.FooImpl2();", " }", "}")); assertAbout(javaSources()) .that(asList(foo, module, main)) .processedWith(new FooImplGenerator()) .compilesWithoutError(); assertAbout(javaSources()) .that(asList(foo, module, main)) .processedWith(concat(daggerProcessors(), asList(new FooImplGenerator()))) .failsToCompile() .withErrorContaining("Could not find types required by provides methods for [FooModule]"); } @Test public void withstandsMissingTypeReferencedInTransitiveJITDependency() { JavaFileObject main = JavaFileObjects.forSourceString("Main", "" + "import javax.inject.Inject;\n" + "import myPackage.FooImpl;\n" + "class Main {\n" + " @Inject FooImpl f;\n" + "}\n" ); JavaFileObject module = JavaFileObjects.forSourceString("FooModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "@Module(injects = { Main.class })\n" + "class FooModule {\n" + "}\n" ); assertAbout(javaSources()) .that(asList(foo, module, main)) .processedWith(concat(daggerProcessors(), asList(new FooImplGenerator()))) .compilesWithoutError(); } @Test public void verifyFooImplGeneratorIsCompilingWithoutDagger() { JavaFileObject module = JavaFileObjects.forSourceString("FooModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import myPackage.Foo;\n" + "@Module(injects = { Main.class })\n" + "class FooModule {\n" + " @Provides Foo provideFoo(myPackage.FooImpl impl) {\n" + " return impl;\n" + " }\n" + "}\n" ); assertAbout(javaSources()) .that(asList(foo, module, main)) .processedWith(new FooImplGenerator()) .compilesWithoutError(); } @SupportedAnnotationTypes("*") private final class FooImplGenerator extends AbstractProcessor { boolean written = false; @Override public boolean process(Set na1, RoundEnvironment na2) { if (!written) { try { JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile("FooImpl"); Writer writer = sourceFile.openWriter(); writer.write("" + "package myPackage;\n" + "import javax.inject.Inject;\n" + "public final class FooImpl implements Foo {\n" + " @Inject public FooImpl() { }\n" + "}\n" ); writer.close(); sourceFile = processingEnv.getFiler().createSourceFile("FooImpl2"); writer = sourceFile.openWriter(); writer.write("" + "package myPackage;\n" + "import javax.inject.Inject;\n" + "public final class FooImpl2 implements Foo {\n" + " @Inject public FooImpl2() { }\n" + "}\n" ); writer.close(); written = true; } catch (IOException e) { throw new RuntimeException(e); } } return false; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/validation/LibraryModuleTest.java ================================================ /** * 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.validation; 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; import static dagger.tests.integration.ProcessorTestUtils.daggerProcessors; @RunWith(JUnit4.class) public final class LibraryModuleTest { @Test public void unusedProviderMethodsPassOnLibrary() { JavaFileObject source = JavaFileObjects.forSourceString("Library", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import java.lang.Override;\n" + "@Module(library = true)\n" + "class TestModule {\n" + " @Provides String string() {\n" + " return \"string\";\n" + " }\n" + "}\n" ); assertAbout(javaSource()) .that(source) .processedWith(daggerProcessors()) .compilesWithoutError(); } @Test public void unusedProviderMethodsFailOnNonLibrary() { JavaFileObject source = JavaFileObjects.forSourceString("Library", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import java.lang.Override;\n" + "@Module(library = false)\n" + "class TestModule {\n" + " @Provides String string() {\n" + " return \"string\";\n" + " }\n" + "}\n" ); assertAbout(javaSource()) .that(source) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining("Graph validation failed:").in(source).onLine(5).and() .withErrorContaining("You have these unused @Provider methods:").in(source).onLine(5).and() .withErrorContaining("1. TestModule.string()").in(source).onLine(5).and() .withErrorContaining("Set library=true in your module").in(source).onLine(5); } @Test public void injectsOfInterfaceMakesProvidesBindingNotAnOrphan() { JavaFileObject foo = JavaFileObjects.forSourceString("Foo", "interface Foo {}"); JavaFileObject module = JavaFileObjects.forSourceString("TestModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Singleton;\n" + "@Module(injects = Foo.class, library = false)\n" + "class TestModule {\n" + " @Singleton @Provides Foo provideFoo() {\n" + " return new Foo() {};\n" + " }\n" + "}\n" ); assertAbout(javaSources()) .that(Arrays.asList(foo, module)) .processedWith(daggerProcessors()) .compilesWithoutError(); } @Test public void injectsOfClassMakesProvidesBindingNotAnOrphan() { JavaFileObject foo = JavaFileObjects.forSourceString("Foo", "class Foo {}"); JavaFileObject module = JavaFileObjects.forSourceString("TestModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Singleton;\n" + "@Module(injects = Foo.class, library = false)\n" + "class TestModule {\n" + " @Singleton @Provides Foo provideFoo() {\n" + " return new Foo() {};\n" + " }\n" + "}\n" ); assertAbout(javaSources()) .that(Arrays.asList(foo, module)) .processedWith(daggerProcessors()) .compilesWithoutError(); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/validation/ScopeAnnotationUseTest.java ================================================ /** * 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.validation; 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.tests.integration.ProcessorTestUtils.daggerProcessors; import static java.util.Arrays.asList; /** * Integration tests for the validation processors related to the use * of Scoping Annotations. */ // TODO(cgruber): Audit this class when http://github.com/google/compile-testing // has error/warning counts and other warning predicates available. @RunWith(JUnit4.class) public class ScopeAnnotationUseTest { private static final String ABSTRACTION_SCOPING_TEXT = "Scoping annotations are only allowed on concrete types and @Provides methods:"; // TODO(cgruber): uncomment when http://github.com/google/compile-testing supports warnings. //private static final String MISUSED_SCOPE_TEXT = // "Dagger will ignore scoping annotations on methods that are not @Provides methods:"; @Test public void compileSucceedsScopeOnConcreteType() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import javax.inject.Inject;\n" + "import javax.inject.Singleton;\n" + "@Singleton\n" + "class Test {\n" + " @Inject public Test() { }\n" + "}\n" ); // TODO(cgruber): uncomment when http://github.com/google/compile-testing has hasNoWarnings() assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError(); //.and().hasNoWarnings(); } @Test public void compileSucceedsScopeOnProvidesMethod() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Singleton;\n" + "@Module(library = true, injects = String.class)\n" + "class Test {\n" + " @Provides @Singleton public String provideString() { return \"\"; }\n" + "}\n" ); // TODO(cgruber): uncomment when http://github.com/google/compile-testing has hasNoWarnings() assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError(); //.and().hasNoWarnings(); } @Test public void compileSucceedsWithScopedSuppressedNonProvidesMethod() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @SuppressWarnings(\"scoping\")\n" + " @Singleton void method() { }\n" + "}\n" ); // TODO(cgruber): uncomment when http://github.com/google/compile-testing has hasNoWarnings() assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError(); //.and().hasNoWarnings(); } @Test public void compileSucceedsWithScopedMultiplySuppressedNonProvidesMethod() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @SuppressWarnings({\"blah\", \"scoping\", \"foo\"})\n" + " @Singleton void method() { }\n" + "}\n" ); // TODO(cgruber): uncomment when http://github.com/google/compile-testing has hasNoWarnings() assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError(); //.and().hasNoWarnings(); } @Test public void compileWarnsWithScopedNonProvidesMethod() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @Singleton void method() { }\n" + "}\n" ); // TODO(cgruber): uncomment when http://github.com/google/compile-testing supports warnings. assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError(); //.withWarningContaining(MISUSED_SCOPE_TEXT).in(sourceFile).onLine(3).atColumn(49).and() //.withWarningContaining("Test.method()").in(sourceFile).onLine(3).atColumn(49); } @Test public void compileWarnsWithScopedIncorrectlySuppressedNonProvidesMethod() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @SuppressWarnings(\"some string other than 'scoping'\")\n" + " @Singleton void method() { }\n" + "}\n" ); // TODO(cgruber): uncomment when http://github.com/google/compile-testing supports warnings. assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .compilesWithoutError(); //.withWarningContaining(MISUSED_SCOPE_TEXT).in(sourceFile).onLine(4).atColumn(49).and() //.withWarningContaining("Test.method()").in(sourceFile).onLine(4).atColumn(49); } @Test public void compileFailsWithScopeOnInterface() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @Module(injects = TestType.class) class TestModule { }\n" + " @Singleton interface TestType { }\n" + "}\n" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining(ABSTRACTION_SCOPING_TEXT).in(sourceFile) .onLine(5).atColumn(14).and() .withErrorContaining("Test.TestType") .in(sourceFile).onLine(5).atColumn(14); } @Test public void compileFailsWithScopeOnAbstractClass() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @Module(injects = TestType.class) class TestModule { }\n" + " @Singleton abstract class TestType { }\n" + "}\n" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining(ABSTRACTION_SCOPING_TEXT) .in(sourceFile).onLine(5).atColumn(23).and() .withErrorContaining("Test.TestType") .in(sourceFile).onLine(5).atColumn(23); } @Test public void compileFailsWithScopeOnField() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import javax.inject.Inject;\n" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @Singleton String field;\n" + " @Inject public Test() { }\n" + " @Module(injects = Test.class) class TestModule { }\n" + "}\n" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining(ABSTRACTION_SCOPING_TEXT) .in(sourceFile).onLine(5).atColumn(21).and() .withErrorContaining("Test.field") .in(sourceFile).onLine(5).atColumn(21); } @Test public void compileFailsWithScopeOnMethodParameter() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Singleton;\n" + "@Module(library = true, injects = String.class)\n" + "class Test {\n" + " @Provides int provideInteger() { return 0; }\n" + " @Provides String provideString(@Singleton int intParam) { return \"\"; }\n" + "}\n" ); assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining(ABSTRACTION_SCOPING_TEXT) .in(sourceFile).onLine(7).atColumn(49).and() .withErrorContaining("intParam") .in(sourceFile).onLine(7).atColumn(49); } @Test public void compileFailsWithMultipleScopeAnnotations() { JavaFileObject annotation = JavaFileObjects.forSourceString("MyScope", "" + "import java.lang.annotation.Retention;\n" + "import javax.inject.Scope;\n" + "import static java.lang.annotation.RetentionPolicy.RUNTIME;\n" + "@Scope @Retention(RUNTIME)\n" + "public @interface MyScope { }\n" ); JavaFileObject module = JavaFileObjects.forSourceString("MyModule", "" + "import dagger.Module;\n" + "import dagger.Provides;\n" + "import javax.inject.Singleton;\n" + "@Module(library = true, injects = Injectable.class)\n" + "class MyModule {\n" + " @Provides @Singleton @MyScope String method() { return \"\"; }\n" + "}\n" ); JavaFileObject injectable = JavaFileObjects.forSourceString("Test", "" + "import javax.inject.Inject;\n" + "import javax.inject.Singleton;\n" + "@Singleton @MyScope\n" + "class Injectable {\n" + " @Inject String string;\n" + "}\n" ); String error = "Only one scoping annotation is allowed per element: "; assertAbout(javaSources()) .that(asList(annotation, module, injectable)) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining(error + "MyModule.method()") .in(module).onLine(6).atColumn(40).and() .withErrorContaining(error + "Injectable") .in(injectable).onLine(4).atColumn(1); } @Test public void compileFailsWithScopeOnConstructor() { JavaFileObject sourceFile = JavaFileObjects.forSourceString("Test", "" + "import dagger.Module;\n" + "import javax.inject.Inject;\n" + "import javax.inject.Singleton;\n" + "class Test {\n" + " @Singleton @Inject public Test() { }\n" + " @Module(injects = Test.class) class TestModule { }\n" + "}\n" ); String singletonErrorText = "" + "Singleton annotations have no effect on constructors. " + "Did you mean to annotate the class?"; assertAbout(javaSource()) .that(sourceFile) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining(ABSTRACTION_SCOPING_TEXT) .in(sourceFile).onLine(5).atColumn(29).and() .withErrorContaining("Test.Test()") .in(sourceFile).onLine(5).atColumn(29).and() .withErrorContaining(singletonErrorText) .in(sourceFile).onLine(6).atColumn(33); } } ================================================ FILE: compiler/src/test/java/dagger/tests/integration/validation/SimpleMissingDependencyTest.java ================================================ /** * 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.validation; 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 dagger.tests.integration.ProcessorTestUtils.daggerProcessors; @RunWith(JUnit4.class) public class SimpleMissingDependencyTest { @Test public void missingDependency() { JavaFileObject file = JavaFileObjects.forSourceString("MissingDep", "" + "import dagger.Module;\n" + "import javax.inject.Inject;\n" + "class MissingDep {\n" + " @Inject Dependency dep;\n" + " static interface Dependency {\n" + " void doit();\n" + " }\n" + " @Module(injects = MissingDep.class)\n" + " static class DaModule {\n" + " /* missing */ // @Provides Dependency a() { return new Dependency(); }\n" + " }\n" + "}\n" ); assertAbout(javaSource()) .that(file) .processedWith(daggerProcessors()) .failsToCompile() .withErrorContaining("MissingDep$Dependency could not be bound") .in(file).onLine(9).and() .withErrorContaining("required by MissingDep for MissingDep.DaModule") .in(file).onLine(9); } } ================================================ FILE: core/pom.xml ================================================ 4.0.0 com.squareup.dagger dagger-parent 1.2.6-SNAPSHOT ../pom.xml dagger Dagger javax.inject javax.inject junit junit test com.google.truth truth test org.codehaus.mojo animal-sniffer-maven-plugin 1.8 sniff-api check org.codehaus.mojo.signature java15 1.0 maven-javadoc-plugin dagger.internal:dagger.internal.* maven-jar-plugin test-jar ================================================ FILE: core/src/main/java/dagger/Lazy.java ================================================ /* * 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()}. * *

Example

* The differences between direct injection, provider * injection and lazy injection are best demonstrated * with an example. Start with a module that computes a different integer for * each use:

 *   @Module
 *   public class CounterModule {
 *
 *     int next = 100;
 *
 *     @Provides Integer provideInteger() {
 *       System.out.println("computing...");
 *       return next++;
 *     }
 *   }
 * 
* *

Direct Injection

* This class injects that integer and prints it 3 times:

 *   public class DirectCounter {
 *
 *     @Inject Integer value;
 *
 *     public void print() {
 *       System.out.println("printing...");
 *       System.out.println(value);
 *       System.out.println(value);
 *       System.out.println(value);
 *     }
 *   }
 * 
* Injecting a {@code DirectCounter} and invoking {@code print()} reveals that * the value is computed before it is required:

 *   computing...
 *   printing...
 *   100
 *   100
 *   100
 * 
* *

Provider Injection

* This class injects a {@linkplain javax.inject.Provider provider} for the * integer. It calls {@code Provider.get()} 3 times and prints each result: *

 *   public class ProviderCounter {
 *
 *     @Inject Provider provider;
 *
 *     public void print() {
 *       System.out.println("printing...");
 *       System.out.println(provider.get());
 *       System.out.println(provider.get());
 *       System.out.println(provider.get());
 *     }
 *   }
 * 
* Injecting a {@code ProviderCounter} and invoking {@code print()} shows that * a new value is computed each time {@code Provider.get()} is used:

 *   printing...
 *   computing...
 *   100
 *   computing...
 *   101
 *   computing...
 *   102
 * 
* *

Lazy Injection

* This class injects a {@code Lazy} for the integer. Like the provider above, * it calls {@code Lazy.get()} 3 times and prints each result:

 *   public static class LazyCounter {
 *
 *     @Inject Lazy lazy;
 *
 *     public void print() {
 *       System.out.println("printing...");
 *       System.out.println(lazy.get());
 *       System.out.println(lazy.get());
 *       System.out.println(lazy.get());
 *     }
 *   }
 * 
* 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:

 *   printing...
 *   computing...
 *   100
 *   100
 *   100
 * 
* *

Lazy != Singleton

* 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: *

 *     public void run() {
 *       ObjectGraph graph = ObjectGraph.create(new CounterModule());
 *
 *       LazyCounter counter1 = graph.get(LazyCounter.class);
 *       counter1.print();
 *
 *       LazyCounter counter2 = graph.get(LazyCounter.class);
 *       counter2.print();
 *     }
 * 
* The program's output demonstrates that each {@code Lazy} works independently: *

 *   printing...
 *   computing...
 *   100
 *   100
 *   100
 *   printing...
 *   computing...
 *   101
 *   101
 *   101
 * 
* Use {@linkplain javax.inject.Singleton @Singleton} to share one instance * among all clients, and {@code Lazy} for lazy computation in a single client. */ public interface Lazy { /** * Return the underlying value, computing the value if necessary. All calls to * the same {@code Lazy} instance will return the same result. */ T get(); } ================================================ FILE: core/src/main/java/dagger/MembersInjector.java ================================================ /* * 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 type to inject members of * * @author Bob Lee * @author Jesse Wilson */ public interface MembersInjector { /** * Injects dependencies into the fields and methods of {@code instance}. * Ignores the presence or absence of an injectable constructor. * *

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 to inject members on. May be {@code null}. */ void injectMembers(T instance); } ================================================ FILE: core/src/main/java/dagger/Module.java ================================================ /* * 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 { /** * Returns classes that object graphs created with this module must be able to * inject. This includes both classes passed to {@link ObjectGraph#get}, * the types of instances passed {@link ObjectGraph#inject} and * {@link javax.inject.Inject} annotated classes that need to be scoped to the * resulting object graphs. * *

It is an error to call {@link ObjectGraph#get} or {@link * ObjectGraph#inject} with a type that isn't listed in the {@code injects} * set for any of the object graph's modules. Making such a call will trigger * an {@code IllegalArgumentException} at runtime. * *

Maintaining this set is onerous, but doing so provides benefits to the * application. This set enables dagger to perform more aggressive static * analysis than would be otherwise possible: *

    *
  • Detect missing bindings. Dagger can check that all * injected dependencies can be satisfied. Set {@code complete=false} to * disable this check for the current module. *
  • Detect unused bindings. Dagger can check that all * provides methods are used to satisfy injected dependencies. Set * {@code library=true} to disable this check for the current module. *
*/ Class[] injects() default { }; Class[] staticInjections() default { }; /** * True if {@code @Provides} methods from this module are permitted to * override those of other modules. This is a dangerous feature as it permits * binding conflicts to go unnoticed. It should only be used in test and * development modules. */ boolean overrides() default false; /** * 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 { }; /** * An optional {@code @Module}-annotated class upon which this module can be * {@link ObjectGraph#plus added} to form a complete graph. */ Class addsTo() default Void.class; /** * True if all of the bindings required by this module can also be satisfied * by this module, its {@link #includes} and its {@link #addsTo}. If a module * is complete it is eligible for additional static checking: tools can detect * if required bindings are not available. Modules that have external * dependencies must use {@code complete = false}. */ boolean complete() default true; /** * False if all the included bindings in this module are necessary to satisfy * all of its {@link #injects injectable types}. If a module is not a library * module, it is eligible for additional static checking: tools can detect if * included bindings are not necessary. If you provide bindings that are not * used by this module's graph, then you must declare {@code library = true}. * *

This is intended to help you detect dead code. */ boolean library() default false; } ================================================ FILE: core/src/main/java/dagger/ObjectGraph.java ================================================ /* * 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. */ package dagger; import dagger.internal.Binding; import dagger.internal.BindingsGroup; import dagger.internal.FailoverLoader; import dagger.internal.Keys; import dagger.internal.Linker; import dagger.internal.Loader; import dagger.internal.ModuleAdapter; import dagger.internal.Modules; import dagger.internal.ProblemDetector; import dagger.internal.SetBinding; import dagger.internal.StaticInjection; import dagger.internal.ThrowingErrorHandler; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * A graph of objects linked by their dependencies. * *

The following injection features are supported: *

    *
  • Field injection. A class may have any number of field injections, and * fields may be of any visibility. Static fields will be injected each * time an instance is injected. *
  • Constructor injection. A class may have a single * {@code @Inject}-annotated constructor. Classes that have fields * injected may omit the {@code @Inject} annotation if they have a public * no-arguments constructor. *
  • Injection of {@code @Provides} method parameters. *
  • {@code @Provides} methods annotated {@code @Singleton}. *
  • Constructor-injected classes annotated {@code @Singleton}. *
  • Injection of {@code Provider}s. *
  • Injection of {@code MembersInjector}s. *
  • Qualifier annotations on injected parameters and fields. *
  • JSR 330 annotations. *
* *

The following injection features are not currently supported: *

    *
  • Method injection.
  • *
  • Circular dependencies.
  • *
*/ public abstract class ObjectGraph { ObjectGraph() { } /** * Returns an instance of {@code type}. * * @throws IllegalArgumentException if {@code type} is not one of this object * graph's {@link Module#injects injectable types}. */ public abstract T get(Class type); /** * Injects the members of {@code instance}, including injectable members * inherited from its supertypes. * * @throws IllegalArgumentException if the runtime type of {@code instance} is * not one of this object graph's {@link Module#injects injectable types}. */ public abstract T inject(T instance); /** * Returns a new object graph that includes all of the objects in this graph, * plus additional objects in the {@literal @}{@link Module}-annotated * modules. This graph is a subgraph of the returned graph. * *

The current graph is not modified by this operation: its objects and the * dependency links between them are unchanged. But this graph's objects may * be shared by both graphs. For example, the singletons of this graph may be * injected and used by the returned graph. * *

This does not inject any members or validate the graph. * See {@link #create} for guidance on injection and validation. */ public abstract ObjectGraph plus(Object... modules); /** * Do runtime graph problem detection. For fastest graph creation, rely on * build time tools for graph validation. * * @throws IllegalStateException if this graph has problems. */ public abstract void validate(); /** * Injects the static fields of the classes listed in the object graph's * {@code staticInjections} property. */ public abstract void injectStatics(); /** * Returns a new dependency graph using the {@literal @}{@link * Module}-annotated modules. * *

This does not inject any members. Most applications * should call {@link #injectStatics} to inject static members and {@link * #inject} or get {@link #get(Class)} to inject instance members when this * method has returned. * *

This does not validate the graph. Rely on build time * tools for graph validation, or call {@link #validate} to find problems in * the graph at runtime. */ public static ObjectGraph create(Object... modules) { return DaggerObjectGraph.makeGraph(null, new FailoverLoader(), modules); } // visible for testing static ObjectGraph createWith(Loader loader, Object... modules) { return DaggerObjectGraph.makeGraph(null, loader, modules); } // TODO(cgruber): Move this internal implementation of ObjectGraph into the internal package. static class DaggerObjectGraph extends ObjectGraph { private final DaggerObjectGraph base; private final Linker linker; private final Loader plugin; private final Map, StaticInjection> staticInjections; private final Map> injectableTypes; private final List> setBindings; DaggerObjectGraph(DaggerObjectGraph base, Linker linker, Loader plugin, Map, StaticInjection> staticInjections, Map> injectableTypes, List> setBindings) { this.base = base; this.linker = checkNotNull(linker, "linker"); this.plugin = checkNotNull(plugin, "plugin"); this.staticInjections = checkNotNull(staticInjections, "staticInjections"); this.injectableTypes = checkNotNull(injectableTypes, "injectableTypes"); this.setBindings = checkNotNull(setBindings, "setBindings"); } private static T checkNotNull(T object, String label) { if (object == null) throw new NullPointerException(label); return object; } static ObjectGraph makeGraph(DaggerObjectGraph base, Loader plugin, Object... modules) { Map> injectableTypes = new LinkedHashMap>(); Map, StaticInjection> staticInjections = new LinkedHashMap, StaticInjection>(); StandardBindings baseBindings = (base == null) ? new StandardBindings() : new StandardBindings(base.setBindings); BindingsGroup overrideBindings = new OverridesBindings(); Map, Object> loadedModules = Modules.loadModules(plugin, modules); for (Entry, Object> loadedModule : loadedModules.entrySet()) { ModuleAdapter moduleAdapter = (ModuleAdapter) loadedModule.getKey(); for (int i = 0; i < moduleAdapter.injectableTypes.length; i++) { injectableTypes.put(moduleAdapter.injectableTypes[i], moduleAdapter.moduleClass); } for (int i = 0; i < moduleAdapter.staticInjections.length; i++) { staticInjections.put(moduleAdapter.staticInjections[i], null); } try { BindingsGroup addTo = moduleAdapter.overrides ? overrideBindings : baseBindings; moduleAdapter.getBindings(addTo, loadedModule.getValue()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException( moduleAdapter.moduleClass.getSimpleName() + ": " + e.getMessage(), e); } } // Create a linker and install all of the user's bindings Linker linker = new Linker((base != null) ? base.linker : null, plugin, new ThrowingErrorHandler()); linker.installBindings(baseBindings); linker.installBindings(overrideBindings); return new DaggerObjectGraph( base, linker, plugin, staticInjections, injectableTypes, baseBindings.setBindings); } @Override public ObjectGraph plus(Object... modules) { linkEverything(); return makeGraph(this, plugin, modules); } private void linkStaticInjections() { for (Map.Entry, StaticInjection> entry : staticInjections.entrySet()) { StaticInjection staticInjection = entry.getValue(); if (staticInjection == null) { staticInjection = plugin.getStaticInjection(entry.getKey()); entry.setValue(staticInjection); } staticInjection.attach(linker); } } private void linkInjectableTypes() { for (Map.Entry> entry : injectableTypes.entrySet()) { linker.requestBinding(entry.getKey(), entry.getValue(), entry.getValue().getClassLoader(), false, true); } } @Override public void validate() { Map> allBindings = linkEverything(); new ProblemDetector().detectProblems(allBindings.values()); } /** * Links all bindings, injectable types and static injections. */ private Map> linkEverything() { Map> bindings = linker.fullyLinkedBindings(); if (bindings != null) { return bindings; } synchronized (linker) { if ((bindings = linker.fullyLinkedBindings()) != null) { return bindings; } linkStaticInjections(); linkInjectableTypes(); return linker.linkAll(); // Linker.linkAll() implicitly does Linker.linkRequested(). } } @Override public void injectStatics() { // We call linkStaticInjections() twice on purpose. The first time through // we request all of the bindings we need. The linker returns null for // bindings it doesn't have. Then we ask the linker to link all of those // requested bindings. Finally we call linkStaticInjections() again: this // time the linker won't return null because everything has been linked. synchronized (linker) { linkStaticInjections(); linker.linkRequested(); linkStaticInjections(); } for (Map.Entry, StaticInjection> entry : staticInjections.entrySet()) { entry.getValue().inject(); } } @Override public T get(Class type) { String key = Keys.get(type); String injectableTypeKey = type.isInterface() ? key : Keys.getMembersKey(type); ClassLoader classLoader = type.getClassLoader(); @SuppressWarnings("unchecked") // The linker matches keys to bindings by their type. Binding binding = (Binding) getInjectableTypeBinding(classLoader, injectableTypeKey, key); return binding.get(); } @Override public T inject(T instance) { String membersKey = Keys.getMembersKey(instance.getClass()); ClassLoader classLoader = instance.getClass().getClassLoader(); @SuppressWarnings("unchecked") // The linker matches keys to bindings by their type. Binding binding = (Binding) getInjectableTypeBinding(classLoader, membersKey, membersKey); binding.injectMembers(instance); return instance; } /** * @param classLoader the {@code ClassLoader} used to load dependent bindings. * @param injectableKey the key used to store the injectable type. This * is a provides key for interfaces and a members injection key for * other types. That way keys can always be created, even if the type * has no injectable constructor. * @param key the key to use when retrieving the binding. This may be a * regular (provider) key or a members key. */ private Binding getInjectableTypeBinding( ClassLoader classLoader, String injectableKey, String key) { Class moduleClass = null; for (DaggerObjectGraph graph = this; graph != null; graph = graph.base) { moduleClass = graph.injectableTypes.get(injectableKey); if (moduleClass != null) break; } if (moduleClass == null) { throw new IllegalArgumentException("No inject registered for " + injectableKey + ". You must explicitly add it to the 'injects' option in one of your modules."); } synchronized (linker) { Binding binding = linker.requestBinding(key, moduleClass, classLoader, false, true); if (binding == null || !binding.isLinked()) { linker.linkRequested(); binding = linker.requestBinding(key, moduleClass, classLoader, false, true); } return binding; } } } /** * A BindingsGroup which fails when existing values are clobbered and sets aside * {@link SetBinding}. */ private static final class StandardBindings extends BindingsGroup { private final List> setBindings; public StandardBindings() { setBindings = new ArrayList>(); } public StandardBindings(List> baseSetBindings) { setBindings = new ArrayList>(baseSetBindings.size()); for (SetBinding sb : baseSetBindings) { @SuppressWarnings({ "rawtypes", "unchecked" }) SetBinding child = new SetBinding(sb); setBindings.add(child); put(child.provideKey, child); } } @Override public Binding contributeSetBinding(String key, SetBinding value) { setBindings.add(value); return super.put(key, value); } } /** * A BindingsGroup which throws an {@link IllegalArgumentException} when a * {@link SetBinding} is contributed, since overrides modules cannot contribute such * bindings. */ private static final class OverridesBindings extends BindingsGroup { OverridesBindings() { } @Override public Binding contributeSetBinding(String key, SetBinding value) { throw new IllegalArgumentException("Module overrides cannot contribute set bindings."); } } } ================================================ FILE: core/src/main/java/dagger/Provides.java ================================================ /* * 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 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}, and the * returned value is contributed to the set. The object graph will pass dependencies to the * method as parameters. The {@code Set} produced from the accumulation of values will be * immutable. */ SET, /** * Like {@link #SET}, except the method's return type is {@code Set}, 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; } Type type() default Type.UNIQUE; } ================================================ FILE: core/src/main/java/dagger/internal/ArrayQueue.java ================================================ /* * Written by Josh Bloch of Google Inc. and released to the public domain, * as explained at http://creativecommons.org/publicdomain/zero/1.0/. * * Adapted from https://android.googlesource.com/platform/libcore/+ * android-4.2.2_r1/luni/src/main/java/java/util/ArrayDeque.java */ package dagger.internal; import java.lang.reflect.Array; import java.util.AbstractCollection; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; /** * Resizable-array implementation of the {@link Queue} interface. Array * queues have no capacity restrictions; they grow as necessary to support * usage. They are not thread-safe; in the absence of external * synchronization, they do not support concurrent access by multiple threads. * Null elements are prohibited. This class is likely to be faster than * {@link LinkedList} when used as a queue. * *

Most ArrayBackedQueue operations run in amortized constant time. * Exceptions include {@link #remove(Object) remove}, {@link * #removeFirstOccurrence removeFirstOccurrence}, {@link #contains contains}, * {@link #iterator iterator.remove()}, and the bulk operations, all of which * run in linear time. * *

The iterators returned by this class's iterator method are * fail-fast: If the queue is modified at any time after the iterator * is created, in any way except through the iterator's own remove * method, the iterator will generally throw a {@link * ConcurrentModificationException}. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking * arbitrary, non-deterministic behavior at an undetermined time in the * future. * *

Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators * throw ConcurrentModificationException on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: the fail-fast behavior of iterators * should be used only to detect bugs. * *

This class and its iterator implement all of the * optional methods of the {@link Collection} and {@link * Iterator} interfaces. * * @author Josh Bloch and Doug Lea * @param the type of elements held in this collection */ public class ArrayQueue extends AbstractCollection implements Queue, Cloneable, java.io.Serializable { /** * The array in which the elements of the queue are stored. * The capacity of the queue is the length of this array, which is * always a power of two. The array is never allowed to become * full, except transiently within an addX method where it is * resized (see doubleCapacity) immediately upon becoming full, * thus avoiding head and tail wrapping around to equal each * other. We also guarantee that all array cells not holding * queue elements are always null. */ private transient Object[] elements; /** * The index of the element at the head of the queue (which is the * element that would be removed by remove() or pop()); or an * arbitrary number equal to tail if the queue is empty. */ private transient int head; /** * The index at which the next element would be added to the tail * of the queue (via addLast(E), add(E), or push(E)). */ private transient int tail; /** * The minimum capacity that we'll use for a newly created queue. * Must be a power of 2. */ private static final int MIN_INITIAL_CAPACITY = 8; // ****** Array allocation and resizing utilities ****** /** * Allocate empty array to hold the given number of elements. * * @param numElements the number of elements to hold */ private void allocateElements(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. // Tests "<=" because arrays aren't kept full. if (numElements >= initialCapacity) { initialCapacity = numElements; initialCapacity |= (initialCapacity >>> 1); initialCapacity |= (initialCapacity >>> 2); initialCapacity |= (initialCapacity >>> 4); initialCapacity |= (initialCapacity >>> 8); initialCapacity |= (initialCapacity >>> 16); initialCapacity++; if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements } elements = new Object[initialCapacity]; } /** * Double the capacity of this queue. Call only when full, i.e., * when head and tail have wrapped around to become equal. */ private void doubleCapacity() { // assert head == tail; int p = head; int n = elements.length; int r = n - p; // number of elements to the right of p int newCapacity = n << 1; if (newCapacity < 0) throw new IllegalStateException("Sorry, queue too big"); Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r); System.arraycopy(elements, 0, a, r, p); elements = a; head = 0; tail = n; } /** * Constructs an empty array queue with an initial capacity * sufficient to hold 16 elements. */ public ArrayQueue() { elements = new Object[16]; } /** * Constructs an empty array queue with an initial capacity * sufficient to hold the specified number of elements. * * @param numElements lower bound on initial capacity of the queue */ public ArrayQueue(int numElements) { allocateElements(numElements); } /** * Constructs a queue containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. (The first element returned by the collection's * iterator becomes the first element, or front of the * queue.) * * @param c the collection whose elements are to be placed into the queue * @throws NullPointerException if the specified collection is null */ public ArrayQueue(Collection c) { allocateElements(c.size()); addAll(c); } /** * Inserts the specified element at the end of this queue. * *

This method is equivalent to {@link #offer}. * * @param e the element to add * @return true (as specified by {@link Collection#add}) * @throws NullPointerException if the specified element is null */ @Override public boolean add(E e) { if (e == null) throw new NullPointerException("e == null"); elements[tail] = e; if ((tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity(); return true; } /** * Inserts the specified element at the end of this queue. * * @param e the element to add * @return true (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ @Override public boolean offer(E e) { return add(e); } /** * Retrieves and removes the head of the queue represented by this queue. * * This method differs from {@link #poll poll} only in that it throws an * exception if this queue is empty. * * @return the head of the queue represented by this queue * @throws NoSuchElementException {@inheritDoc} */ @Override public E remove() { E x = poll(); if (x == null) throw new NoSuchElementException(); return x; } /** * Retrieves and removes the head of the queue represented by this queue * (in other words, the first element of this queue), or returns * null if this queue is empty. * * @return the head of the queue represented by this queue, or * null if this queue is empty */ @Override public E poll() { int h = head; @SuppressWarnings("unchecked") E result = (E) elements[h]; // Element is null if queue empty if (result == null) return null; elements[h] = null; // Must null out slot head = (h + 1) & (elements.length - 1); return result; } /** * Retrieves, but does not remove, the head of the queue represented by * this queue. This method differs from {@link #peek peek} only in * that it throws an exception if this queue is empty. * * @return the head of the queue represented by this queue * @throws NoSuchElementException {@inheritDoc} */ @Override public E element() { @SuppressWarnings("unchecked") E result = (E) elements[head]; if (result == null) throw new NoSuchElementException(); return result; } /** * Retrieves, but does not remove, the head of the queue represented by * this queue, or returns null if this queue is empty. * * @return the head of the queue represented by this queue, or * null if this queue is empty */ @Override public E peek() { @SuppressWarnings("unchecked") E result = (E) elements[head]; // elements[head] is null if queue empty return result; } /** * Removes the element at the specified position in the elements array, * adjusting head and tail as necessary. This can result in motion of * elements backwards or forwards in the array. * *

This method is called delete rather than remove to emphasize * that its semantics differ from those of {@link List#remove(int)}. * * @return true if elements moved backwards */ private boolean delete(int i) { //checkInvariants(); final Object[] elements = this.elements; final int mask = elements.length - 1; final int h = head; final int t = tail; final int front = (i - h) & mask; final int back = (t - i) & mask; // Invariant: head <= i < tail mod circularity if (front >= ((t - h) & mask)) throw new ConcurrentModificationException(); // Optimize for least element motion if (front < back) { if (h <= i) { System.arraycopy(elements, h, elements, h + 1, front); } else { // Wrap around System.arraycopy(elements, 0, elements, 1, i); elements[0] = elements[mask]; System.arraycopy(elements, h, elements, h + 1, mask - h); } elements[h] = null; head = (h + 1) & mask; return false; } else { if (i < t) { // Copy the null tail as well System.arraycopy(elements, i + 1, elements, i, back); tail = t - 1; } else { // Wrap around System.arraycopy(elements, i + 1, elements, i, mask - i); elements[mask] = elements[0]; System.arraycopy(elements, 1, elements, 0, t); tail = (t - 1) & mask; } return true; } } // *** Collection Methods *** /** * Returns the number of elements in this queue. * * @return the number of elements in this queue */ @Override public int size() { return (tail - head) & (elements.length - 1); } /** * Returns true if this queue contains no elements. * * @return true if this queue contains no elements */ @Override public boolean isEmpty() { return head == tail; } /** * Returns an iterator over the elements in this queue. The elements * will be ordered from first (head) to last (tail). This is the same * order that elements would be queueued (via successive calls to * {@link #remove} or popped (via successive calls to {@link #pop}). * * @return an iterator over the elements in this queue */ @Override public Iterator iterator() { return new QueueIterator(); } private class QueueIterator implements Iterator { /** * Index of element to be returned by subsequent call to next. */ private int cursor = head; /** * Tail recorded at construction (also in remove), to stop * iterator and also to check for comodification. */ private int fence = tail; /** * Index of element returned by most recent call to next. * Reset to -1 if element is deleted by a call to remove. */ private int lastRet = -1; @Override public boolean hasNext() { return cursor != fence; } @Override public E next() { if (cursor == fence) throw new NoSuchElementException(); @SuppressWarnings("unchecked") E result = (E) elements[cursor]; // This check doesn't catch all possible comodifications, // but does catch the ones that corrupt traversal if (tail != fence || result == null) throw new ConcurrentModificationException(); lastRet = cursor; cursor = (cursor + 1) & (elements.length - 1); return result; } @Override public void remove() { if (lastRet < 0) throw new IllegalStateException(); if (delete(lastRet)) { // if left-shifted, undo increment in next() cursor = (cursor - 1) & (elements.length - 1); fence = tail; } lastRet = -1; } } /** * Returns true if this queue contains the specified element. * More formally, returns true if and only if this queue contains * at least one element e such that o.equals(e). * * @param o object to be checked for containment in this queue * @return true if this queue contains the specified element */ @Override public boolean contains(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; Object x; while ((x = elements[i]) != null) { if (o.equals(x)) return true; i = (i + 1) & mask; } return false; } /** * Removes a single instance of the specified element from this queue. * If the queue does not contain the element, it is unchanged. * More formally, removes the first element e such that * o.equals(e) (if such an element exists). * Returns true if this queue contained the specified element * (or equivalently, if this queue changed as a result of the call). * * @param o element to be removed from this queue, if present * @return true if this queue contained the specified element */ @Override public boolean remove(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; Object x; while ((x = elements[i]) != null) { if (o.equals(x)) { delete(i); return true; } i = (i + 1) & mask; } return false; } /** * Removes all of the elements from this queue. * The queue will be empty after this call returns. */ @Override public void clear() { int h = head; int t = tail; if (h != t) { // clear all cells head = tail = 0; int i = h; int mask = elements.length - 1; do { elements[i] = null; i = (i + 1) & mask; } while (i != t); } } /** * Returns an array containing all of the elements in this queue * in proper sequence (from first to last element). * *

The returned array will be "safe" in that no references to it are * maintained by this queue. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. * *

This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this queue */ @Override public Object[] toArray() { return toArray(new Object[size()]); } /** * Returns an array containing all of the elements in this queue in * proper sequence (from first to last element); the runtime type of the * returned array is that of the specified array. If the queue fits in * the specified array, it is returned therein. Otherwise, a new array * is allocated with the runtime type of the specified array and the * size of this queue. * *

If this queue fits in the specified array with room to spare * (i.e., the array has more elements than this queue), the element in * the array immediately following the end of the queue is set to * null. * *

Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * *

Suppose x is a queue known to contain only strings. * The following code can be used to dump the queue into a newly * allocated array of String: * *

 {@code String[] y = x.toArray(new String[0]);}
* * Note that toArray(new Object[0]) is identical in function to * toArray(). * * @param a the array into which the elements of the queue are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose * @return an array containing all of the elements in this queue * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in * this queue * @throws NullPointerException if the specified array is null */ @Override public T[] toArray(T[] a) { int size = size(); if (a.length < size) a = (T[]) java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); if (head < tail) { System.arraycopy(elements, head, a, 0, size()); } else if (head > tail) { int headPortionLen = elements.length - head; System.arraycopy(elements, head, a, 0, headPortionLen); System.arraycopy(elements, 0, a, headPortionLen, tail); } if (a.length > size) a[size] = null; return a; } // *** Object methods *** /** * Returns a copy of this queue. * * @return a copy of this queue */ @Override public ArrayQueue clone() { try { ArrayQueue result = (ArrayQueue) super.clone(); E[] newElements = (E[]) Array.newInstance(elements.getClass().getComponentType(), elements.length); System.arraycopy(elements, 0, newElements, 0, elements.length); result.elements = newElements; return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } /** * Appease the serialization gods. */ private static final long serialVersionUID = 2340985798034038923L; /** * Serialize this queue. * * @serialData The current size (int) of the queue, * followed by all of its elements (each an object reference) in * first-to-last order. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); // Write out size s.writeInt(size()); // Write out elements in order. int mask = elements.length - 1; for (int i = head; i != tail; i = (i + 1) & mask) s.writeObject(elements[i]); } /** * Deserialize this queue. */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); // Read in size and allocate array int size = s.readInt(); allocateElements(size); head = 0; tail = size; // Read in all elements in the proper order. for (int i = 0; i < size; i++) elements[i] = s.readObject(); } } ================================================ FILE: core/src/main/java/dagger/internal/Binding.java ================================================ /* * 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.internal; import dagger.MembersInjector; import java.util.Set; import javax.inject.Provider; /** * Injects a value of a specific type. */ public abstract class Binding implements Provider, MembersInjector { public static final Binding UNRESOLVED = new Binding(null, null, false, null) { @Override public Object get() { throw new AssertionError("Unresolved binding should never be called to inject."); } @Override public void injectMembers(Object t) { throw new AssertionError("Unresolved binding should never be called to inject."); } }; protected static final boolean IS_SINGLETON = true; protected static final boolean NOT_SINGLETON = false; /** Set if the provided instance is always the same object. */ private static final int SINGLETON = 1 << 0; /** Set if this binding's {@link #attach} completed without any missing dependencies. */ private static final int LINKED = 1 << 1; /** Set if {@link ProblemDetector} is actively visiting this binding. */ private static final int VISITING = 1 << 2; /** Set if {@link ProblemDetector} has confirmed this binding has no circular dependencies. */ private static final int CYCLE_FREE = 1 << 3; private static final int DEPENDED_ON = 1 << 4; private static final int LIBRARY = 1 << 5; /** The key used to provide instances of 'T', or null if this binding cannot provide instances. */ public final String provideKey; /** The key used to inject members of 'T', or null if this binding cannot inject members. */ public final String membersKey; /** Bitfield of states like SINGLETON and LINKED. */ private int bits; public final Object requiredBy; protected Binding(String provideKey, String membersKey, boolean singleton, Object requiredBy) { if (singleton && provideKey == null) { throw new InvalidBindingException(Keys.getClassName(membersKey), "is exclusively members injected and therefore cannot be scoped"); } this.provideKey = provideKey; this.membersKey = membersKey; this.requiredBy = requiredBy; this.bits = (singleton ? SINGLETON : 0); } /** * Links this binding to its dependencies. */ public void attach(Linker linker) { } @Override public void injectMembers(T t) { // If no members to inject, no-op. Some classes will have no injectable members even // if their supertypes do. } @Override public T get() { throw new UnsupportedOperationException("No injectable constructor on " + getClass().getName()); } /** * Populates {@code getBindings} and {@code injectMembersBindings} with the * bindings used by this binding to satisfy {@link #get} and {@link * #injectMembers} calls, respectively. * * @param getBindings the bindings required by this binding's {@code get} * method. Although {@code get} usually calls into {@code injectMembers}, * this does not contain the injectMembers bindings. * @param injectMembersBindings the bindings required by this binding's {@code * injectMembers} method. */ public void getDependencies(Set> getBindings, Set> injectMembersBindings) { // Do nothing. No override == no dependencies to contribute. } void setLinked() { bits |= LINKED; } public boolean isLinked() { return (bits & LINKED) != 0; } boolean isSingleton() { return (bits & SINGLETON) != 0; } public boolean isVisiting() { return (bits & VISITING) != 0; } public void setVisiting(boolean visiting) { this.bits = visiting ? (bits | VISITING) : (bits & ~VISITING); } public boolean isCycleFree() { return (bits & CYCLE_FREE) != 0; } public void setCycleFree(boolean cycleFree) { this.bits = cycleFree ? (bits | CYCLE_FREE) : (bits & ~CYCLE_FREE); } public void setLibrary(boolean library) { this.bits = library ? (bits | LIBRARY) : (bits & ~LIBRARY); } public boolean library() { return (bits & LIBRARY) != 0; } public void setDependedOn(boolean dependedOn) { this.bits = dependedOn ? (bits | DEPENDED_ON) : (bits & ~DEPENDED_ON); } public boolean dependedOn() { return (bits & DEPENDED_ON) != 0; } @Override public String toString() { return getClass().getSimpleName() + "[provideKey=\"" + provideKey + "\", memberskey=\"" + membersKey + "\"]"; } /** An exception thrown by anything attempting to construct a binding which is invalid. */ public static class InvalidBindingException extends RuntimeException { public final String type; public InvalidBindingException(String type, String error) { super(error); this.type = type; } } } ================================================ FILE: core/src/main/java/dagger/internal/BindingsGroup.java ================================================ /* * Copyright (C) 2013 Square, Inc. * Copyright (C) 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.Map.Entry; import java.util.Set; /** * A grouping of bindings that fails when existing values are clobbered, to be used in collecting * the initial set of bindings for a graph (from provides methods). */ public abstract class BindingsGroup { private final Map> bindings = new LinkedHashMap>(); public abstract Binding contributeSetBinding(String key, SetBinding value); public Binding contributeProvidesBinding(String key, ProvidesBinding value) { return put(key, value); } protected Binding put(String key, Binding value) { Binding clobbered = bindings.put(key, value); if (clobbered != null) { bindings.put(key, clobbered); // Put things back as they were. throw new IllegalArgumentException("Duplicate:\n " + clobbered + "\n " + value); } return null; } public Binding get(String key) { return bindings.get(key); } public final Set>> entrySet() { return bindings.entrySet(); } @Override public String toString() { return getClass().getSimpleName() + bindings.toString(); } } ================================================ FILE: core/src/main/java/dagger/internal/BuiltInBinding.java ================================================ /* * 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.internal; /** * Injects a Provider or a MembersInjector. */ final class BuiltInBinding extends Binding { private final String delegateKey; private final ClassLoader classLoader; private Binding delegate; public BuiltInBinding( String key, Object requiredBy, ClassLoader classLoader, String delegateKey) { super(key, null, false, requiredBy); this.classLoader = classLoader; this.delegateKey = delegateKey; } @Override public void attach(Linker linker) { delegate = linker.requestBinding(delegateKey, requiredBy, classLoader); } @Override public void injectMembers(T t) { throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") // At runtime we know 'T' is a Provider or MembersInjector. @Override public T get() { return (T) delegate; } public Binding getDelegate() { return delegate; } // public void getDependencies() not overridden. // We don't add 'delegate' because it isn't actually used by get() or injectMembers(). } ================================================ FILE: core/src/main/java/dagger/internal/FailoverLoader.java ================================================ /* * Copyright (C) 2013 Square, Inc. * Copyright (C) 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.internal.loaders.ReflectiveAtInjectBinding; import dagger.internal.loaders.ReflectiveStaticInjection; import static dagger.internal.loaders.GeneratedAdapters.INJECT_ADAPTER_SUFFIX; import static dagger.internal.loaders.GeneratedAdapters.MODULE_ADAPTER_SUFFIX; import static dagger.internal.loaders.GeneratedAdapters.STATIC_INJECTION_SUFFIX; /** * Handles loading/finding of modules, injection bindings, and static injections by use of a * strategy of "load the appropriate generated code" or, if no such code is found, create a * reflective equivalent. */ public final class FailoverLoader extends Loader { /* * Note that String.concat is used throughout this code because it is the most efficient way to * concatenate _two_ strings. javac uses StringBuilder for the + operator and it has proven to * be wasteful in terms of both CPU and memory allocated. */ private final Memoizer, ModuleAdapter> loadedAdapters = new Memoizer, ModuleAdapter>() { @Override protected ModuleAdapter create(Class type) { ModuleAdapter result = instantiate(type.getName().concat(MODULE_ADAPTER_SUFFIX), type.getClassLoader()); if (result == null) { throw new IllegalStateException("Module adapter for " + type + " could not be loaded. " + "Please ensure that code generation was run for this module."); } return result; } }; /** * Obtains a module adapter for {@code module} from the first responding resolver. */ @SuppressWarnings("unchecked") // cache ensures types match @Override public ModuleAdapter getModuleAdapter(Class type) { return (ModuleAdapter) loadedAdapters.get(type); } @Override public Binding getAtInjectBinding( String key, String className, ClassLoader classLoader, boolean mustHaveInjections) { Binding result = instantiate(className.concat(INJECT_ADAPTER_SUFFIX), classLoader); if (result != null) { return result; // Found loadable adapter, returning it. } Class type = loadClass(classLoader, className); if (type.equals(Void.class)) { throw new IllegalStateException( String.format("Could not load class %s needed for binding %s", className, key)); } if (type.isInterface()) { return null; // Short-circuit since we can't build reflective bindings for interfaces. } return ReflectiveAtInjectBinding.create(type, mustHaveInjections); } @Override public StaticInjection getStaticInjection(Class injectedClass) { StaticInjection result = instantiate( injectedClass.getName().concat(STATIC_INJECTION_SUFFIX), injectedClass.getClassLoader()); if (result != null) { return result; } return ReflectiveStaticInjection.create(injectedClass); } } ================================================ FILE: core/src/main/java/dagger/internal/Keys.java ================================================ /* * 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. */ package dagger.internal; import dagger.Lazy; import dagger.MembersInjector; import java.lang.annotation.Annotation; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Set; import javax.inject.Provider; import javax.inject.Qualifier; /** * Formats strings that identify the value to be injected. Keys are of one of * three forms: *
    *
  1. {@code com.square.Foo}: provides instances of Foo. *
  2. {@code @com.square.Bar/com.square.Foo}: provides instances of Foo * qualified by the annotation. *
  3. {@code members/com.square.Foo}: injects members of Foo. *
* Bindings from {@code @Provides} methods are of the first two types. BindingsGroup * created from {@code @Inject}-annotated members of a class are of the first * and last types. */ public final class Keys { private static final String PROVIDER_PREFIX = Provider.class.getCanonicalName() + "<"; private static final String MEMBERS_INJECTOR_PREFIX = MembersInjector.class.getCanonicalName() + "<"; private static final String LAZY_PREFIX = Lazy.class.getCanonicalName() + "<"; private static final String SET_PREFIX = Set.class.getCanonicalName() + "<"; private static final Memoizer, Boolean> IS_QUALIFIER_ANNOTATION = new Memoizer, Boolean>() { @Override protected Boolean create(Class annotationType) { return annotationType.isAnnotationPresent(Qualifier.class); } }; Keys() { } /** Returns a key for {@code type} with no annotation. */ public static String get(Type type) { return get(type, null); } /** Returns a key for the members of {@code type}. */ public static String getMembersKey(Class key) { // for classes key.getName() is equivalent to get(key) return "members/".concat(key.getName()); } /** Returns a key for {@code type} annotated by {@code annotation}. */ private static String get(Type type, Annotation annotation) { type = boxIfPrimitive(type); if (annotation == null && type instanceof Class && !((Class) type).isArray()) { return ((Class) type).getName(); } StringBuilder result = new StringBuilder(); if (annotation != null) { result.append(annotation).append("/"); } typeToString(type, result, true); return result.toString(); } /** * Returns a key for {@code type} annotated with {@code annotations}, * wrapped by {@code Set}, reporting failures against {@code subject}. * * @param annotations the annotations on a single method, field or parameter. * This array may contain at most one qualifier annotation. */ public static String getSetKey(Type type, Annotation[] annotations, Object subject) { Annotation qualifier = extractQualifier(annotations, subject); type = boxIfPrimitive(type); StringBuilder result = new StringBuilder(); if (qualifier != null) { result.append(qualifier).append("/"); } result.append(SET_PREFIX); typeToString(type, result, true); result.append(">"); return result.toString(); } /** * Returns a key for {@code type} annotated with {@code annotations}, * reporting failures against {@code subject}. * * @param annotations the annotations on a single method, field or parameter. * This array may contain at most one qualifier annotation. */ public static String get(Type type, Annotation[] annotations, Object subject) { return get(type, extractQualifier(annotations, subject)); } /** * Validates that among {@code annotations} there exists only one annotation which is, itself * qualified by {@code \@Qualifier} */ private static Annotation extractQualifier(Annotation[] annotations, Object subject) { Annotation qualifier = null; for (Annotation a : annotations) { if (!IS_QUALIFIER_ANNOTATION.get(a.annotationType())) { continue; } if (qualifier != null) { throw new IllegalArgumentException("Too many qualifier annotations on " + subject); } qualifier = a; } return qualifier; } /** * @param topLevel true if this is a top-level type where primitive types * like 'int' are forbidden. Recursive calls pass 'false' to support * arrays like {@code int[]}. */ private static void typeToString(Type type, StringBuilder result, boolean topLevel) { if (type instanceof Class) { Class c = (Class) type; if (c.isArray()) { typeToString(c.getComponentType(), result, false); result.append("[]"); } else if (c.isPrimitive()) { if (topLevel) { throw new UnsupportedOperationException("Uninjectable type " + c.getName()); } result.append(c.getName()); } else { result.append(c.getName()); } } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; typeToString(parameterizedType.getRawType(), result, true); Type[] arguments = parameterizedType.getActualTypeArguments(); result.append("<"); for (int i = 0; i < arguments.length; i++) { if (i != 0) { result.append(", "); } typeToString(arguments[i], result, true); } result.append(">"); } else if (type instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) type; typeToString(genericArrayType.getGenericComponentType(), result, false); result.append("[]"); } else { throw new UnsupportedOperationException("Uninjectable type " + type); } } /** * Returns a key for the type provided by, or injected by this key. For * example, if this is a key for a {@code Provider}, this returns the * key for {@code Foo}. This retains annotations and supports both Provider * keys and MembersInjector keys. */ static String getBuiltInBindingsKey(String key) { int start = startOfType(key); if (substringStartsWith(key, start, PROVIDER_PREFIX)) { return extractKey(key, start, key.substring(0, start), PROVIDER_PREFIX); } else if (substringStartsWith(key, start, MEMBERS_INJECTOR_PREFIX)) { return extractKey(key, start, "members/", MEMBERS_INJECTOR_PREFIX); } else { return null; } } /** * Returns a key for the underlying binding of a Lazy value. For example, * if this is a key for a {@code Lazy}, this returns the key for * {@code Foo}. This retains annotations. */ static String getLazyKey(String key) { int start = startOfType(key); if (substringStartsWith(key, start, LAZY_PREFIX)) { return extractKey(key, start, key.substring(0, start), LAZY_PREFIX); } else { return null; } } /** * Returns the start of a key if it is a plain key, and the start of the * underlying key if it is an annotated key */ private static int startOfType(String key) { return (key.startsWith("@")) ? key.lastIndexOf('/') + 1 : 0; } /** * Returns an unwrapped key (the key for T from a Provider for example), * removing all wrapping key information, but preserving annotations or known * prefixes. * * @param key the key from which the delegate key should be extracted. * @param start * an index into the key representing the key's "real" start after * any annotations. * @param delegatePrefix * key prefix elements extracted from the underlying delegate * (annotations, "members/", etc.) * @param prefix the prefix to strip. */ private static String extractKey(String key, int start, String delegatePrefix, String prefix) { return delegatePrefix + key.substring(start + prefix.length(), key.length() - 1); } /** Returns true if {@code string.substring(offset).startsWith(substring)}. */ private static boolean substringStartsWith(String string, int offset, String substring) { return string.regionMatches(offset, substring, 0, substring.length()); } /** Returns true if {@code key} has a qualifier annotation. */ public static boolean isAnnotated(String key) { return key.startsWith("@"); } /** * Returns the class name for {@code key}, if {@code key} was created with a * class instance. Returns null if {@code key} represents a parameterized type * or an array type. */ public static String getClassName(String key) { int start = 0; if (key.startsWith("@") || key.startsWith("members/")) { start = key.lastIndexOf('/') + 1; } return (key.indexOf('<', start) == -1 && key.indexOf('[', start) == -1) ? key.substring(start) : null; } /** Returns true if {@code name} is the name of a platform-provided class. */ public static boolean isPlatformType(String name) { return name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android."); } private static Type boxIfPrimitive(Type type) { if (type == byte.class) return Byte.class; if (type == short.class) return Short.class; if (type == int.class) return Integer.class; if (type == long.class) return Long.class; if (type == char.class) return Character.class; if (type == boolean.class) return Boolean.class; if (type == float.class) return Float.class; if (type == double.class) return Double.class; if (type == void.class) return Void.class; return type; } } ================================================ FILE: core/src/main/java/dagger/internal/LazyBinding.java ================================================ /* * 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.internal; import dagger.Lazy; /** * Injects a Lazy wrapper for a type T */ final class LazyBinding extends Binding> { final static Object NOT_PRESENT = new Object(); private final String lazyKey; private final ClassLoader loader; Binding delegate; LazyBinding(String key, Object requiredBy, ClassLoader loader, String lazyKey) { super(key, null, false, requiredBy); this.loader = loader; this.lazyKey = lazyKey; } @SuppressWarnings("unchecked") // At runtime we know it's a Binding>. @Override public void attach(Linker linker) { delegate = (Binding) linker.requestBinding(lazyKey, requiredBy, loader); } @Override public void injectMembers(Lazy t) { throw new UnsupportedOperationException(); // Injecting into a custom Lazy not supported. } @Override public Lazy get() { return new Lazy() { private volatile Object cacheValue = NOT_PRESENT; @SuppressWarnings("unchecked") // Delegate is of type T @Override public T get() { if (cacheValue == NOT_PRESENT) { synchronized (this) { if (cacheValue == NOT_PRESENT) { cacheValue = delegate.get(); } } } return (T) cacheValue; } }; } // public void getDependencies() not overridden. // We don't add 'delegate' because it isn't actually used by get() or injectMembers(). } ================================================ FILE: core/src/main/java/dagger/internal/Linker.java ================================================ /* * 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.internal; import dagger.internal.Binding.InvalidBindingException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; /** * Links bindings to their dependencies. */ public final class Linker { static final Object UNINITIALIZED = new Object(); /** * The base {@code Linker} which will be consulted to satisfy bindings not * otherwise satisfiable from this {@code Linker}. The top-most {@code Linker} * in a chain will have a null base linker. */ private final Linker base; /** Bindings requiring a call to attach(). May contain deferred bindings. */ private final Queue> toLink = new ArrayQueue>(); /** True unless calls to requestBinding() were unable to satisfy the binding. */ private boolean attachSuccess = true; /** All errors encountered during injection. */ private final List errors = new ArrayList(); /** All of the object graph's bindings. This may contain unlinked bindings. */ private final Map> bindings = new HashMap>(); /** * An unmodifiable map containing all of the bindings available in this linker, fully linked. * This will be null if the bindings are not yet fully linked. It provides both a signal * of completion of the {@link #linkAll()} method, as well as a place to reference the final, * fully linked map of bindings. */ private volatile Map> linkedBindings = null; private final Loader plugin; private final ErrorHandler errorHandler; public Linker(Linker base, Loader plugin, ErrorHandler errorHandler) { if (plugin == null) throw new NullPointerException("plugin"); if (errorHandler == null) throw new NullPointerException("errorHandler"); this.base = base; this.plugin = plugin; this.errorHandler = errorHandler; } /** * Adds all bindings in {@code toInstall}. The caller must call either {@link * #linkAll} or {@link #requestBinding} and {@link #linkRequested} before the * bindings can be used. * * This method may only be called before {@link #linkAll()}. Subsequent calls to * {@link #installBindings(BindingsGroup)} will throw an {@link IllegalStateException}. */ public void installBindings(BindingsGroup toInstall) { if (linkedBindings != null) { throw new IllegalStateException("Cannot install further bindings after calling linkAll()."); } for (Map.Entry> entry : toInstall.entrySet()) { bindings.put(entry.getKey(), scope(entry.getValue())); } } /** * Links all known bindings (whether requested or installed), plus all of their * transitive dependencies. This loads injectable types' bindings as necessary to fill in * the gaps. If this method has returned successfully at least once, all further * work is short-circuited. * * @throws AssertionError if this method is not called within a synchronized block which * holds this {@link Linker} as the lock object. */ public Map> linkAll() { assertLockHeld(); if (linkedBindings != null) { return linkedBindings; } for (Binding binding : bindings.values()) { if (!binding.isLinked()) { toLink.add(binding); } } linkRequested(); // This method throws if bindings are not resolvable/linkable. linkedBindings = Collections.unmodifiableMap(bindings); return linkedBindings; } /** * Returns the map of all bindings available to this {@link Linker}, if and only if * {@link #linkAll()} has successfully returned at least once, otherwise it returns null; */ public Map> fullyLinkedBindings() { return linkedBindings; } /** * Links all requested bindings plus their transitive dependencies. This * creates JIT bindings as necessary to fill in the gaps. * * @throws AssertionError if this method is not called within a synchronized block which * holds this {@link Linker} as the lock object. */ public void linkRequested() { assertLockHeld(); Binding binding; while ((binding = toLink.poll()) != null) { if (binding instanceof DeferredBinding) { DeferredBinding deferred = (DeferredBinding) binding; String key = deferred.deferredKey; boolean mustHaveInjections = deferred.mustHaveInjections; if (bindings.containsKey(key)) { continue; // A binding for this key has since been linked. } try { Binding resolvedBinding = createBinding(key, binding.requiredBy, deferred.classLoader, mustHaveInjections); resolvedBinding.setLibrary(binding.library()); resolvedBinding.setDependedOn(binding.dependedOn()); // Fail if the type of binding we got wasn't capable of what was requested. if (!key.equals(resolvedBinding.provideKey) && !key.equals(resolvedBinding.membersKey)) { throw new IllegalStateException("Unable to create binding for " + key); } // Enqueue the JIT binding so its own dependencies can be linked. Binding scopedBinding = scope(resolvedBinding); toLink.add(scopedBinding); putBinding(scopedBinding); } catch (InvalidBindingException e) { addError(e.type + " " + e.getMessage() + " required by " + binding.requiredBy); bindings.put(key, Binding.UNRESOLVED); } catch (UnsupportedOperationException e) { addError("Unsupported: " + e.getMessage() + " required by " + binding.requiredBy); bindings.put(key, Binding.UNRESOLVED); } catch (IllegalArgumentException e) { addError(e.getMessage() + " required by " + binding.requiredBy); bindings.put(key, Binding.UNRESOLVED); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } else { // Attempt to attach the binding to its dependencies. If any dependency // is not available, the attach will fail. We'll enqueue creation of // that dependency and retry the attachment later. attachSuccess = true; binding.attach(this); if (attachSuccess) { binding.setLinked(); } else { toLink.add(binding); } } } try { errorHandler.handleErrors(errors); } finally { errors.clear(); } } /** * Don't permit bindings to be linked without a lock. Callers should lock * before requesting any bindings, link the requested bindings, retrieve * the linked bindings, and then release the lock. */ private void assertLockHeld() { if (!Thread.holdsLock(this)) throw new AssertionError(); } /** * Returns a binding for the key in {@code deferred}. The type of binding * to be created depends on the key's type: *
    *
  • Injections of {@code Provider}, {@code MembersInjector}, and * {@code Lazy} will delegate to the bindings of {@code Foo}, {@code Bar}, and * {@code Blah} respectively. *
  • Injections of raw types will use the injectable constructors of those classes. *
  • Any other injection types require @Provides bindings and will error out. *
*/ private Binding createBinding(String key, Object requiredBy, ClassLoader classLoader, boolean mustHaveInjections) { String builtInBindingsKey = Keys.getBuiltInBindingsKey(key); if (builtInBindingsKey != null) { return new BuiltInBinding(key, requiredBy, classLoader, builtInBindingsKey); } String lazyKey = Keys.getLazyKey(key); if (lazyKey != null) { return new LazyBinding(key, requiredBy, classLoader, lazyKey); } String className = Keys.getClassName(key); if (className == null) { throw new InvalidBindingException(key, "is a generic class or an array and can only be bound with concrete type parameter(s) " + "in a @Provides method."); } if (Keys.isAnnotated(key)) { throw new InvalidBindingException(key, "is a @Qualifier-annotated type and must be bound by a @Provides method."); } Binding binding = plugin.getAtInjectBinding(key, className, classLoader, mustHaveInjections); if (binding != null) { return binding; } throw new InvalidBindingException(className, "could not be bound with key " + key); } /** @deprecated Older, generated code still using this should be re-generated. */ @Deprecated public Binding requestBinding(String key, Object requiredBy) { return requestBinding( key, requiredBy, getClass().getClassLoader(), true, true); } /** * Returns the binding if it exists immediately. Otherwise this returns * null. If the returned binding didn't exist or was unlinked, it will be * enqueued to be linked. */ public Binding requestBinding(String key, Object requiredBy, ClassLoader classLoader) { return requestBinding(key, requiredBy, classLoader, true, true); } /** @deprecated Older, generated code still using this should be re-generated. */ @Deprecated public Binding requestBinding(String key, Object requiredBy, boolean mustHaveInjections, boolean library) { return requestBinding(key, requiredBy, getClass().getClassLoader(), mustHaveInjections, library); } /** * Returns the binding if it exists immediately. Otherwise this returns * null. If the returned binding didn't exist or was unlinked, it will be * enqueued to be linked. * * @param mustHaveInjections true if the the referenced key requires either an * {@code @Inject} annotation is produced by a {@code @Provides} method. * This isn't necessary for Module.injects types because frameworks need * to inject arbitrary classes like JUnit test cases and Android * activities. It also isn't necessary for supertypes. */ public Binding requestBinding(String key, Object requiredBy, ClassLoader classLoader, boolean mustHaveInjections, boolean library) { assertLockHeld(); Binding binding = null; for (Linker linker = this; linker != null; linker = linker.base) { binding = linker.bindings.get(key); if (binding != null) { if (linker != this && !binding.isLinked()) throw new AssertionError(); break; } } if (binding == null) { // We can't satisfy this binding. Make sure it'll work next time! Binding deferredBinding = new DeferredBinding(key, classLoader, requiredBy, mustHaveInjections); deferredBinding.setLibrary(library); deferredBinding.setDependedOn(true); toLink.add(deferredBinding); attachSuccess = false; return null; } if (!binding.isLinked()) { toLink.add(binding); // This binding was never linked; link it now! } binding.setLibrary(library); binding.setDependedOn(true); return binding; } private void putBinding(final Binding binding) { // At binding insertion time it's possible that another binding for the same // key to already exist. This occurs when an @Provides method returns a type T // and we also inject the members of that type. if (binding.provideKey != null) { putIfAbsent(bindings, binding.provideKey, binding); } if (binding.membersKey != null) { putIfAbsent(bindings, binding.membersKey, binding); } } /** * Returns a scoped binding for {@code binding}. */ static Binding scope(final Binding binding) { if (!binding.isSingleton() || binding instanceof SingletonBinding) { return binding; // Default scoped binding or already a scoped binding. } return new SingletonBinding(binding); } /** * Puts the mapping {@code key, value} in {@code map} if no mapping for {@code * key} already exists. */ private void putIfAbsent(Map map, K key, V value) { V replaced = map.put(key, value); // Optimistic: prefer only one hash operation lookup. if (replaced != null) { map.put(key, replaced); } } /** Enqueue {@code message} as a fatal error to be reported to the user. */ private void addError(String message) { errors.add(message); } /** * A Binding that implements singleton behaviour around an existing binding. */ private static class SingletonBinding extends Binding { private final Binding binding; private volatile Object onlyInstance = UNINITIALIZED; SingletonBinding(Binding binding) { super(binding.provideKey, binding.membersKey, true, binding.requiredBy); this.binding = binding; } @Override public void attach(Linker linker) { binding.attach(linker); } @Override public void injectMembers(T t) { binding.injectMembers(t); } @SuppressWarnings("unchecked") // onlyInstance is either 'UNINITIALIZED' or a 'T'. @Override public T get() { if (onlyInstance == UNINITIALIZED) { synchronized (this) { if (onlyInstance == UNINITIALIZED) { onlyInstance = binding.get(); } } } return (T) onlyInstance; } @Override public void getDependencies(Set> get, Set> injectMembers) { binding.getDependencies(get, injectMembers); } @Override public boolean isCycleFree() { return binding.isCycleFree(); } @Override public boolean isLinked() { return binding.isLinked(); } @Override public boolean isVisiting() { return binding.isVisiting(); } @Override public boolean library() { return binding.library(); } @Override public boolean dependedOn() { return binding.dependedOn(); } @Override public void setCycleFree(final boolean cycleFree) { binding.setCycleFree(cycleFree); } @Override public void setVisiting(final boolean visiting) { binding.setVisiting(visiting); } @Override public void setLibrary(boolean library) { binding.setLibrary(true); } @Override public void setDependedOn(boolean dependedOn) { binding.setDependedOn(dependedOn); } @Override protected boolean isSingleton() { return true; } @Override protected void setLinked() { binding.setLinked(); } @Override public String toString() { return "@Singleton/" + binding.toString(); } } /** Handles linker errors appropriately. */ public interface ErrorHandler { ErrorHandler NULL = new ErrorHandler() { @Override public void handleErrors(List errors) { } }; /** * Fail if any errors have been enqueued. * Implementations may throw exceptions or report the errors through another * channel. Callers are responsible for clearing enqueued errors. * * @param errors a potentially empty list of error messages. */ void handleErrors(List errors); } private static class DeferredBinding extends Binding { /** Loader originally intended to load this binding, to be used in loading the actual one */ final ClassLoader classLoader; final String deferredKey; final boolean mustHaveInjections; DeferredBinding(String deferredKey, ClassLoader classLoader, Object requiredBy, boolean mustHaveInjections) { super(null, null, false, requiredBy); this.deferredKey = deferredKey; this.classLoader = classLoader; this.mustHaveInjections = mustHaveInjections; } @Override public void injectMembers(Object t) { throw new UnsupportedOperationException("Deferred bindings must resolve first."); } @Override public void getDependencies(Set> get, Set> injectMembers) { throw new UnsupportedOperationException("Deferred bindings must resolve first."); } @Override public String toString() { return "DeferredBinding[deferredKey=" + deferredKey + "]"; } } } ================================================ FILE: core/src/main/java/dagger/internal/Loader.java ================================================ /* * Copyright (C) 2012 Square, Inc. * Copyright (C) 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.reflect.AccessibleObject; /** * Provides a point of configuration of the basic resolving functions within Dagger, namely * that of Module handling, injection binding creation, and static injection. A plugin must * provide all resolution methods */ public abstract class Loader { private final Memoizer>> caches = new Memoizer>>() { @Override protected Memoizer> create(final ClassLoader classLoader) { return new Memoizer>() { @Override protected Class create(String className) { try { return classLoader.loadClass(className); } catch (ClassNotFoundException e) { return Void.class; // Cache the failure (negative case). } } }; } }; /** * Returns a binding that uses {@code @Inject} annotations, or null if no valid binding can * be found or created. */ public abstract Binding getAtInjectBinding( String key, String className, ClassLoader classLoader, boolean mustHaveInjections); /** * Returns a module adapter for {@code moduleClass} or throws a {@code TypeNotPresentException} if * none can be found. */ public abstract ModuleAdapter getModuleAdapter(Class moduleClass); /** * Returns the static injection for {@code injectedClass}. */ public abstract StaticInjection getStaticInjection(Class injectedClass); /** * Loads a class from a {@code ClassLoader}-specific cache if it's already there, or * loads it from the given {@code ClassLoader} and caching it for future requests. Failures * to load are also cached using the Void.class type. A null {@code ClassLoader} is assumed * to be the system classloader. */ protected Class loadClass(ClassLoader classLoader, String name) { // A null classloader is the system classloader. classLoader = (classLoader != null) ? classLoader : ClassLoader.getSystemClassLoader(); return caches.get(classLoader).get(name); } /** * Instantiates a class using its default constructor and the given {@link ClassLoader}. This * method does not attempt to {@linkplain AccessibleObject#setAccessible set accessibility}. */ protected T instantiate(String name, ClassLoader classLoader) { try { Class generatedClass = loadClass(classLoader, name); if (generatedClass == Void.class) { return null; } @SuppressWarnings("unchecked") T instance = (T) generatedClass.newInstance(); return instance; } catch (InstantiationException e) { throw new RuntimeException("Failed to initialize " + name, e); } catch (IllegalAccessException e) { throw new RuntimeException("Failed to initialize " + name, e); } } } ================================================ FILE: core/src/main/java/dagger/internal/Memoizer.java ================================================ /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Represents an operation whose results are memoized. Results returned by invocations of * {@link #create(Object)} are memoized so that the same object is returned for multiple invocations * of {@link #get(Object)} for the same key. */ abstract class Memoizer { private final Map map; private final Lock readLock; private final Lock writeLock; public Memoizer() { this.map = new LinkedHashMap(); ReadWriteLock lock = new ReentrantReadWriteLock(); this.readLock = lock.readLock(); this.writeLock = lock.writeLock(); } public final V get(K key) { if (key == null) { throw new NullPointerException("key == null"); } // check to see if we already have a value readLock.lock(); try { V value = map.get(key); if (value != null) { return value; } } finally { readLock.unlock(); } // create a new value. this may race and we might create more than one instance, but that's ok V newValue = create(key); if (newValue == null) { throw new NullPointerException("create returned null"); } // write the new value and return it writeLock.lock(); try { map.put(key, newValue); return newValue; } finally { writeLock.unlock(); } } protected abstract V create(K key); @Override public final String toString() { readLock.lock(); try { return map.toString(); } finally { readLock.unlock(); } } } ================================================ FILE: core/src/main/java/dagger/internal/ModuleAdapter.java ================================================ /* * 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. */ package dagger.internal; /** * Extracts bindings from an {@code @Module}-annotated class. */ public abstract class ModuleAdapter { public final Class moduleClass; public final String[] injectableTypes; public final Class[] staticInjections; public final boolean overrides; public final Class[] includes; public final boolean complete; public final boolean library; protected ModuleAdapter(Class moduleClass, String[] injectableTypes, Class[] staticInjections, boolean overrides, Class[] includes, boolean complete, boolean library) { this.moduleClass = moduleClass; this.injectableTypes = injectableTypes; this.staticInjections = staticInjections; this.overrides = overrides; this.includes = includes; this.complete = complete; this.library = library; } /** * Returns bindings for the {@code @Provides} methods of {@code module}. The * returned bindings must be linked before they can be used to inject values. */ @SuppressWarnings("unused") public void getBindings(BindingsGroup map, T module) { // no-op; } /** * Returns a new instance of the module class created using a no-args * constructor. Only used when a manually-constructed module is not supplied. */ protected T newModule() { throw new UnsupportedOperationException("No no-args constructor on " + getClass().getName()); } @Override public final boolean equals(Object obj) { if (obj == this) { return true; } else if (obj instanceof ModuleAdapter) { ModuleAdapter that = (ModuleAdapter) obj; return this.moduleClass.equals(that.moduleClass); } else { return false; } } @Override public final int hashCode() { return moduleClass.hashCode(); } } ================================================ FILE: core/src/main/java/dagger/internal/Modules.java ================================================ /* * 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. */ package dagger.internal; import java.util.LinkedHashMap; import java.util.Map; /** * Static helper for organizing modules. */ public final class Modules { private Modules() { } /** * Returns a full set of module adapters, including module adapters for included * modules. */ public static Map, Object> loadModules(Loader loader, Object[] seedModulesOrClasses) { Map, Object> seedAdapters = new LinkedHashMap, Object>(seedModulesOrClasses.length); for (int i = 0; i < seedModulesOrClasses.length; i++) { if (seedModulesOrClasses[i] instanceof Class) { ModuleAdapter adapter = loader.getModuleAdapter((Class) seedModulesOrClasses[i]); seedAdapters.put(adapter, adapter.newModule()); } else { ModuleAdapter adapter = loader.getModuleAdapter(seedModulesOrClasses[i].getClass()); seedAdapters.put(adapter, seedModulesOrClasses[i]); } } // Add the adapters that we have module instances for. This way we won't // construct module objects when we have a user-supplied instance. Map, Object> result = new LinkedHashMap, Object>(seedAdapters); // Next collect included modules Map, ModuleAdapter> transitiveInclusions = new LinkedHashMap, ModuleAdapter>(); for (ModuleAdapter adapter : seedAdapters.keySet()) { collectIncludedModulesRecursively(loader, adapter, transitiveInclusions); } // and create them if necessary for (ModuleAdapter dependency : transitiveInclusions.values()) { if (!result.containsKey(dependency)) { result.put(dependency, dependency.newModule()); } } return result; } /** * Fills {@code result} with the module adapters for the includes of {@code * adapter}, and their includes recursively. */ private static void collectIncludedModulesRecursively(Loader plugin, ModuleAdapter adapter, Map, ModuleAdapter> result) { for (Class include : adapter.includes) { if (!result.containsKey(include)) { ModuleAdapter includedModuleAdapter = plugin.getModuleAdapter(include); result.put(include, includedModuleAdapter); collectIncludedModulesRecursively(plugin, includedModuleAdapter, result); } } } } ================================================ FILE: core/src/main/java/dagger/internal/ProblemDetector.java ================================================ /* * 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.internal; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * Detects problems like cyclic dependencies. */ public final class ProblemDetector { public void detectCircularDependencies(Collection> bindings) { detectCircularDependencies(bindings, new ArrayList>()); } public void detectUnusedBinding(Collection> bindings) { List unusedBindings = new ArrayList(); for (Binding binding : bindings) { if (!binding.library() && !binding.dependedOn()) { unusedBindings.add(binding); } } if (!unusedBindings.isEmpty()) { StringBuilder builder = new StringBuilder(); builder.append("You have these unused @Provider methods:"); for (int i = 0; i < unusedBindings.size(); i++) { builder.append("\n ").append(i + 1).append(". ") .append(unusedBindings.get(i).requiredBy); } builder.append("\n Set library=true in your module to disable this check."); throw new IllegalStateException(builder.toString()); } } private static void detectCircularDependencies(Collection> bindings, List> path) { for (Binding binding : bindings) { if (binding.isCycleFree()) { continue; } if (binding.isVisiting()) { int index = path.indexOf(binding); StringBuilder message = new StringBuilder() .append("Dependency cycle:"); for (int i = index; i < path.size(); i++) { message.append("\n ").append(i - index).append(". ") .append(path.get(i).provideKey).append(" bound by ").append(path.get(i)); } message.append("\n ").append(0).append(". ").append(binding.provideKey); throw new IllegalStateException(message.toString()); } binding.setVisiting(true); path.add(binding); try { ArraySet> dependencies = new ArraySet>(); binding.getDependencies(dependencies, dependencies); detectCircularDependencies(dependencies, path); binding.setCycleFree(true); } finally { path.remove(path.size() - 1); binding.setVisiting(false); } } } public void detectProblems(Collection> values) { detectCircularDependencies(values); detectUnusedBinding(values); } static class ArraySet extends AbstractSet { private final ArrayList list = new ArrayList(); @Override public boolean add(T t) { list.add(t); return true; } @Override public Iterator iterator() { return list.iterator(); } @Override public int size() { throw new UnsupportedOperationException(); } } } ================================================ FILE: core/src/main/java/dagger/internal/ProvidesBinding.java ================================================ /* * 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; /** * A {@code Binding} which delegates to a module method. */ public abstract class ProvidesBinding extends Binding { protected final String moduleClass; protected final String methodName; /** * Creates a new {@code ProvidesBinding} with the given "provides" key, a flag as to whether * this binding should be scoped, and the requiredBy object for traceability. */ public ProvidesBinding(String key, boolean singleton, String moduleClass, String methodName) { // Set requiredBy as fullMethodName to preserve older debugging meaning. super(key, null, singleton, moduleClass + "." + methodName + "()"); this.moduleClass = moduleClass; this.methodName = methodName; } /** * A provides binding is responsible for implementing storage of the module instance, and * delegation to that module instance's method. */ @Override public abstract T get(); @Override public String toString() { return getClass().getName() + "[key=" + provideKey + " method=" + moduleClass + "." + methodName + "()" + "]"; } } ================================================ FILE: core/src/main/java/dagger/internal/SetBinding.java ================================================ /* * 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.internal; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * A {@code Binding} which contains contributors (other bindings marked with * {@code @Provides} {@code @OneOf}), to which it delegates provision * requests on an as-needed basis. */ public final class SetBinding extends Binding> { public static void add(BindingsGroup bindings, String setKey, Binding binding) { prepareSetBinding(bindings, setKey, binding).contributors.add(Linker.scope(binding)); } @SuppressWarnings("unchecked") private static SetBinding prepareSetBinding( BindingsGroup bindings, String setKey, Binding binding) { Binding previous = bindings.get(setKey); SetBinding setBinding; if (previous instanceof SetBinding) { setBinding = (SetBinding) previous; setBinding.setLibrary(setBinding.library() && binding.library()); return setBinding; } else if (previous != null) { throw new IllegalArgumentException("Duplicate:\n " + previous + "\n " + binding); } else { setBinding = new SetBinding(setKey, binding.requiredBy); setBinding.setLibrary(binding.library()); bindings.contributeSetBinding(setKey, setBinding); return (SetBinding) bindings.get(setKey); // BindingMap.put() copies SetBindings. } } /** * A {@link SetBinding} with whose contributing bindings this set-binding provides a union * view. */ private final SetBinding parent; /** * A {@link Set} of {@link Binding} instances which contribute values to the injected set. */ private final List> contributors; /** * Creates a new {@code SetBinding} with the given "provides" key, and the requiredBy object * for traceability. */ public SetBinding(String key, Object requiredBy) { super(key, null, false, requiredBy); parent = null; contributors = new ArrayList>(); } /** * Creates a new {@code SetBinding} with all of the contributing bindings of the provided * original {@code SetBinding}. */ public SetBinding(SetBinding original) { super(original.provideKey, null, false, original.requiredBy); parent = original; this.setLibrary(original.library()); this.setDependedOn(original.dependedOn()); contributors = new ArrayList>(); } @Override public void attach(Linker linker) { for (Binding contributor : contributors) { contributor.attach(linker); } } public int size() { int size = 0; for (SetBinding binding = this; binding != null; binding = binding.parent) { size += binding.contributors.size(); } return size; } @SuppressWarnings("unchecked") // Only Binding and Set are added to contributors. @Override public Set get() { List result = new ArrayList(); for (SetBinding setBinding = this; setBinding != null; setBinding = setBinding.parent) { for (int i = 0, size = setBinding.contributors.size(); i < size; i++) { Binding contributor = setBinding.contributors.get(i); Object contribution = contributor.get(); // Let runtime exceptions through. if (contributor.provideKey.equals(provideKey)) { result.addAll((Set) contribution); } else { result.add((T) contribution); } } } return Collections.unmodifiableSet(new LinkedHashSet(result)); } @Override public void getDependencies( Set> getBindings, Set> injectMembersBindings) { for (SetBinding binding = this; binding != null; binding = binding.parent) { getBindings.addAll(binding.contributors); } } @Override public void injectMembers(Set t) { throw new UnsupportedOperationException("Cannot inject members on a contributed Set."); } @Override public String toString() { boolean first = true; StringBuilder builder = new StringBuilder("SetBinding["); for (SetBinding setBinding = this; setBinding != null; setBinding = setBinding.parent) { for (int i = 0, size = setBinding.contributors.size(); i < size; i++) { if (!first) { builder.append(","); } builder.append(setBinding.contributors.get(i)); first = false; } } builder.append("]"); return builder.toString(); } } ================================================ FILE: core/src/main/java/dagger/internal/StaticInjection.java ================================================ /* * 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. */ package dagger.internal; /** * Injects the static fields of a class. */ public abstract class StaticInjection { public abstract void attach(Linker linker); public abstract void inject(); } ================================================ FILE: core/src/main/java/dagger/internal/ThrowingErrorHandler.java ================================================ /* * 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.internal; import java.util.List; /** * Handles errors by throwing an exception containing all the available errors. */ public final class ThrowingErrorHandler implements Linker.ErrorHandler { @Override public void handleErrors(List errors) { if (errors.isEmpty()) { return; } StringBuilder message = new StringBuilder(); message.append("Errors creating object graph:"); for (String error : errors) { message.append("\n ").append(error); } throw new IllegalStateException(message.toString()); } } ================================================ FILE: core/src/main/java/dagger/internal/loaders/GeneratedAdapters.java ================================================ /* * Copyright (C) 2013 Square, Inc. * Copyright (C) 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.loaders; /** * A single point for API used in common by Adapters and Adapter generators */ public final class GeneratedAdapters { private static final String SEPARATOR = "$$"; public static final String INJECT_ADAPTER_SUFFIX = SEPARATOR + "InjectAdapter"; public static final String MODULE_ADAPTER_SUFFIX = SEPARATOR + "ModuleAdapter"; public static final String STATIC_INJECTION_SUFFIX = SEPARATOR + "StaticInjection"; private GeneratedAdapters() { } } ================================================ FILE: core/src/main/java/dagger/internal/loaders/ReflectiveAtInjectBinding.java ================================================ /* * 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.internal.loaders; import dagger.internal.Binding; import dagger.internal.Keys; import dagger.internal.Linker; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; /** * Injects the {@code @Inject}-annotated fields and constructors of a class * using reflection. */ public final class ReflectiveAtInjectBinding extends Binding { private final Field[] fields; private final ClassLoader loader; private final Constructor constructor; private final Class supertype; private final String[] keys; private final Binding[] fieldBindings; private final Binding[] parameterBindings; private Binding supertypeBinding; /** * @param keys keys for the fields, constructor parameters and supertype in * that order. These are precomputed to minimize reflection when {@code * attach} is called multiple times. * @param constructor the injectable constructor, or null if this binding * supports members injection only. * @param supertype the injectable supertype, or null if the supertype is a */ private ReflectiveAtInjectBinding(String provideKey, String membersKey, boolean singleton, Class type, Field[] fields, Constructor constructor, int parameterCount, Class supertype, String[] keys) { super(provideKey, membersKey, singleton, type); this.constructor = constructor; this.fields = fields; this.supertype = supertype; this.keys = keys; this.parameterBindings = new Binding[parameterCount]; this.fieldBindings = new Binding[fields.length]; this.loader = type.getClassLoader(); } @SuppressWarnings("unchecked") // We're careful to make keys and bindings match up. @Override public void attach(Linker linker) { int k = 0; for (int i = 0; i < fields.length; i++) { if (fieldBindings[i] == null) { fieldBindings[i] = linker.requestBinding(keys[k], fields[i], loader); } k++; } if (constructor != null) { for (int i = 0; i < parameterBindings.length; i++) { if (parameterBindings[i] == null) { parameterBindings[i] = linker.requestBinding(keys[k], constructor, loader); } k++; } } if (supertype != null && supertypeBinding == null) { supertypeBinding = (Binding) linker.requestBinding(keys[k], membersKey, loader, false, true); } } @Override public T get() { if (constructor == null) { throw new UnsupportedOperationException(); } Object[] args = new Object[parameterBindings.length]; for (int i = 0; i < parameterBindings.length; i++) { args[i] = parameterBindings[i].get(); } T result; try { result = constructor.newInstance(args); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); throw cause instanceof RuntimeException ? (RuntimeException) cause : new RuntimeException(cause); } catch (IllegalAccessException e) { throw new AssertionError(e); } catch (InstantiationException e) { throw new RuntimeException(e); } injectMembers(result); return result; } @Override public void injectMembers(T t) { try { for (int i = 0; i < fields.length; i++) { fields[i].set(t, fieldBindings[i].get()); } if (supertypeBinding != null) { supertypeBinding.injectMembers(t); } } catch (IllegalAccessException e) { throw new AssertionError(e); } } @Override public void getDependencies(Set> get, Set> injectMembers) { if (parameterBindings != null) { Collections.addAll(get, parameterBindings); } Collections.addAll(injectMembers, fieldBindings); if (supertypeBinding != null) { injectMembers.add(supertypeBinding); } } @Override public String toString() { return provideKey != null ? provideKey : membersKey; } public static Binding create(Class type, boolean mustHaveInjections) { boolean singleton = type.isAnnotationPresent(Singleton.class); List keys = new ArrayList(); // Lookup the injectable fields and their corresponding keys. List injectedFields = new ArrayList(); for (Class c = type; c != Object.class; c = c.getSuperclass()) { for (Field field : c.getDeclaredFields()) { if (!field.isAnnotationPresent(Inject.class) || Modifier.isStatic(field.getModifiers())) { continue; } if ((field.getModifiers() & Modifier.PRIVATE) != 0) { throw new IllegalStateException("Can't inject private field: " + field); } field.setAccessible(true); injectedFields.add(field); keys.add(Keys.get(field.getGenericType(), field.getAnnotations(), field)); } } // Look up @Inject-annotated constructors. If there's no @Inject-annotated // constructor, use a default public constructor if the class has other // injections. Otherwise treat the class as non-injectable. Constructor injectedConstructor = null; for (Constructor constructor : getConstructorsForType(type)) { if (!constructor.isAnnotationPresent(Inject.class)) { continue; } if (injectedConstructor != null) { throw new InvalidBindingException(type.getName(), "has too many injectable constructors"); } injectedConstructor = constructor; } if (injectedConstructor == null) { if (!injectedFields.isEmpty()) { try { injectedConstructor = type.getDeclaredConstructor(); } catch (NoSuchMethodException ignored) { } } else if (mustHaveInjections) { throw new InvalidBindingException(type.getName(), "has no injectable members. Do you want to add an injectable constructor?"); } } int parameterCount; String provideKey; if (injectedConstructor != null) { if ((injectedConstructor.getModifiers() & Modifier.PRIVATE) != 0) { throw new IllegalStateException("Can't inject private constructor: " + injectedConstructor); } provideKey = Keys.get(type); injectedConstructor.setAccessible(true); Type[] types = injectedConstructor.getGenericParameterTypes(); parameterCount = types.length; if (parameterCount != 0) { Annotation[][] annotations = injectedConstructor.getParameterAnnotations(); for (int p = 0; p < types.length; p++) { keys.add(Keys.get(types[p], annotations[p], injectedConstructor)); } } } else { provideKey = null; parameterCount = 0; if (singleton) { throw new IllegalArgumentException( "No injectable constructor on @Singleton " + type.getName()); } } Class supertype = type.getSuperclass(); if (supertype != null) { if (Keys.isPlatformType(supertype.getName())) { supertype = null; } else { keys.add(Keys.getMembersKey(supertype)); } } String membersKey = Keys.getMembersKey(type); return new ReflectiveAtInjectBinding(provideKey, membersKey, singleton, type, injectedFields.toArray(new Field[injectedFields.size()]), injectedConstructor, parameterCount, supertype, keys.toArray(new String[keys.size()])); } @SuppressWarnings("unchecked") // Class.getDeclaredConstructors is an unsafe API. private static Constructor[] getConstructorsForType(Class type) { return (Constructor[]) type.getDeclaredConstructors(); } } ================================================ FILE: core/src/main/java/dagger/internal/loaders/ReflectiveStaticInjection.java ================================================ /* * 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.internal.loaders; import dagger.internal.Binding; import dagger.internal.Keys; import dagger.internal.Linker; import dagger.internal.StaticInjection; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; /** * Uses reflection to inject the static fields of a class. */ public final class ReflectiveStaticInjection extends StaticInjection { private final ClassLoader loader; private final Field[] fields; private Binding[] bindings; private ReflectiveStaticInjection(ClassLoader loader, Field[] fields) { this.fields = fields; this.loader = loader; } @Override public void attach(Linker linker) { bindings = new Binding[fields.length]; for (int i = 0; i < fields.length; i++) { Field field = fields[i]; String key = Keys.get(field.getGenericType(), field.getAnnotations(), field); bindings[i] = linker.requestBinding(key, field, loader); } } @Override public void inject() { try { for (int f = 0; f < fields.length; f++) { fields[f].set(null, bindings[f].get()); } } catch (IllegalAccessException e) { throw new AssertionError(e); } } public static StaticInjection create(Class injectedClass) { List fields = new ArrayList(); for (Field field : injectedClass.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) && field.isAnnotationPresent(Inject.class)) { field.setAccessible(true); fields.add(field); } } if (fields.isEmpty()) { throw new IllegalArgumentException("No static injections: " + injectedClass.getName()); } return new ReflectiveStaticInjection(injectedClass.getClassLoader(), fields.toArray(new Field[fields.size()])); } } ================================================ FILE: core/src/test/java/dagger/ExtensionTest.java ================================================ /* * 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; import dagger.internal.TestingLoader; import java.util.Arrays; import javax.inject.Inject; import javax.inject.Singleton; 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.assertNotNull; @RunWith(JUnit4.class) public final class ExtensionTest { @Singleton static class A { @Inject A() {} } static class B { @Inject A a; } @Singleton static class C { @Inject A a; @Inject B b; } static class D { @Inject A a; @Inject B b; @Inject C c; } @Module(injects = { A.class, B.class }) static class RootModule { } @Module(addsTo = RootModule.class, injects = { C.class, D.class }) static class ExtensionModule { } @Test public void basicExtension() { assertNotNull(ObjectGraph.createWith(new TestingLoader(), new RootModule()) .plus(new ExtensionModule())); } @Test public void basicInjection() { ObjectGraph root = ObjectGraph.createWith(new TestingLoader(), new RootModule()); assertThat(root.get(A.class)).isNotNull(); assertThat(root.get(A.class)).isSameAs(root.get(A.class)); // Present and Singleton. assertThat(root.get(B.class)).isNotSameAs(root.get(B.class)); // Not singleton. assertFailInjectNotRegistered(root, C.class); // Not declared in RootModule. assertFailInjectNotRegistered(root, D.class); // Not declared in RootModule. // Extension graph behaves as the root graph would for root-ish things. ObjectGraph extension = root.plus(new ExtensionModule()); assertThat(root.get(A.class)).isSameAs(extension.get(A.class)); assertThat(root.get(B.class)).isNotSameAs(extension.get(B.class)); assertThat(root.get(B.class).a).isSameAs(extension.get(B.class).a); assertThat(extension.get(C.class).a).isNotNull(); assertThat(extension.get(D.class).c).isNotNull(); } @Test public void scopedGraphs() { ObjectGraph app = ObjectGraph.createWith(new TestingLoader(), new RootModule()); assertThat(app.get(A.class)).isNotNull(); assertThat(app.get(A.class)).isSameAs(app.get(A.class)); assertThat(app.get(B.class)).isNotSameAs(app.get(B.class)); assertFailInjectNotRegistered(app, C.class); assertFailInjectNotRegistered(app, D.class); ObjectGraph request1 = app.plus(new ExtensionModule()); ObjectGraph request2 = app.plus(new ExtensionModule()); for (ObjectGraph request : Arrays.asList(request1, request2)) { assertThat(request.get(A.class)).isNotNull(); assertThat(request.get(A.class)).isSameAs(request.get(A.class)); assertThat(request.get(B.class)).isNotSameAs(request.get(B.class)); assertThat(request.get(C.class)).isNotNull(); assertThat(request.get(C.class)).isSameAs(request.get(C.class)); assertThat(request.get(D.class)).isNotSameAs(request.get(D.class)); } // Singletons are one-per-graph-instance where they are declared. assertThat(request1.get(C.class)).isNotSameAs(request2.get(C.class)); // Singletons that come from common roots should be one-per-common-graph-instance. assertThat(request1.get(C.class).a).isSameAs(request2.get(C.class).a); } private void assertFailInjectNotRegistered(ObjectGraph graph, Class clazz) { try { assertThat(graph.get(clazz)).isNull(); } catch (IllegalArgumentException e) { assertThat(e.getMessage()).contains("No inject"); } } } ================================================ FILE: core/src/test/java/dagger/ExtensionWithSetBindingsTest.java ================================================ /* * 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; import dagger.internal.TestingLoader; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.inject.Inject; import javax.inject.Singleton; 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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public final class ExtensionWithSetBindingsTest { private static final AtomicInteger counter = new AtomicInteger(0); @Singleton static class RealSingleton { @Inject Set ints; } @Singleton static class Main { @Inject Set ints; } @Module(injects = RealSingleton.class) static class RootModule { @Provides(type=SET) @Singleton Integer provideA() { return counter.getAndIncrement(); } @Provides(type=SET) @Singleton Integer provideB() { return counter.getAndIncrement(); } } @Module(addsTo = RootModule.class, injects = Main.class ) static class ExtensionModule { @Provides(type=SET) @Singleton Integer provideC() { return counter.getAndIncrement(); } @Provides(type=SET) @Singleton Integer provideD() { return counter.getAndIncrement(); } } @Module static class EmptyModule { } @Module(library = true) static class DuplicateModule { @Provides @Singleton String provideFoo() { return "foo"; } @Provides @Singleton String provideBar() { return "bar"; } } @Test public void basicInjectionWithExtension() { ObjectGraph root = ObjectGraph.createWith(new TestingLoader(), new RootModule()); RealSingleton rs = root.get(RealSingleton.class); assertThat(rs.ints).containsExactly(0, 1); ObjectGraph extension = root.plus(new ExtensionModule()); Main main = extension.get(Main.class); assertThat(main.ints).containsExactly(0, 1, 2, 3); // Second time around. ObjectGraph extension2 = root.plus(new ExtensionModule()); Main main2 = extension2.get(Main.class); assertThat(main2.ints).containsExactly(0, 1, 4, 5); } @Module(includes = ExtensionModule.class, overrides = true) static class TestModule { @Provides(type=SET) @Singleton Integer provide9999() { return 9999; } } @Test public void basicInjectionWithExtensionAndOverrides() { try { ObjectGraph.createWith(new TestingLoader(), new RootModule()).plus(new TestModule()); fail("Should throw exception."); } catch (IllegalArgumentException e) { assertEquals("TestModule: Module overrides cannot contribute set bindings.", e.getMessage()); } } @Test public void duplicateBindingsInSecondaryModule() { try { ObjectGraph.createWith(new TestingLoader(), new EmptyModule(), new DuplicateModule()); fail("Should throw exception."); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().startsWith("DuplicateModule: Duplicate")); } } } ================================================ FILE: core/src/test/java/dagger/ExtensionWithStateTest.java ================================================ /* * 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; import dagger.internal.TestingLoader; import javax.inject.Inject; 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 final class ExtensionWithStateTest { static class A { } static class B { @Inject A a; } @Module( injects = A.class, // for testing complete = false ) static class RootModule { final A a; RootModule(A a) { this.a = a; } @Provides A provideA() { return a; } } @Module(addsTo = RootModule.class, injects = { B.class }) static class ExtensionModule { } @Test public void basicInjectionWithExtension() { A a = new A(); ObjectGraph root = ObjectGraph.createWith(new TestingLoader(), new RootModule(a)); assertThat(root.get(A.class)).isSameAs(a); // Extension graph behaves as the root graph would for root-ish things. ObjectGraph extension = root.plus(new ExtensionModule()); assertThat(extension.get(A.class)).isSameAs(a); assertThat(extension.get(B.class).a).isSameAs(a); } } ================================================ FILE: core/src/test/java/dagger/InjectStaticsTest.java ================================================ /* * 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.TestingLoader; import javax.inject.Inject; 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 final class InjectStaticsTest { @Before public void setUp() { InjectsOneField.staticField = null; InjectsStaticAndNonStatic.staticField = null; } public static class InjectsOneField { @Inject static String staticField; } public static class InjectsStaticAndNonStatic { @Inject Integer nonStaticField; @Inject static String staticField; } @Test public void injectStatics() { @Module(staticInjections = InjectsOneField.class) class TestModule { @Provides String provideString() { return "static"; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(),new TestModule()); assertThat(InjectsOneField.staticField).isNull(); graph.injectStatics(); assertThat(InjectsOneField.staticField).isEqualTo("static"); } @Test public void instanceFieldsNotInjectedByInjectStatics() { @Module( staticInjections = InjectsStaticAndNonStatic.class, injects = InjectsStaticAndNonStatic.class) class TestModule { @Provides String provideString() { return "static"; } @Provides Integer provideInteger() { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertThat(InjectsStaticAndNonStatic.staticField).isNull(); graph.injectStatics(); assertThat(InjectsStaticAndNonStatic.staticField).isEqualTo("static"); } @Test public void staticFieldsNotInjectedByInjectMembers() { @Module( staticInjections = InjectsStaticAndNonStatic.class, injects = InjectsStaticAndNonStatic.class) class TestModule { @Provides String provideString() { throw new AssertionError(); } @Provides Integer provideInteger() { return 5; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertThat(InjectsStaticAndNonStatic.staticField).isNull(); InjectsStaticAndNonStatic object = new InjectsStaticAndNonStatic(); graph.inject(object); assertThat(InjectsStaticAndNonStatic.staticField).isNull(); assertThat(object.nonStaticField).isEqualTo(5); } } ================================================ FILE: core/src/test/java/dagger/InjectionOfLazyTest.java ================================================ /* * 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; import dagger.internal.TestingLoader; import java.util.concurrent.atomic.AtomicInteger; import javax.inject.Inject; import javax.inject.Provider; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; /** * Tests of injection of Lazy bindings. */ @RunWith(JUnit4.class) public final class InjectionOfLazyTest { @Test public void lazyValueCreation() { final AtomicInteger counter = new AtomicInteger(); class TestEntryPoint { @Inject Lazy i; @Inject Lazy j; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides Integer provideInteger() { return counter.incrementAndGet(); } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertEquals(0, counter.get()); assertEquals(1, ep.i.get().intValue()); assertEquals(1, counter.get()); assertEquals(2, ep.j.get().intValue()); assertEquals(1, ep.i.get().intValue()); assertEquals(2, counter.get()); } @Test public void lazyNullCreation() { final AtomicInteger provideCounter = new AtomicInteger(0); class TestEntryPoint { @Inject Lazy i; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides String provideInteger() { provideCounter.incrementAndGet(); return null; } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertEquals(0, provideCounter.get()); assertNull(ep.i.get()); assertEquals(1, provideCounter.get()); assertNull(ep.i.get()); // still null assertEquals(1, provideCounter.get()); // still only called once. } @Test public void providerOfLazyOfSomething() { final AtomicInteger counter = new AtomicInteger(); class TestEntryPoint { @Inject Provider> providerOfLazyInteger; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides Integer provideInteger() { return counter.incrementAndGet(); } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertEquals(0, counter.get()); Lazy i = ep.providerOfLazyInteger.get(); assertEquals(1, i.get().intValue()); assertEquals(1, counter.get()); assertEquals(1, i.get().intValue()); Lazy j = ep.providerOfLazyInteger.get(); assertEquals(2, j.get().intValue()); assertEquals(2, counter.get()); assertEquals(1, i.get().intValue()); } @Test public void sideBySideLazyVsProvider() { final AtomicInteger counter = new AtomicInteger(); class TestEntryPoint { @Inject Provider providerOfInteger; @Inject Lazy lazyInteger; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides Integer provideInteger() { return counter.incrementAndGet(); } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertEquals(0, counter.get()); assertEquals(0, counter.get()); assertEquals(1, ep.lazyInteger.get().intValue()); assertEquals(1, counter.get()); assertEquals(2, ep.providerOfInteger.get().intValue()); // fresh instance assertEquals(1, ep.lazyInteger.get().intValue()); // still the same instance assertEquals(2, counter.get()); assertEquals(3, ep.providerOfInteger.get().intValue()); // fresh instance assertEquals(1, ep.lazyInteger.get().intValue()); // still the same instance. } private T injectWithModule(T ep, Object ... modules) { return ObjectGraph.createWith(new TestingLoader(), modules).inject(ep); } } ================================================ FILE: core/src/test/java/dagger/InjectionTest.java ================================================ /* * Copyright (C) 2010 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.TestingLoader; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.RandomAccess; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; 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.assertEquals; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public final class InjectionTest { @Test public void basicInjection() { class TestEntryPoint { @Inject Provider gProvider; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides E provideE(F f) { return new E(f); } @Provides F provideF() { return new F(); } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); G g = entryPoint.gProvider.get(); assertThat(g.a).isNotNull(); assertThat(g.b).isNotNull(); assertThat(g.c).isNotNull(); assertThat(g.d).isNotNull(); assertThat(g.e).isNotNull(); assertThat(g.e.f).isNotNull(); } static class A { @Inject A() {} } static class B { @Inject B() {} } @Singleton static class C { @Inject C() {} } @Singleton static class D { @Inject D() {} } static class E { F f; E(F f) { this.f = f; } } static class F {} static class G { @Inject A a; @Inject B b; C c; D d; @Inject E e; @Inject G(C c, D d) { this.c = c; this.d = d; } } @Test public void providerInjection() { class TestEntryPoint { @Inject Provider aProvider; } @Module(injects = TestEntryPoint.class) class TestModule { } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.aProvider.get()).isNotNull(); assertThat(entryPoint.aProvider.get()).isNotNull(); assertThat(entryPoint.aProvider.get()).isNotSameAs(entryPoint.aProvider.get()); } @Test public void singletons() { class TestEntryPoint { @Inject Provider fProvider; @Inject Provider iProvider; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides @Singleton F provideF() { return new F(); } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.fProvider.get()).isSameAs(entryPoint.fProvider.get()); assertThat(entryPoint.iProvider.get()).isSameAs(entryPoint.iProvider.get()); } @Singleton static class I { @Inject I() {} } @Test public void bindingAnnotations() { final A one = new A(); final A two = new A(); class TestEntryPoint { @Inject A a; @Inject @Named("one") A aOne; @Inject @Named("two") A aTwo; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides @Named("one") A getOne() { return one; } @Provides @Named("two") A getTwo() { return two; } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.a).isNotNull(); assertThat(one).isSameAs(entryPoint.aOne); assertThat(two).isSameAs(entryPoint.aTwo); } @Test public void singletonBindingAnnotationAndProvider() { class TestEntryPoint { @Inject Provider lProvider; } @Module(injects = TestEntryPoint.class) class TestModule { A a1; A a2; @Provides @Singleton @Named("one") F provideF(Provider aProvider) { a1 = aProvider.get(); a2 = aProvider.get(); return new F(); } } TestEntryPoint entryPoint = new TestEntryPoint(); TestModule module = new TestModule(); ObjectGraph.createWith(new TestingLoader(), module).inject(entryPoint); entryPoint.lProvider.get(); assertThat(module.a1).isNotNull(); assertThat(module.a2).isNotNull(); assertThat(module.a1).isNotSameAs(module.a2); assertThat(entryPoint.lProvider.get()).isSameAs(entryPoint.lProvider.get()); } @Singleton public static class L { @Inject @Named("one") F f; @Inject Provider lProvider; } @Test public void singletonInGraph() { class TestEntryPoint { @Inject N n1; @Inject N n2; @Inject F f1; @Inject F f2; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides @Singleton F provideF() { return new F(); } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.f1).isSameAs(entryPoint.f2); assertThat(entryPoint.f1).isSameAs(entryPoint.n1.f1); assertThat(entryPoint.f1).isSameAs(entryPoint.n1.f2); assertThat(entryPoint.f1).isSameAs(entryPoint.n2.f1); assertThat(entryPoint.f1).isSameAs(entryPoint.n2.f2); assertThat(entryPoint.f1).isSameAs(entryPoint.n1.fProvider.get()); assertThat(entryPoint.f1).isSameAs(entryPoint.n2.fProvider.get()); } public static class N { @Inject F f1; @Inject F f2; @Inject Provider fProvider; } @Test public void noJitBindingsForAnnotations() { class TestEntryPoint { @Inject @Named("a") A a; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } @Test public void injectableSupertypes() { class TestEntryPoint { @Inject Q q; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides F provideF() { return new F(); } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.q.f).isNotNull(); } @Test public void uninjectableSupertypes() { class TestEntryPoint { @Inject T t; } @Module(injects = TestEntryPoint.class) class TestModule { } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.t).isNotNull(); } public static class P { @Inject F f; } public static class Q extends P { @Inject Q() {} } static class S { } public static class T extends S { @Inject T() {} } @Test public void singletonsAreNotEager() { class TestEntryPoint { @Inject Provider aProvider; } @Module(injects = TestEntryPoint.class) class TestModule { boolean sInjected = false; @Provides F provideF(R r) { return new F(); } @Provides @Singleton S provideS() { sInjected = true; return new S(); } } R.injected = false; TestEntryPoint entryPoint = new TestEntryPoint(); TestModule module = new TestModule(); ObjectGraph.createWith(new TestingLoader(), module).inject(entryPoint); assertThat(R.injected).isFalse(); assertThat(module.sInjected).isFalse(); } @Singleton static class R { static boolean injected = false; @Inject R() { injected = true; } } @Test public void providesSet() { final Set set = Collections.emptySet(); class TestEntryPoint { @Inject Set set; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides Set provideSet() { return set; } } TestEntryPoint entryPoint = new TestEntryPoint(); TestModule module = new TestModule(); ObjectGraph.createWith(new TestingLoader(), module).inject(entryPoint); assertThat(entryPoint.set).isSameAs(set); } @Test public void providesSetValues() { final Set set = Collections.emptySet(); class TestEntryPoint { @Inject Set set; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type = Provides.Type.SET_VALUES) Set provideSet() { return set; } } TestEntryPoint entryPoint = new TestEntryPoint(); TestModule module = new TestModule(); ObjectGraph.createWith(new TestingLoader(), module).inject(entryPoint); // copies into immutable collection assertThat(entryPoint.set).isNotSameAs(set); assertThat(entryPoint.set).isEqualTo(set); } @Test public void providerMethodsConflict() { @Module class TestModule { @Provides A provideA1() { throw new AssertionError(); } @Provides A provideA2() { throw new AssertionError(); } } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()); fail(); } catch (IllegalArgumentException expected) { } } @Test public void providesSetConflictsWithProvidesTypeSet() { @Module class TestModule { @Provides(type = Provides.Type.SET) A provideSetElement() { throw new AssertionError(); } @Provides Set provideSet() { throw new AssertionError(); } } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()); fail(); } catch (IllegalArgumentException expected) { } } @Test public void providesSetConflictsWithProvidesTypeSetValues() { @Module class TestModule { @Provides(type = Provides.Type.SET_VALUES) Set provideSetContribution() { throw new AssertionError(); } @Provides Set provideSet() { throw new AssertionError(); } } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()); fail(); } catch (IllegalArgumentException expected) { } } @Test public void providesSetOfProvidersIsDifferentThanProvidesTypeSetValues() { final Set set = Collections.emptySet(); final Set> providers = Collections.emptySet(); class TestEntryPoint { @Inject Set set; @Inject Set> providers; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type = Provides.Type.SET_VALUES) Set provideSetContribution() { return set; } @Provides Set> provideProviders() { return providers; } } TestEntryPoint entryPoint = new TestEntryPoint(); TestModule module = new TestModule(); ObjectGraph.createWith(new TestingLoader(), module).inject(entryPoint); // copies into immutable collection assertThat(entryPoint.set).isNotSameAs(set); assertThat(entryPoint.set).isEqualTo(set); assertThat(entryPoint.providers).isSameAs(providers); } @Test public void singletonsInjectedOnlyIntoProviders() { class TestEntryPoint { @Inject Provider aProvider; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides @Singleton A provideA() { return new A(); } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.aProvider.get()).isSameAs(entryPoint.aProvider.get()); } @Test public void moduleOverrides() { class TestEntryPoint { @Inject Provider eProvider; } @Module(injects = TestEntryPoint.class) class BaseModule { @Provides F provideF() { throw new AssertionError(); } @Provides E provideE(F f) { return new E(f); } } @Module(overrides = true) class OverridesModule { @Provides F provideF() { return new F(); } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new BaseModule(), new OverridesModule()).inject(entryPoint); E e = entryPoint.eProvider.get(); assertThat(e).isNotNull(); assertThat(e.f).isNotNull(); } @Test public void noJitBindingsForInterfaces() { class TestEntryPoint { @Inject RandomAccess randomAccess; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } @Test public void objectGraphGetInterface() { final Runnable runnable = new Runnable() { @Override public void run() { } }; @Module(injects = Runnable.class) class TestModule { @Provides Runnable provideRunnable() { return runnable; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); graph.validate(); assertThat(graph.get(Runnable.class)).isSameAs(runnable); } @Test public void noProvideBindingsForAbstractClasses() { class TestEntryPoint { @Inject AbstractList abstractList; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } static class ExtendsParameterizedType extends AbstractList { @Inject String string; @Override public Integer get(int i) { throw new AssertionError(); } @Override public int size() { throw new AssertionError(); } } /** * We've had bugs where we look for the wrong keys when a class extends a * parameterized class. Explicitly test that we can inject such classes. */ @Test public void extendsParameterizedType() { class TestEntryPoint { @Inject ExtendsParameterizedType extendsParameterizedType; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides String provideString() { return "injected"; } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.extendsParameterizedType.string).isEqualTo("injected"); } @Test public void injectParameterizedType() { class TestEntryPoint { @Inject List listOfStrings; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides List provideList() { return Arrays.asList("a", "b"); } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); assertThat(entryPoint.listOfStrings).isEqualTo(Arrays.asList("a", "b")); } @Test public void injectWildcardType() { class TestEntryPoint { @Inject List listOfNumbers; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides List provideList() { return Arrays.asList(1, 2); } } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()); fail(); } catch (UnsupportedOperationException expected) { } } static class Parameterized { @Inject String string; } @Test public void noConstructorInjectionsForClassesWithTypeParameters() { class TestEntryPoint { @Inject Parameterized parameterized; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides String provideString() { return "injected"; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } @Test public void moduleWithNoProvidesMethods() { @Module class TestModule { } ObjectGraph.createWith(new TestingLoader(), new TestModule()); } @Test public void getInstance() { final AtomicInteger next = new AtomicInteger(0); @Module(injects = Integer.class) class TestModule { @Provides Integer provideInteger() { return next.getAndIncrement(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertThat((int) graph.get(Integer.class)).isEqualTo(0); assertThat((int) graph.get(Integer.class)).isEqualTo(1); } @Test public void getInstanceRequiresEntryPoint() { @Module class TestModule { @Provides Integer provideInteger() { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.get(Integer.class); fail(); } catch (IllegalArgumentException expected) { } } @Test public void getInstanceOfPrimitive() { @Module(injects = int.class) class TestModule { @Provides int provideInt() { return 1; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertEquals(1, (int) graph.get(int.class)); } @Test public void getInstanceOfArray() { @Module(injects = int[].class) class TestModule { @Provides int[] provideIntArray() { return new int[] { 1, 2, 3 }; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertEquals("[1, 2, 3]", Arrays.toString(graph.get(int[].class))); } @Test public void getInstanceAndInjectMembersUseDifferentKeys() { class BoundTwoWays { @Inject String s; } @Module(injects = BoundTwoWays.class) class TestModule { @Provides BoundTwoWays provideBoundTwoWays() { BoundTwoWays result = new BoundTwoWays(); result.s = "Pepsi"; return result; } @Provides String provideString() { return "Coke"; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); BoundTwoWays provided = graph.get(BoundTwoWays.class); assertEquals("Pepsi", provided.s); BoundTwoWays membersInjected = new BoundTwoWays(); graph.inject(membersInjected); assertEquals("Coke", membersInjected.s); } static class NoInjections { NoInjections(Void noDefaultConstructorEither) { } } @Test public void entryPointNeedsNoInjectAnnotation() { @Module(injects = NoInjections.class) class TestModule { } ObjectGraph.createWith(new TestingLoader(), new TestModule()).validate(); } static class InjectMembersOnly { InjectMembersOnly(Void noInjectableConstructor) { } @Inject String string; } @Test public void cannotGetOnMembersOnlyInjectionPoint() { @Module(injects = InjectMembersOnly.class) class TestModule { @Provides String provideString() { return "injected"; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.get(InjectMembersOnly.class); fail(); } catch (IllegalStateException expected) { } InjectMembersOnly instance = new InjectMembersOnly(null); graph.inject(instance); assertThat(instance.string).isEqualTo("injected"); } @Test public void nonEntryPointNeedsInjectAnnotation() { @Module class TestModule { @Provides String provideString(NoInjections noInjections) { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } static class TwoAtInjectConstructors { @Inject TwoAtInjectConstructors() { } @Inject TwoAtInjectConstructors(String s) { } } @Test public void twoAtInjectConstructorsIsRejected() { @Module(injects = TwoAtInjectConstructors.class) class TestModule { @Provides String provideString() { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } @Test public void runtimeProvidesMethodsExceptionsAreNotWrapped() { class TestEntryPoint { @Inject String string; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides String provideString() { throw new ClassCastException("foo"); } } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(new TestEntryPoint()); fail(); } catch (ClassCastException e) { assertThat(e.getMessage()).isEqualTo("foo"); } } static class ThrowsOnConstruction { @Inject ThrowsOnConstruction() { throw new ClassCastException("foo"); } } @Test public void runtimeConstructorExceptionsAreNotWrapped() { @Module(injects = ThrowsOnConstruction.class) class TestModule { } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()).get(ThrowsOnConstruction.class); fail(); } catch (ClassCastException e) { assertThat(e.getMessage()).isEqualTo("foo"); } } static class SingletonLinkedFromExtension { @Inject C c; // Singleton. } @Module(complete = false, injects = C.class) static class RootModule { } @Module(addsTo = RootModule.class, injects = SingletonLinkedFromExtension.class) static class ExtensionModule { } @Test public void testSingletonLinkingThroughExtensionGraph() { ObjectGraph root = ObjectGraph.createWith(new TestingLoader(), new RootModule()); // DO NOT CALL root.get(C.class)) HERE to get forced-linking behaviour from plus(); ObjectGraph extension = root.plus(new ExtensionModule()); assertThat(extension.get(SingletonLinkedFromExtension.class).c).isSameAs(root.get(C.class)); } @Test public void privateFieldsFail() { class Test { @Inject private Object nope; } @Module(injects = Test.class) class TestModule { @Provides Object provideObject() { return null; } } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(new Test()); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage()).contains("Can't inject private field: "); } } @Test public void privateConstructorsFail() { class Test { @Inject private Test() { } } @Module(injects = Test.class) class TestModule { } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()).get(Test.class); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage()).contains("Can't inject private constructor: "); } } /** https://github.com/square/dagger/issues/231 */ @Test public void atInjectAlwaysRequiredForConstruction() { @Module(injects = ArrayList.class) class TestModule { } ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); objectGraph.validate(); try { objectGraph.get(ArrayList.class); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage()).contains("Unable to create binding for java.util.ArrayList"); } } } ================================================ FILE: core/src/test/java/dagger/LazyInjectionTest.java ================================================ /* * 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.TestingLoader; import javax.inject.Inject; 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 final class LazyInjectionTest { @Test public void getLazyDoesNotCauseInjectedTypesToBeLoaded() { @Module(injects = LazyEntryPoint.class) class TestModule { } ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertThat(lazyEntryPointLoaded).isFalse(); } private static boolean lazyEntryPointLoaded = false; static class LazyEntryPoint { static { lazyEntryPointLoaded = true; } } @Test public void getLazyDoesNotCauseProvidesParametersToBeLoaded() { @Module class TestModule { @Provides Object provideObject(LazyProvidesParameter parameter) { throw new AssertionError(); } } ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertThat(lazyProvidesParameterLoaded).isFalse(); } private static boolean lazyProvidesParameterLoaded = false; static class LazyProvidesParameter { static { lazyProvidesParameterLoaded = true; } } @Test public void getLazyDoesNotCauseProvidesResultToBeLoaded() { @Module class TestModule { @Provides LazyProvidesResult provideLazy() { throw new AssertionError(); } } ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertThat(lazyProvidesResultLoaded).isFalse(); } private static boolean lazyProvidesResultLoaded = false; static class LazyProvidesResult { static { lazyProvidesResultLoaded = true; } } @Test public void getLazyDoesNotCauseStaticsToBeLoaded() { @Module(staticInjections = LazyInjectStatics.class) class TestModule { } ObjectGraph.createWith(new TestingLoader(), new TestModule()); assertThat(LazyInjectStaticsLoaded).isFalse(); } private static boolean LazyInjectStaticsLoaded = false; static class LazyInjectStatics { static { LazyInjectStaticsLoaded = true; } } @Test public void lazyInjectionRequiresProvidesMethod() { class TestEntryPoint { @Inject String injected; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides String provideString(Integer integer) { return integer.toString(); } @Provides Integer provideInteger() { return 5; } } ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); TestEntryPoint entryPoint = new TestEntryPoint(); objectGraph.inject(entryPoint); assertThat(entryPoint.injected).isEqualTo("5"); } } ================================================ FILE: core/src/test/java/dagger/MembersInjectorTest.java ================================================ /* * 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.TestingLoader; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; 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 MembersInjector injection, and how object graph features interact with * types unconstructable types (types that support members injection only). */ @RunWith(JUnit4.class) public final class MembersInjectorTest { @Test public void injectMembers() { class TestEntryPoint { @Inject MembersInjector membersInjector; } @Module(injects = TestEntryPoint.class) class StringModule { @Provides String provideString() { return "injected"; } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new StringModule()).inject(entryPoint); Injectable injectable = new Injectable(); entryPoint.membersInjector.injectMembers(injectable); assertThat(injectable.injected).isEqualTo("injected"); } static class Injectable { @Inject String injected; } static class Unconstructable { final String constructor; @Inject String injected; Unconstructable(String constructor) { this.constructor = constructor; } } @Test public void membersInjectorOfUnconstructableIsOkay() { class TestEntryPoint { @Inject MembersInjector membersInjector; } @Module(injects = TestEntryPoint.class) class StringModule { @Provides String provideString() { return "injected"; } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new StringModule()).inject(entryPoint); Unconstructable object = new Unconstructable("constructor"); entryPoint.membersInjector.injectMembers(object); assertThat(object.constructor).isEqualTo("constructor"); assertThat(object.injected).isEqualTo("injected"); } @Test public void injectionOfUnconstructableFails() { class TestEntryPoint { @Inject Unconstructable unconstructable; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.get(TestEntryPoint.class); fail(); } catch (IllegalStateException expected) { } } @Test public void instanceInjectionOfMembersOnlyType() { class TestEntryPoint { @Inject Provider provider; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.get(TestEntryPoint.class); fail(); } catch (IllegalStateException expected) { } } @Test public void rejectUnconstructableSingleton() { class TestEntryPoint { @Inject MembersInjector membersInjector; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.get(TestEntryPoint.class); fail(); } catch (IllegalStateException expected) { } } @Singleton static class UnconstructableSingleton { final String constructor; @Inject String injected; UnconstructableSingleton(String constructor) { this.constructor = constructor; } } class NonStaticInner { @Inject String injected; } @Test public void membersInjectorOfNonStaticInnerIsOkay() { class TestEntryPoint { @Inject MembersInjector membersInjector; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides String provideString() { return "injected"; } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); NonStaticInner nonStaticInner = new NonStaticInner(); entryPoint.membersInjector.injectMembers(nonStaticInner); assertThat(nonStaticInner.injected).isEqualTo("injected"); } @Test public void instanceInjectionOfNonStaticInnerFailsEarly() { class TestEntryPoint { @Inject NonStaticInner nonStaticInner; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.get(TestEntryPoint.class); fail(); } catch (IllegalStateException expected) { } } @Test public void providesMethodsAndMembersInjectionDoNotConflict() { class InjectsString { @Inject String value; } class TestEntryPoint { @Inject Provider provider; @Inject MembersInjector membersInjector; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides InjectsString provideInjectsString() { InjectsString result = new InjectsString(); result.value = "provides"; return result; } @Provides String provideString() { return "members"; } } TestEntryPoint entryPoint = new TestEntryPoint(); ObjectGraph.createWith(new TestingLoader(), new TestModule()).inject(entryPoint); InjectsString provided = entryPoint.provider.get(); assertThat(provided.value).isEqualTo("provides"); InjectsString membersInjected = new InjectsString(); entryPoint.membersInjector.injectMembers(membersInjected); assertThat(membersInjected.value).isEqualTo("members"); } } ================================================ FILE: core/src/test/java/dagger/ModuleTest.java ================================================ /* * 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.TestingLoader; import javax.inject.Inject; 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; //TODO: Migrate to compiler. @RunWith(JUnit4.class) public final class ModuleTest { static class TestEntryPoint { @Inject String s; } @Module(injects = TestEntryPoint.class) static class ModuleWithEntryPoint { } @Test public void childModuleWithEntryPoint() { @Module(includes = ModuleWithEntryPoint.class) class TestModule { @Provides String provideString() { return "injected"; } } ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); TestEntryPoint entryPoint = objectGraph.get(TestEntryPoint.class); assertThat(entryPoint.s).isEqualTo("injected"); } static class TestStaticInjection { @Inject static String s; } @Module(staticInjections = TestStaticInjection.class) static class ModuleWithStaticInjection { } @Test public void childModuleWithStaticInjection() { @Module(includes = ModuleWithStaticInjection.class) class TestModule { @Provides String provideString() { return "injected"; } } ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); TestStaticInjection.s = null; objectGraph.injectStatics(); assertThat(TestStaticInjection.s).isEqualTo("injected"); } @Module static class ModuleWithBinding { @Provides String provideString() { return "injected"; } } @Test public void childModuleWithBinding() { @Module( injects = TestEntryPoint.class, includes = ModuleWithBinding.class ) class TestModule { } ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); TestEntryPoint entryPoint = new TestEntryPoint(); objectGraph.inject(entryPoint); assertThat(entryPoint.s).isEqualTo("injected"); } @Module(includes = ModuleWithBinding.class) static class ModuleWithChildModule { } @Test public void childModuleWithChildModule() { @Module( injects = TestEntryPoint.class, includes = ModuleWithChildModule.class ) class TestModule { } ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); TestEntryPoint entryPoint = new TestEntryPoint(); objectGraph.inject(entryPoint); assertThat(entryPoint.s).isEqualTo("injected"); } @Module static class ModuleWithConstructor { private final String value; ModuleWithConstructor(String value) { this.value = value; } @Provides String provideString() { return value; } } @Test public void childModuleMissingManualConstruction() { @Module(includes = ModuleWithConstructor.class) class TestModule { } try { ObjectGraph.createWith(new TestingLoader(), new TestModule()); fail(); } catch (IllegalArgumentException expected) { } } @Test public void childModuleWithManualConstruction() { @Module( injects = TestEntryPoint.class, includes = ModuleWithConstructor.class ) class TestModule { } ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new ModuleWithConstructor("a"), new TestModule()); TestEntryPoint entryPoint = new TestEntryPoint(); objectGraph.inject(entryPoint); assertThat(entryPoint.s).isEqualTo("a"); } static class A {} static class B { @Inject A a; } @Module(injects = A.class) public static class TestModuleA { @Provides A a() { return new A(); } } @Module(includes = TestModuleA.class, injects = B.class) public static class TestModuleB {} @Test public void autoInstantiationOfModules() { // Have to make these non-method-scoped or instantiation errors occur. ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), TestModuleA.class); assertThat(objectGraph.get(A.class)).isNotNull(); } @Test public void autoInstantiationOfIncludedModules() { // Have to make these non-method-scoped or instantiation errors occur. ObjectGraph objectGraph = ObjectGraph.createWith(new TestingLoader(), new TestModuleB()); // TestModuleA auto-created. assertThat(objectGraph.get(A.class)).isNotNull(); assertThat(objectGraph.get(B.class).a).isNotNull(); } static class ModuleMissingModuleAnnotation {} @Module(includes = ModuleMissingModuleAnnotation.class) static class ChildModuleMissingModuleAnnotation {} @Test public void childModuleMissingModuleAnnotation() { try { ObjectGraph.createWith(new TestingLoader(), new ChildModuleMissingModuleAnnotation()); } catch (IllegalArgumentException e) { assertThat(e.getMessage()) .contains("No @Module on dagger.ModuleTest$ModuleMissingModuleAnnotation"); } } @Module static class ThreadModule extends Thread {} @Test public void moduleExtendingClassThrowsException() { try { ObjectGraph.createWith(new TestingLoader(), new ThreadModule()); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage()).startsWith("Modules must not extend from other classes: "); } } @Test public void provideProviderFails() { @Module class ProvidesProviderModule { @Provides Provider provideObject() { return null; } } try { ObjectGraph.createWith(new TestingLoader(), new ProvidesProviderModule()); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage()).startsWith("@Provides method must not return Provider directly: "); assertThat(e.getMessage()).endsWith("ProvidesProviderModule.provideObject"); } } @Test public void provideRawProviderFails() { @Module class ProvidesRawProviderModule { @Provides Provider provideObject() { return null; } } try { ObjectGraph.createWith(new TestingLoader(), new ProvidesRawProviderModule()); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage()).startsWith("@Provides method must not return Provider directly: "); assertThat(e.getMessage()).endsWith("ProvidesRawProviderModule.provideObject"); } } @Test public void provideLazyFails() { @Module class ProvidesLazyModule { @Provides Lazy provideObject() { return null; } } try { ObjectGraph.createWith(new TestingLoader(), new ProvidesLazyModule()); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage()).startsWith("@Provides method must not return Lazy directly: "); assertThat(e.getMessage()).endsWith("ProvidesLazyModule.provideObject"); } } @Test public void provideRawLazyFails() { @Module class ProvidesRawLazyModule { @Provides Lazy provideObject() { return null; } } try { ObjectGraph.createWith(new TestingLoader(), new ProvidesRawLazyModule()); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage()).startsWith("@Provides method must not return Lazy directly: "); assertThat(e.getMessage()).endsWith("ProvidesRawLazyModule.provideObject"); } } } ================================================ FILE: core/src/test/java/dagger/ProblemDetectorTest.java ================================================ /* * 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.TestingLoader; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public final class ProblemDetectorTest { @Test public void atInjectCircularDependenciesDetected() { class TestEntryPoint { @Inject Rock rock; } @Module(injects = TestEntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (RuntimeException expected) { } } @Test public void providesCircularDependenciesDetected() { @Module class TestModule { @Provides Integer provideInteger(String s) { throw new AssertionError(); } @Provides String provideString(Integer i) { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (RuntimeException expected) { } } @Test public void validateLazy() { @Module(library = true) class TestModule { @Provides Integer dependOnLazy(Lazy lazyString) { throw new AssertionError(); } @Provides String provideLazyValue() { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); graph.validate(); } static class Rock { @Inject Scissors scissors; } static class Scissors { @Inject Paper paper; } static class Paper { @Inject Rock rock; } } ================================================ FILE: core/src/test/java/dagger/SetBindingTest.java ================================================ /* * 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; import dagger.internal.TestingLoader; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; 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; import static java.util.Collections.emptySet; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public final class SetBindingTest { @Test public void multiValueBindings_SingleModule() { class TestEntryPoint { @Inject Set strings; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type=SET) String provideFirstString() { return "string1"; } @Provides(type=SET) String provideSecondString() { return "string2"; } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertEquals(set("string1", "string2"), ep.strings); } @Test public void multiValueBindings_MultiModule() { class TestEntryPoint { @Inject Set strings; } @Module class TestIncludesModule { @Provides(type=SET) String provideSecondString() { return "string2"; } } @Module(injects = TestEntryPoint.class, includes = TestIncludesModule.class) class TestModule { @Provides(type=SET) String provideFirstString() { return "string1"; } @Provides(type=SET_VALUES) Set provideDefaultStrings() { return emptySet(); } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule(), new TestIncludesModule()); assertEquals(set("string1", "string2"), ep.strings); } @Test public void multiValueBindings_MultiModule_NestedSet() { class TestEntryPoint { @Inject Set> stringses; } @Module class TestIncludesModule { @Provides(type=SET) Set provideSecondStrings() { return set("string2"); } } @Module(injects = TestEntryPoint.class, includes = TestIncludesModule.class) class TestModule { @Provides(type=SET) Set provideFirstStrings() { return set("string1"); } @Provides(type=SET_VALUES) Set> provideDefaultStringeses() { return set(set("string3")); } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule(), new TestIncludesModule()); assertEquals(set(set("string1"),set("string2"), set("string3")), ep.stringses); } @Test public void multiValueBindings_WithSingletonAndDefaultValues() { final AtomicInteger singletonCounter = new AtomicInteger(100); final AtomicInteger defaultCounter = new AtomicInteger(200); class TestEntryPoint { @Inject Set objects1; @Inject Set objects2; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type=SET) @Singleton Integer a() { return singletonCounter.getAndIncrement(); } @Provides(type=SET) Integer b() { return defaultCounter.getAndIncrement(); } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertEquals(set(100, 200), ep.objects1); assertEquals(set(100, 201), ep.objects2); } @Test public void multiValueBindings_WithSingletonsAcrossMultipleInjectableTypes() { final AtomicInteger singletonCounter = new AtomicInteger(100); final AtomicInteger defaultCounter = new AtomicInteger(200); class TestEntryPoint1 { @Inject Set objects1; } class TestEntryPoint2 { @Inject Set objects2; } @Module(injects = { TestEntryPoint1.class, TestEntryPoint2.class }) class TestModule { @Provides(type=SET) @Singleton Integer a() { return singletonCounter.getAndIncrement(); } @Provides(type=SET) Integer b() { return defaultCounter.getAndIncrement(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); TestEntryPoint1 ep1 = graph.inject(new TestEntryPoint1()); TestEntryPoint2 ep2 = graph.inject(new TestEntryPoint2()); assertEquals(set(100, 200), ep1.objects1); assertEquals(set(100, 201), ep2.objects2); } @Test public void multiValueBindings_WithQualifiers() { class TestEntryPoint { @Inject Set strings; @Inject @Named("foo") Set fooStrings; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type=SET_VALUES) Set provideString1() { return set("string1"); } @Provides(type=SET) String provideString2() { return "string2"; } @Provides(type=SET) @Named("foo") String provideString3() { return "string3"; } @Provides(type=SET_VALUES) @Named("foo") Set provideString4() { return set("string4"); } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertEquals(set("string1", "string2"), ep.strings); assertEquals(set("string4", "string3"), ep.fooStrings); } // TODO(cgruber): Move this into an example project. @Test public void sampleMultiBindingLogger() { class TestEntryPoint { @Inject Logger logger; public void doStuff() { Throwable t = new NullPointerException("Naughty Naughty"); this.logger.log("Logging an error", t); } } final AtomicReference logoutput = new AtomicReference(); @Module class LogModule { @Provides(type=SET) LogSink outputtingLogSink() { return new LogSink() { @Override public void log(LogMessage message) { StringWriter sw = new StringWriter(); message.error.printStackTrace(new PrintWriter(sw)); logoutput.set(message.message + "\n" + sw.getBuffer().toString()); } }; } } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type=SET) LogSink nullLogger() { return new LogSink() { @Override public void log(LogMessage message) {} }; } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(),new TestModule(), new LogModule()); assertNull(logoutput.get()); ep.doStuff(); assertNotNull(logoutput.get()); assertThat(logoutput.get()).contains("Naughty Naughty"); assertThat(logoutput.get()).contains("NullPointerException"); } @Test public void duplicateValuesContributed() { class TestEntryPoint { @Inject Set strings; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type=SET) String provideString1() { return "a"; } @Provides(type=SET) String provideString2() { return "a"; } @Provides(type=SET) String provideString3() { return "b"; } } TestEntryPoint ep = injectWithModule(new TestEntryPoint(), new TestModule()); assertThat(ep.strings).containsExactly("a", "b"); } @Test public void validateSetBinding() { class TestEntryPoint { @Inject Set strings; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type=SET) String provideString1() { return "string1"; } @Provides(type=SET) String provideString2() { return "string2"; } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); graph.validate(); } @Test public void validateEmptySetBinding() { class TestEntryPoint { @Inject Set strings; } @Module(injects = TestEntryPoint.class) class TestModule { @Provides(type=SET_VALUES) Set provideDefault() { return emptySet(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); graph.validate(); } @Test public void validateLibraryModules() { class TestEntryPoint {} @Module(library = true) class SetModule { @Provides(type = SET) public String provideString() { return ""; } } @Module(injects = TestEntryPoint.class, includes = SetModule.class) class TestModule {} ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule(), new SetModule()); graph.validate(); } @Test public void validateLibraryModules_nonLibraryContributors() { class TestEntryPoint {} @Module(library = true) class SetModule1 { @Provides(type = SET) public String provideString() { return "a"; } } @Module class SetModule2 { @Provides(type = SET) public String provideString() { return "b"; } } @Module(injects = TestEntryPoint.class, includes = { SetModule1.class, SetModule2.class }) class TestModule {} ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule(), new SetModule1(), new SetModule2()); try { graph.validate(); fail(); } catch (IllegalStateException expected) {} } static class Logger { @Inject Set loggers; public void log(String text, Throwable error) { LogMessage m = new LogMessage(text, error); for (LogSink sink : loggers) { sink.log(m); } } } static class LogMessage { public final String message; public final Throwable error; public LogMessage (String message, Throwable error) { this.message = message; this.error = error; } } static interface LogSink { void log(LogMessage message); } private T injectWithModule(T ep, Object ... modules) { return ObjectGraph.createWith(new TestingLoader(), modules).inject(ep); } private Set set(T... ts) { return new LinkedHashSet(Arrays.asList(ts)); } } ================================================ FILE: core/src/test/java/dagger/ThreadSafetyTest.java ================================================ /* * 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; import dagger.internal.TestingLoader; import java.util.ArrayList; import java.util.List; 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.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.inject.Inject; import javax.inject.Singleton; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; /** * Test Singleton and Lazy bindings for thread-safety. */ @RunWith(JUnit4.class) public final class ThreadSafetyTest { private static final Integer FIRST_VALUE = 0; private static final int THREAD_COUNT = 100; private final ExecutorService es = Executors.newFixedThreadPool(THREAD_COUNT); private final CountDownLatch latch = new CountDownLatch(THREAD_COUNT + 1); static class LazyEntryPoint { @Inject Lazy lazy; } @Module(injects = { Long.class, LazyEntryPoint.class }) static class LatchingModule { private final AtomicInteger count = new AtomicInteger(FIRST_VALUE); private final CountDownLatch latch; LatchingModule(CountDownLatch latch) { this.latch = latch; } @Provides @Singleton Long provideLong() { return Long.valueOf(provideInteger()); } @Provides Integer provideInteger() { try { latch.await(); } catch (InterruptedException e) { throw new AssertionError("Interrupted Thread!!"); } return count.getAndIncrement(); } } @Test public void concurrentSingletonAccess() throws Exception { final List> futures = new ArrayList>(); final ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new LatchingModule(latch)); for (int i = 0; i < THREAD_COUNT; i++) { futures.add(es.submit(new Callable() { @Override public Long call() { latch.countDown(); return graph.get(Long.class); } })); } latch.countDown(); for (Future future : futures) { assertThat(future.get(1, TimeUnit.SECONDS)) .named("Lock failure - count should never increment") .isEqualTo(0); } } @Test public void concurrentLazyAccess() throws Exception { final List> futures = new ArrayList>(); final ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new LatchingModule(latch)); final LazyEntryPoint lep = graph.get(LazyEntryPoint.class); for (int i = 0; i < THREAD_COUNT; i++) { futures.add(es.submit(new Callable() { @Override public Integer call() { latch.countDown(); return lep.lazy.get(); } })); } latch.countDown(); for (Future future : futures) { assertThat(future.get(1, TimeUnit.SECONDS)) .named("Lock failure - count should never increment") .isEqualTo(0); } } } ================================================ FILE: core/src/test/java/dagger/UnusedProviderTest.java ================================================ /* * 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; import dagger.internal.TestingLoader; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Set; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public class UnusedProviderTest { @Test public void unusedProvidesMethod_whenModuleLibrary_passes() throws Exception { class EntryPoint { } class BagOfMoney { } @Module(injects = EntryPoint.class, library = true) class TestModule { @Provides BagOfMoney providesMoney() { return new BagOfMoney(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); graph.validate(); } @Test public void unusedProviderMethod_whenNotLibraryModule_fails() throws Exception { class EntryPoint { } class BagOfMoney { } @Module(injects = EntryPoint.class) class TestModule { @Provides BagOfMoney providesMoney() { return new BagOfMoney(); } } try { ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); graph.validate(); fail("Validation should have exploded!"); } catch (IllegalStateException expected) { } } @Test public void whenLibraryModulePlussedToNecessaryModule_shouldNotFailOnUnusedLibraryModule() throws Exception { class EntryPoint { } class BagOfMoney { } @Module(injects = EntryPoint.class, library = true) class ExampleLibraryModule { @Provides BagOfMoney providesMoney() { return new BagOfMoney(); } } @Module(injects = EntryPoint.class) class TestModule { } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); graph = graph.plus(new ExampleLibraryModule()); graph.validate(); } @Test public void unusedSetBinding() throws Exception { @Module class TestModule { @Provides(type = Provides.Type.SET) String provideA() { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } @Test public void unusedSetValuesBinding() throws Exception { @Module class TestModule { @Provides(type = Provides.Type.SET_VALUES) Set provideA() { throw new AssertionError(); } } ObjectGraph graph = ObjectGraph.createWith(new TestingLoader(), new TestModule()); try { graph.validate(); fail(); } catch (IllegalStateException expected) { } } } ================================================ FILE: core/src/test/java/dagger/internal/FailoverLoaderTest.java ================================================ /* * 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; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; /** * A test case to deal with fall-back to reflection where the concrete type has been generated * but the parent has no {@code @Inject} annotation, and so has not been generated. */ @RunWith(JUnit4.class) public final class FailoverLoaderTest { @Module(injects = Entry$Point.class) static class TestModule { @Provides String aString() { return "a"; } } /** A reflective module that will be loaded in place of a generated module for this test. */ static final class TestModule$$ModuleAdapter extends TestingModuleAdapter { public TestModule$$ModuleAdapter() { super(TestModule.class, TestModule.class.getAnnotation(Module.class)); } } static class Entry$Point { @Inject String a; } @Test public void simpleInjectionWithUnGeneratedCode() { Entry$Point entryPoint = new Entry$Point(); ObjectGraph.create(new TestModule()).inject(entryPoint); assertThat(entryPoint.a).isEqualTo("a"); } } ================================================ FILE: core/src/test/java/dagger/internal/KeysTest.java ================================================ /** * 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.internal; import dagger.Lazy; import dagger.MembersInjector; import dagger.Provides; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import javax.inject.Named; 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 dagger.Provides.Type.SET; @RunWith(JUnit4.class) public final class KeysTest { int primitive; @Test public void lonePrimitiveGetsBoxed() throws NoSuchFieldException { assertThat(fieldKey("primitive")) .isEqualTo("java.lang.Integer"); } Map> mapStringListInteger; @Test public void parameterizedTypes() throws NoSuchFieldException { assertThat(fieldKey("mapStringListInteger")) .isEqualTo("java.util.Map>"); } Map mapStringArrayInt; @Test public void parameterizedTypeOfPrimitiveArray() throws NoSuchFieldException { assertThat(fieldKey("mapStringArrayInt")) .isEqualTo("java.util.Map"); } @Named("foo") String annotatedType; @Test public void annotatedType() throws NoSuchFieldException { assertThat(fieldKey("annotatedType")) .isEqualTo("@javax.inject.Named(value=foo)/java.lang.String"); } String className; @Test public void testGetClassName() throws NoSuchFieldException { assertThat(Keys.getClassName(fieldKey("className"))) .isEqualTo("java.lang.String"); } @Named("foo") String classNameWithAnnotation; @Test public void testGetClassNameWithoutAnnotation() throws NoSuchFieldException { assertThat(Keys.getClassName(fieldKey("classNameWithAnnotation"))) .isEqualTo("java.lang.String"); } String[] classNameArray; @Test public void testGetClassNameArray() throws NoSuchFieldException { assertThat(Keys.getClassName(fieldKey("classNameArray"))).isNull(); } List classNameParameterized; @Test public void testGetClassParameterized() throws NoSuchFieldException { assertThat(Keys.getClassName(fieldKey("classNameParameterized"))).isNull(); } @Named("foo") String annotated; @Test public void testAnnotated() throws NoSuchFieldException { assertThat(fieldKey("annotated")).isEqualTo("@javax.inject.Named(value=foo)/java.lang.String"); assertThat(Keys.isAnnotated(fieldKey("annotated"))).isTrue(); } String notAnnotated; @Test public void testIsAnnotatedFalse() throws NoSuchFieldException { assertThat(Keys.isAnnotated(fieldKey("notAnnotated"))).isFalse(); } Provider providerOfType; String providedType; @Test public void testGetDelegateKey() throws NoSuchFieldException { assertThat(Keys.getBuiltInBindingsKey(fieldKey("providerOfType"))) .isEqualTo(fieldKey("providedType")); } @Named("/@") Provider providerOfTypeAnnotated; @Named("/@") String providedTypeAnnotated; @Test public void testGetDelegateKeyWithAnnotation() throws NoSuchFieldException { assertThat(Keys.getBuiltInBindingsKey(fieldKey("providerOfTypeAnnotated"))) .isEqualTo(fieldKey("providedTypeAnnotated")); } @Named("/@") MembersInjector membersInjectorOfType; @Named("/@") String injectedType; @Test public void testGetDelegateKeyWithMembersInjector() throws NoSuchFieldException { assertThat(Keys.getBuiltInBindingsKey(fieldKey("membersInjectorOfType"))) .isEqualTo("members/java.lang.String"); } @Named("/@") Lazy lazyAnnotatedString; @Named("/@") String eagerAnnotatedString; @Test public void testAnnotatedGetLazyKey() throws NoSuchFieldException { assertThat(Keys.getLazyKey(fieldKey("lazyAnnotatedString"))) .isEqualTo(fieldKey("eagerAnnotatedString")); } Lazy lazyString; String eagerString; @Test public void testGetLazyKey() throws NoSuchFieldException { assertThat(Keys.getLazyKey(fieldKey("lazyString"))).isEqualTo(fieldKey("eagerString")); } @Test public void testGetLazyKey_WrongKeyType() throws NoSuchFieldException { assertThat(Keys.getLazyKey(fieldKey("providerOfTypeAnnotated"))).isNull(); } @Provides(type=SET) String elementProvides() { return "foo"; } @Test public void testGetElementKey_NoQualifier() throws NoSuchMethodException { Method method = KeysTest.class.getDeclaredMethod("elementProvides", new Class[]{}); assertThat(Keys.getSetKey(method.getGenericReturnType(), method.getAnnotations(), method)) .isEqualTo("java.util.Set"); } @Named("foo") @Provides(type=SET) String qualifiedElementProvides() { return "foo"; } @Test public void testGetElementKey_WithQualifier() throws NoSuchMethodException { Method method = KeysTest.class.getDeclaredMethod("qualifiedElementProvides", new Class[]{}); assertThat(Keys.getSetKey(method.getGenericReturnType(), method.getAnnotations(), method)) .isEqualTo("@javax.inject.Named(value=foo)/java.util.Set"); } private String fieldKey(String fieldName) throws NoSuchFieldException { Field field = KeysTest.class.getDeclaredField(fieldName); return Keys.get(field.getGenericType(), field.getAnnotations(), field); } } ================================================ FILE: core/src/test/java/dagger/internal/SingletonBindingTest.java ================================================ /** * 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; 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 final class SingletonBindingTest { private Binding wrappedBinding; private Binding singletonBinding; @Before public void setUp() { wrappedBinding = new StringBinding(); singletonBinding = Linker.scope(wrappedBinding); } @Test public void testSingletonBindingIsSingleton() { assertThat(singletonBinding.isSingleton()).isTrue(); } // This next batch of tests validates that SingletonBinding consistently delegates to the wrapped binding for state. @Test public void testSingletonBindingDelegatesSetLinked() { singletonBinding.setLinked(); assertThat(wrappedBinding.isLinked()).isTrue(); } @Test public void testSingletonBindingDelegatesIsLinked() { wrappedBinding.setLinked(); assertThat(singletonBinding.isLinked()).isTrue(); } @Test public void testSingletonBindingDelegatesSetVisiting() { singletonBinding.setVisiting(true); assertThat(wrappedBinding.isVisiting()).isTrue(); } @Test public void testSingletonBindingDelegatesIsVisiting() { wrappedBinding.setVisiting(true); assertThat(singletonBinding.isVisiting()).isTrue(); } @Test public void testSingletonBindingDelegatesSetCycleFree() { singletonBinding.setCycleFree(true); assertThat(wrappedBinding.isCycleFree()).isTrue(); } @Test public void testSingletonBindingDelegatesIsCycleFree() { wrappedBinding.setCycleFree(true); assertThat(singletonBinding.isCycleFree()).isTrue(); } private static class StringBinding extends Binding { private StringBinding() { super("dummy", "dummy", true, "dummy"); // 3rd arg true => singleton } } } ================================================ FILE: core/src/test/java/dagger/internal/TestingLoader.java ================================================ /* * Copyright (C) 2013 Square, Inc. * Copyright (C) 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.internal.loaders.ReflectiveAtInjectBinding; import dagger.internal.loaders.ReflectiveStaticInjection; /** * A test-only loader that merely uses reflection to test internals. */ public final class TestingLoader extends Loader { @Override public ModuleAdapter getModuleAdapter(Class type) { ModuleAdapter adapter = TestingModuleAdapter.create(type); return adapter; } @Override public Binding getAtInjectBinding(String key, String className, ClassLoader ignored, boolean mustHaveInjections) { try { Class type = getClass().getClassLoader().loadClass(className); if (type.isInterface()) { return null; // Short-circuit since we can't build reflective bindings for interfaces. } return ReflectiveAtInjectBinding.create(type, mustHaveInjections); } catch (ClassNotFoundException e) { throw new TypeNotPresentException( String.format("Could not find %s needed for binding %s", className, key), e); } } @Override public StaticInjection getStaticInjection(Class injectedClass) { return ReflectiveStaticInjection.create(injectedClass); } } ================================================ FILE: core/src/test/java/dagger/internal/TestingModuleAdapter.java ================================================ /* * 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.internal; import dagger.Lazy; import dagger.Module; import dagger.Provides; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Set; import javax.inject.Provider; import javax.inject.Singleton; //TODO: Reduce the complexity of this and/or replace with a mock or fake. public class TestingModuleAdapter extends ModuleAdapter { public TestingModuleAdapter(Class moduleClass, Module annotation) { super( moduleClass, injectableTypesToKeys(annotation.injects()), annotation.staticInjections(), annotation.overrides(), annotation.includes(), annotation.complete(), annotation.library()); } private static String[] injectableTypesToKeys(Class[] injectableTypes) { String[] result = new String[injectableTypes.length]; for (int i = 0; i < injectableTypes.length; i++) { Class injectableType = injectableTypes[i]; result[i] = injectableType.isInterface() ? Keys.get(injectableType) : Keys.getMembersKey(injectableType); } return result; } @Override public void getBindings(BindingsGroup bindings, M module) { for (Class c = moduleClass; !c.equals(Object.class); c = c.getSuperclass()) { for (Method method : c.getDeclaredMethods()) { Provides provides = method.getAnnotation(Provides.class); if (provides != null) { Type genericReturnType = method.getGenericReturnType(); Type typeToCheck = genericReturnType; if (genericReturnType instanceof ParameterizedType) { typeToCheck = ((ParameterizedType) genericReturnType).getRawType(); } if (Provider.class.equals(typeToCheck)) { throw new IllegalStateException("@Provides method must not return Provider directly: " + c.getName() + "." + method.getName()); } if (Lazy.class.equals(typeToCheck)) { throw new IllegalStateException("@Provides method must not return Lazy directly: " + c.getName() + "." + method.getName()); } String key = Keys.get(genericReturnType, method.getAnnotations(), method); switch (provides.type()) { case UNIQUE: handleBindings(bindings, module, method, key, library); break; case SET: String setKey = Keys.getSetKey(method.getGenericReturnType(), method.getAnnotations(), method); handleSetBindings(bindings, module, method, setKey, key, library); break; case SET_VALUES: handleSetBindings(bindings, module, method, key, key, library); break; default: throw new AssertionError("Unknown @Provides type " + provides.type()); } } } } } private void handleBindings(BindingsGroup bindings, M module, Method method, String key, boolean library) { bindings.contributeProvidesBinding(key, new ReflectiveProvidesBinding(method, key, moduleClass.getName(), module, library)); } private void handleSetBindings(BindingsGroup bindings, M module, Method method, String setKey, String providerKey, boolean library) { SetBinding.add(bindings, setKey, new ReflectiveProvidesBinding( method, providerKey, moduleClass.getName(), module, library)); } @Override public M newModule() { try { Constructor constructor = moduleClass.getDeclaredConstructor(); constructor.setAccessible(true); return (M)constructor.newInstance(); } catch (InvocationTargetException e) { throw new IllegalArgumentException(e.getCause()); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Could not construct " + moduleClass.getName() + " as it lacks an accessible no-args constructor. This module must be passed" + " in as an instance, or an accessible no-args constructor must be added.", e); } catch (InstantiationException e) { throw new IllegalArgumentException("Failed to construct " + moduleClass.getName(), e); } catch (IllegalAccessException e) { throw new AssertionError(); } } @Override public String toString() { return "TestingModuleAdapter[" + this.moduleClass.getName() + "]"; } /** * Creates a TestingModuleAdapter or throws an {@code IllegalArgumentException}. */ public static ModuleAdapter create(Class moduleClass) { Module annotation = moduleClass.getAnnotation(Module.class); if (annotation == null) { throw new IllegalArgumentException("No @Module on " + moduleClass.getName()); } if (!moduleClass.getSuperclass().equals(Object.class)) { throw new IllegalArgumentException( "Modules must not extend from other classes: " + moduleClass.getName()); } return new TestingModuleAdapter(moduleClass, annotation); } /** * Invokes a method to provide a value. The method's parameters are injected. */ private static final class ReflectiveProvidesBinding extends ProvidesBinding { private Binding[] parameters; private final Method method; private final Object instance; public ReflectiveProvidesBinding(Method method, String key, String moduleClass, Object instance, boolean library) { super(key, method.isAnnotationPresent(Singleton.class), moduleClass, method.getName()); this.method = method; this.instance = instance; method.setAccessible(true); setLibrary(library); } @Override public void attach(Linker linker) { Type[] types = method.getGenericParameterTypes(); Annotation[][] annotations = method.getParameterAnnotations(); parameters = new Binding[types.length]; for (int i = 0; i < parameters.length; i++) { String key = Keys.get(types[i], annotations[i], method + " parameter " + i); parameters[i] = linker.requestBinding(key, method, instance.getClass().getClassLoader()); } } @Override public T get() { Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { args[i] = parameters[i].get(); } try { return (T) method.invoke(instance, args); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); throw cause instanceof RuntimeException ? (RuntimeException) cause : new RuntimeException(cause); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } @Override public void getDependencies(Set> get, Set> injectMembers) { for (Binding binding : parameters) { get.add(binding); } } @Override public void injectMembers(T t) { throw new AssertionError("Provides method bindings are not MembersInjectors"); } } } ================================================ FILE: deploy_website.sh ================================================ #!/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" 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 that isn't versioned (1.x, 2.x) ls | grep -E -v '^\d+\.x$' | xargs rm -rf # Copy website files from real repo cp -R ../website/* . # 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 ================================================ FILE: examples/android-activity-graphs/README.md ================================================ 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_ ================================================ FILE: examples/android-activity-graphs/pom.xml ================================================ 4.0.0 com.squareup.dagger.example dagger-example-parent 1.2.6-SNAPSHOT android-activity-graphs Examples: Android - Activity Graphs apk com.squareup.dagger dagger ${project.version} com.squareup.dagger dagger-compiler ${project.version} true com.google.android android provided com.google.android support-v4 com.simpligility.maven.plugins android-maven-plugin true ================================================ FILE: examples/android-activity-graphs/src/main/AndroidManifest.xml ================================================ ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ActivityModule.java ================================================ /* * 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.content.Context; import com.example.dagger.activitygraphs.ui.ActivityTitleController; import com.example.dagger.activitygraphs.ui.HomeActivity; import com.example.dagger.activitygraphs.ui.HomeFragment; import dagger.Module; import dagger.Provides; import javax.inject.Singleton; /** * This module represents objects which exist only for the scope of a single activity. We can * safely create singletons using the activity instance because the entire object graph will only * ever exist inside of that activity. */ @Module( injects = { HomeActivity.class, HomeFragment.class }, addsTo = AndroidModule.class, library = true ) public class ActivityModule { private final DemoBaseActivity activity; public ActivityModule(DemoBaseActivity activity) { this.activity = activity; } /** * Allow the activity context to be injected but require that it be annotated with * {@link ForActivity @ForActivity} to explicitly differentiate it from application context. */ @Provides @Singleton @ForActivity Context provideActivityContext() { return activity; } @Provides @Singleton ActivityTitleController provideTitleController() { return new ActivityTitleController(activity); } } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/AndroidModule.java ================================================ /* * 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.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(library = true) 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 @ForApplication} 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); } } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoApplication.java ================================================ /* * 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 dagger.ObjectGraph; import java.util.Arrays; import java.util.List; public class DemoApplication extends Application { private ObjectGraph applicationGraph; @Override public void onCreate() { super.onCreate(); applicationGraph = ObjectGraph.create(getModules().toArray()); } /** * A list of modules to use for the application graph. Subclasses can override this method to * provide additional modules provided they call {@code super.getModules()}. */ protected List getModules() { return Arrays.asList(new AndroidModule(this)); } ObjectGraph getApplicationGraph() { return applicationGraph; } } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoBaseActivity.java ================================================ /* * 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.os.Bundle; import android.support.v4.app.FragmentActivity; import dagger.ObjectGraph; import java.util.Arrays; import java.util.List; /** Base activity which sets up a per-activity object graph and performs injection. */ public abstract class DemoBaseActivity extends FragmentActivity { private ObjectGraph activityGraph; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create the activity graph by .plus-ing our modules onto the application graph. DemoApplication application = (DemoApplication) getApplication(); activityGraph = application.getApplicationGraph().plus(getModules().toArray()); // Inject ourselves so subclasses will have dependencies fulfilled when this method returns. activityGraph.inject(this); } @Override protected void onDestroy() { // Eagerly clear the reference to the activity graph to allow it to be garbage collected as // soon as possible. activityGraph = null; super.onDestroy(); } /** * A list of modules to use for the individual activity graph. Subclasses can override this * method to provide additional modules provided they call and include the modules returned by * calling {@code super.getModules()}. */ protected List getModules() { return Arrays.asList(new ActivityModule(this)); } /** Inject the supplied {@code object} using the activity-specific graph. */ public void inject(Object object) { activityGraph.inject(object); } } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/DemoBaseFragment.java ================================================ /* * 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.os.Bundle; import android.support.v4.app.Fragment; /** Base fragment which performs injection using the activity object graph of its parent. */ public class DemoBaseFragment extends Fragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ((DemoBaseActivity) getActivity()).inject(this); } } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ForActivity.java ================================================ /* * 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.Qualifier; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Qualifier @Retention(RUNTIME) public @interface ForActivity { } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ForApplication.java ================================================ /* * 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.Qualifier; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Qualifier @Retention(RUNTIME) public @interface ForApplication { } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/ActivityTitleController.java ================================================ /* * 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; /** * A simple abstraction which provides the ability to set the title on an activity. *

* 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. */ public class ActivityTitleController { private final Activity activity; public ActivityTitleController(Activity activity) { this.activity = activity; } public void setTitle(CharSequence title) { activity.setTitle(title); } } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeActivity.java ================================================ /* * 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 com.example.dagger.activitygraphs.DemoBaseActivity; import javax.inject.Inject; public class HomeActivity extends DemoBaseActivity { @Inject LocationManager locationManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // After the super.onCreate call returns we are guaranteed our injections are available. if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(android.R.id.content, HomeFragment.newInstance()) .commit(); } // TODO do something with the injected dependencies here! } } ================================================ FILE: examples/android-activity-graphs/src/main/java/com/example/dagger/activitygraphs/ui/HomeFragment.java ================================================ /* * 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.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.example.dagger.activitygraphs.DemoBaseFragment; import javax.inject.Inject; import static android.view.Gravity.CENTER; public class HomeFragment extends DemoBaseFragment { public static HomeFragment newInstance() { return new HomeFragment(); } @Inject ActivityTitleController titleController; @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"); } } ================================================ FILE: examples/android-activity-graphs/src/main/res/values/strings.xml ================================================ Dagger Activity Graph ================================================ FILE: examples/android-simple/README.md ================================================ 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_ ================================================ FILE: examples/android-simple/pom.xml ================================================ 4.0.0 com.squareup.dagger.example dagger-example-parent 1.2.6-SNAPSHOT android-simple Examples: Android - Simple apk com.squareup.dagger dagger ${project.version} com.squareup.dagger dagger-compiler ${project.version} true com.google.android android provided com.simpligility.maven.plugins android-maven-plugin true ================================================ FILE: examples/android-simple/src/main/AndroidManifest.xml ================================================ ================================================ FILE: examples/android-simple/src/main/java/com/example/dagger/simple/AndroidModule.java ================================================ /* * 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(library = true) 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); } } ================================================ FILE: examples/android-simple/src/main/java/com/example/dagger/simple/DemoApplication.java ================================================ /* * 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 dagger.ObjectGraph; import java.util.Arrays; import java.util.List; public class DemoApplication extends Application { private ObjectGraph graph; @Override public void onCreate() { super.onCreate(); graph = ObjectGraph.create(getModules().toArray()); } protected List getModules() { return Arrays.asList( new AndroidModule(this), new DemoModule() ); } public void inject(Object object) { graph.inject(object); } } ================================================ FILE: examples/android-simple/src/main/java/com/example/dagger/simple/DemoBaseActivity.java ================================================ /* * 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 DemoBaseActivity 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()).inject(this); } } ================================================ FILE: examples/android-simple/src/main/java/com/example/dagger/simple/DemoModule.java ================================================ /* * 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 com.example.dagger.simple.ui.HomeActivity; import dagger.Module; @Module( injects = HomeActivity.class, complete = false ) public class DemoModule { // TODO put your application-specific providers here! } ================================================ FILE: examples/android-simple/src/main/java/com/example/dagger/simple/ForApplication.java ================================================ /* * 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 { } ================================================ FILE: examples/android-simple/src/main/java/com/example/dagger/simple/ui/HomeActivity.java ================================================ /* * 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 com.example.dagger.simple.DemoBaseActivity; import javax.inject.Inject; public class HomeActivity extends DemoBaseActivity { @Inject LocationManager locationManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // After the super.onCreate call returns we are guaranteed our injections are available. // TODO do something with the injected dependencies here! } } ================================================ FILE: examples/android-simple/src/main/res/values/strings.xml ================================================ Dagger Simple ================================================ FILE: examples/pom.xml ================================================ 4.0.0 com.squareup.dagger dagger-parent 1.2.6-SNAPSHOT com.squareup.dagger.example dagger-example-parent pom Examples simple android true simple android-simple android-activity-graphs com.google.android android 4.1.1.4 com.google.android support-v4 r7 com.simpligility.maven.plugins android-maven-plugin 4.1.1 16 ================================================ FILE: examples/simple/pom.xml ================================================ 4.0.0 com.squareup.dagger.example dagger-example-parent 1.2.6-SNAPSHOT simple Examples: Simple com.squareup.dagger dagger ${project.version} com.squareup.dagger dagger-compiler ${project.version} true junit junit test org.mockito mockito-core test ================================================ FILE: examples/simple/src/main/java/coffee/CoffeeApp.java ================================================ package coffee; import javax.inject.Inject; import dagger.ObjectGraph; public class CoffeeApp implements Runnable { @Inject CoffeeMaker coffeeMaker; @Override public void run() { coffeeMaker.brew(); } public static void main(String[] args) { ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule()); CoffeeApp coffeeApp = objectGraph.get(CoffeeApp.class); coffeeApp.run(); } } ================================================ FILE: examples/simple/src/main/java/coffee/CoffeeMaker.java ================================================ package coffee; import dagger.Lazy; import javax.inject.Inject; class CoffeeMaker { @Inject Lazy heater; // Don't want to create a possibly costly heater until we need it. @Inject Pump pump; public void brew() { heater.get().on(); pump.pump(); System.out.println(" [_]P coffee! [_]P "); heater.get().off(); } } ================================================ FILE: examples/simple/src/main/java/coffee/DripCoffeeModule.java ================================================ package coffee; import dagger.Module; import dagger.Provides; import javax.inject.Singleton; @Module( injects = CoffeeApp.class, includes = PumpModule.class ) class DripCoffeeModule { @Provides @Singleton Heater provideHeater() { return new ElectricHeater(); } } ================================================ FILE: examples/simple/src/main/java/coffee/ElectricHeater.java ================================================ 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; } } ================================================ FILE: examples/simple/src/main/java/coffee/Heater.java ================================================ package coffee; interface Heater { void on(); void off(); boolean isHot(); } ================================================ FILE: examples/simple/src/main/java/coffee/Pump.java ================================================ package coffee; interface Pump { void pump(); } ================================================ FILE: examples/simple/src/main/java/coffee/PumpModule.java ================================================ package coffee; import dagger.Module; import dagger.Provides; @Module(complete = false, library = true) class PumpModule { @Provides Pump providePump(Thermosiphon pump) { return pump; } } ================================================ FILE: examples/simple/src/main/java/coffee/Thermosiphon.java ================================================ 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 => =>"); } } } ================================================ FILE: examples/simple/src/test/java/coffee/CoffeeMakerTest.java ================================================ /** * Copyright (C) 2015 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 coffee; import dagger.Module; import dagger.ObjectGraph; import dagger.Provides; import javax.inject.Inject; import javax.inject.Singleton; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mockito; public class CoffeeMakerTest { @Inject CoffeeMaker coffeeMaker; @Inject Heater heater; @Before public void setUp() { ObjectGraph.create(new TestModule()).inject(this); } @Module( includes = DripCoffeeModule.class, injects = CoffeeMakerTest.class, overrides = true ) static class TestModule { @Provides @Singleton Heater provideHeater() { return Mockito.mock(Heater.class); } } @Test public void testHeaterIsTurnedOnAndThenOff() { Mockito.when(heater.isHot()).thenReturn(true); coffeeMaker.brew(); Mockito.verify(heater, Mockito.times(1)).on(); Mockito.verify(heater, Mockito.times(1)).off(); } } ================================================ FILE: pom.xml ================================================ 4.0.0 org.sonatype.oss oss-parent 7 com.squareup.dagger dagger-parent pom 1.2.6-SNAPSHOT Dagger (Parent) A fast dependency injector for Android and Java. https://github.com/square/dagger compiler core examples UTF-8 1.6 1 1.7.0 19.0 4.13.1 0.28 0.8 1.10.19 http://github.com/square/dagger/ scm:git:git://github.com/square/dagger.git scm:git:ssh://git@github.com/square/dagger.git HEAD GitHub Issues http://github.com/square/dagger/issues Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt Square, Inc. http://squareup.com javax.inject javax.inject ${javax.inject.version} com.squareup javapoet ${javapoet.version} com.google.guava guava ${guava.version} junit junit ${junit.version} org.easytesting fest-assert ${fest.version} com.google.testing.compile compile-testing ${compile-testing.version} com.google.truth truth ${truth.version} org.mockito mockito-core ${mockito.version} org.apache.maven.plugins maven-invoker-plugin 1.7 org.apache.maven.plugins maven-compiler-plugin 3.1 org.apache.maven.plugins maven-compiler-plugin ${java.version} ${java.version} -Xlint:all true true org.apache.maven.plugins maven-release-plugin 2.4.2 org.apache.maven.scm maven-scm-provider-gitexe 1.9 true org.apache.maven.plugins maven-checkstyle-plugin 2.10 true true checkstyle.xml compile checkstyle ================================================ FILE: website/index.html ================================================ Dagger

A fast dependency injector for Android and Java

Deprecated – Please upgrade to Dagger 2

Square's Dagger 1.x is deprecated in favor of Google's Dagger 2. Please see the migration guide for help with the upgrade.

Introduction

The best classes in any application are the ones that do stuff: the BarcodeDecoder, the KoopaPhysicsEngine, and the AudioStreamer. These classes have dependencies; perhaps a BarcodeCameraFinder, DefaultPhysicsEngine, and an HttpStreamer.

To contrast, the worst classes in any application are the ones that take up space without doing much at all: the BarcodeDecoderFactory, the CameraServiceLoader, and the MutableContextWrapper. These classes are the clumsy duct tape that wires the interesting stuff together.

Dagger is a replacement for these FactoryFactory classes. It allows you to focus on the interesting classes. Declare dependencies, specify how to satisfy them, and ship your app.

By building on standard javax.inject annotations (JSR-330), each class is easy to test. You don't need a bunch of boilerplate just to swap the RpcCreditCardService out for a FakeCreditCardService.

Dependency injection isn't just for testing. It also makes it easy to create reusable, interchangeable modules. You can share the same AuthenticationModule across all of your apps. And you can run DevLoggingModule during development and ProdLoggingModule in production to get the right behavior in each situation.

For more information, watch an introductory talk by Jesse Wilson at QCon 2012.

Using Dagger

We'll demonstrate dependency injection and Dagger by building a coffee maker. For complete sample code that you can compile and run, see Dagger's coffee example.

Declaring Dependencies

Dagger constructs instances of your application classes and satisfies their dependencies. It uses the javax.inject.Inject annotation to identify which constructors and fields it is interested in.

Use @Inject to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.

class Thermosiphon implements Pump {
  private final Heater heater;

  @Inject
  Thermosiphon(Heater heater) {
    this.heater = heater;
  }

  ...
}

Dagger can inject fields directly. In this example it obtains a Heater instance for the heater field and a Pump instance for the pump field.

class CoffeeMaker {
  @Inject Heater heater;
  @Inject Pump pump;

  ...
}

If your class has @Inject-annotated fields but no @Inject-annotated constructor, Dagger will use a no-argument constructor if it exists. Classes that lack @Inject annotations cannot be constructed by Dagger.

Dagger does not support method injection.

Satisfying Dependencies

By default, Dagger satisfies each dependency by constructing an instance of the requested type as described above. When you request a CoffeeMaker, it'll obtain one by calling new CoffeeMaker() and setting its injectable fields.

But @Inject doesn't work everywhere:

  • Interfaces can't be constructed.
  • Third-party classes can't be annotated.
  • Configurable objects must be configured!

For these cases where @Inject is insufficient or awkward, use an @Provides-annotated method to satisfy a dependency. The method's return type defines which dependency it satisfies.

For example, provideHeater() is invoked whenever a Heater is required:

@Provides Heater provideHeater() {
  return new ElectricHeater();
}

It's possible for @Provides methods to have dependencies of their own. This one returns a Thermosiphon whenever a Pump is required:

@Provides Pump providePump(Thermosiphon pump) {
  return pump;
}

All @Provides methods must belong to a module. These are just classes that have an @Module annotation.

@Module
class DripCoffeeModule {
  @Provides Heater provideHeater() {
    return new ElectricHeater();
  }

  @Provides Pump providePump(Thermosiphon pump) {
    return pump;
  }
}

By convention, @Provides methods are named with a provide prefix and module classes are named with a Module suffix.

Building the Graph

The @Inject and @Provides-annotated classes form a graph of objects, linked by their dependencies. Obtain this graph by calling ObjectGraph.create(), which accepts one or more modules:

ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());

In order to put the graph to use we need to bootstrap injection. This usually requires injecting the main class of a command line app, or the activity classes of an Android app. In our coffee example, the CoffeeApp class is used to start dependency injection. We ask the graph to provide an injected instance of the class:

class CoffeeApp implements Runnable {
  @Inject CoffeeMaker coffeeMaker;

  @Override public void run() {
    coffeeMaker.brew();
  }

  public static void main(String[] args) {
    ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());
    CoffeeApp coffeeApp = objectGraph.get(CoffeeApp.class);
    ...
  }
}

The only thing that's missing is that the injectable class CoffeeApp isn't known by the graph. We need to explicitly register it as an injected type in the @Module annotation.

@Module(
    injects = CoffeeApp.class
)
class DripCoffeeModule {
  ...
}

The injects option enables the complete graph to be validated at compile time. Detecting problems early speeds up development and takes some of the danger out of refactoring.

Now that the graph is constructed and the root object is injected, we run our coffee maker app. Fun.

$ java -cp ... coffee.CoffeeApp
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
 [_]P coffee! [_]P

Singletons

Annotate an @Provides method or injectable class with @Singleton. The graph will use a single instance of the value for all of its clients.

@Provides @Singleton Heater provideHeater() {
  return new ElectricHeater();
}

The @Singleton annotation on an injectable class also serves as documentation. It reminds potential maintainers that this class may be shared by multiple threads.

@Singleton
class CoffeeMaker {
  ...
}

Lazy injections

Sometimes you need an object to be instantiated lazily. For any binding T, you can create a Lazy<T> which defers instantiation until the first call to Lazy<T>'s get() method. If T is a singleton, then Lazy<T> will be the same instance for all injections within the ObjectGraph. Otherwise, each injection site will get its own Lazy<T> instance. Regardless, subsequent calls to any given instance of Lazy<T> will return the same underlying instance of T.

class GridingCoffeeMaker {
  @Inject Lazy<Grinder> lazyGrinder;

  public void brew() {
    while (needsGrinding()) {
      // Grinder created once on first call to .get() and cached.
      lazyGrinder.get().grind();
    }
  }
}

Provider injections

Sometimes you need multiple instances to be returned instead of just injecting a single value. While you have several options (Factories, Builders, etc.) one option is to inject a Provider<T> instead of just T. A Provider<T> creates a new instance of T each time .get() is called.

class BigCoffeeMaker {
  @Inject Provider<Filter> filterProvider;

  public void brew(int numberOfPots) {
    ...
    for (int p = 0; p < numberOfPots; p++) {
      maker.addFilter(filterProvider.get()); //new filter every time.
      maker.addCoffee(...);
      maker.percolate();
      ...
    }
  }
}

Note: Injecting Provider<T> has the possibility of creating confusing code, and may be a design smell of mis-scoped or mis-structured objects in your graph. Often you will want to use a Factory<T> or a Lazy<T> or re-organize the lifetimes and structure of your code to be able to just inject a T. Injecting Provider<T> can, however, be a life saver in some cases. A common use is when you must use a legacy architecture that doesn't line up with your object's natural lifetimes (e.g. servlets are singletons by design, but only are valid in the context of request-specfic data).

Qualifiers

Sometimes the type alone is insufficient to identify a dependency. For example, a sophisticated coffee maker app may want separate heaters for the water and the hot plate.

In this case, we add a qualifier annotation. This is any annotation that itself has a @Qualifier annotation. Here's the declaration of @Named, a qualifier annotation included in javax.inject:

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
  String value() default "";
}

You can create your own qualifier annotations, or just use @Named. Apply qualifiers by annotating the field or parameter of interest. The type and qualifier annotation will both be used to identify the dependency.

class ExpensiveCoffeeMaker {
  @Inject @Named("water") Heater waterHeater;
  @Inject @Named("hot plate") Heater hotPlateHeater;
  ...
}

Supply qualified values by annotating the corresponding @Provides method.

@Provides @Named("hot plate") Heater provideHotPlateHeater() {
  return new ElectricHeater(70);
}

@Provides @Named("water") Heater provideWaterHeater() {
  return new ElectricHeater(93);
}

Dependencies may not have multiple qualifier annotations.

Static Injection

Warning: This feature should be used sparingly because static dependencies are difficult to test and reuse.

Dagger can inject static fields. Classes that declare static fields with @Inject annotations must be listed as staticInjections in a module annotation.

@Module(
    staticInjections = LegacyCoffeeUtils.class
)
class LegacyModule {
}

Use ObjectGraph.injectStatics() to populate these static fields with their injected values:

ObjectGraph objectGraph = ObjectGraph.create(new LegacyModule());
objectGraph.injectStatics();

Note: Static injection only operates for modules in the immediate graph. If you call injectStatics() on a graph created from a call to plus(), static injections on modules in the extended graph will not be performed.

Compile-time Validation

Dagger includes an annotation processor that validates modules and injections. This processor is strict and will cause a compiler error if any bindings are invalid or incomplete. For example, this module is missing a binding for Executor:

@Module
class DripCoffeeModule {
  @Provides Heater provideHeater(Executor executor) {
    return new CpuHeater(executor);
  }
}

When compiling it, javac rejects the missing binding:

[ERROR] COMPILATION ERROR :
[ERROR] error: No binding for java.util.concurrent.Executor
               required by provideHeater(java.util.concurrent.Executor)

Fix the problem either by adding an @Provides-annotated method for Executor, or by marking the module as incomplete. Incomplete modules are permitted to have missing dependencies.

@Module(complete = false)
class DripCoffeeModule {
  @Provides Heater provideHeater(Executor executor) {
    return new CpuHeater(executor);
  }
}

Modules which provide types that are unused by the listed injects classes will also trigger an error.

@Module(injects = Example.class)
class DripCoffeeModule {
  @Provides Heater provideHeater() {
    return new ElectricHeater();
  }
  @Provides Chiller provideChiller() {
    return new ElectricChiller();
  }
}

Because the Example inject in the module only uses the Heater, javac rejects the unused binding:

[ERROR] COMPILATION ERROR:
[ERROR]: Graph validation failed: You have these unused @Provider methods:
      1. coffee.DripCoffeeModule.provideChiller()
      Set library=true in your module to disable this check.

If your module's bindings will be used outside of the listed injects then mark the module as a library.

@Module(
  injects = Example.class,
  library = true
)
class DripCoffeeModule {
  @Provides Heater provideHeater() {
    return new ElectricHeater();
  }
  @Provides Chiller provideChiller() {
    return new ElectricChiller();
  }
}

To get the most out of compile-time validation, create a module that includes all of your application's modules. The annotation processor will detect problems across the modules and report them.

@Module(
    includes = {
        DripCoffeeModule.class,
        ExecutorModule.class
    }
)
public class CoffeeAppModule {
}

The annotation processor is enabled automatically when you include Dagger's jar file on your compile classpath.

Compile-time Code Generation

Dagger's annotation processor may also generate source files with names like CoffeeMaker$InjectAdapter.java or DripCoffeeModule$ModuleAdapter. These files are Dagger implementation details. You shouldn't need to use them directly, though they can be handy when step-debugging through an injection.

Module overrides

Dagger will fail with an error if there are multiple competing @Provides methods for the same dependency. But sometimes it's necessary to replace production code with a substitute for development or testing. Using overrides = true in a module annotation lets you take precedence over the bindings of other modules.

This JUnit test overrides DripCoffeeModule's binding for Heater with a mock object from Mockito. The mock gets injected into the CoffeeMaker and also into the test.

public class CoffeeMakerTest {
  @Inject CoffeeMaker coffeeMaker;
  @Inject Heater heater;

  @Before public void setUp() {
    ObjectGraph.create(new TestModule()).inject(this);
  }

  @Module(
      includes = DripCoffeeModule.class,
      injects = CoffeeMakerTest.class,
      overrides = true
  )
  static class TestModule {
    @Provides @Singleton Heater provideHeater() {
      return Mockito.mock(Heater.class);
    }
  }

  @Test public void testHeaterIsTurnedOnAndThenOff() {
    Mockito.when(heater.isHot()).thenReturn(true);
    coffeeMaker.brew();
    Mockito.verify(heater, Mockito.times(1)).on();
    Mockito.verify(heater, Mockito.times(1)).off();
  }
}

Overrides are best suited for small variations on the application:

  • Replacing the real implementation with a mock for unit tests.
  • Replacing LDAP authentication with fake authentication for development.

For more substantial variations it's often simpler to use a different combination of modules.

Download

Latest JAR Latest Compiler JAR

You will need to include the Dagger JAR in your application's runtime. In order to activate code generation you will need to include the compiler JAR in your build at compile time.

The source code to the Dagger, its samples, and this website is available on GitHub.

Maven

<dependency>
  <groupId>com.squareup.dagger</groupId>
  <artifactId>dagger</artifactId>
  <version>(insert latest version)</version>
</dependency>
<dependency>
  <groupId>com.squareup.dagger</groupId>
  <artifactId>dagger-compiler</artifactId>
  <version>(insert latest version)</version>
  <optional>true</optional>
</dependency>

Upgrading from Guice

Some notable Guice features that Dagger doesn't support:

  • Injecting final fields and private members. For best performance Dagger generates code. Work around this by using constructor injection.
  • Eager singletons. Work around this by creating an EagerSingletons class that declares static fields for each eager singleton.
  • Method injection.
  • Classes that lack @Inject annotations cannot be constructed by Dagger, even if they have a no-argument constructor.

Contributing

If you would like to contribute code 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. Please also make sure your code compiles by running mvn clean verify.

Before your code can be accepted into the project you must also sign the Individual Contributor License Agreement (CLA).

License

Copyright 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.
================================================ FILE: website/static/app-theme.css ================================================ /* http://www.colorhexa.com/487fb9 */ /*** Primary ***/ header, #subtitle, a.dl { background-color: #487fb9; } .content-nav li.active a, .content-nav li.active a:hover { border-left-color: #487fb9; } /*** One step left on the monochromatic scale ***/ header menu li a:hover, a.dl:hover { background-color: #4072a7; } a { color: #4072a7; } /*** Three steps left on the monochromatic scale ***/ a:hover { color: #325983; } /****************************************************************\ **** Syntax highlighting styles ******************************** \****************************************************************/ .pln { color: #000; } .str { color: #396695; } .kwd { color: #666; } .com { color: #800; } .typ { color: #222; } .lit { color: #666; } .pun { color: #888; } .opn { color: #888; } .clo { color: #888; } .tag { color: #396695; } .atn { color: #606; } .atv { color: #080; } .dec { color: #606; } .var { color: #606; } .fun { color: #f00; } ================================================ FILE: website/static/app.css ================================================ html, body { font-family: 'Roboto', sans-serif; font-size: 15px; } body { background-color: #f6f6f6; padding-bottom: 50px; padding-top: 80px; } header { min-height: 80px; color: #f6f6f6; position: fixed; top: 0; left: 0; width: 100%; z-index: 99; } header h1 { margin: 10px 0; font-size: 50px; line-height: 60px; font-weight: 100; text-rendering: auto; } header menu { margin: 20px 0 0; padding: 0; height: 40px; } header menu ul { margin: 0; padding: 0; float: right; } header menu li { list-style: none; float: left; margin: 0; padding: 0; } header menu li a { display: inline-block; height: 40px; font-size: 17px; line-height: 40px; padding: 0 20px; color: #f6f6f6; } header menu li a:hover { color: #f6f6f6; text-decoration: none; } header menu li a img { margin: 0; padding: 5px 0; vertical-align: bottom; width: 30px; height: 30px; } #subtitle { position: absolute; top: 80px; left: 0; width: 100%; } h2 { font-weight: 200; font-size: 26px; line-height: 30px; padding: 15px 0; margin: 0; color: #eee; } h2 strong { font-weight: 300; } a.dl { font-weight: 300; font-size: 30px; line-height: 40px; padding: 3px 10px; display: inline-block; border-radius: 6px; color: #f0f0f0; margin: 5px 0; } a.dl:hover { color: #f0f0f0; text-decoration: none; } .content-nav { margin-top: 130px; width: 220px; } .content-nav.affix { top: 0; } .content-nav li.active a, .content-nav li.active a:hover { background-color: transparent; color: #555; border-left-width: 2px; } .content-nav .secondary a { color: #aaa; } .content-nav .secondary a:hover { color: #888; } h3 { font-weight: 300; font-style: italic; color: #888; font-size: 20px; padding-top: 115px; margin-top: 0; } h4 { font-weight: 400; text-transform: uppercase; color: #888; font-size: 15px; padding-top: 20px; } p.license { font-family: fixed-width; } .row .logo { text-align: center; margin-top: 150px; } .row .logo img { height: 30px; } pre, code { color: #666; } code { border: 0; background-color: transparent; } /* Widescreen desktop. */ @media (min-width: 1200px) { .content-nav { width: 270px; } } /* Smaller width browser, tablets. */ @media (max-width: 979px) { .content-nav { width: 166px; } } /* One-column mobile display. */ @media (max-width: 767px) { header { position: absolute; top: 0; left: 0; width: 100%; padding-left: 20px; } header menu { display: none; } #subtitle { position: absolute; top: 80px; left: 0; width: 100%; padding-left: 20px; } .content-nav { display: none; } } ================================================ FILE: website/static/prettify.js ================================================ !function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; (function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a= b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;ah[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com", /^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+ s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/, V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-", /^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/], ["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}), ["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q, hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]); p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="
"+a+"
";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1}); return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i