aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk31
-rw-r--r--CHANGELOG.md63
-rw-r--r--README.android8
-rw-r--r--compiler/pom.xml32
-rw-r--r--compiler/src/it/functional-tests/pom.xml12
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java1
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java12
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java2
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java2
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/builder/RequiresSubcomponentBuilder.java38
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java2
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java4
-rw-r--r--compiler/src/it/functional-tests/src/main/java/test/subcomponent/StaticChildModule.java28
-rw-r--r--compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java9
-rw-r--r--compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java43
-rw-r--r--compiler/src/it/producers-functional-tests/pom.xml8
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedComponent.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/DependedComponent.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedModule.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/DependedModule.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProducerModule.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/DependedProducerModule.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProductionComponent.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/DependedProductionComponent.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentComponent.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/DependentComponent.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentProducerModule.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/DependentProducerModule.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingComponent.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingComponent.java)9
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingProducerModule.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingProducerModule.java)39
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/Request.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/Request.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/Response.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/Response.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseModule.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/ResponseModule.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseProducerModule.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/ResponseProducerModule.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleComponent.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/SimpleComponent.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleProducerModule.java (renamed from compiler/src/it/producers-functional-tests/src/main/java/test/SimpleProducerModule.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/ComponentDependency.java22
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleComponent.java38
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleProducerModule.java37
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/DepComponent.java22
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/IntModule.java27
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/StringModule.java27
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/TestComponentWithBuilder.java37
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoredComponent.java24
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoringModule.java36
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/ServingModule.java51
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StringStub.java22
-rw-r--r--compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StubModule.java49
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/producerstest/DependentTest.java (renamed from compiler/src/it/producers-functional-tests/src/test/java/test/DependentTest.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/producerstest/MultibindingTest.java73
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/producerstest/ProducerFactoryTest.java (renamed from compiler/src/it/producers-functional-tests/src/test/java/test/ProducerFactoryTest.java)64
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/producerstest/SimpleTest.java (renamed from compiler/src/it/producers-functional-tests/src/test/java/test/SimpleTest.java)2
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/producerstest/badexecutor/BadExecutorTest.java74
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/producerstest/builder/ProductionComponentBuilderTest.java78
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/producerstest/monitoring/MonitoringTest.java157
-rw-r--r--compiler/src/it/producers-functional-tests/src/test/java/test/MultibindingTest.java35
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java679
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/Binding.java68
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/BindingGraph.java329
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java309
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java37
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java10
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java31
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java27
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java178
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ContributionBindingFormatter.java (renamed from compiler/src/main/java/dagger/internal/codegen/ProductionBindingFormatter.java)24
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java139
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java3
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java23
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java14
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/FrameworkField.java52
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java33
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java1
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java91
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/Key.java31
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java2
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java2
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/MapKeys.java1
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java5
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java8
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java2
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java2
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/MonitoringModuleGenerator.java99
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java61
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java402
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java108
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java56
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java161
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ProvisionBindingFormatter.java48
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java209
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/SourceFiles.java166
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java14
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/Util.java25
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java15
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java10
-rw-r--r--compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java179
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java3
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java169
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java2
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java74
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/InaccessibleTypeTest.java (renamed from compiler/src/test/java/dagger/internal/codegen/PackageProxyTest.java)46
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java5
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java2
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java46
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java3
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java37
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java222
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java106
-rw-r--r--compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java25
-rw-r--r--compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java1
-rw-r--r--core/src/main/java/dagger/Subcomponent.java3
-rw-r--r--dagger2_annotation_processor.mk19
-rw-r--r--examples/android-activity-graphs/pom.xml3
-rw-r--r--examples/android-activity-graphs/src/main/AndroidManifest.xml (renamed from examples/android-activity-graphs/AndroidManifest.xml)0
-rw-r--r--examples/android-simple/pom.xml3
-rw-r--r--examples/android-simple/src/main/AndroidManifest.xml (renamed from examples/android-simple/AndroidManifest.xml)0
-rw-r--r--examples/pom.xml7
-rw-r--r--java_annotation_processors.mk69
-rw-r--r--pom.xml21
-rw-r--r--producers/pom.xml10
-rw-r--r--producers/src/main/java/dagger/producers/Produced.java8
-rw-r--r--producers/src/main/java/dagger/producers/ProductionComponent.java38
-rw-r--r--producers/src/main/java/dagger/producers/internal/AbstractProducer.java41
-rw-r--r--producers/src/main/java/dagger/producers/internal/Producers.java63
-rw-r--r--producers/src/main/java/dagger/producers/internal/SetOfProducedProducer.java117
-rw-r--r--producers/src/main/java/dagger/producers/internal/SetProducer.java7
-rw-r--r--producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java61
-rw-r--r--producers/src/main/java/dagger/producers/monitoring/ProducerToken.java2
-rw-r--r--producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java3
-rw-r--r--producers/src/main/java/dagger/producers/monitoring/internal/MonitorCache.java62
-rw-r--r--producers/src/main/java/dagger/producers/monitoring/internal/Monitors.java371
-rw-r--r--producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java51
-rw-r--r--producers/src/test/java/dagger/producers/internal/ProducersTest.java12
-rw-r--r--producers/src/test/java/dagger/producers/internal/SetOfProducedProducerTest.java134
-rw-r--r--producers/src/test/java/dagger/producers/internal/SetProducerTest.java35
-rw-r--r--producers/src/test/java/dagger/producers/monitoring/internal/MonitorsTest.java453
-rw-r--r--resources/META-INF/services/javax.annotation.processing.Processor1
131 files changed, 5259 insertions, 1974 deletions
diff --git a/Android.mk b/Android.mk
index f88f64120..08419f01d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,7 +24,7 @@ LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, core/src/main/java/)
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_JAVA_LIBRARIES := \
dagger2-inject-host \
guavalib
@@ -41,12 +41,11 @@ LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, producers/src/main/java/)
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_JAVA_LIBRARIES := \
dagger2-host \
dagger2-inject-host \
guavalib
-#LOCAL_JACK_ENABLED := disabled
include $(BUILD_HOST_JAVA_LIBRARY)
# build dagger2 compiler host jar
@@ -59,6 +58,10 @@ LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, compiler/src/main/java/)
+# Manually include META-INF/services/javax.annotation.processing.Processor
+# as the AutoService processor doesn't work properly.
+LOCAL_JAVA_RESOURCE_DIRS := resources
+
LOCAL_STATIC_JAVA_LIBRARIES := \
dagger2-host \
dagger2-auto-common-host \
@@ -73,12 +76,12 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
# Disable the default discovery for annotation processors and explicitly specify
# the path and classes needed. This is needed because otherwise it breaks a code
# indexing tool that doesn't, as yet do automatic discovery.
-PROCESSOR_JARS := \
- $(LOCAL_PATH)/../../out/host/common/obj/JAVA_LIBRARIES/guavalib_intermediates/javalib.jar \
- $(LOCAL_PATH)/lib/auto-common-1.0-20151022.071545-39$(COMMON_JAVA_PACKAGE_SUFFIX) \
- $(LOCAL_PATH)/lib/auto-factory-1.0-20150915.183854-35$(COMMON_JAVA_PACKAGE_SUFFIX) \
- $(LOCAL_PATH)/lib/auto-service-1.0-rc2$(COMMON_JAVA_PACKAGE_SUFFIX) \
- $(LOCAL_PATH)/lib/auto-value-1.0$(COMMON_JAVA_PACKAGE_SUFFIX) \
+PROCESSOR_LIBRARIES := \
+ dagger2-auto-common-host \
+ dagger2-auto-factory-host \
+ dagger2-auto-service-host \
+ dagger2-auto-value-host \
+ guavalib
PROCESSOR_CLASSES := \
com.google.auto.factory.processor.AutoFactoryProcessor \
@@ -86,14 +89,8 @@ PROCESSOR_CLASSES := \
com.google.auto.value.processor.AutoAnnotationProcessor \
com.google.auto.value.processor.AutoValueProcessor
-LOCAL_JAVACFLAGS += -processorpath $(subst $(space),:,$(strip $(PROCESSOR_JARS)))
+include $(LOCAL_PATH)/java_annotation_processors.mk
-# Specify only one processor class per -processor option as
-# the indexing tool does not parse the -processor value as a
-# comma separated list.
-LOCAL_JAVACFLAGS += $(foreach class,$(PROCESSOR_CLASSES),-processor $(class))
-
-#LOCAL_JACK_ENABLED := disabled
include $(BUILD_HOST_JAVA_LIBRARY)
# Build host dependencies.
@@ -106,6 +103,6 @@ LOCAL_PREBUILT_JAVA_LIBRARIES := \
dagger2-auto-service-host:lib/auto-service-1.0-rc2$(COMMON_JAVA_PACKAGE_SUFFIX) \
dagger2-auto-value-host:lib/auto-value-1.0$(COMMON_JAVA_PACKAGE_SUFFIX) \
dagger2-google-java-format:lib/google-java-format-0.1-20151017.042846-2$(COMMON_JAVA_PACKAGE_SUFFIX) \
- dagger2-inject-host:lib/javax-inject$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dagger2-inject-host:lib/javax-inject$(COMMON_JAVA_PACKAGE_SUFFIX)
include $(BUILD_HOST_PREBUILT)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 957840eca..3a38bfc00 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,58 @@
Change Log
==========
-Version 1.2.0 *(2013-12-13)*
-----------------------------
+Dagger 2 (Components)
+---------------------
+
+### Version 2.0.2 *(2015-11-03)*
+
+A patch release, most crucially including:
+
+ * A fix to the way processor validation of types is done that permits dagger to play
+ more nicely with other processors, avoiding over-validating aspects that it doesn't
+ need, which may yet not have been generated by other processors in a different round
+ of processing.
+ * Some improved error reporting for edge-cases
+ * Fix to prevent incompatible versions of Guava on the classpath from blowing up processing
+ * Support a more robust set of types for map keys in map bindings (primitive types, etc.)
+
+### Version 2.0.1 *(2015-05-28)*
+
+A maintenance release fixing immediate issues following the Dagger 2.0 release, including:
+
+ * Speed up Graph Validation (reduce build times by 10s of seconds on sampled large projects)
+ * Generate correct code for @MapKey annotation types (beta)
+ * Fix to properly emit code for class literal values in @MapKey annotations.
+ * Fix for injecting component dependencies
+ * Fixes to generated code to account for differences in generics handling in ecg vs. javac.
+ * Subcomponents can now be abstract classes.
+ * Subcomponents now properly build the object graph in some cases involving explicit bindings
+ and (sub)components without scope.
+ * Improve runtime performance of SetFactory (set multibindings)
+ * Other smaller fixes, refactorings, etc.
+
+### Version 2.0.0 *(2015-04-21)*
+
+The initial release of the 2.0 code-line, supporting:
+
+ * `@Component` interfaces representing a custom API to access a graph of objects
+ * JSR-330 injection automation using `@Inject` signals, `@Qualifiers`
+ * Simple bindings of implementations to interfaces, custom provision of objects, and set-bindings
+ * Compile-time validation of graph structure (cycles, missing bindings, duplicate bindings)
+ * Generation of
+ - backing implementations for components
+ - factories for `@Inject` constructors and modules
+ - members-injectors for `@Inject` methods and fields
+ * Beta support for
+ - Map bindings
+ - [Producers](http://google.github.io/dagger/api/latest/dagger/producers/Producer.html)
+
+==============================================================
+
+Dagger 1 (ObjectGraph)
+----------------------
+
+### Version 1.2.0 *(2013-12-13)*
* Numerous performance improvements in both the compiler and runtime.
* Use more efficient `String` concatenation.
@@ -15,8 +65,7 @@ Version 1.2.0 *(2013-12-13)*
module adapters.
-Version 1.1.0 *(2013-08-05)*
-----------------------------
+### 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`.
@@ -27,14 +76,12 @@ Version 1.1.0 *(2013-08-05)*
* Update JavaWriter to 2.1.1.
-Version 1.0.1 *(2013-06-03)*
-----------------------------
+### 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)*
-----------------------------
+### Version 1.0.0 *(2013-05-07)*
Initial release.
diff --git a/README.android b/README.android
index 94ed4714c..1983221b5 100644
--- a/README.android
+++ b/README.android
@@ -2,7 +2,11 @@ URL: https://github.com/google/dagger.git
License: Apache 2
Description: "Dagger 2 - A fast dependency injector for Android and Java"
-Version: fdf1a9e905782dd2885799c6b34d9f1da842a7e1
+Version: 91f7d8bda5b1ef2fdf768758c88d3e5f069210ea
+
+Upstream depends (slightly) on Guava v19 but we only have Guava v18 in
+Android at the moment so we have reverted those changes that introduced
+a dependency on Guava v19.
Local Patches:
- None
+ Revert "Stop using deprecated Futures method." - upstream 0a2ca81c0f78c621968deb58f4b42117db43fec4
diff --git a/compiler/pom.xml b/compiler/pom.xml
index 40e0fa27d..72252f1dd 100644
--- a/compiler/pom.xml
+++ b/compiler/pom.xml
@@ -46,25 +46,27 @@
<artifactId>auto-common</artifactId>
</dependency>
<dependency>
- <groupId>com.google.auto.service</groupId>
- <artifactId>auto-service</artifactId>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
<optional>true</optional>
</dependency>
- <!-- TODO(gak): Restore this presumably as javapoet when appropriate.
<dependency>
- <groupId>com.squareup</groupId>
- <artifactId>javawriter</artifactId>
+ <groupId>com.google.googlejavaformat</groupId>
+ <artifactId>google-java-format</artifactId>
</dependency>
- -->
<dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
+ <groupId>com.google.auto.service</groupId>
+ <artifactId>auto-service</artifactId>
+ <scope>provided</scope> <!-- to leave out of the all-deps jar -->
</dependency>
-
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
- <optional>true</optional>
+ <scope>provided</scope> <!-- to leave out of the all-deps jar -->
<version>1.0</version>
</dependency>
@@ -187,6 +189,16 @@
<shadedPattern>dagger.shaded.auto.common</shadedPattern>
</relocation>
</relocations>
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/*.SF</exclude>
+ <exclude>META-INF/*.DSA</exclude>
+ <exclude>META-INF/*.RSA</exclude>
+ </excludes>
+ </filter>
+ </filters>
</configuration>
</execution>
</executions>
diff --git a/compiler/src/it/functional-tests/pom.xml b/compiler/src/it/functional-tests/pom.xml
index 1eca20c14..ce3dd4e62 100644
--- a/compiler/src/it/functional-tests/pom.xml
+++ b/compiler/src/it/functional-tests/pom.xml
@@ -29,6 +29,10 @@ limitations under the License.
<name>Functional Tests</name>
<dependencies>
<dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>${project.version}</version>
@@ -40,18 +44,22 @@ limitations under the License.
<optional>true</optional>
</dependency>
<dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject-tck</artifactId>
+ </dependency>
+ <dependency>
<!-- For map-bindings -->
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${auto.value.version}</version>
- <optional>true</optional>
+ <scope>provided</scope> <!-- to leave out of the all-deps jar -->
</dependency>
<dependency>
<!-- For map-bindings -->
<groupId>com.google.auto.factory</groupId>
<artifactId>auto-factory</artifactId>
<version>${auto.factory.version}</version>
- <optional>true</optional>
+ <scope>provided</scope> <!-- to leave out of the all-deps jar -->
</dependency>
<dependency>
diff --git a/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java b/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java
index 7cad3bd1a..ac0624f83 100644
--- a/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java
+++ b/compiler/src/it/functional-tests/src/main/java/test/MultibindingComponent.java
@@ -33,6 +33,7 @@ import test.sub.ContributionsModule;
)
interface MultibindingComponent {
Map<String, String> map();
+ Map<String, String[]> mapOfArrays();
Map<String, Provider<String>> mapOfProviders();
Set<String> mapKeys();
Collection<String> mapValues();
diff --git a/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java b/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java
index f356850b3..4a7577e76 100644
--- a/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java
+++ b/compiler/src/it/functional-tests/src/main/java/test/MultibindingModule.java
@@ -46,6 +46,18 @@ class MultibindingModule {
return "bar value";
}
+ @Provides(type = MAP)
+ @StringKey("foo")
+ static String[] provideFooArrayValue(double doubleDependency) {
+ return new String[] {"foo1", "foo2"};
+ }
+
+ @Provides(type = MAP)
+ @StringKey("bar")
+ static String[] provideBarArrayValue() {
+ return new String[] {"bar1", "bar2"};
+ }
+
@Provides(type = SET)
static int provideFiveToSet() {
return 5;
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java b/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java
index 59c29ab34..690c91ad7 100644
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java
+++ b/compiler/src/it/functional-tests/src/main/java/test/builder/MiddleChild.java
@@ -24,6 +24,8 @@ interface MiddleChild {
Grandchild.Builder grandchildBuilder();
+ RequiresSubcomponentBuilder<Grandchild.Builder> requiresGrandchildBuilder();
+
@Subcomponent.Builder
interface Builder {
MiddleChild build();
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java b/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java
index f901b8863..584eff6ef 100644
--- a/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java
+++ b/compiler/src/it/functional-tests/src/main/java/test/builder/ParentComponent.java
@@ -26,4 +26,6 @@ interface ParentComponent {
MiddleChild.Builder middleBuilder();
OtherMiddleChild.Builder otherBuilder();
+
+ RequiresSubcomponentBuilder<MiddleChild.Builder> requiresMiddleChildBuilder();
}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/builder/RequiresSubcomponentBuilder.java b/compiler/src/it/functional-tests/src/main/java/test/builder/RequiresSubcomponentBuilder.java
new file mode 100644
index 000000000..ee9963227
--- /dev/null
+++ b/compiler/src/it/functional-tests/src/main/java/test/builder/RequiresSubcomponentBuilder.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package test.builder;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+class RequiresSubcomponentBuilder<B> {
+ private final Provider<B> subcomponentBuilderProvider;
+ private final B subcomponentBuilder;
+
+ @Inject
+ RequiresSubcomponentBuilder(Provider<B> subcomponentBuilderProvider, B subcomponentBuilder) {
+ this.subcomponentBuilderProvider = subcomponentBuilderProvider;
+ this.subcomponentBuilder = subcomponentBuilder;
+ }
+
+ Provider<B> subcomponentBuilderProvider() {
+ return subcomponentBuilderProvider;
+ }
+
+ B subcomponentBuilder() {
+ return subcomponentBuilder;
+ }
+}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java
index 6c061bc5c..2529433ae 100644
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java
+++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildAbstractClassComponent.java
@@ -17,6 +17,6 @@ package test.subcomponent;
import dagger.Subcomponent;
-@Subcomponent(modules = ChildModule.class)
+@Subcomponent(modules = {ChildModule.class, StaticChildModule.class})
abstract class ChildAbstractClassComponent implements ChildComponent {
}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java
index 67d66cae9..d3c28f2b4 100644
--- a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java
+++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/ChildComponent.java
@@ -19,7 +19,7 @@ import dagger.Subcomponent;
import java.util.Set;
import javax.inject.Provider;
-@Subcomponent(modules = ChildModule.class)
+@Subcomponent(modules = {ChildModule.class, StaticChildModule.class})
interface ChildComponent {
Provider<UnscopedType> getUnscopedTypeProvider();
@@ -28,4 +28,6 @@ interface ChildComponent {
Set<Object> objectSet();
GrandchildComponent newGrandchildComponent();
+
+ Object object();
}
diff --git a/compiler/src/it/functional-tests/src/main/java/test/subcomponent/StaticChildModule.java b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/StaticChildModule.java
new file mode 100644
index 000000000..f7fd49058
--- /dev/null
+++ b/compiler/src/it/functional-tests/src/main/java/test/subcomponent/StaticChildModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package test.subcomponent;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class StaticChildModule {
+ private StaticChildModule() {}
+
+ @Provides static Object provideStaticObject() {
+ return "static";
+ }
+}
diff --git a/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java b/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java
index 5e06f848f..1b7e30233 100644
--- a/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java
+++ b/compiler/src/it/functional-tests/src/test/java/test/MultibindingTest.java
@@ -49,6 +49,15 @@ public class MultibindingTest {
assertThat(map).containsEntry("bar", "bar value");
}
+ @Test public void mapOfArrays() {
+ Map<String, String[]> map = multibindingComponent.mapOfArrays();
+ assertThat(map).hasSize(2);
+ assertThat(map).containsKey("foo");
+ assertThat(map.get("foo")).asList().containsExactly("foo1", "foo2").inOrder();
+ assertThat(map).containsKey("bar");
+ assertThat(map.get("bar")).asList().containsExactly("bar1", "bar2").inOrder();
+ }
+
@Test public void mapOfProviders() {
Map<String, Provider<String>> mapOfProviders = multibindingComponent.mapOfProviders();
assertThat(mapOfProviders).hasSize(2);
diff --git a/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java b/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java
index ba590d2d9..46f5388ec 100644
--- a/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java
+++ b/compiler/src/it/functional-tests/src/test/java/test/builder/BuilderTest.java
@@ -163,7 +163,7 @@ public class BuilderTest {
assertThat(child2.l()).isEqualTo(6L);
assertThat(child2.b()).isEqualTo((byte)70);
}
-
+
@Test
public void grandchildren() {
ParentComponent parent = DaggerParentComponent.create();
@@ -222,4 +222,45 @@ public class BuilderTest {
assertThat(child.i()).isEqualTo(21);
}
+ @Test
+ public void requireSubcomponentBuilderProviders() {
+ ParentComponent parent = DaggerParentComponent.create();
+ MiddleChild middle =
+ parent
+ .requiresMiddleChildBuilder()
+ .subcomponentBuilderProvider()
+ .get()
+ .set(new StringModule("sam"))
+ .build();
+ Grandchild grandchild =
+ middle
+ .requiresGrandchildBuilder()
+ .subcomponentBuilderProvider()
+ .get()
+ .set(new IntModuleIncludingDoubleAndFloat(12))
+ .build();
+ assertThat(middle.s()).isEqualTo("sam");
+ assertThat(grandchild.i()).isEqualTo(12);
+ assertThat(grandchild.s()).isEqualTo("sam");
+ }
+
+ @Test
+ public void requireSubcomponentBuilders() {
+ ParentComponent parent = DaggerParentComponent.create();
+ MiddleChild middle =
+ parent
+ .requiresMiddleChildBuilder()
+ .subcomponentBuilder()
+ .set(new StringModule("sam"))
+ .build();
+ Grandchild grandchild =
+ middle
+ .requiresGrandchildBuilder()
+ .subcomponentBuilder()
+ .set(new IntModuleIncludingDoubleAndFloat(12))
+ .build();
+ assertThat(middle.s()).isEqualTo("sam");
+ assertThat(grandchild.i()).isEqualTo(12);
+ assertThat(grandchild.s()).isEqualTo("sam");
+ }
}
diff --git a/compiler/src/it/producers-functional-tests/pom.xml b/compiler/src/it/producers-functional-tests/pom.xml
index bc1c3c7b5..a0d16498a 100644
--- a/compiler/src/it/producers-functional-tests/pom.xml
+++ b/compiler/src/it/producers-functional-tests/pom.xml
@@ -29,6 +29,10 @@ limitations under the License.
<name>Producers Functional Tests</name>
<dependencies>
<dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>${project.version}</version>
@@ -56,10 +60,6 @@ limitations under the License.
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedComponent.java
index fa392dd8e..a80ea4968 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedComponent.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedComponent.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.Component;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedModule.java
index 604107025..dc62612cd 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedModule.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedModule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import dagger.Module;
import dagger.Provides;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProducerModule.java
index ad0c792a9..fe47c9973 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProducerModule.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProducerModule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProductionComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProductionComponent.java
index 57f175812..ba98e3698 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/DependedProductionComponent.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependedProductionComponent.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.ProductionComponent;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/DependentComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentComponent.java
index 4b14f99b5..85709f0f3 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/DependentComponent.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentComponent.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.ProductionComponent;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/DependentProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentProducerModule.java
index 234c088d3..e16c82212 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/DependentProducerModule.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/DependentProducerModule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingComponent.java
index 561ad4a89..6277e621a 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingComponent.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingComponent.java
@@ -13,14 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Produced;
import dagger.producers.ProductionComponent;
import java.util.Set;
+import producerstest.MultibindingProducerModule.PossiblyThrowingSet;
@ProductionComponent(modules = MultibindingProducerModule.class)
interface MultibindingComponent {
ListenableFuture<Set<String>> strs();
ListenableFuture<Integer> strCount();
+
+ ListenableFuture<Set<Produced<String>>> successfulSet();
+
+ @PossiblyThrowingSet
+ ListenableFuture<Set<Produced<String>>> possiblyThrowingSet();
}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingProducerModule.java
index 4651afcc6..09cd5bdbc 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/MultibindingProducerModule.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/MultibindingProducerModule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
@@ -21,29 +21,56 @@ import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.ProducerModule;
import dagger.producers.Produces;
import java.util.Set;
+import javax.inject.Qualifier;
import static dagger.producers.Produces.Type.SET;
import static dagger.producers.Produces.Type.SET_VALUES;
@ProducerModule
final class MultibindingProducerModule {
- @Produces(type = SET) ListenableFuture<String> futureStr() {
+ @Qualifier
+ @interface PossiblyThrowingSet {}
+
+ @Produces(type = SET)
+ static ListenableFuture<String> futureStr() {
return Futures.immediateFuture("foo");
}
- @Produces(type = SET) String str() {
+ @Produces(type = SET)
+ static String str() {
return "bar";
}
- @Produces(type = SET_VALUES) ListenableFuture<Set<String>> futureStrs() {
+ @Produces(type = SET_VALUES)
+ static ListenableFuture<Set<String>> futureStrs() {
return Futures.<Set<String>>immediateFuture(ImmutableSet.of("foo1", "foo2"));
}
- @Produces(type = SET_VALUES) Set<String> strs() {
+ @Produces(type = SET_VALUES)
+ static Set<String> strs() {
return ImmutableSet.of("bar1", "bar2");
}
- @Produces int strCount(Set<String> strs) {
+ @Produces
+ static int strCount(Set<String> strs) {
return strs.size();
}
+
+ @Produces(type = SET)
+ @PossiblyThrowingSet
+ static String successfulStringForSet() {
+ return "singleton";
+ }
+
+ @Produces(type = SET_VALUES)
+ @PossiblyThrowingSet
+ static Set<String> successfulStringsForSet() {
+ return ImmutableSet.of("double", "ton");
+ }
+
+ @Produces(type = SET)
+ @PossiblyThrowingSet
+ static String throwingStringForSet() {
+ throw new RuntimeException("monkey");
+ }
}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/Request.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Request.java
index 039d0fe55..0227be6bd 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/Request.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Request.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import javax.inject.Inject;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/Response.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Response.java
index 7a46e5b0e..8618ff5e1 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/Response.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/Response.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
final class Response {
private final String data;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseModule.java
index 4a83c6c83..1edbe8aa6 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseModule.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseModule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import dagger.Module;
import dagger.Provides;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseProducerModule.java
index f3ad22939..0ef2ae886 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/ResponseProducerModule.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/ResponseProducerModule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleComponent.java
index 583cd50f0..1d1e49233 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleComponent.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleComponent.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.ProductionComponent;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleProducerModule.java
index 98f3cfb56..2d831ed7f 100644
--- a/compiler/src/it/producers-functional-tests/src/main/java/test/SimpleProducerModule.java
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/SimpleProducerModule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/ComponentDependency.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/ComponentDependency.java
new file mode 100644
index 000000000..7bba4ea4e
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/ComponentDependency.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.badexecutor;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+interface ComponentDependency {
+ ListenableFuture<Double> doubleDep();
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleComponent.java
new file mode 100644
index 000000000..6b3536eae
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleComponent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.badexecutor;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionComponent;
+
+/**
+ * A component that contains entry points that exercise different execution paths, for verifying the
+ * behavior when the executor throws a {@link java.util.concurrent.RejectedExecutionException}.
+ */
+@ProductionComponent(dependencies = ComponentDependency.class, modules = SimpleProducerModule.class)
+interface SimpleComponent {
+ /** An entry point exposing a producer method with no args. */
+ ListenableFuture<String> noArgStr();
+
+ /** An entry point exposing a producer method that depends on another producer method. */
+ ListenableFuture<Integer> singleArgInt();
+
+ /** An entry point exposing a producer method that depends on a component dependency method. */
+ ListenableFuture<Boolean> singleArgBool();
+
+ /** An entry point exposing a component dependency method. */
+ ListenableFuture<Double> doubleDep();
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleProducerModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleProducerModule.java
new file mode 100644
index 000000000..00ab037bd
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/badexecutor/SimpleProducerModule.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.badexecutor;
+
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+@ProducerModule
+final class SimpleProducerModule {
+ @Produces
+ static String noArgStr() {
+ return "no arg string";
+ }
+
+ @Produces
+ static int singleArgInt(String arg) {
+ return arg.length();
+ }
+
+ @Produces
+ static boolean singleArgBool(double arg) {
+ return arg > 0.0;
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/DepComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/DepComponent.java
new file mode 100644
index 000000000..dadde7b05
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/DepComponent.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.builder;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+interface DepComponent {
+ ListenableFuture<Double> d();
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/IntModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/IntModule.java
new file mode 100644
index 000000000..7f99836d3
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/IntModule.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.builder;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class IntModule {
+ @Provides
+ static int i() {
+ return 42;
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/StringModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/StringModule.java
new file mode 100644
index 000000000..cdf0793da
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/StringModule.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.builder;
+
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+
+@ProducerModule
+final class StringModule {
+ @Produces
+ static String str(int i) {
+ return "arg: " + i;
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/TestComponentWithBuilder.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/TestComponentWithBuilder.java
new file mode 100644
index 000000000..16dc9bad7
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/builder/TestComponentWithBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.builder;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionComponent;
+import java.util.concurrent.Executor;
+
+@ProductionComponent(
+ modules = {StringModule.class, IntModule.class},
+ dependencies = DepComponent.class
+)
+interface TestComponentWithBuilder {
+ ListenableFuture<String> s();
+ ListenableFuture<Double> d();
+
+ @ProductionComponent.Builder
+ interface Builder {
+ Builder depComponent(DepComponent depComponent);
+ Builder strModule(StringModule strModule);
+ Builder executor(Executor executor);
+ TestComponentWithBuilder build();
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoredComponent.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoredComponent.java
new file mode 100644
index 000000000..48acbabac
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoredComponent.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionComponent;
+
+@ProductionComponent(modules = {MonitoringModule.class, StubModule.class, ServingModule.class})
+interface MonitoredComponent {
+ ListenableFuture<String> output();
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoringModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoringModule.java
new file mode 100644
index 000000000..0c4209076
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/MonitoringModule.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.monitoring;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+
+import static dagger.Provides.Type.SET;
+
+@Module
+final class MonitoringModule {
+ private final ProductionComponentMonitor.Factory monitorFactory;
+
+ MonitoringModule(ProductionComponentMonitor.Factory monitorFactory) {
+ this.monitorFactory = monitorFactory;
+ }
+
+ @Provides(type = SET)
+ ProductionComponentMonitor.Factory monitorFactory() {
+ return monitorFactory;
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/ServingModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/ServingModule.java
new file mode 100644
index 000000000..d5bec22ce
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/ServingModule.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import javax.inject.Qualifier;
+import producerstest.monitoring.StubModule.ForServer1;
+import producerstest.monitoring.StubModule.ForServer2;
+
+@ProducerModule
+final class ServingModule {
+ @Qualifier
+ @interface RequestData {}
+
+ @Qualifier
+ @interface IntermediateData {}
+
+ @Produces
+ @RequestData
+ static String requestData() {
+ return "Hello, World!";
+ }
+
+ @Produces
+ @IntermediateData
+ static ListenableFuture<String> callServer1(
+ @RequestData String data, @ForServer1 StringStub stub) {
+ return stub.run(data);
+ }
+
+ @Produces
+ static ListenableFuture<String> callServer2(
+ @IntermediateData String data, @ForServer2 StringStub stub) {
+ return stub.run(data);
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StringStub.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StringStub.java
new file mode 100644
index 000000000..195dd283d
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StringStub.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.monitoring;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+interface StringStub {
+ ListenableFuture<String> run(String input);
+}
diff --git a/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StubModule.java b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StubModule.java
new file mode 100644
index 000000000..dc8ab6260
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/main/java/producerstest/monitoring/StubModule.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.monitoring;
+
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Qualifier;
+
+@Module
+final class StubModule {
+ @Qualifier
+ @interface ForServer1 {}
+
+ @Qualifier
+ @interface ForServer2 {}
+
+ private final StringStub server1;
+ private final StringStub server2;
+
+ StubModule(StringStub server1, StringStub server2) {
+ this.server1 = server1;
+ this.server2 = server2;
+ }
+
+ @Provides
+ @ForServer1
+ StringStub server1() {
+ return server1;
+ }
+
+ @Provides
+ @ForServer2
+ StringStub server2() {
+ return server2;
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/test/DependentTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/DependentTest.java
index 15aa48165..b2533d735 100644
--- a/compiler/src/it/producers-functional-tests/src/test/java/test/DependentTest.java
+++ b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/DependentTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/MultibindingTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/MultibindingTest.java
new file mode 100644
index 000000000..4ddc7c6bc
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/MultibindingTest.java
@@ -0,0 +1,73 @@
+/*
+* Copyright (C) 2015 Google, Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package producerstest;
+
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.MoreExecutors;
+import dagger.producers.Produced;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(JUnit4.class)
+public class MultibindingTest {
+ @Test
+ public void setBinding() throws Exception {
+ MultibindingComponent multibindingComponent =
+ DaggerMultibindingComponent.builder().executor(MoreExecutors.directExecutor()).build();
+ assertThat(multibindingComponent.strs().get())
+ .containsExactly("foo", "foo1", "foo2", "bar", "bar1", "bar2");
+ assertThat(multibindingComponent.strCount().get()).isEqualTo(6);
+ }
+
+ @Test
+ public void setBindingOfProduced() throws Exception {
+ MultibindingComponent multibindingComponent =
+ DaggerMultibindingComponent.builder().executor(MoreExecutors.directExecutor()).build();
+ assertThat(multibindingComponent.successfulSet().get())
+ .containsExactly(
+ Produced.successful("foo"),
+ Produced.successful("foo1"),
+ Produced.successful("foo2"),
+ Produced.successful("bar"),
+ Produced.successful("bar1"),
+ Produced.successful("bar2"));
+ }
+
+ @Test
+ public void setBindingOfProducedWithFailures() throws Exception {
+ MultibindingComponent multibindingComponent =
+ DaggerMultibindingComponent.builder().executor(MoreExecutors.directExecutor()).build();
+ Set<Produced<String>> possiblyThrowingSet = multibindingComponent.possiblyThrowingSet().get();
+ Set<String> successes = new HashSet<>();
+ Set<ExecutionException> failures = new HashSet<>();
+ for (Produced<String> str : possiblyThrowingSet) {
+ try {
+ successes.add(str.get());
+ } catch (ExecutionException e) {
+ failures.add(e);
+ }
+ }
+ assertThat(successes).containsExactly("singleton", "double", "ton");
+ assertThat(failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(failures).getCause()).hasMessage("monkey");
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/test/ProducerFactoryTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/ProducerFactoryTest.java
index 4c347318a..9a5602990 100644
--- a/compiler/src/it/producers-functional-tests/src/test/java/test/ProducerFactoryTest.java
+++ b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/ProducerFactoryTest.java
@@ -13,44 +13,50 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+package producerstest;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
-
import dagger.producers.Producer;
import dagger.producers.monitoring.ProducerMonitor;
import dagger.producers.monitoring.ProducerToken;
import dagger.producers.monitoring.ProductionComponentMonitor;
-
+import java.util.concurrent.ExecutionException;
+import javax.inject.Provider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.concurrent.ExecutionException;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.when;
@RunWith(JUnit4.class)
public class ProducerFactoryTest {
@Mock private ProductionComponentMonitor componentMonitor;
- @Mock private ProducerMonitor monitor;
+ private ProducerMonitor monitor;
+ private Provider<ProductionComponentMonitor> componentMonitorProvider;
@Before
public void setUpMocks() {
MockitoAnnotations.initMocks(this);
+ monitor = Mockito.mock(ProducerMonitor.class, Mockito.CALLS_REAL_METHODS);
when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor);
+ componentMonitorProvider =
+ new Provider<ProductionComponentMonitor>() {
+ @Override
+ public ProductionComponentMonitor get() {
+ return componentMonitor;
+ }
+ };
}
@Test
@@ -58,7 +64,7 @@ public class ProducerFactoryTest {
ProducerToken token = ProducerToken.create(SimpleProducerModule_StrFactory.class);
Producer<String> producer =
new SimpleProducerModule_StrFactory(
- componentMonitor, MoreExecutors.directExecutor());
+ MoreExecutors.directExecutor(), componentMonitorProvider);
assertThat(producer.get().get()).isEqualTo("str");
InOrder order = inOrder(componentMonitor, monitor);
order.verify(componentMonitor).producerMonitorFor(token);
@@ -73,7 +79,7 @@ public class ProducerFactoryTest {
Producer<Integer> intProducer = producerOfFuture(intFuture);
Producer<String> producer =
new SimpleProducerModule_StrWithArgFactory(
- componentMonitor, MoreExecutors.directExecutor(), intProducer);
+ MoreExecutors.directExecutor(), componentMonitorProvider, intProducer);
assertThat(producer.get().isDone()).isFalse();
intFuture.set(42);
assertThat(producer.get().get()).isEqualTo("str with arg");
@@ -88,7 +94,7 @@ public class ProducerFactoryTest {
Producer<SettableFuture<String>> strFutureProducer = producerOfFuture(strFutureFuture);
Producer<String> producer =
new SimpleProducerModule_SettableFutureStrFactory(
- componentMonitor, MoreExecutors.directExecutor(), strFutureProducer);
+ MoreExecutors.directExecutor(), componentMonitorProvider, strFutureProducer);
assertThat(producer.get().isDone()).isFalse();
InOrder order = inOrder(componentMonitor, monitor);
@@ -115,7 +121,7 @@ public class ProducerFactoryTest {
Producer<SettableFuture<String>> strFutureProducer = producerOfFuture(strFutureFuture);
Producer<String> producer =
new SimpleProducerModule_SettableFutureStrFactory(
- componentMonitor, MoreExecutors.directExecutor(), strFutureProducer);
+ MoreExecutors.directExecutor(), componentMonitorProvider, strFutureProducer);
assertThat(producer.get().isDone()).isFalse();
InOrder order = inOrder(componentMonitor, monitor);
@@ -145,7 +151,7 @@ public class ProducerFactoryTest {
Producer<String> producer =
new SimpleProducerModule_ThrowingProducerFactory(
- componentMonitor, MoreExecutors.directExecutor());
+ MoreExecutors.directExecutor(), componentMonitorProvider);
assertThat(producer.get().isDone()).isTrue();
InOrder order = inOrder(componentMonitor, monitor);
@@ -164,25 +170,9 @@ public class ProducerFactoryTest {
order.verifyNoMoreInteractions();
}
- @Test
- public void nullComponentMonitor() throws Exception {
- Producer<String> producer =
- new SimpleProducerModule_StrFactory(null, MoreExecutors.directExecutor());
- assertThat(producer.get().get()).isEqualTo("str");
- verifyZeroInteractions(componentMonitor, monitor);
- }
-
- @Test
- public void nullMonitor() throws Exception {
- when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(null);
-
- ProducerToken token = ProducerToken.create(SimpleProducerModule_StrFactory.class);
- Producer<String> producer =
- new SimpleProducerModule_StrFactory(
- componentMonitor, MoreExecutors.directExecutor());
- assertThat(producer.get().get()).isEqualTo("str");
- verify(componentMonitor).producerMonitorFor(token);
- verifyZeroInteractions(monitor);
+ @Test(expected = NullPointerException.class)
+ public void nullComponentMonitorProvider() throws Exception {
+ new SimpleProducerModule_StrFactory(MoreExecutors.directExecutor(), null);
}
private static <T> Producer<T> producerOfFuture(final ListenableFuture<T> future) {
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/test/SimpleTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/SimpleTest.java
index e6e73961c..cacc0f11d 100644
--- a/compiler/src/it/producers-functional-tests/src/test/java/test/SimpleTest.java
+++ b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/SimpleTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test;
+package producerstest;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Test;
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/badexecutor/BadExecutorTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/badexecutor/BadExecutorTest.java
new file mode 100644
index 000000000..8a49797f5
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/badexecutor/BadExecutorTest.java
@@ -0,0 +1,74 @@
+package producerstest.badexecutor;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.RejectedExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+/** This test verifies behavior when the executor throws {@link RejectedExecutionException}. */
+@RunWith(JUnit4.class)
+public final class BadExecutorTest {
+ private SimpleComponent component;
+
+ @Before
+ public void setUpComponent() {
+ ComponentDependency dependency =
+ new ComponentDependency() {
+ @Override
+ public ListenableFuture<Double> doubleDep() {
+ return Futures.immediateFuture(42.0);
+ }
+ };
+ ListeningExecutorService executorService = MoreExecutors.newDirectExecutorService();
+ component =
+ DaggerSimpleComponent.builder()
+ .executor(executorService)
+ .componentDependency(dependency)
+ .build();
+ executorService.shutdown();
+ }
+
+ @Test
+ public void rejectNoArgMethod() throws Exception {
+ try {
+ component.noArgStr().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause()).isInstanceOf(RejectedExecutionException.class);
+ }
+ }
+
+ @Test
+ public void rejectSingleArgMethod() throws Exception {
+ try {
+ component.singleArgInt().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause()).isInstanceOf(RejectedExecutionException.class);
+ }
+ }
+
+ @Test
+ public void rejectSingleArgFromComponentDepMethod() throws Exception {
+ try {
+ component.singleArgBool().get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(e.getCause()).isInstanceOf(RejectedExecutionException.class);
+ }
+ }
+
+ @Test
+ public void doNotRejectComponentDepMethod() throws Exception {
+ assertThat(component.doubleDep().get()).isEqualTo(42.0);
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/builder/ProductionComponentBuilderTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/builder/ProductionComponentBuilderTest.java
new file mode 100644
index 000000000..715761df4
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/builder/ProductionComponentBuilderTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.builder;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+
+/** Tests for {@link dagger.producers.ProductionComponent.Builder}. */
+@RunWith(JUnit4.class)
+public final class ProductionComponentBuilderTest {
+
+ @Test
+ public void successfulBuild() throws Exception {
+ TestComponentWithBuilder component =
+ DaggerTestComponentWithBuilder.builder()
+ .executor(MoreExecutors.directExecutor())
+ .depComponent(depComponent(15.3))
+ .strModule(new StringModule())
+ .build();
+ assertThat(component.s().get()).isEqualTo("arg: 42");
+ assertThat(component.d().get()).isEqualTo(15.3);
+ }
+
+ @Test
+ public void successfulBuild_withMissingZeroArgModule() throws Exception {
+ TestComponentWithBuilder component =
+ DaggerTestComponentWithBuilder.builder()
+ .executor(MoreExecutors.directExecutor())
+ .depComponent(depComponent(15.3))
+ .build();
+ assertThat(component.s().get()).isEqualTo("arg: 42");
+ assertThat(component.d().get()).isEqualTo(15.3);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void missingExecutor() {
+ DaggerTestComponentWithBuilder.builder()
+ .depComponent(depComponent(15.3))
+ .strModule(new StringModule())
+ .build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void missingDepComponent() {
+ DaggerTestComponentWithBuilder.builder()
+ .executor(MoreExecutors.directExecutor())
+ .strModule(new StringModule())
+ .build();
+ }
+
+ private static DepComponent depComponent(final double value) {
+ return new DepComponent() {
+ @Override
+ public ListenableFuture<Double> d() {
+ return Futures.immediateFuture(value);
+ }
+ };
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/producerstest/monitoring/MonitoringTest.java b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/monitoring/MonitoringTest.java
new file mode 100644
index 000000000..3efb2b5b1
--- /dev/null
+++ b/compiler/src/it/producers-functional-tests/src/test/java/producerstest/monitoring/MonitoringTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package producerstest.monitoring;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/** Tests for production components using monitoring. */
+@RunWith(JUnit4.class)
+public final class MonitoringTest {
+ @Mock private ProductionComponentMonitor.Factory componentMonitorFactory;
+ @Mock private StringStub server1;
+ @Mock private StringStub server2;
+ private SettableFuture<String> server1Future;
+ private SettableFuture<String> server2Future;
+ private FakeProductionComponentMonitor componentMonitor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ componentMonitor = new FakeProductionComponentMonitor();
+ when(componentMonitorFactory.create(any())).thenReturn(componentMonitor);
+ server1Future = SettableFuture.create();
+ server2Future = SettableFuture.create();
+ when(server1.run(any(String.class))).thenReturn(server1Future);
+ when(server2.run(any(String.class))).thenReturn(server2Future);
+ }
+
+ @Test
+ public void basicMonitoring() throws Exception {
+ MonitoredComponent component =
+ DaggerMonitoredComponent.builder()
+ .executor(MoreExecutors.directExecutor())
+ .monitoringModule(new MonitoringModule(componentMonitorFactory))
+ .stubModule(new StubModule(server1, server2))
+ .build();
+ ListenableFuture<String> output = component.output();
+ assertThat(componentMonitor.monitors).hasSize(3);
+ ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
+ ImmutableList.copyOf(componentMonitor.monitors.entrySet());
+ assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
+ assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
+ assertThat(entries.get(2).getKey().toString()).contains("RequestData");
+
+ ProducerMonitor callServer2Monitor = entries.get(0).getValue();
+ ProducerMonitor callServer1Monitor = entries.get(1).getValue();
+ ProducerMonitor requestDataMonitor = entries.get(2).getValue();
+
+ InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ inOrder.verify(requestDataMonitor).methodStarting();
+ inOrder.verify(requestDataMonitor).methodFinished();
+ inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
+ inOrder.verify(callServer1Monitor).methodStarting();
+ inOrder.verify(callServer1Monitor).methodFinished();
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+
+ server1Future.set("server 1 response");
+ inOrder.verify(callServer1Monitor).succeeded("server 1 response");
+ inOrder.verify(callServer2Monitor).methodStarting();
+ inOrder.verify(callServer2Monitor).methodFinished();
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+
+ server2Future.set("server 2 response");
+ inOrder.verify(callServer2Monitor).succeeded("server 2 response");
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ assertThat(output.get()).isEqualTo("server 2 response");
+ }
+
+ @Test
+ public void basicMonitoringWithFailure() throws Exception {
+ MonitoredComponent component =
+ DaggerMonitoredComponent.builder()
+ .executor(MoreExecutors.directExecutor())
+ .monitoringModule(new MonitoringModule(componentMonitorFactory))
+ .stubModule(new StubModule(server1, server2))
+ .build();
+ ListenableFuture<String> output = component.output();
+ assertThat(componentMonitor.monitors).hasSize(3);
+ ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
+ ImmutableList.copyOf(componentMonitor.monitors.entrySet());
+ assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
+ assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
+ assertThat(entries.get(2).getKey().toString()).contains("RequestData");
+
+ ProducerMonitor callServer2Monitor = entries.get(0).getValue();
+ ProducerMonitor callServer1Monitor = entries.get(1).getValue();
+ ProducerMonitor requestDataMonitor = entries.get(2).getValue();
+
+ InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ inOrder.verify(requestDataMonitor).methodStarting();
+ inOrder.verify(requestDataMonitor).methodFinished();
+ inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
+ inOrder.verify(callServer1Monitor).methodStarting();
+ inOrder.verify(callServer1Monitor).methodFinished();
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+
+ RuntimeException cause = new RuntimeException("monkey");
+ server1Future.setException(cause);
+ inOrder.verify(callServer1Monitor).failed(cause);
+ inOrder.verify(callServer2Monitor).failed(any(Throwable.class));
+ verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
+ try {
+ output.get();
+ fail();
+ } catch (ExecutionException e) {
+ assertThat(Throwables.getRootCause(e)).isSameAs(cause);
+ }
+ }
+
+ private static final class FakeProductionComponentMonitor implements ProductionComponentMonitor {
+ final Map<ProducerToken, ProducerMonitor> monitors = new LinkedHashMap<>();
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ ProducerMonitor monitor = mock(ProducerMonitor.class);
+ monitors.put(token, monitor);
+ return monitor;
+ }
+ }
+}
diff --git a/compiler/src/it/producers-functional-tests/src/test/java/test/MultibindingTest.java b/compiler/src/it/producers-functional-tests/src/test/java/test/MultibindingTest.java
deleted file mode 100644
index 20c86dc52..000000000
--- a/compiler/src/it/producers-functional-tests/src/test/java/test/MultibindingTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-* Copyright (C) 2015 Google, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package test;
-
-import com.google.common.util.concurrent.MoreExecutors;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import static com.google.common.truth.Truth.assertThat;
-
-@RunWith(JUnit4.class)
-public class MultibindingTest {
- @Test public void multibinding() throws Exception {
- MultibindingComponent multibindingComponent = DaggerMultibindingComponent.builder()
- .executor(MoreExecutors.directExecutor())
- .build();
- assertThat(multibindingComponent.strs().get())
- .containsExactly("foo", "foo1", "foo2", "bar", "bar1", "bar2");
- assertThat(multibindingComponent.strCount().get()).isEqualTo(6);
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java b/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java
index e0425c925..db890b246 100644
--- a/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java
+++ b/compiler/src/main/java/dagger/internal/codegen/AbstractComponentWriter.java
@@ -19,7 +19,6 @@ import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
-import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -41,7 +40,6 @@ import dagger.internal.SetFactory;
import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.ComponentGenerator.MemberSelect;
-import dagger.internal.codegen.ComponentGenerator.ProxyClassAndField;
import dagger.internal.codegen.writer.ClassName;
import dagger.internal.codegen.writer.ClassWriter;
import dagger.internal.codegen.writer.ConstructorWriter;
@@ -53,25 +51,22 @@ import dagger.internal.codegen.writer.Snippet;
import dagger.internal.codegen.writer.StringLiteral;
import dagger.internal.codegen.writer.TypeName;
import dagger.internal.codegen.writer.TypeNames;
-import dagger.internal.codegen.writer.TypeWriter;
import dagger.internal.codegen.writer.VoidName;
import dagger.producers.Producer;
import dagger.producers.internal.Producers;
+import dagger.producers.internal.SetOfProducedProducer;
import dagger.producers.internal.SetProducer;
import java.util.Collection;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.annotation.Generated;
import javax.inject.Provider;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
@@ -88,6 +83,7 @@ import static com.google.auto.common.MoreTypes.asDeclared;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.DELEGATED;
import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.INITIALIZED;
@@ -95,20 +91,21 @@ import static dagger.internal.codegen.AbstractComponentWriter.InitializationStat
import static dagger.internal.codegen.Binding.bindingPackageFor;
import static dagger.internal.codegen.ComponentGenerator.MemberSelect.staticMethodInvocationWithCast;
import static dagger.internal.codegen.ComponentGenerator.MemberSelect.staticSelect;
+import static dagger.internal.codegen.ContributionBinding.contributionTypeFor;
+import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.ENUM_INSTANCE;
+import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
import static dagger.internal.codegen.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD;
import static dagger.internal.codegen.MapKeys.getMapKeySnippet;
import static dagger.internal.codegen.MembersInjectionBinding.Strategy.NO_OP;
-import static dagger.internal.codegen.ProvisionBinding.FactoryCreationStrategy.ENUM_INSTANCE;
-import static dagger.internal.codegen.ProvisionBinding.Kind.PROVISION;
-import static dagger.internal.codegen.SourceFiles.factoryNameForProductionBinding;
-import static dagger.internal.codegen.SourceFiles.factoryNameForProvisionBinding;
import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.SourceFiles.indexDependenciesByUnresolvedKey;
import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
import static dagger.internal.codegen.Util.getKeyTypeOfMap;
import static dagger.internal.codegen.Util.getProvidedValueTypeOfMap;
import static dagger.internal.codegen.Util.isMapWithNonProvidedValues;
+import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
import static dagger.internal.codegen.writer.Snippet.memberSelectSnippet;
import static dagger.internal.codegen.writer.Snippet.nullCheck;
import static javax.lang.model.element.Modifier.ABSTRACT;
@@ -131,12 +128,13 @@ abstract class AbstractComponentWriter {
protected final Set<JavaWriter> javaWriters = new LinkedHashSet<>();
protected final ClassName name;
protected final BindingGraph graph;
- private final Map<String, ProxyClassAndField> packageProxies = new HashMap<>();
private final Map<BindingKey, InitializationState> initializationStates = new HashMap<>();
+ private final Map<Binding, InitializationState> contributionInitializationStates =
+ new HashMap<>();
protected ClassWriter componentWriter;
- private ImmutableMap<BindingKey, MemberSelect> memberSelectSnippets;
- private ImmutableMap<ContributionBinding, MemberSelect> multibindingContributionSnippets;
- private ImmutableSet<BindingKey> enumBindingKeys;
+ private final Map<BindingKey, MemberSelect> memberSelectSnippets = new HashMap<>();
+ private final Map<ContributionBinding, MemberSelect> multibindingContributionSnippets =
+ new HashMap<>();
protected ConstructorWriter constructorWriter;
protected Optional<ClassName> builderName = Optional.absent();
@@ -242,6 +240,16 @@ abstract class AbstractComponentWriter {
initializationStates.put(bindingKey, state);
}
+ private InitializationState getContributionInitializationState(Binding binding) {
+ return contributionInitializationStates.containsKey(binding)
+ ? contributionInitializationStates.get(binding)
+ : UNINITIALIZED;
+ }
+
+ private void setContributionInitializationState(Binding binding, InitializationState state) {
+ contributionInitializationStates.put(binding, state);
+ }
+
ImmutableSet<JavaWriter> write() {
if (javaWriters.isEmpty()) {
writeComponent();
@@ -445,109 +453,51 @@ abstract class AbstractComponentWriter {
protected abstract void addFactoryMethods();
private void addFields() {
- Map<BindingKey, MemberSelect> memberSelectSnippetsBuilder = Maps.newHashMap();
- Map<ContributionBinding, MemberSelect> multibindingContributionSnippetsBuilder =
- Maps.newHashMap();
- ImmutableSet.Builder<BindingKey> enumBindingKeysBuilder = ImmutableSet.builder();
-
for (ResolvedBindings resolvedBindings : graph.resolvedBindings().values()) {
- addField(
- memberSelectSnippetsBuilder,
- multibindingContributionSnippetsBuilder,
- enumBindingKeysBuilder,
- resolvedBindings);
+ addField(resolvedBindings);
}
-
- memberSelectSnippets = ImmutableMap.copyOf(memberSelectSnippetsBuilder);
- multibindingContributionSnippets = ImmutableMap.copyOf(multibindingContributionSnippetsBuilder);
- enumBindingKeys = enumBindingKeysBuilder.build();
}
- private void addField(
- Map<BindingKey, MemberSelect> memberSelectSnippetsBuilder,
- Map<ContributionBinding, MemberSelect> multibindingContributionSnippetsBuilder,
- ImmutableSet.Builder<BindingKey> enumBindingKeysBuilder,
- ResolvedBindings resolvedBindings) {
+ private void addField(ResolvedBindings resolvedBindings) {
BindingKey bindingKey = resolvedBindings.bindingKey();
- // No field needed for unique contributions inherited from the parent.
- if (resolvedBindings.isUniqueContribution() && resolvedBindings.ownedBindings().isEmpty()) {
+ // No field needed if there are no owned bindings.
+ if (resolvedBindings.ownedBindings().isEmpty()) {
return;
}
-
+
// No field needed for bindings with no dependencies or state.
Optional<MemberSelect> staticMemberSelect = staticMemberSelect(resolvedBindings);
if (staticMemberSelect.isPresent()) {
- // TODO(gak): refactor to use enumBindingKeys throughout the generator
- enumBindingKeysBuilder.add(bindingKey);
- memberSelectSnippetsBuilder.put(bindingKey, staticMemberSelect.get());
+ memberSelectSnippets.put(bindingKey, staticMemberSelect.get());
return;
}
- String bindingPackage = bindingPackageFor(resolvedBindings.bindings()).or(name.packageName());
-
- final Optional<String> proxySelector;
- final TypeWriter classWithFields;
- final Set<Modifier> fieldModifiers;
-
- if (bindingPackage.equals(name.packageName())) {
- // no proxy
- proxySelector = Optional.absent();
- // component gets the fields
- classWithFields = componentWriter;
- // private fields
- fieldModifiers = EnumSet.of(PRIVATE);
- } else {
- // get or create the proxy
- ProxyClassAndField proxyClassAndField = packageProxies.get(bindingPackage);
- if (proxyClassAndField == null) {
- JavaWriter proxyJavaWriter = JavaWriter.inPackage(bindingPackage);
- javaWriters.add(proxyJavaWriter);
- ClassWriter proxyWriter = proxyJavaWriter.addClass(name.simpleName() + "_PackageProxy");
- proxyWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getCanonicalName());
- proxyWriter.addModifiers(PUBLIC, FINAL);
- // create the field for the proxy in the component
- FieldWriter proxyFieldWriter =
- componentWriter.addField(
- proxyWriter.name(), bindingPackage.replace('.', '_') + "_Proxy");
- proxyFieldWriter.addModifiers(PRIVATE, FINAL);
- proxyFieldWriter.setInitializer("new %s()", proxyWriter.name());
- proxyClassAndField = ProxyClassAndField.create(proxyWriter, proxyFieldWriter);
- packageProxies.put(bindingPackage, proxyClassAndField);
- }
- // add the field for the member select
- proxySelector = Optional.of(proxyClassAndField.proxyFieldWriter().name());
- // proxy gets the fields
- classWithFields = proxyClassAndField.proxyWriter();
- // public fields in the proxy
- fieldModifiers = EnumSet.of(PUBLIC);
- }
-
+ Optional<String> bindingPackage = bindingPackageFor(resolvedBindings.bindings());
+ boolean useRawType = bindingPackage.isPresent()
+ && !bindingPackage.get().equals(name.packageName());
if (bindingKey.kind().equals(BindingKey.Kind.CONTRIBUTION)) {
- ImmutableSet<? extends ContributionBinding> contributionBindings =
+ ImmutableSet<ContributionBinding> contributionBindings =
resolvedBindings.contributionBindings();
- if (ContributionBinding.bindingTypeFor(contributionBindings).isMultibinding()) {
+ if (ContributionBinding.contributionTypeFor(contributionBindings).isMultibinding()) {
// note that here we rely on the order of the resolved bindings being from parent to child
// otherwise, the numbering wouldn't work
int contributionNumber = 0;
for (ContributionBinding contributionBinding : contributionBindings) {
if (!contributionBinding.isSyntheticBinding()) {
contributionNumber++;
- if (resolvedBindings.ownedBindings().contains(contributionBinding)) {
+ if (resolvedBindings.ownedContributionBindings().contains(contributionBinding)) {
FrameworkField contributionBindingField =
FrameworkField.createForSyntheticContributionBinding(
- bindingKey, contributionNumber, contributionBinding);
+ contributionNumber, contributionBinding);
FieldWriter contributionField =
- classWithFields.addField(
- contributionBindingField.frameworkType(), contributionBindingField.name());
- contributionField.addModifiers(fieldModifiers);
+ addFrameworkField(useRawType, contributionBindingField);
ImmutableList<String> contributionSelectTokens =
new ImmutableList.Builder<String>()
- .addAll(proxySelector.asSet())
.add(contributionField.name())
.build();
- multibindingContributionSnippetsBuilder.put(
+ multibindingContributionSnippets.put(
contributionBinding,
MemberSelect.instanceSelect(name, memberSelectSnippet(contributionSelectTokens)));
}
@@ -557,51 +507,63 @@ abstract class AbstractComponentWriter {
}
FrameworkField bindingField = FrameworkField.createForResolvedBindings(resolvedBindings);
- FieldWriter frameworkField =
- classWithFields.addField(bindingField.frameworkType(), bindingField.name());
- frameworkField.addModifiers(fieldModifiers);
+ FieldWriter frameworkField = addFrameworkField(useRawType, bindingField);
ImmutableList<String> memberSelectTokens =
new ImmutableList.Builder<String>()
- .addAll(proxySelector.asSet())
.add(frameworkField.name())
.build();
- memberSelectSnippetsBuilder.put(
+ memberSelectSnippets.put(
bindingKey,
MemberSelect.instanceSelect(name, Snippet.memberSelectSnippet(memberSelectTokens)));
}
+ private FieldWriter addFrameworkField(boolean useRawType,
+ FrameworkField contributionBindingField) {
+ FieldWriter contributionField =
+ componentWriter.addField(
+ useRawType
+ ? contributionBindingField.frameworkType().type()
+ : contributionBindingField.frameworkType(),
+ contributionBindingField.name());
+ contributionField.addModifiers(PRIVATE);
+ if (useRawType) {
+ contributionField.annotate(SuppressWarnings.class).setValue("rawtypes");
+ }
+ return contributionField;
+ }
+
/**
* If {@code resolvedBindings} is an unscoped provision binding with no factory arguments or a
- * no-op members injection binding, then we do't need a field to hold its factory. In that case,
+ * no-op members injection binding, then we don't need a field to hold its factory. In that case,
* this method returns the static member select snippet that returns the factory or no-op members
* injector.
*/
private Optional<MemberSelect> staticMemberSelect(ResolvedBindings resolvedBindings) {
- if (resolvedBindings.bindings().size() != 1) {
- return Optional.absent();
- }
switch (resolvedBindings.bindingKey().kind()) {
case CONTRIBUTION:
+ if (resolvedBindings.contributionBindings().size() != 1) {
+ return Optional.absent();
+ }
ContributionBinding contributionBinding =
getOnlyElement(resolvedBindings.contributionBindings());
- if (contributionBinding.bindingType().isMultibinding()
- || !(contributionBinding instanceof ProvisionBinding)) {
+ if (contributionBinding.contributionType().isMultibinding()
+ || !(contributionBinding.bindingType().equals(Binding.Type.PROVISION))) {
return Optional.absent();
}
- ProvisionBinding provisionBinding = (ProvisionBinding) contributionBinding;
- if (provisionBinding.factoryCreationStrategy().equals(ENUM_INSTANCE)
- && !provisionBinding.scope().isPresent()) {
+ if (contributionBinding.factoryCreationStrategy().equals(ENUM_INSTANCE)
+ && !contributionBinding.scope().isPresent()) {
return Optional.of(
staticSelect(
- factoryNameForProvisionBinding(provisionBinding), Snippet.format("create()")));
+ generatedClassNameForBinding(contributionBinding), Snippet.format("create()")));
}
break;
case MEMBERS_INJECTION:
- if (getOnlyElement(resolvedBindings.membersInjectionBindings())
- .injectionStrategy()
- .equals(NO_OP)) {
+ Optional<MembersInjectionBinding> membersInjectionBinding =
+ resolvedBindings.membersInjectionBinding();
+ if (membersInjectionBinding.isPresent()
+ && membersInjectionBinding.get().injectionStrategy().equals(NO_OP)) {
return Optional.of(
staticMethodInvocationWithCast(
ClassName.fromClass(MembersInjectors.class),
@@ -639,13 +601,14 @@ abstract class AbstractComponentWriter {
interfaceMethod.annotate(Override.class);
interfaceMethod.addModifiers(PUBLIC);
BindingKey bindingKey = interfaceRequest.bindingKey();
+ MemberSelect memberSelect = getMemberSelect(bindingKey);
+ Snippet memberSelectSnippet = memberSelect.getSnippetFor(name);
switch (interfaceRequest.kind()) {
case MEMBERS_INJECTOR:
- Snippet membersInjectorSelect = getMemberSelectSnippet(bindingKey);
List<? extends VariableElement> parameters = requestElement.getParameters();
if (parameters.isEmpty()) {
// we're returning the framework type
- interfaceMethod.body().addSnippet("return %s;", membersInjectorSelect);
+ interfaceMethod.body().addSnippet("return %s;", memberSelectSnippet);
} else {
VariableElement parameter = Iterables.getOnlyElement(parameters);
Name parameterName = parameter.getSimpleName();
@@ -657,9 +620,7 @@ abstract class AbstractComponentWriter {
.body()
.addSnippet(
"%s.injectMembers(%s);",
- // In this case we know we won't need the cast because we're never going to
- // pass the reference to anything.
- membersInjectorSelect,
+ memberSelectSnippet,
parameterName);
if (!requestType.getReturnType().getKind().equals(VOID)) {
interfaceMethod.body().addSnippet("return %s;", parameterName);
@@ -667,7 +628,7 @@ abstract class AbstractComponentWriter {
}
break;
case INSTANCE:
- if (enumBindingKeys.contains(bindingKey)
+ if (memberSelect.staticMember()
&& bindingKey.key().type().getKind().equals(DECLARED)
&& !((DeclaredType) bindingKey.key().type()).getTypeArguments().isEmpty()) {
// If using a parameterized enum type, then we need to store the factory
@@ -679,7 +640,7 @@ abstract class AbstractComponentWriter {
interfaceMethod
.body()
.addSnippet(
- "%s factory = %s;", factoryType, getMemberSelectSnippet(bindingKey));
+ "%s factory = %s;", factoryType, memberSelectSnippet);
interfaceMethod.body().addSnippet("return factory.get();");
break;
}
@@ -694,7 +655,7 @@ abstract class AbstractComponentWriter {
.addSnippet(
"return %s;",
frameworkTypeUsageStatement(
- getMemberSelectSnippet(bindingKey), interfaceRequest.kind()));
+ memberSelectSnippet, interfaceRequest.kind()));
break;
default:
throw new AssertionError();
@@ -712,13 +673,27 @@ abstract class AbstractComponentWriter {
}
}
+ private static final int SNIPPETS_PER_INITIALIZATION_METHOD = 100;
+
private void initializeFrameworkTypes() {
- List<List<BindingKey>> partitions =
- Lists.partition(graph.resolvedBindings().keySet().asList(), 100);
+ ImmutableList.Builder<Snippet> snippetsBuilder = ImmutableList.builder();
+ for (BindingKey bindingKey : graph.resolvedBindings().keySet()) {
+ snippetsBuilder.add(initializeFrameworkType(bindingKey));
+ }
+ ImmutableList<Snippet> snippets = snippetsBuilder.build();
+
+ List<List<Snippet>> partitions = Lists.partition(snippets, SNIPPETS_PER_INITIALIZATION_METHOD);
for (int i = 0; i < partitions.size(); i++) {
MethodWriter initializeMethod =
componentWriter.addMethod(VoidName.VOID, "initialize" + ((i == 0) ? "" : i));
- initializeMethod.body();
+ /* TODO(gak): Strictly speaking, we only need the suppression here if we are also initializing
+ * a raw field in this method, but the structure of this code makes it awkward to pass that
+ * bit through. This will be cleaned up when we no longer separate fields and initilization
+ * as we do now. */
+ initializeMethod.annotate(SuppressWarnings.class).setValue("unchecked");
+ for (Snippet snippet : partitions.get(i)) {
+ initializeMethod.body().addSnippet(snippet);
+ }
initializeMethod.addModifiers(PRIVATE);
if (builderName.isPresent()) {
initializeMethod.addParameter(builderName.get(), "builder").addModifiers(FINAL);
@@ -726,111 +701,141 @@ abstract class AbstractComponentWriter {
} else {
constructorWriter.body().addSnippet("%s();", initializeMethod.name());
}
- for (BindingKey bindingKey : partitions.get(i)) {
- ResolvedBindings resolvedBindings = graph.resolvedBindings().get(bindingKey);
- switch (bindingKey.kind()) {
- case CONTRIBUTION:
- ImmutableSet<? extends ContributionBinding> bindings =
- resolvedBindings.contributionBindings();
-
- switch (ContributionBinding.bindingTypeFor(bindings)) {
- case SET:
- boolean hasOnlyProvisions =
- Iterables.all(bindings, Predicates.instanceOf(ProvisionBinding.class));
- ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
- for (ContributionBinding binding : bindings) {
- Optional<MemberSelect> multibindingContributionSnippet =
- getMultibindingContributionSnippet(binding);
- checkState(
- multibindingContributionSnippet.isPresent(), "%s was not found", binding);
- Snippet snippet = multibindingContributionSnippet.get().getSnippetFor(name);
- if (multibindingContributionSnippet.get().owningClass().equals(name)) {
- Snippet initializeSnippet = initializeFactoryForContributionBinding(binding);
- initializeMethod.body().addSnippet("this.%s = %s;", snippet, initializeSnippet);
- }
- parameterSnippets.add(snippet);
- }
- Snippet initializeSetSnippet =
- Snippet.format(
- "%s.create(%s)",
- hasOnlyProvisions
- ? ClassName.fromClass(SetFactory.class)
- : ClassName.fromClass(SetProducer.class),
- Snippet.makeParametersSnippet(parameterSnippets.build()));
- initializeMember(initializeMethod, bindingKey, initializeSetSnippet);
- break;
- case MAP:
- if (Sets.filter(bindings, Predicates.instanceOf(ProductionBinding.class))
- .isEmpty()) {
- @SuppressWarnings("unchecked") // checked by the instanceof filter above
- ImmutableSet<ProvisionBinding> provisionBindings =
- (ImmutableSet<ProvisionBinding>) bindings;
- for (ProvisionBinding provisionBinding : provisionBindings) {
- Optional<MemberSelect> multibindingContributionSnippet =
- getMultibindingContributionSnippet(provisionBinding);
- if (!isMapWithNonProvidedValues(provisionBinding.key().type())
- && multibindingContributionSnippet.isPresent()
- && multibindingContributionSnippet.get().owningClass().equals(name)) {
- initializeMethod
- .body()
- .addSnippet(
- "this.%s = %s;",
- multibindingContributionSnippet.get().getSnippetFor(name),
- initializeFactoryForProvisionBinding(provisionBinding));
- }
- }
- initializeMember(
- initializeMethod, bindingKey, initializeMapBinding(provisionBindings));
- } else {
- // TODO(beder): Implement producer map bindings.
- throw new IllegalStateException("producer map bindings not implemented yet");
- }
- break;
- case UNIQUE:
- if (!resolvedBindings.ownedContributionBindings().isEmpty()) {
- ContributionBinding binding = Iterables.getOnlyElement(bindings);
- if (binding instanceof ProvisionBinding) {
- ProvisionBinding provisionBinding = (ProvisionBinding) binding;
- if (!provisionBinding.factoryCreationStrategy().equals(ENUM_INSTANCE)
- || provisionBinding.scope().isPresent()) {
- initializeDelegateFactories(binding, initializeMethod);
- initializeMember(
- initializeMethod,
- bindingKey,
- initializeFactoryForProvisionBinding(provisionBinding));
- }
- } else if (binding instanceof ProductionBinding) {
- ProductionBinding productionBinding = (ProductionBinding) binding;
- initializeMember(
- initializeMethod,
- bindingKey,
- initializeFactoryForProductionBinding(productionBinding));
- } else {
- throw new AssertionError();
- }
- }
- break;
- default:
- throw new IllegalStateException();
- }
- break;
- case MEMBERS_INJECTION:
- MembersInjectionBinding binding =
- Iterables.getOnlyElement(resolvedBindings.membersInjectionBindings());
- if (!binding.injectionStrategy().equals(MembersInjectionBinding.Strategy.NO_OP)) {
- initializeDelegateFactories(binding, initializeMethod);
- initializeMember(
- initializeMethod, bindingKey, initializeMembersInjectorForBinding(binding));
- }
- break;
+ }
+ }
+
+ /**
+ * Returns a single snippet representing the initialization of the framework type.
+ *
+ * <p>Note that this must be a single snippet because initialization snippets can be invoked from
+ * any place in any order. By requiring a single snippet (often of concatenated snippets) we
+ * ensure that things like local variables always behave as expected by the initialization logic.
+ */
+ private Snippet initializeFrameworkType(BindingKey bindingKey) {
+ ResolvedBindings resolvedBindings = graph.resolvedBindings().get(bindingKey);
+
+ // There's no field for inherited bindings.
+ if (resolvedBindings.ownedBindings().isEmpty()) {
+ return Snippet.format("");
+ }
+
+ switch (bindingKey.kind()) {
+ case CONTRIBUTION:
+ switch (contributionTypeFor(resolvedBindings.contributionBindings())) {
+ case SET:
+ return initializeSetMultibindings(resolvedBindings);
+ case MAP:
+ return initializeMapMultibindings(resolvedBindings);
+ case UNIQUE:
+ return initializeUniqueContributionBinding(resolvedBindings);
default:
throw new AssertionError();
}
+
+ case MEMBERS_INJECTION:
+ return initializeMembersInjectionBinding(resolvedBindings);
+
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ private Snippet initializeSetMultibindings(ResolvedBindings resolvedBindings) {
+ ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
+
+ ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
+ for (ContributionBinding binding : resolvedBindings.contributionBindings()) {
+ Optional<MemberSelect> multibindingContributionSnippet =
+ getMultibindingContributionSnippet(binding);
+ checkState(multibindingContributionSnippet.isPresent(), "%s was not found", binding);
+ Snippet snippet = multibindingContributionSnippet.get().getSnippetFor(name);
+ if (multibindingContributionSnippet.get().owningClass().equals(name)
+ // the binding might already be initialized by a different set binding that shares the
+ // same contributions (e.g., Set<T> and Set<Produced<T>>)
+ && getContributionInitializationState(binding)
+ .equals(InitializationState.UNINITIALIZED)) {
+ Snippet initializeSnippet = initializeFactoryForContributionBinding(binding);
+ initializationSnippets.add(Snippet.format("this.%s = %s;", snippet, initializeSnippet));
+ setContributionInitializationState(binding, InitializationState.INITIALIZED);
}
+ parameterSnippets.add(snippet);
+ }
+ Class<?> factoryClass =
+ Iterables.all(resolvedBindings.contributionBindings(), Binding.Type.PROVISION)
+ ? SetFactory.class
+ : Util.isSetOfProduced(resolvedBindings.bindingKey().key().type())
+ ? SetOfProducedProducer.class
+ : SetProducer.class;
+ Snippet initializeSetSnippet =
+ Snippet.format(
+ "%s.create(%s)",
+ ClassName.fromClass(factoryClass),
+ makeParametersSnippet(parameterSnippets.build()));
+ initializationSnippets.add(
+ initializeMember(resolvedBindings.bindingKey(), initializeSetSnippet));
+
+ return Snippet.concat(initializationSnippets.build());
+ }
+
+ private Snippet initializeMapMultibindings(ResolvedBindings resolvedBindings) {
+ ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
+
+ if (any(resolvedBindings.contributionBindings(), Binding.Type.PRODUCTION)) {
+ // TODO(beder): Implement producer map bindings.
+ throw new IllegalStateException("producer map bindings not implemented yet");
+ }
+ for (ContributionBinding binding : resolvedBindings.contributionBindings()) {
+ Optional<MemberSelect> multibindingContributionSnippet =
+ getMultibindingContributionSnippet(binding);
+ if (!isMapWithNonProvidedValues(binding.key().type())
+ && multibindingContributionSnippet.isPresent()
+ && multibindingContributionSnippet.get().owningClass().equals(name)) {
+ initializationSnippets.add(
+ Snippet.format(
+ "this.%s = %s;",
+ multibindingContributionSnippet.get().getSnippetFor(name),
+ initializeFactoryForContributionBinding(binding)));
+ }
+ }
+ initializationSnippets.add(
+ initializeMember(
+ resolvedBindings.bindingKey(),
+ initializeMapBinding(resolvedBindings.contributionBindings())));
+
+ return Snippet.concat(initializationSnippets.build());
+ }
+
+ private Snippet initializeUniqueContributionBinding(ResolvedBindings resolvedBindings) {
+ ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
+
+ ContributionBinding binding = getOnlyElement(resolvedBindings.ownedContributionBindings());
+ if (!binding.factoryCreationStrategy().equals(ENUM_INSTANCE) || binding.scope().isPresent()) {
+ initializationSnippets.add(initializeDelegateFactories(binding));
+ initializationSnippets.add(
+ initializeMember(
+ resolvedBindings.bindingKey(), initializeFactoryForContributionBinding(binding)));
+ }
+
+ return Snippet.concat(initializationSnippets.build());
+ }
+
+ private Snippet initializeMembersInjectionBinding(ResolvedBindings resolvedBindings) {
+ ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
+
+ MembersInjectionBinding binding = resolvedBindings.membersInjectionBinding().get();
+ if (!binding.injectionStrategy().equals(MembersInjectionBinding.Strategy.NO_OP)) {
+ initializationSnippets.add(initializeDelegateFactories(binding));
+ initializationSnippets.add(
+ initializeMember(
+ resolvedBindings.bindingKey(), initializeMembersInjectorForBinding(binding)));
}
+
+ return Snippet.concat(initializationSnippets.build());
}
- private void initializeDelegateFactories(Binding binding, MethodWriter initializeMethod) {
+ private Snippet initializeDelegateFactories(Binding binding) {
+ ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
+
for (Collection<DependencyRequest> requestsForKey :
indexDependenciesByUnresolvedKey(types, binding.dependencies()).asMap().values()) {
BindingKey dependencyKey =
@@ -840,37 +845,40 @@ abstract class AbstractComponentWriter {
.toSet());
if (!getMemberSelect(dependencyKey).staticMember()
&& getInitializationState(dependencyKey).equals(UNINITIALIZED)) {
- initializeMethod
- .body()
- .addSnippet(
+ initializationSnippets.add(
+ Snippet.format(
"this.%s = new %s();",
getMemberSelectSnippet(dependencyKey),
- ClassName.fromClass(DelegateFactory.class));
+ ClassName.fromClass(DelegateFactory.class)));
setInitializationState(dependencyKey, DELEGATED);
}
}
+
+ return Snippet.concat(initializationSnippets.build());
}
- private void initializeMember(
- MethodWriter initializeMethod, BindingKey bindingKey, Snippet initializationSnippet) {
+ private Snippet initializeMember(BindingKey bindingKey, Snippet initializationSnippet) {
+ ImmutableList.Builder<Snippet> initializationSnippets = ImmutableList.builder();
+
Snippet memberSelect = getMemberSelectSnippet(bindingKey);
Snippet delegateFactoryVariable = delegateFactoryVariableSnippet(bindingKey);
if (getInitializationState(bindingKey).equals(DELEGATED)) {
- initializeMethod
- .body()
- .addSnippet(
+ initializationSnippets.add(
+ Snippet.format(
"%1$s %2$s = (%1$s) %3$s;",
ClassName.fromClass(DelegateFactory.class),
delegateFactoryVariable,
- memberSelect);
+ memberSelect));
}
- initializeMethod.body().addSnippet("this.%s = %s;", memberSelect, initializationSnippet);
+ initializationSnippets.add(
+ Snippet.format("this.%s = %s;", memberSelect, initializationSnippet));
if (getInitializationState(bindingKey).equals(DELEGATED)) {
- initializeMethod
- .body()
- .addSnippet("%s.setDelegatedProvider(%s);", delegateFactoryVariable, memberSelect);
+ initializationSnippets.add(
+ Snippet.format("%s.setDelegatedProvider(%s);", delegateFactoryVariable, memberSelect));
}
setInitializationState(bindingKey, INITIALIZED);
+
+ return Snippet.concat(initializationSnippets.build());
}
private Snippet delegateFactoryVariableSnippet(BindingKey key) {
@@ -878,16 +886,6 @@ abstract class AbstractComponentWriter {
}
private Snippet initializeFactoryForContributionBinding(ContributionBinding binding) {
- if (binding instanceof ProvisionBinding) {
- return initializeFactoryForProvisionBinding((ProvisionBinding) binding);
- } else if (binding instanceof ProductionBinding) {
- return initializeFactoryForProductionBinding((ProductionBinding) binding);
- } else {
- throw new AssertionError();
- }
- }
-
- private Snippet initializeFactoryForProvisionBinding(ProvisionBinding binding) {
TypeName bindingKeyTypeName = TypeNames.forTypeMirror(binding.key().type());
switch (binding.bindingKind()) {
case COMPONENT:
@@ -898,32 +896,17 @@ abstract class AbstractComponentWriter {
bindingKeyTypeName.equals(componentDefinitionTypeName())
? "this"
: getComponentContributionSnippet(MoreTypes.asTypeElement(binding.key().type())));
+
case COMPONENT_PROVISION:
- TypeElement bindingTypeElement =
- graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement());
- if (binding.nullableType().isPresent()
- || nullableValidationType.equals(Diagnostic.Kind.WARNING)) {
- Snippet nullableSnippet =
- binding.nullableType().isPresent()
- ? Snippet.format("@%s ", TypeNames.forTypeMirror(binding.nullableType().get()))
- : Snippet.format("");
- return Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s>() {",
- " private final %6$s %7$s = %3$s;",
- " %5$s@Override public %2$s get() {",
- " return %7$s.%4$s();",
- " }",
- "}"),
- /* 1 */ ClassName.fromClass(Factory.class),
- /* 2 */ bindingKeyTypeName,
- /* 3 */ getComponentContributionSnippet(bindingTypeElement),
- /* 4 */ binding.bindingElement().getSimpleName().toString(),
- /* 5 */ nullableSnippet,
- /* 6 */ TypeNames.forTypeMirror(bindingTypeElement.asType()),
- /* 7 */ simpleVariableName(bindingTypeElement));
- } else {
+ {
+ TypeElement bindingTypeElement =
+ graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement());
+ String localFactoryVariable = simpleVariableName(bindingTypeElement);
+ Snippet callFactoryMethodSnippet =
+ Snippet.format(
+ "%s.%s()",
+ localFactoryVariable,
+ binding.bindingElement().getSimpleName().toString());
// TODO(sameb): This throws a very vague NPE right now. The stack trace doesn't
// help to figure out what the method or return type is. If we include a string
// of the return type or method name in the error message, that can defeat obfuscation.
@@ -932,95 +915,126 @@ abstract class AbstractComponentWriter {
// What should we do?
StringLiteral failMsg =
StringLiteral.forValue(CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD);
+ Snippet getMethodBody =
+ binding.nullableType().isPresent()
+ || nullableValidationType.equals(Diagnostic.Kind.WARNING)
+ ? Snippet.format("return %s;", callFactoryMethodSnippet)
+ : Snippet.format(
+ Joiner.on('\n')
+ .join(
+ "%s provided = %s;",
+ "if (provided == null) {",
+ " throw new NullPointerException(%s);",
+ "}",
+ "return provided;"),
+ bindingKeyTypeName,
+ callFactoryMethodSnippet,
+ failMsg);
return Snippet.format(
Joiner.on('\n')
.join(
"new %1$s<%2$s>() {",
- " private final %6$s %7$s = %3$s;",
- " @Override public %2$s get() {",
- " %2$s provided = %7$s.%4$s();",
- " if (provided == null) {",
- " throw new NullPointerException(%5$s);",
- " }",
- " return provided;",
+ " private final %5$s %6$s = %3$s;",
+ " %4$s@Override public %2$s get() {",
+ " %7$s",
" }",
"}"),
/* 1 */ ClassName.fromClass(Factory.class),
/* 2 */ bindingKeyTypeName,
/* 3 */ getComponentContributionSnippet(bindingTypeElement),
- /* 4 */ binding.bindingElement().getSimpleName().toString(),
- /* 5 */ failMsg,
- /* 6 */ TypeNames.forTypeMirror(bindingTypeElement.asType()),
- /* 7 */ simpleVariableName(bindingTypeElement));
+ /* 4 */ nullableSnippet(binding.nullableType()),
+ /* 5 */ TypeNames.forTypeMirror(bindingTypeElement.asType()),
+ /* 6 */ localFactoryVariable,
+ /* 7 */ getMethodBody);
}
- case INJECTION:
- case PROVISION:
- List<Snippet> parameters =
- Lists.newArrayListWithCapacity(binding.dependencies().size() + 1);
- if (binding.bindingKind().equals(PROVISION)
- && !binding.bindingElement().getModifiers().contains(STATIC)) {
- parameters.add(getComponentContributionSnippet(binding.contributedBy().get()));
- }
- parameters.addAll(getDependencyParameters(binding));
- Snippet factorySnippet =
- Snippet.format(
- "%s.create(%s)",
- factoryNameForProvisionBinding(binding),
- Snippet.makeParametersSnippet(parameters));
- return binding.scope().isPresent()
- ? Snippet.format(
- "%s.create(%s)", ClassName.fromClass(ScopedProvider.class), factorySnippet)
- : factorySnippet;
- default:
- throw new AssertionError();
- }
- }
-
- private Snippet initializeFactoryForProductionBinding(ProductionBinding binding) {
- switch (binding.bindingKind()) {
- case COMPONENT_PRODUCTION:
- TypeElement bindingTypeElement =
- graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement());
+ case SUBCOMPONENT_BUILDER:
return Snippet.format(
Joiner.on('\n')
.join(
"new %1$s<%2$s>() {",
- " private final %6$s %7$s = %4$s;",
- " @Override public %3$s<%2$s> get() {",
- " return %7$s.%5$s();",
+ " @Override public %2$s get() {",
+ " return %3$s();",
" }",
"}"),
- /* 1 */ ClassName.fromClass(Producer.class),
- /* 2 */ TypeNames.forTypeMirror(binding.key().type()),
- /* 3 */ ClassName.fromClass(ListenableFuture.class),
- /* 4 */ getComponentContributionSnippet(bindingTypeElement),
- /* 5 */ binding.bindingElement().getSimpleName().toString(),
- /* 6 */ TypeNames.forTypeMirror(bindingTypeElement.asType()),
- /* 7 */ simpleVariableName(bindingTypeElement));
+ /* 1 */ ClassName.fromClass(Factory.class),
+ /* 2 */ bindingKeyTypeName,
+ /* 3 */ binding.bindingElement().getSimpleName().toString());
+
+ case INJECTION:
+ case PROVISION:
+ {
+ List<Snippet> parameters =
+ Lists.newArrayListWithCapacity(binding.dependencies().size() + 1);
+ if (binding.bindingKind().equals(PROVISION)
+ && !binding.bindingElement().getModifiers().contains(STATIC)) {
+ parameters.add(getComponentContributionSnippet(binding.contributedBy().get()));
+ }
+ parameters.addAll(getDependencyParameters(binding));
+
+ Snippet factorySnippet =
+ Snippet.format(
+ "%s.create(%s)",
+ generatedClassNameForBinding(binding),
+ Snippet.makeParametersSnippet(parameters));
+ return binding.scope().isPresent()
+ ? Snippet.format(
+ "%s.create(%s)", ClassName.fromClass(ScopedProvider.class), factorySnippet)
+ : factorySnippet;
+ }
+
+ case COMPONENT_PRODUCTION:
+ {
+ TypeElement bindingTypeElement =
+ graph.componentDescriptor().dependencyMethodIndex().get(binding.bindingElement());
+ return Snippet.format(
+ Joiner.on('\n')
+ .join(
+ "new %1$s<%2$s>() {",
+ " private final %6$s %7$s = %4$s;",
+ " @Override public %3$s<%2$s> get() {",
+ " return %7$s.%5$s();",
+ " }",
+ "}"),
+ /* 1 */ ClassName.fromClass(Producer.class),
+ /* 2 */ TypeNames.forTypeMirror(binding.key().type()),
+ /* 3 */ ClassName.fromClass(ListenableFuture.class),
+ /* 4 */ getComponentContributionSnippet(bindingTypeElement),
+ /* 5 */ binding.bindingElement().getSimpleName().toString(),
+ /* 6 */ TypeNames.forTypeMirror(bindingTypeElement.asType()),
+ /* 7 */ simpleVariableName(bindingTypeElement));
+ }
+
case IMMEDIATE:
case FUTURE_PRODUCTION:
- List<Snippet> parameters =
- Lists.newArrayListWithCapacity(binding.dependencies().size() + 3);
- // TODO(beder): Pass the actual ProductionComponentMonitor.
- parameters.add(Snippet.format("null"));
- if (!binding.bindingElement().getModifiers().contains(STATIC)) {
- parameters.add(getComponentContributionSnippet(binding.bindingTypeElement()));
+ {
+ List<Snippet> parameters =
+ Lists.newArrayListWithCapacity(binding.implicitDependencies().size() + 2);
+ if (!binding.bindingElement().getModifiers().contains(STATIC)) {
+ parameters.add(getComponentContributionSnippet(binding.bindingTypeElement()));
+ }
+ parameters.add(
+ getComponentContributionSnippet(
+ graph.componentDescriptor().executorDependency().get()));
+ parameters.addAll(getProducerDependencyParameters(binding));
+
+ return Snippet.format(
+ "new %s(%s)",
+ generatedClassNameForBinding(binding),
+ Snippet.makeParametersSnippet(parameters));
}
- parameters.add(
- getComponentContributionSnippet(
- graph.componentDescriptor().executorDependency().get()));
- parameters.addAll(getProducerDependencyParameters(binding));
- return Snippet.format(
- "new %s(%s)",
- factoryNameForProductionBinding(binding),
- Snippet.makeParametersSnippet(parameters));
default:
throw new AssertionError();
}
}
+ private Snippet nullableSnippet(Optional<DeclaredType> nullableType) {
+ return nullableType.isPresent()
+ ? Snippet.format("@%s ", TypeNames.forTypeMirror(nullableType.get()))
+ : Snippet.format("");
+ }
+
private Snippet initializeMembersInjectorForBinding(MembersInjectionBinding binding) {
switch (binding.injectionStrategy()) {
case NO_OP:
@@ -1076,8 +1090,9 @@ abstract class AbstractComponentWriter {
private List<Snippet> getProducerDependencyParameters(Binding binding) {
ImmutableList.Builder<Snippet> parameters = ImmutableList.builder();
for (Collection<DependencyRequest> requestsForKey :
- SourceFiles.indexDependenciesByUnresolvedKey(
- types, binding.dependencies()).asMap().values()) {
+ SourceFiles.indexDependenciesByUnresolvedKey(types, binding.implicitDependencies())
+ .asMap()
+ .values()) {
BindingKey key = Iterables.getOnlyElement(FluentIterable.from(requestsForKey)
.transform(DependencyRequest.BINDING_KEY_FUNCTION));
ResolvedBindings resolvedBindings = graph.resolvedBindings().get(key);
@@ -1097,9 +1112,9 @@ abstract class AbstractComponentWriter {
return parameters.build();
}
- private Snippet initializeMapBinding(Set<ProvisionBinding> bindings) {
+ private Snippet initializeMapBinding(Set<ContributionBinding> bindings) {
// Get type information from the first binding.
- ProvisionBinding firstBinding = bindings.iterator().next();
+ ContributionBinding firstBinding = bindings.iterator().next();
DeclaredType mapType = asDeclared(firstBinding.key().type());
if (isMapWithNonProvidedValues(mapType)) {
@@ -1117,7 +1132,7 @@ abstract class AbstractComponentWriter {
TypeNames.forTypeMirror(getProvidedValueTypeOfMap(mapType)), // V of Map<K, Provider<V>>
bindings.size()));
- for (ProvisionBinding binding : bindings) {
+ for (ContributionBinding binding : bindings) {
snippets.add(
Snippet.format(
" .put(%s, %s)",
@@ -1127,7 +1142,7 @@ abstract class AbstractComponentWriter {
snippets.add(Snippet.format(" .build()"));
- return Snippet.join(Joiner.on('\n'), snippets.build());
+ return Snippet.concat(snippets.build());
}
private static String simpleVariableName(TypeElement typeElement) {
diff --git a/compiler/src/main/java/dagger/internal/codegen/Binding.java b/compiler/src/main/java/dagger/internal/codegen/Binding.java
index 29f17b3c5..0a6b84052 100644
--- a/compiler/src/main/java/dagger/internal/codegen/Binding.java
+++ b/compiler/src/main/java/dagger/internal/codegen/Binding.java
@@ -17,11 +17,15 @@ package dagger.internal.codegen;
import com.google.auto.common.MoreElements;
import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import dagger.MembersInjector;
+import dagger.producers.Producer;
import java.util.List;
import java.util.Set;
+import javax.inject.Provider;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.Name;
@@ -48,6 +52,59 @@ import static javax.lang.model.element.Modifier.PUBLIC;
* @since 2.0
*/
abstract class Binding {
+
+ /**
+ * The subtype of this binding.
+ */
+ enum Type implements Predicate<Binding> {
+ /** A binding with this type is a {@link ProvisionBinding}. */
+ PROVISION(Provider.class),
+ /** A binding with this type is a {@link MembersInjectionBinding}. */
+ MEMBERS_INJECTION(MembersInjector.class),
+ /** A binding with this type is a {@link ProductionBinding}. */
+ PRODUCTION(Producer.class),
+ ;
+
+ private final Class<?> frameworkClass;
+
+ private Type(Class<?> frameworkClass) {
+ this.frameworkClass = frameworkClass;
+ }
+
+ /**
+ * Returns the framework class associated with bindings of this type.
+ */
+ Class<?> frameworkClass() {
+ return frameworkClass;
+ }
+
+ BindingKey.Kind bindingKeyKind() {
+ switch (this) {
+ case MEMBERS_INJECTION:
+ return BindingKey.Kind.MEMBERS_INJECTION;
+ case PROVISION:
+ case PRODUCTION:
+ return BindingKey.Kind.CONTRIBUTION;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public boolean apply(Binding binding) {
+ return this.equals(binding.bindingType());
+ }
+ }
+
+ abstract Binding.Type bindingType();
+
+ /**
+ * Returns the framework class associated with this binding.
+ */
+ Class<?> frameworkClass() {
+ return bindingType().frameworkClass();
+ }
+
static Optional<String> bindingPackageFor(Iterable<? extends Binding> bindings) {
ImmutableSet.Builder<String> bindingPackagesBuilder = ImmutableSet.builder();
for (Binding binding : bindings) {
@@ -67,6 +124,10 @@ abstract class Binding {
/** The {@link Key} that is provided by this binding. */
protected abstract Key key();
+ BindingKey bindingKey() {
+ return BindingKey.create(bindingType().bindingKeyKind(), key());
+ }
+
/** Returns the {@link Element} instance that is responsible for declaring the binding. */
abstract Element bindingElement();
@@ -164,6 +225,13 @@ abstract class Binding {
*/
abstract boolean hasNonDefaultTypeParameters();
+ /**
+ * The scope of this binding.
+ */
+ Scope scope() {
+ return Scope.unscoped();
+ }
+
// TODO(sameb): Remove the TypeElement parameter and pull it from the TypeMirror.
static boolean hasNonDefaultTypeParameters(TypeElement element, TypeMirror type, Types types) {
// If the element has no type parameters, nothing can be wrong.
diff --git a/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java b/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java
index f170f3988..b95143803 100644
--- a/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java
+++ b/compiler/src/main/java/dagger/internal/codegen/BindingGraph.java
@@ -26,13 +26,16 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeTraverser;
import dagger.Component;
import dagger.Subcomponent;
+import dagger.internal.codegen.Binding.Type;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.producers.Producer;
import dagger.producers.ProductionComponent;
import java.util.ArrayDeque;
import java.util.Collection;
@@ -45,6 +48,7 @@ import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import javax.inject.Inject;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
@@ -54,9 +58,13 @@ import javax.lang.model.util.Elements;
import static com.google.auto.common.MoreElements.getAnnotationMirror;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.any;
+import static com.google.common.collect.Sets.union;
import static dagger.internal.codegen.BindingKey.Kind.CONTRIBUTION;
import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod;
+import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor.isOfKind;
+import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind.SUBCOMPONENT_BUILDER;
import static dagger.internal.codegen.ComponentDescriptor.Kind.PRODUCTION_COMPONENT;
import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
import static dagger.internal.codegen.MembersInjectionBinding.Strategy.INJECT_MEMBERS;
@@ -104,29 +112,33 @@ abstract class BindingGraph {
* {@link ProductionComponent}.
*/
ImmutableSet<TypeElement> componentRequirements() {
- return SUBGRAPH_TRAVERSER.preOrderTraversal(this)
- .transformAndConcat(new Function<BindingGraph, Iterable<ResolvedBindings>>() {
- @Override
- public Iterable<ResolvedBindings> apply(BindingGraph input) {
- return input.resolvedBindings().values();
- }
- })
- .transformAndConcat(new Function<ResolvedBindings, Set<? extends ContributionBinding>>() {
- @Override
- public Set<? extends ContributionBinding> apply(ResolvedBindings input) {
- return (input.bindingKey().kind().equals(CONTRIBUTION))
- ? input.contributionBindings()
- : ImmutableSet.<ContributionBinding>of();
- }
- })
- .transformAndConcat(new Function<ContributionBinding, Set<TypeElement>>() {
- @Override
- public Set<TypeElement> apply(ContributionBinding input) {
- return input.bindingElement().getModifiers().contains(STATIC)
- ? ImmutableSet.<TypeElement>of()
- : input.contributedBy().asSet();
- }
- })
+ return SUBGRAPH_TRAVERSER
+ .preOrderTraversal(this)
+ .transformAndConcat(
+ new Function<BindingGraph, Iterable<ResolvedBindings>>() {
+ @Override
+ public Iterable<ResolvedBindings> apply(BindingGraph input) {
+ return input.resolvedBindings().values();
+ }
+ })
+ .transformAndConcat(
+ new Function<ResolvedBindings, Set<ContributionBinding>>() {
+ @Override
+ public Set<ContributionBinding> apply(ResolvedBindings input) {
+ return (input.bindingKey().kind().equals(CONTRIBUTION))
+ ? input.contributionBindings()
+ : ImmutableSet.<ContributionBinding>of();
+ }
+ })
+ .transformAndConcat(
+ new Function<ContributionBinding, Set<TypeElement>>() {
+ @Override
+ public Set<TypeElement> apply(ContributionBinding input) {
+ return input.bindingElement().getModifiers().contains(STATIC)
+ ? ImmutableSet.<TypeElement>of()
+ : input.contributedBy().asSet();
+ }
+ })
.filter(in(ownedModuleTypes()))
.append(componentDescriptor().dependencies())
.append(componentDescriptor().executorDependency().asSet())
@@ -145,20 +157,17 @@ abstract class BindingGraph {
private final Elements elements;
private final InjectBindingRegistry injectBindingRegistry;
private final Key.Factory keyFactory;
- private final DependencyRequest.Factory dependencyRequestFactory;
private final ProvisionBinding.Factory provisionBindingFactory;
private final ProductionBinding.Factory productionBindingFactory;
Factory(Elements elements,
InjectBindingRegistry injectBindingRegistry,
Key.Factory keyFactory,
- DependencyRequest.Factory dependencyRequestFactory,
ProvisionBinding.Factory provisionBindingFactory,
ProductionBinding.Factory productionBindingFactory) {
this.elements = elements;
this.injectBindingRegistry = injectBindingRegistry;
this.keyFactory = keyFactory;
- this.dependencyRequestFactory = dependencyRequestFactory;
this.provisionBindingFactory = provisionBindingFactory;
this.productionBindingFactory = productionBindingFactory;
}
@@ -169,16 +178,11 @@ abstract class BindingGraph {
private BindingGraph create(
Optional<Resolver> parentResolver, ComponentDescriptor componentDescriptor) {
- ImmutableSet.Builder<ProvisionBinding> explicitProvisionBindingsBuilder =
- ImmutableSet.builder();
- ImmutableSet.Builder<ProductionBinding> explicitProductionBindingsBuilder =
- ImmutableSet.builder();
+ ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
// binding for the component itself
TypeElement componentDefinitionType = componentDescriptor.componentDefinitionType();
- ProvisionBinding componentBinding =
- provisionBindingFactory.forComponent(componentDefinitionType);
- explicitProvisionBindingsBuilder.add(componentBinding);
+ explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDefinitionType));
// Collect Component dependencies.
Optional<AnnotationMirror> componentMirror =
@@ -188,34 +192,35 @@ abstract class BindingGraph {
? MoreTypes.asTypeElements(getComponentDependencies(componentMirror.get()))
: ImmutableSet.<TypeElement>of();
for (TypeElement componentDependency : componentDependencyTypes) {
- explicitProvisionBindingsBuilder.add(
- provisionBindingFactory.forComponent(componentDependency));
+ explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDependency));
List<ExecutableElement> dependencyMethods =
ElementFilter.methodsIn(elements.getAllMembers(componentDependency));
for (ExecutableElement method : dependencyMethods) {
// MembersInjection methods aren't "provided" explicitly, so ignore them.
if (isComponentContributionMethod(elements, method)) {
- if (componentDescriptor.kind().equals(PRODUCTION_COMPONENT)
- && isComponentProductionMethod(elements, method)) {
- explicitProductionBindingsBuilder.add(
- productionBindingFactory.forComponentMethod(method));
- } else {
- explicitProvisionBindingsBuilder.add(
- provisionBindingFactory.forComponentMethod(method));
- }
+ explicitBindingsBuilder.add(
+ componentDescriptor.kind().equals(PRODUCTION_COMPONENT)
+ && isComponentProductionMethod(elements, method)
+ ? productionBindingFactory.forComponentMethod(method)
+ : provisionBindingFactory.forComponentMethod(method));
}
}
}
+ // Bindings for subcomponent builders.
+ for (ComponentMethodDescriptor subcomponentMethodDescriptor :
+ Iterables.filter(
+ componentDescriptor.subcomponents().keySet(), isOfKind(SUBCOMPONENT_BUILDER))) {
+ explicitBindingsBuilder.add(
+ provisionBindingFactory.forSubcomponentBuilderMethod(
+ subcomponentMethodDescriptor.methodElement(),
+ componentDescriptor.componentDefinitionType()));
+ }
+
// Collect transitive module bindings.
for (ModuleDescriptor moduleDescriptor : componentDescriptor.transitiveModules()) {
for (ContributionBinding binding : moduleDescriptor.bindings()) {
- if (binding instanceof ProvisionBinding) {
- explicitProvisionBindingsBuilder.add((ProvisionBinding) binding);
- }
- if (binding instanceof ProductionBinding) {
- explicitProductionBindingsBuilder.add((ProductionBinding) binding);
- }
+ explicitBindingsBuilder.add(binding);
}
}
@@ -223,8 +228,7 @@ abstract class BindingGraph {
new Resolver(
parentResolver,
componentDescriptor,
- explicitBindingsByKey(explicitProvisionBindingsBuilder.build()),
- explicitBindingsByKey(explicitProductionBindingsBuilder.build()));
+ explicitBindingsByKey(explicitBindingsBuilder.build()));
for (ComponentMethodDescriptor componentMethod : componentDescriptor.componentMethods()) {
Optional<DependencyRequest> componentMethodRequest = componentMethod.dependencyRequest();
if (componentMethodRequest.isPresent()) {
@@ -269,9 +273,8 @@ abstract class BindingGraph {
private final class Resolver {
final Optional<Resolver> parentResolver;
final ComponentDescriptor componentDescriptor;
- final ImmutableSetMultimap<Key, ProvisionBinding> explicitProvisionBindings;
- final ImmutableSet<ProvisionBinding> explicitProvisionBindingsSet;
- final ImmutableSetMultimap<Key, ProductionBinding> explicitProductionBindings;
+ final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
+ final ImmutableSet<ContributionBinding> explicitBindingsSet;
final Map<BindingKey, ResolvedBindings> resolvedBindings;
final Deque<BindingKey> cycleStack = new ArrayDeque<>();
final Cache<BindingKey, Boolean> dependsOnLocalMultibindingsCache =
@@ -280,134 +283,132 @@ abstract class BindingGraph {
Resolver(
Optional<Resolver> parentResolver,
ComponentDescriptor componentDescriptor,
- ImmutableSetMultimap<Key, ProvisionBinding> explicitProvisionBindings,
- ImmutableSetMultimap<Key, ProductionBinding> explicitProductionBindings) {
+ ImmutableSetMultimap<Key, ContributionBinding> explicitBindings) {
assert parentResolver != null;
this.parentResolver = parentResolver;
assert componentDescriptor != null;
this.componentDescriptor = componentDescriptor;
- assert explicitProvisionBindings != null;
- this.explicitProvisionBindings = explicitProvisionBindings;
- this.explicitProvisionBindingsSet = ImmutableSet.copyOf(explicitProvisionBindings.values());
- assert explicitProductionBindings != null;
- this.explicitProductionBindings = explicitProductionBindings;
+ assert explicitBindings != null;
+ this.explicitBindings = explicitBindings;
+ this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
this.resolvedBindings = Maps.newLinkedHashMap();
}
/**
- * Looks up the bindings associated with a given dependency request and returns them. In the
- * event that the binding is owned by a parent component it will trigger resolution in that
- * component's resolver but will return an {@link Optional#absent} value.
+ * Looks up the bindings associated with a given dependency request and returns them.
+ *
+ * <p>Requests for {@code Map<K, V>} for which there are only bindings for
+ * {@code Map<K, Provider<V>>} will resolve to a single implicit binding for the latter map
+ * (and similarly for {@link Producer}s).
+ *
+ * <p>If there are no explicit bindings for a contribution, looks for implicit
+ * {@link Inject @Inject}-annotated constructor types.
*/
ResolvedBindings lookUpBindings(DependencyRequest request) {
BindingKey bindingKey = request.bindingKey();
switch (bindingKey.kind()) {
case CONTRIBUTION:
// First, check for explicit keys (those from modules and components)
- ImmutableSet<ProvisionBinding> explicitProvisionBindingsForKey =
- getExplicitProvisionBindings(bindingKey.key());
- ImmutableSet<ProductionBinding> explicitProductionBindingsForKey =
- getExplicitProductionBindings(bindingKey.key());
+ ImmutableSet<ContributionBinding> explicitBindingsForKey =
+ getExplicitBindings(bindingKey.key());
// If the key is Map<K, V>, get its implicit binding keys, which are either
// Map<K, Provider<V>> or Map<K, Producer<V>>, and grab their explicit bindings.
Optional<Key> mapProviderKey = keyFactory.implicitMapProviderKeyFrom(bindingKey.key());
- ImmutableSet<ProvisionBinding> explicitMapProvisionBindings = ImmutableSet.of();
+ ImmutableSet.Builder<ContributionBinding> explicitMapBindingsBuilder =
+ ImmutableSet.builder();
if (mapProviderKey.isPresent()) {
- explicitMapProvisionBindings = getExplicitProvisionBindings(mapProviderKey.get());
+ explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProviderKey.get()));
}
Optional<Key> mapProducerKey = keyFactory.implicitMapProducerKeyFrom(bindingKey.key());
- ImmutableSet<ProductionBinding> explicitMapProductionBindings = ImmutableSet.of();
if (mapProducerKey.isPresent()) {
- explicitMapProductionBindings = getExplicitProductionBindings(mapProducerKey.get());
+ explicitMapBindingsBuilder.addAll(getExplicitBindings(mapProducerKey.get()));
}
-
- if (!explicitProvisionBindingsForKey.isEmpty()
- || !explicitProductionBindingsForKey.isEmpty()) {
- // we have some explicit binding for this key, so we collect all explicit implicit map
- // bindings that might conflict with this and let the validator sort it out
- ImmutableSet.Builder<ContributionBinding> ownedBindings = ImmutableSet.builder();
- ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding>
- inheritedBindings = ImmutableSetMultimap.builder();
- for (ProvisionBinding provisionBinding :
- Sets.union(explicitProvisionBindingsForKey, explicitMapProvisionBindings)) {
- if (isResolvedInParent(request, provisionBinding)
- && !shouldOwnParentBinding(request, provisionBinding)) {
- inheritedBindings.put(
- getOwningResolver(provisionBinding).get().componentDescriptor,
- provisionBinding);
- } else {
- ownedBindings.add(provisionBinding);
- }
+ ImmutableSet<ContributionBinding> explicitMapBindings =
+ explicitMapBindingsBuilder.build();
+
+ // If the key is Set<Produced<T>>, then we look up bindings by the alternate key Set<T>.
+ Optional<Key> setKeyFromProduced =
+ keyFactory.implicitSetKeyFromProduced(bindingKey.key());
+ ImmutableSet<ContributionBinding> explicitSetBindings =
+ setKeyFromProduced.isPresent()
+ ? getExplicitBindings(setKeyFromProduced.get())
+ : ImmutableSet.<ContributionBinding>of();
+
+ if (!explicitBindingsForKey.isEmpty() || !explicitSetBindings.isEmpty()) {
+ /* If there are any explicit bindings for this key, then combine those with any
+ * conflicting Map<K, Provider<V>> bindings and let the validator fail. */
+ ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding> bindings =
+ ImmutableSetMultimap.builder();
+ for (ContributionBinding binding :
+ union(explicitBindingsForKey, union(explicitSetBindings, explicitMapBindings))) {
+ bindings.put(getOwningComponent(request, binding), binding);
}
- return ResolvedBindings.create(bindingKey,
+ return ResolvedBindings.forContributionBindings(
+ bindingKey, componentDescriptor, bindings.build());
+ } else if (any(explicitMapBindings, Binding.Type.PRODUCTION)) {
+ /* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
+ * some explicit Map<K, Producer<V>> bindings, then this binding must have only the
+ * implicit dependency on Map<K, Producer<V>>. */
+ return ResolvedBindings.forContributionBindings(
+ bindingKey,
+ componentDescriptor,
+ productionBindingFactory.implicitMapOfProducerBinding(request));
+ } else if (any(explicitMapBindings, Binding.Type.PROVISION)) {
+ /* If this binding is for Map<K, V> and there are no explicit Map<K, V> bindings but
+ * some explicit Map<K, Provider<V>> bindings, then this binding must have only the
+ * implicit dependency on Map<K, Provider<V>>. */
+ return ResolvedBindings.forContributionBindings(
+ bindingKey,
componentDescriptor,
- ownedBindings
- .addAll(explicitProductionBindingsForKey)
- .addAll(explicitMapProductionBindings)
- .build(),
- inheritedBindings.build());
+ provisionBindingFactory.implicitMapOfProviderBinding(request));
} else {
- if (!explicitMapProductionBindings.isEmpty()) {
- // if we have any explicit Map<K, Producer<V>> bindings, then this Map<K, V> binding
- // must be considered an implicit ProductionBinding
- DependencyRequest implicitRequest =
- dependencyRequestFactory.forImplicitMapBinding(request, mapProducerKey.get());
- return ResolvedBindings.create(
- bindingKey,
- componentDescriptor,
- productionBindingFactory.forImplicitMapBinding(request, implicitRequest));
- } else if (!explicitMapProvisionBindings.isEmpty()) {
- // if there are Map<K, Provider<V>> bindings, then it'll be an implicit
- // ProvisionBinding
- DependencyRequest implicitRequest =
- dependencyRequestFactory.forImplicitMapBinding(request, mapProviderKey.get());
- return ResolvedBindings.create(
- bindingKey,
- componentDescriptor,
- provisionBindingFactory.forImplicitMapBinding(request, implicitRequest));
- } else {
- // no explicit binding, look it up.
- Optional<ProvisionBinding> provisionBinding =
- injectBindingRegistry.getOrFindProvisionBinding(bindingKey.key());
- if (provisionBinding.isPresent()) {
- if (isResolvedInParent(request, provisionBinding.get())
- && !shouldOwnParentBinding(request, provisionBinding.get())) {
- return ResolvedBindings.create(
- bindingKey,
- componentDescriptor,
- ImmutableSet.<Binding>of(),
- ImmutableSetMultimap.of(
- getOwningResolver(provisionBinding.get()).get().componentDescriptor,
- provisionBinding.get()));
- }
- }
- return ResolvedBindings.create(
- bindingKey,
- componentDescriptor,
- provisionBinding.asSet(),
- ImmutableSetMultimap.<ComponentDescriptor, Binding>of());
- }
+ /* If there are no explicit bindings at all, look for an implicit @Inject-constructed
+ * binding. */
+ Optional<ProvisionBinding> provisionBinding =
+ injectBindingRegistry.getOrFindProvisionBinding(bindingKey.key());
+ ComponentDescriptor owningComponent =
+ provisionBinding.isPresent()
+ && isResolvedInParent(request, provisionBinding.get())
+ && !shouldOwnParentBinding(request, provisionBinding.get())
+ ? getOwningResolver(provisionBinding.get()).get().componentDescriptor
+ : componentDescriptor;
+ return ResolvedBindings.forContributionBindings(
+ bindingKey,
+ componentDescriptor,
+ ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>builder()
+ .putAll(owningComponent, provisionBinding.asSet())
+ .build());
}
+
case MEMBERS_INJECTION:
// no explicit deps for members injection, so just look it up
- return ResolvedBindings.create(
- bindingKey,
- componentDescriptor,
- rollUpMembersInjectionBindings(bindingKey.key()));
+ return ResolvedBindings.forMembersInjectionBinding(
+ bindingKey, componentDescriptor, rollUpMembersInjectionBindings(bindingKey.key()));
default:
throw new AssertionError();
}
}
/**
- * Returns {@code true} if {@code provisionBinding} is owned by a parent resolver. If so,
- * calls {@link #resolve(DependencyRequest) resolve(request)} on that resolver.
+ * If {@code binding} should be owned by a parent component, resolves the binding in that
+ * component's resolver and returns that component. Otherwise returns the component for this
+ * resolver.
+ */
+ private ComponentDescriptor getOwningComponent(
+ DependencyRequest request, ContributionBinding binding) {
+ return isResolvedInParent(request, binding) && !shouldOwnParentBinding(request, binding)
+ ? getOwningResolver(binding).get().componentDescriptor
+ : componentDescriptor;
+ }
+
+ /**
+ * Returns {@code true} if {@code binding} is owned by a parent resolver. If so, calls
+ * {@link #resolve(DependencyRequest) resolve(request)} on that resolver.
*/
- private boolean isResolvedInParent(
- DependencyRequest request, ProvisionBinding provisionBinding) {
- Optional<Resolver> owningResolver = getOwningResolver(provisionBinding);
+ private boolean isResolvedInParent(DependencyRequest request, ContributionBinding binding) {
+ Optional<Resolver> owningResolver = getOwningResolver(binding);
if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
owningResolver.get().resolve(request);
return true;
@@ -417,14 +418,14 @@ abstract class BindingGraph {
}
/**
- * Returns {@code true} if {@code provisionBinding}, which was previously resolved by a parent
+ * Returns {@code true} if {@code binding}, which was previously resolved by a parent
* resolver, should be moved into this resolver's bindings for {@code request} because it is
* unscoped and {@linkplain #dependsOnLocalMultibindings(ResolvedBindings) depends on local
* multibindings}, or {@code false} if it can satisfy {@code request} as an inherited binding.
*/
private boolean shouldOwnParentBinding(
- DependencyRequest request, ProvisionBinding provisionBinding) {
- return !isScoped(provisionBinding)
+ DependencyRequest request, ContributionBinding binding) {
+ return !binding.scope().isPresent()
&& dependsOnLocalMultibindings(
getPreviouslyResolvedBindings(request.bindingKey()).get());
}
@@ -446,9 +447,9 @@ abstract class BindingGraph {
return membersInjectionBinding;
}
- private Optional<Resolver> getOwningResolver(ProvisionBinding provisionBinding) {
+ private Optional<Resolver> getOwningResolver(ContributionBinding provisionBinding) {
for (Resolver requestResolver : getResolverLineage().reverse()) {
- if (requestResolver.explicitProvisionBindingsSet.contains(provisionBinding)) {
+ if (requestResolver.explicitBindingsSet.contains(provisionBinding)) {
return Optional.of(requestResolver);
}
}
@@ -477,18 +478,10 @@ abstract class BindingGraph {
return ImmutableList.copyOf(Lists.reverse(resolverList));
}
- private ImmutableSet<ProvisionBinding> getExplicitProvisionBindings(Key requestKey) {
- ImmutableSet.Builder<ProvisionBinding> explicitBindingsForKey = ImmutableSet.builder();
+ private ImmutableSet<ContributionBinding> getExplicitBindings(Key requestKey) {
+ ImmutableSet.Builder<ContributionBinding> explicitBindingsForKey = ImmutableSet.builder();
for (Resolver resolver : getResolverLineage()) {
- explicitBindingsForKey.addAll(resolver.explicitProvisionBindings.get(requestKey));
- }
- return explicitBindingsForKey.build();
- }
-
- private ImmutableSet<ProductionBinding> getExplicitProductionBindings(Key requestKey) {
- ImmutableSet.Builder<ProductionBinding> explicitBindingsForKey = ImmutableSet.builder();
- for (Resolver resolver : getResolverLineage()) {
- explicitBindingsForKey.addAll(resolver.explicitProductionBindings.get(requestKey));
+ explicitBindingsForKey.addAll(resolver.explicitBindings.get(requestKey));
}
return explicitBindingsForKey.build();
}
@@ -575,7 +568,8 @@ abstract class BindingGraph {
}
for (Binding binding : previouslyResolvedBindings.bindings()) {
- if (!isScoped(binding) && !(binding instanceof ProductionBinding)) {
+ if (!binding.scope().isPresent()
+ && !binding.bindingType().equals(Type.PRODUCTION)) {
for (DependencyRequest dependency : binding.implicitDependencies()) {
if (dependsOnLocalMultibindings(
getPreviouslyResolvedBindings(dependency.bindingKey()).get(),
@@ -594,16 +588,7 @@ abstract class BindingGraph {
}
private boolean hasLocalContributions(ResolvedBindings resolvedBindings) {
- return !explicitProvisionBindings.get(resolvedBindings.bindingKey().key()).isEmpty()
- || !explicitProductionBindings.get(resolvedBindings.bindingKey().key()).isEmpty();
- }
-
- private boolean isScoped(Binding binding) {
- if (binding instanceof ProvisionBinding) {
- ProvisionBinding provisionBinding = (ProvisionBinding) binding;
- return provisionBinding.scope().isPresent();
- }
- return false;
+ return !explicitBindings.get(resolvedBindings.bindingKey().key()).isEmpty();
}
ImmutableMap<BindingKey, ResolvedBindings> getResolvedBindings() {
diff --git a/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java b/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java
index 3af59d61b..f8010c301 100644
--- a/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/BindingGraphValidator.java
@@ -40,7 +40,7 @@ import dagger.Lazy;
import dagger.MapKey;
import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ContributionBinding.BindingType;
+import dagger.internal.codegen.ContributionBinding.ContributionType;
import dagger.internal.codegen.writer.TypeNames;
import java.util.ArrayDeque;
import java.util.Arrays;
@@ -59,6 +59,7 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleTypeVisitor6;
@@ -67,26 +68,35 @@ import javax.tools.Diagnostic;
import static com.google.auto.common.MoreElements.getAnnotationMirror;
import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.auto.common.MoreTypes.asTypeElements;
import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.all;
+import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Iterables.indexOf;
import static com.google.common.collect.Iterables.skip;
+import static com.google.common.collect.Maps.filterKeys;
+import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor.isOfKind;
+import static dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind.SUBCOMPONENT;
import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
import static dagger.internal.codegen.ContributionBinding.indexMapBindingsByAnnotationType;
import static dagger.internal.codegen.ContributionBinding.indexMapBindingsByMapKey;
import static dagger.internal.codegen.ErrorMessages.DUPLICATE_SIZE_LIMIT;
import static dagger.internal.codegen.ErrorMessages.INDENT;
import static dagger.internal.codegen.ErrorMessages.MEMBERS_INJECTION_WITH_UNBOUNDED_TYPE;
-import static dagger.internal.codegen.ErrorMessages.NULLABLE_TO_NON_NULLABLE;
import static dagger.internal.codegen.ErrorMessages.REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_FORMAT;
import static dagger.internal.codegen.ErrorMessages.REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_OR_PRODUCER_FORMAT;
import static dagger.internal.codegen.ErrorMessages.REQUIRES_PROVIDER_FORMAT;
import static dagger.internal.codegen.ErrorMessages.REQUIRES_PROVIDER_OR_PRODUCER_FORMAT;
import static dagger.internal.codegen.ErrorMessages.duplicateMapKeysError;
import static dagger.internal.codegen.ErrorMessages.inconsistentMapKeyAnnotationsError;
+import static dagger.internal.codegen.ErrorMessages.nullableToNonNullable;
import static dagger.internal.codegen.ErrorMessages.stripCommonTypePrefixes;
+import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
import static dagger.internal.codegen.Util.getKeyTypeOfMap;
import static dagger.internal.codegen.Util.getProvidedValueTypeOfMap;
import static dagger.internal.codegen.Util.getValueTypeOfMap;
@@ -101,8 +111,7 @@ public class BindingGraphValidator {
private final InjectBindingRegistry injectBindingRegistry;
private final ValidationType scopeCycleValidationType;
private final Diagnostic.Kind nullableValidationType;
- private final ProvisionBindingFormatter provisionBindingFormatter;
- private final ProductionBindingFormatter productionBindingFormatter;
+ private final ContributionBindingFormatter contributionBindingFormatter;
private final MethodSignatureFormatter methodSignatureFormatter;
private final DependencyRequestFormatter dependencyRequestFormatter;
private final KeyFormatter keyFormatter;
@@ -112,8 +121,7 @@ public class BindingGraphValidator {
InjectBindingRegistry injectBindingRegistry,
ValidationType scopeCycleValidationType,
Diagnostic.Kind nullableValidationType,
- ProvisionBindingFormatter provisionBindingFormatter,
- ProductionBindingFormatter productionBindingFormatter,
+ ContributionBindingFormatter contributionBindingFormatter,
MethodSignatureFormatter methodSignatureFormatter,
DependencyRequestFormatter dependencyRequestFormatter,
KeyFormatter keyFormatter) {
@@ -121,8 +129,7 @@ public class BindingGraphValidator {
this.injectBindingRegistry = injectBindingRegistry;
this.scopeCycleValidationType = scopeCycleValidationType;
this.nullableValidationType = nullableValidationType;
- this.provisionBindingFormatter = provisionBindingFormatter;
- this.productionBindingFormatter = productionBindingFormatter;
+ this.contributionBindingFormatter = contributionBindingFormatter;
this.methodSignatureFormatter = methodSignatureFormatter;
this.dependencyRequestFormatter = dependencyRequestFormatter;
this.keyFormatter = keyFormatter;
@@ -166,6 +173,13 @@ public class BindingGraphValidator {
new HashSet<DependencyRequest>());
}
}
+
+ for (Map.Entry<ComponentMethodDescriptor, ComponentDescriptor> entry :
+ filterKeys(subject.componentDescriptor().subcomponents(), isOfKind(SUBCOMPONENT))
+ .entrySet()) {
+ validateSubcomponentFactoryMethod(
+ entry.getKey().methodElement(), entry.getValue().componentDefinitionType());
+ }
for (BindingGraph subgraph : subject.subgraphs().values()) {
Validation subgraphValidation =
@@ -175,6 +189,39 @@ public class BindingGraphValidator {
}
}
+ private void validateSubcomponentFactoryMethod(
+ ExecutableElement factoryMethod, TypeElement subcomponentType) {
+ BindingGraph subgraph = subject.subgraphs().get(factoryMethod);
+ FluentIterable<TypeElement> missingModules =
+ FluentIterable.from(subgraph.componentRequirements())
+ .filter(not(in(subgraphFactoryMethodParameters(factoryMethod))))
+ .filter(
+ new Predicate<TypeElement>() {
+ @Override
+ public boolean apply(TypeElement moduleType) {
+ return !componentCanMakeNewInstances(moduleType);
+ }
+ });
+ if (!missingModules.isEmpty()) {
+ reportBuilder.addError(
+ String.format(
+ "%s requires modules which have no visible default constructors. "
+ + "Add the following modules as parameters to this method: %s",
+ subcomponentType.getQualifiedName(),
+ Joiner.on(", ").join(missingModules.toSet())),
+ factoryMethod);
+ }
+ }
+
+ private ImmutableSet<TypeElement> subgraphFactoryMethodParameters(
+ ExecutableElement factoryMethod) {
+ DeclaredType componentType =
+ asDeclared(subject.componentDescriptor().componentDefinitionType().asType());
+ ExecutableType factoryMethodType =
+ asExecutable(types.asMemberOf(componentType, factoryMethod));
+ return asTypeElements(factoryMethodType.getParameterTypes());
+ }
+
/**
* Traverse the resolved dependency requests, validating resolved bindings, and reporting any
* cycles found.
@@ -235,59 +282,39 @@ public class BindingGraphValidator {
return false;
}
- ImmutableSet.Builder<ProvisionBinding> provisionBindingsBuilder =
- ImmutableSet.builder();
- ImmutableSet.Builder<ProductionBinding> productionBindingsBuilder =
- ImmutableSet.builder();
- ImmutableSet.Builder<MembersInjectionBinding> membersInjectionBindingsBuilder =
- ImmutableSet.builder();
- for (Binding binding : resolvedBinding.bindings()) {
- if (binding instanceof ProvisionBinding) {
- provisionBindingsBuilder.add((ProvisionBinding) binding);
- }
- if (binding instanceof ProductionBinding) {
- productionBindingsBuilder.add((ProductionBinding) binding);
- }
- if (binding instanceof MembersInjectionBinding) {
- membersInjectionBindingsBuilder.add((MembersInjectionBinding) binding);
- }
- }
- ImmutableSet<ProvisionBinding> provisionBindings = provisionBindingsBuilder.build();
- ImmutableSet<ProductionBinding> productionBindings = productionBindingsBuilder.build();
- ImmutableSet<MembersInjectionBinding> membersInjectionBindings =
- membersInjectionBindingsBuilder.build();
-
switch (resolvedBinding.bindingKey().kind()) {
case CONTRIBUTION:
- if (!membersInjectionBindings.isEmpty()) {
+ ImmutableSet<ContributionBinding> contributionBindings =
+ resolvedBinding.contributionBindings();
+ if (any(contributionBindings, Binding.Type.MEMBERS_INJECTION)) {
throw new IllegalArgumentException(
"contribution binding keys should never have members injection bindings");
}
- Set<ContributionBinding> combined = Sets.union(provisionBindings, productionBindings);
- if (!validateNullability(path.peek().request(), combined)) {
+ if (!validateNullability(path.peek().request(), contributionBindings)) {
return false;
}
- if (!productionBindings.isEmpty() && doesPathRequireProvisionOnly(path)) {
+ if (any(contributionBindings, Binding.Type.PRODUCTION)
+ && doesPathRequireProvisionOnly(path)) {
reportProviderMayNotDependOnProducer(path);
return false;
}
- if (combined.size() <= 1) {
+ if (contributionBindings.size() <= 1) {
return true;
}
- ImmutableListMultimap<BindingType, ContributionBinding> bindingsByType =
- ContributionBinding.bindingTypesFor(combined);
- if (bindingsByType.keySet().size() > 1) {
+ ImmutableListMultimap<ContributionType, ContributionBinding> contributionsByType =
+ ContributionBinding.contributionTypesFor(contributionBindings);
+ if (contributionsByType.keySet().size() > 1) {
reportMultipleBindingTypes(path);
return false;
}
- switch (getOnlyElement(bindingsByType.keySet())) {
+ switch (getOnlyElement(contributionsByType.keySet())) {
case UNIQUE:
reportDuplicateBindings(path);
return false;
case MAP:
- boolean duplicateMapKeys = hasDuplicateMapKeys(path, combined);
+ boolean duplicateMapKeys = hasDuplicateMapKeys(path, contributionBindings);
boolean inconsistentMapKeyAnnotationTypes =
- hasInconsistentMapKeyAnnotationTypes(path, combined);
+ hasInconsistentMapKeyAnnotationTypes(path, contributionBindings);
return !duplicateMapKeys && !inconsistentMapKeyAnnotationTypes;
case SET:
break;
@@ -296,21 +323,15 @@ public class BindingGraphValidator {
}
break;
case MEMBERS_INJECTION:
- if (!provisionBindings.isEmpty() || !productionBindings.isEmpty()) {
+ if (!all(resolvedBinding.bindings(), Binding.Type.MEMBERS_INJECTION)) {
throw new IllegalArgumentException(
"members injection binding keys should never have contribution bindings");
}
- if (membersInjectionBindings.size() > 1) {
+ if (resolvedBinding.bindings().size() > 1) {
reportDuplicateBindings(path);
return false;
}
- if (membersInjectionBindings.size() == 1) {
- MembersInjectionBinding binding = getOnlyElement(membersInjectionBindings);
- if (!validateMembersInjectionBinding(binding, path)) {
- return false;
- }
- }
- break;
+ return validateMembersInjectionBinding(getOnlyElement(resolvedBinding.bindings()), path);
default:
throw new AssertionError();
}
@@ -320,34 +341,27 @@ public class BindingGraphValidator {
/** Ensures that if the request isn't nullable, then each contribution is also not nullable. */
private boolean validateNullability(
DependencyRequest request, Set<ContributionBinding> bindings) {
+ if (request.isNullable()) {
+ return true;
+ }
+
+ // Note: the method signature will include the @Nullable in it!
+ /* TODO(sameb): Sometimes javac doesn't include the Element in its output.
+ * (Maybe this happens if the code was already compiled before this point?)
+ * ... we manually print out the request in that case, otherwise the error
+ * message is kind of useless. */
+ String typeName = TypeNames.forTypeMirror(request.key().type()).toString();
+
boolean valid = true;
- if (!request.isNullable()) {
- String typeName = null;
- for (ContributionBinding binding : bindings) {
- if (binding.nullableType().isPresent()) {
- String methodSignature;
- if (binding instanceof ProvisionBinding) {
- ProvisionBinding provisionBinding = (ProvisionBinding) binding;
- methodSignature = provisionBindingFormatter.format(provisionBinding);
- } else {
- ProductionBinding productionBinding = (ProductionBinding) binding;
- methodSignature = productionBindingFormatter.format(productionBinding);
- }
- // Note: the method signature will include the @Nullable in it!
- // TODO(sameb): Sometimes javac doesn't include the Element in its output.
- // (Maybe this happens if the code was already compiled before this point?)
- // ... we manually print ouf the request in that case, otherwise the error
- // message is kind of useless.
- if (typeName == null) {
- typeName = TypeNames.forTypeMirror(request.key().type()).toString();
- }
- reportBuilder.addItem(
- String.format(NULLABLE_TO_NON_NULLABLE, typeName, methodSignature)
- + "\n at: " + dependencyRequestFormatter.format(request),
- nullableValidationType,
- request.requestElement());
- valid = false;
- }
+ for (ContributionBinding binding : bindings) {
+ if (binding.nullableType().isPresent()) {
+ reportBuilder.addItem(
+ nullableToNonNullable(typeName, contributionBindingFormatter.format(binding))
+ + "\n at: "
+ + dependencyRequestFormatter.format(request),
+ nullableValidationType,
+ request.requestElement());
+ valid = false;
}
}
return valid;
@@ -375,9 +389,9 @@ public class BindingGraphValidator {
* {@link MapKey} annotation type.
*/
private boolean hasInconsistentMapKeyAnnotationTypes(
- Deque<ResolvedRequest> path, Set<ContributionBinding> mapBindings) {
+ Deque<ResolvedRequest> path, Set<ContributionBinding> contributionBindings) {
ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
- mapBindingsByAnnotationType = indexMapBindingsByAnnotationType(mapBindings);
+ mapBindingsByAnnotationType = indexMapBindingsByAnnotationType(contributionBindings);
if (mapBindingsByAnnotationType.keySet().size() > 1) {
reportInconsistentMapKeyAnnotations(path, mapBindingsByAnnotationType);
return true;
@@ -390,7 +404,7 @@ public class BindingGraphValidator {
* valid.
*/
private boolean validateMembersInjectionBinding(
- MembersInjectionBinding binding, final Deque<ResolvedRequest> path) {
+ Binding binding, final Deque<ResolvedRequest> path) {
return binding
.key()
.type()
@@ -705,28 +719,27 @@ public class BindingGraphValidator {
for (ResolvedBindings bindings : resolvedBindings.values()) {
if (bindings.bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)) {
for (ContributionBinding contributionBinding : bindings.ownedContributionBindings()) {
- if (contributionBinding instanceof ProvisionBinding) {
- ProvisionBinding provisionBinding = (ProvisionBinding) contributionBinding;
- Scope bindingScope = provisionBinding.scope();
- if (bindingScope.isPresent() && !componentScope.equals(bindingScope)) {
- // Scoped components cannot reference bindings to @Provides methods or @Inject
- // types decorated by a different scope annotation. Unscoped components cannot
- // reference to scoped @Provides methods or @Inject types decorated by any
- // scope annotation.
- switch (provisionBinding.bindingKind()) {
- case PROVISION:
- ExecutableElement provisionMethod =
- MoreElements.asExecutable(provisionBinding.bindingElement());
- incompatiblyScopedMethodsBuilder.add(
- methodSignatureFormatter.format(provisionMethod));
- break;
- case INJECTION:
- incompatiblyScopedMethodsBuilder.add(bindingScope.getReadableSource()
- + " class " + provisionBinding.bindingTypeElement().getQualifiedName());
- break;
- default:
- throw new IllegalStateException();
- }
+ Scope bindingScope = contributionBinding.scope();
+ if (bindingScope.isPresent() && !bindingScope.equals(componentScope)) {
+ // Scoped components cannot reference bindings to @Provides methods or @Inject
+ // types decorated by a different scope annotation. Unscoped components cannot
+ // reference to scoped @Provides methods or @Inject types decorated by any
+ // scope annotation.
+ switch (contributionBinding.bindingKind()) {
+ case PROVISION:
+ ExecutableElement provisionMethod =
+ MoreElements.asExecutable(contributionBinding.bindingElement());
+ incompatiblyScopedMethodsBuilder.add(
+ methodSignatureFormatter.format(provisionMethod));
+ break;
+ case INJECTION:
+ incompatiblyScopedMethodsBuilder.add(
+ bindingScope.getReadableSource()
+ + " class "
+ + contributionBinding.bindingTypeElement().getQualifiedName());
+ break;
+ default:
+ throw new IllegalStateException();
}
}
}
@@ -760,7 +773,7 @@ public class BindingGraphValidator {
ErrorMessages.PROVIDER_ENTRY_POINT_MAY_NOT_DEPEND_ON_PRODUCER_FORMAT,
formatRootRequestKey(path));
} else {
- ImmutableSet<ProvisionBinding> dependentProvisions =
+ ImmutableSet<? extends Binding> dependentProvisions =
provisionsDependingOnLatestRequest(path);
// TODO(beder): Consider displaying all dependent provisions in the error message. If we do
// that, should we display all productions that depend on them also?
@@ -773,8 +786,6 @@ public class BindingGraphValidator {
private void reportMissingBinding(Deque<ResolvedRequest> path) {
Key key = path.peek().request().key();
BindingKey bindingKey = path.peek().request().bindingKey();
- TypeMirror type = key.type();
- String typeName = TypeNames.forTypeMirror(type).toString();
boolean requiresContributionMethod = !key.isValidImplicitProvisionKey(types);
boolean requiresProvision = doesPathRequireProvisionOnly(path);
StringBuilder errorMessage = new StringBuilder();
@@ -785,7 +796,7 @@ public class BindingGraphValidator {
: requiresProvision
? REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_FORMAT
: REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_OR_PRODUCER_FORMAT;
- errorMessage.append(String.format(requiresErrorMessageFormat, typeName));
+ errorMessage.append(String.format(requiresErrorMessageFormat, keyFormatter.format(key)));
if (key.isValidMembersInjectionKey()
&& !injectBindingRegistry.getOrFindMembersInjectionBinding(key).injectionSites()
.isEmpty()) {
@@ -814,10 +825,12 @@ public class BindingGraphValidator {
StringBuilder builder = new StringBuilder();
new Formatter(builder)
.format(ErrorMessages.DUPLICATE_BINDINGS_FOR_KEY_FORMAT, formatRootRequestKey(path));
- for (Binding binding : Iterables.limit(resolvedBinding.bindings(), DUPLICATE_SIZE_LIMIT)) {
- builder.append('\n').append(INDENT).append(formatBinding(binding));
+ for (ContributionBinding binding :
+ Iterables.limit(resolvedBinding.contributionBindings(), DUPLICATE_SIZE_LIMIT)) {
+ builder.append('\n').append(INDENT).append(contributionBindingFormatter.format(binding));
}
- int numberOfOtherBindings = resolvedBinding.bindings().size() - DUPLICATE_SIZE_LIMIT;
+ int numberOfOtherBindings =
+ resolvedBinding.contributionBindings().size() - DUPLICATE_SIZE_LIMIT;
if (numberOfOtherBindings > 0) {
builder.append('\n').append(INDENT)
.append("and ").append(numberOfOtherBindings).append(" other");
@@ -834,28 +847,26 @@ public class BindingGraphValidator {
StringBuilder builder = new StringBuilder();
new Formatter(builder)
.format(ErrorMessages.MULTIPLE_BINDING_TYPES_FOR_KEY_FORMAT, formatRootRequestKey(path));
- ImmutableListMultimap<BindingType, ContributionBinding> bindingsByType =
- ContributionBinding.<ContributionBinding>bindingTypesFor(
- resolvedBinding.contributionBindings());
- for (BindingType type : Ordering.natural().immutableSortedCopy(bindingsByType.keySet())) {
+ ImmutableListMultimap<ContributionType, ContributionBinding> bindingsByType =
+ ContributionBinding.contributionTypesFor(resolvedBinding.contributionBindings());
+ for (ContributionType type :
+ Ordering.natural().immutableSortedCopy(bindingsByType.keySet())) {
builder.append(INDENT);
builder.append(formatBindingType(type));
builder.append(" bindings:\n");
for (ContributionBinding binding : bindingsByType.get(type)) {
- builder.append(INDENT).append(INDENT);
- if (binding instanceof ProvisionBinding) {
- builder.append(provisionBindingFormatter.format((ProvisionBinding) binding));
- } else if (binding instanceof ProductionBinding) {
- builder.append(productionBindingFormatter.format((ProductionBinding) binding));
- }
- builder.append('\n');
+ builder
+ .append(INDENT)
+ .append(INDENT)
+ .append(contributionBindingFormatter.format(binding))
+ .append('\n');
}
}
reportBuilder.addError(builder.toString(), path.getLast().request().requestElement());
}
private void reportDuplicateMapKeys(
- Deque<ResolvedRequest> path, Collection<? extends ContributionBinding> mapBindings) {
+ Deque<ResolvedRequest> path, Collection<ContributionBinding> mapBindings) {
StringBuilder builder = new StringBuilder();
builder.append(duplicateMapKeysError(formatRootRequestKey(path)));
appendBindings(builder, mapBindings, 1);
@@ -1061,33 +1072,32 @@ public class BindingGraphValidator {
}
// otherwise, the second-most-recent bindings determine whether the most recent one must be a
// provision
- ImmutableSet<ProvisionBinding> dependentProvisions = provisionsDependingOnLatestRequest(path);
- return !dependentProvisions.isEmpty();
+ return !provisionsDependingOnLatestRequest(path).isEmpty();
}
/**
* Returns any provision bindings resolved for the second-most-recent request in the given path;
* that is, returns those provision bindings that depend on the latest request in the path.
*/
- private ImmutableSet<ProvisionBinding> provisionsDependingOnLatestRequest(
+ private ImmutableSet<? extends Binding> provisionsDependingOnLatestRequest(
Deque<ResolvedRequest> path) {
Iterator<ResolvedRequest> iterator = path.iterator();
final DependencyRequest request = iterator.next().request();
ResolvedRequest previousResolvedRequest = iterator.next();
- @SuppressWarnings("unchecked") // validated by instanceof below
- ImmutableSet<ProvisionBinding> bindings = (ImmutableSet<ProvisionBinding>) FluentIterable
- .from(previousResolvedRequest.binding().bindings())
- .filter(new Predicate<Binding>() {
- @Override public boolean apply(Binding binding) {
- return binding instanceof ProvisionBinding
- && binding.implicitDependencies().contains(request);
- }
- }).toSet();
- return bindings;
+ return FluentIterable.from(previousResolvedRequest.binding().bindings())
+ .filter(Binding.Type.PROVISION)
+ .filter(
+ new Predicate<Binding>() {
+ @Override
+ public boolean apply(Binding binding) {
+ return binding.implicitDependencies().contains(request);
+ }
+ })
+ .toSet();
}
- private String formatBindingType(BindingType type) {
- switch(type) {
+ private String formatBindingType(ContributionType type) {
+ switch (type) {
case MAP:
return "Map";
case SET:
@@ -1099,30 +1109,18 @@ public class BindingGraphValidator {
}
}
- private String formatBinding(Binding binding) {
- // TODO(beder): Refactor the formatters so we don't need these instanceof checks.
- if (binding instanceof ProvisionBinding) {
- return provisionBindingFormatter.format((ProvisionBinding) binding);
- } else if (binding instanceof ProductionBinding) {
- return productionBindingFormatter.format((ProductionBinding) binding);
- } else {
- throw new IllegalArgumentException(
- "Expected either a ProvisionBinding or a ProductionBinding, not " + binding);
- }
- }
-
private String formatRootRequestKey(Deque<ResolvedRequest> path) {
return keyFormatter.format(path.peek().request().key());
}
private void appendBindings(
- StringBuilder builder, Collection<? extends Binding> bindings, int indentLevel) {
- for (Binding binding : Iterables.limit(bindings, DUPLICATE_SIZE_LIMIT)) {
+ StringBuilder builder, Collection<ContributionBinding> bindings, int indentLevel) {
+ for (ContributionBinding binding : Iterables.limit(bindings, DUPLICATE_SIZE_LIMIT)) {
builder.append('\n');
for (int i = 0; i < indentLevel; i++) {
builder.append(INDENT);
}
- builder.append(formatBinding(binding));
+ builder.append(contributionBindingFormatter.format(binding));
}
int numberOfOtherBindings = bindings.size() - DUPLICATE_SIZE_LIMIT;
if (numberOfOtherBindings > 0) {
@@ -1145,12 +1143,10 @@ public class BindingGraphValidator {
static ResolvedRequest create(DependencyRequest request, BindingGraph graph) {
BindingKey bindingKey = request.bindingKey();
ResolvedBindings resolvedBindings = graph.resolvedBindings().get(bindingKey);
- return new AutoValue_BindingGraphValidator_ResolvedRequest(request,
+ return new AutoValue_BindingGraphValidator_ResolvedRequest(
+ request,
resolvedBindings == null
- ? ResolvedBindings.create(bindingKey,
- graph.componentDescriptor(),
- ImmutableSet.<Binding>of(),
- ImmutableSetMultimap.<ComponentDescriptor, Binding>of())
+ ? ResolvedBindings.noBindings(bindingKey, graph.componentDescriptor())
: resolvedBindings);
}
}
@@ -1162,4 +1158,3 @@ public class BindingGraphValidator {
}
};
}
-
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java b/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java
index 7a5c13520..650fb9dcc 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ComponentDescriptor.java
@@ -19,6 +19,7 @@ import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -73,7 +74,7 @@ abstract class ComponentDescriptor {
enum Kind {
COMPONENT(Component.class, Component.Builder.class, true),
SUBCOMPONENT(Subcomponent.class, Subcomponent.Builder.class, false),
- PRODUCTION_COMPONENT(ProductionComponent.class, null, true);
+ PRODUCTION_COMPONENT(ProductionComponent.class, ProductionComponent.Builder.class, true);
private final Class<? extends Annotation> annotationType;
private final Class<? extends Annotation> builderType;
@@ -203,6 +204,18 @@ abstract class ComponentDescriptor {
abstract ComponentMethodKind kind();
abstract Optional<DependencyRequest> dependencyRequest();
abstract ExecutableElement methodElement();
+
+ /**
+ * A predicate that passes for {@link ComponentMethodDescriptor}s of a given kind.
+ */
+ static Predicate<ComponentMethodDescriptor> isOfKind(final ComponentMethodKind kind) {
+ return new Predicate<ComponentMethodDescriptor>() {
+ @Override
+ public boolean apply(ComponentMethodDescriptor descriptor) {
+ return kind.equals(descriptor.kind());
+ }
+ };
+ }
}
enum ComponentMethodKind {
@@ -212,7 +225,7 @@ abstract class ComponentDescriptor {
SUBCOMPONENT,
SUBCOMPONENT_BUILDER,
}
-
+
@AutoValue
static abstract class BuilderSpec {
abstract TypeElement builderDefinitionType();
@@ -284,6 +297,9 @@ abstract class ComponentDescriptor {
for (TypeMirror moduleIncludesType : getComponentModules(componentMirror)) {
modules.add(moduleDescriptorFactory.create(MoreTypes.asTypeElement(moduleIncludesType)));
}
+ if (kind.equals(Kind.PRODUCTION_COMPONENT)) {
+ modules.add(descriptorForMonitoringModule(componentDefinitionType));
+ }
ImmutableSet<ExecutableElement> unimplementedMethods =
Util.getUnimplementedMethods(elements, componentDefinitionType);
@@ -434,6 +450,23 @@ abstract class ComponentDescriptor {
return Optional.<BuilderSpec>of(new AutoValue_ComponentDescriptor_BuilderSpec(element,
map.build(), buildMethod, element.getEnclosingElement().asType()));
}
+
+ /**
+ * Returns a descriptor for a generated module that handles monitoring for production
+ * components. This module is generated in the {@link MonitoringModuleProcessingStep}.
+ *
+ * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
+ * processor to retry in a later processing round.
+ */
+ private ModuleDescriptor descriptorForMonitoringModule(TypeElement componentDefinitionType) {
+ String generatedMonitorModuleName =
+ SourceFiles.generatedMonitoringModuleName(componentDefinitionType).canonicalName();
+ TypeElement monitoringModule = elements.getTypeElement(generatedMonitorModuleName);
+ if (monitoringModule == null) {
+ throw new TypeNotPresentException(generatedMonitorModuleName, null);
+ }
+ return moduleDescriptorFactory.create(monitoringModule);
+ }
}
static boolean isComponentContributionMethod(Elements elements, ExecutableElement method) {
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java b/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java
index bb75e14bd..72e761cbf 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ComponentGenerator.java
@@ -74,16 +74,6 @@ final class ComponentGenerator extends SourceFileGenerator<BindingGraph> {
return Optional.of(input.componentDescriptor().componentDefinitionType());
}
- @AutoValue
- static abstract class ProxyClassAndField {
- abstract ClassWriter proxyWriter();
- abstract FieldWriter proxyFieldWriter();
-
- static ProxyClassAndField create(ClassWriter proxyWriter, FieldWriter proxyFieldWriter) {
- return new AutoValue_ComponentGenerator_ProxyClassAndField(proxyWriter, proxyFieldWriter);
- }
- }
-
@AutoValue static abstract class MemberSelect {
static MemberSelect instanceSelect(ClassName owningClass, Snippet snippet) {
return new AutoValue_ComponentGenerator_MemberSelect(
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java b/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java
index 1e96a534a..c35058ba3 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ComponentProcessor.java
@@ -80,10 +80,8 @@ public final class ComponentProcessor extends BasicAnnotationProcessor {
nullableValidationType(processingEnv).diagnosticKind().get();
MethodSignatureFormatter methodSignatureFormatter = new MethodSignatureFormatter(types);
- ProvisionBindingFormatter provisionBindingFormatter =
- new ProvisionBindingFormatter(methodSignatureFormatter);
- ProductionBindingFormatter productionBindingFormatter =
- new ProductionBindingFormatter(methodSignatureFormatter);
+ ContributionBindingFormatter contributionBindingFormatter =
+ new ContributionBindingFormatter(methodSignatureFormatter);
DependencyRequestFormatter dependencyRequestFormatter = new DependencyRequestFormatter(types);
KeyFormatter keyFormatter = new KeyFormatter();
@@ -122,6 +120,8 @@ public final class ComponentProcessor extends BasicAnnotationProcessor {
Produces.class);
ProducesMethodValidator producesMethodValidator = new ProducesMethodValidator(elements);
ProductionComponentValidator productionComponentValidator = new ProductionComponentValidator();
+ BuilderValidator productionComponentBuilderValidator =
+ new BuilderValidator(elements, types, ComponentDescriptor.Kind.PRODUCTION_COMPONENT);
Key.Factory keyFactory = new Key.Factory(types, elements);
@@ -133,8 +133,10 @@ public final class ComponentProcessor extends BasicAnnotationProcessor {
new ComponentGenerator(filer, elements, types, keyFactory, nullableDiagnosticType);
ProducerFactoryGenerator producerFactoryGenerator =
new ProducerFactoryGenerator(filer, DependencyRequestMapper.FOR_PRODUCER);
+ MonitoringModuleGenerator monitoringModuleGenerator = new MonitoringModuleGenerator(filer);
- DependencyRequest.Factory dependencyRequestFactory = new DependencyRequest.Factory(keyFactory);
+ DependencyRequest.Factory dependencyRequestFactory =
+ new DependencyRequest.Factory(elements, keyFactory);
ProvisionBinding.Factory provisionBindingFactory =
new ProvisionBinding.Factory(elements, types, keyFactory, dependencyRequestFactory);
ProductionBinding.Factory productionBindingFactory =
@@ -152,13 +154,13 @@ public final class ComponentProcessor extends BasicAnnotationProcessor {
ComponentDescriptor.Factory componentDescriptorFactory = new ComponentDescriptor.Factory(
elements, types, dependencyRequestFactory, moduleDescriptorFactory);
- BindingGraph.Factory bindingGraphFactory = new BindingGraph.Factory(
- elements,
- injectBindingRegistry,
- keyFactory,
- dependencyRequestFactory,
- provisionBindingFactory,
- productionBindingFactory);
+ BindingGraph.Factory bindingGraphFactory =
+ new BindingGraph.Factory(
+ elements,
+ injectBindingRegistry,
+ keyFactory,
+ provisionBindingFactory,
+ productionBindingFactory);
MapKeyGenerator mapKeyGenerator = new MapKeyGenerator(filer);
ComponentHierarchyValidator componentHierarchyValidator = new ComponentHierarchyValidator();
@@ -168,8 +170,7 @@ public final class ComponentProcessor extends BasicAnnotationProcessor {
injectBindingRegistry,
scopeValidationType(processingEnv),
nullableDiagnosticType,
- provisionBindingFormatter,
- productionBindingFormatter,
+ contributionBindingFormatter,
methodSignatureFormatter,
dependencyRequestFormatter,
keyFormatter);
@@ -184,6 +185,7 @@ public final class ComponentProcessor extends BasicAnnotationProcessor {
provisionBindingFactory,
membersInjectionBindingFactory,
injectBindingRegistry),
+ new MonitoringModuleProcessingStep(messager, monitoringModuleGenerator),
new ModuleProcessingStep(
messager,
moduleValidator,
@@ -210,6 +212,7 @@ public final class ComponentProcessor extends BasicAnnotationProcessor {
new ProductionComponentProcessingStep(
messager,
productionComponentValidator,
+ productionComponentBuilderValidator,
componentHierarchyValidator,
bindingGraphValidator,
componentDescriptorFactory,
diff --git a/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java b/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java
index 58fe1ca0c..a9e82a8cf 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ComponentValidator.java
@@ -18,17 +18,13 @@ package dagger.internal.codegen;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
-import com.google.common.base.Joiner;
import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
import dagger.Component;
import dagger.Module;
import dagger.Subcomponent;
@@ -54,7 +50,6 @@ import static com.google.auto.common.MoreElements.getAnnotationMirror;
import static dagger.internal.codegen.ConfigurationAnnotations.enclosedBuilders;
import static dagger.internal.codegen.ConfigurationAnnotations.getComponentModules;
import static dagger.internal.codegen.ConfigurationAnnotations.getTransitiveModules;
-import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
import static javax.lang.model.element.ElementKind.CLASS;
import static javax.lang.model.element.ElementKind.INTERFACE;
import static javax.lang.model.element.Modifier.ABSTRACT;
@@ -264,18 +259,10 @@ final class ComponentValidator {
// TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse
// subcomponents and their modules separately from how it is done in ComponentDescriptor and
// ModuleDescriptor
+ @SuppressWarnings("deprecation")
ImmutableSet<TypeElement> transitiveModules =
getTransitiveModules(types, elements, moduleTypes);
- ImmutableSet<TypeElement> requiredModules =
- FluentIterable.from(transitiveModules)
- .filter(new Predicate<TypeElement>() {
- @Override public boolean apply(TypeElement input) {
- return !componentCanMakeNewInstances(input);
- }
- })
- .toSet();
-
Set<TypeElement> variableTypes = Sets.newHashSet();
for (int i = 0; i < parameterTypes.size(); i++) {
@@ -320,18 +307,6 @@ final class ComponentValidator {
parameter);
}
}
-
- SetView<TypeElement> missingModules =
- Sets.difference(requiredModules, ImmutableSet.copyOf(variableTypes));
- if (!missingModules.isEmpty()) {
- builder.addError(
- String.format(
- "%s requires modules which have no visible default constructors. "
- + "Add the following modules as parameters to this method: %s",
- MoreTypes.asTypeElement(returnType).getQualifiedName(),
- Joiner.on(", ").join(missingModules)),
- method);
- }
}
private void validateSubcomponentBuilderMethod(ValidationReport.Builder<TypeElement> builder,
diff --git a/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java b/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java
index a0a4dc148..831943f2a 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ContributionBinding.java
@@ -20,15 +20,22 @@ import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Function;
import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Component;
import dagger.MapKey;
+import dagger.Provides;
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
import java.util.EnumSet;
import java.util.Set;
-import javax.inject.Provider;
+import javax.inject.Inject;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.TypeElement;
@@ -38,6 +45,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static dagger.internal.codegen.MapKeys.getMapKey;
import static dagger.internal.codegen.MapKeys.unwrapValue;
+import static javax.lang.model.element.Modifier.STATIC;
/**
* An abstract class for a value object representing the mechanism by which a {@link Key} can be
@@ -47,7 +55,19 @@ import static dagger.internal.codegen.MapKeys.unwrapValue;
* @since 2.0
*/
abstract class ContributionBinding extends Binding {
- static enum BindingType {
+
+ @Override
+ Set<DependencyRequest> implicitDependencies() {
+ // Optimization: If we don't need the memberInjectionRequest, don't create more objects.
+ if (!membersInjectionRequest().isPresent()) {
+ return dependencies();
+ } else {
+ // Optimization: Avoid creating an ImmutableSet+Builder just to union two things together.
+ return Sets.union(membersInjectionRequest().asSet(), dependencies());
+ }
+ }
+
+ static enum ContributionType {
/** Represents map bindings. */
MAP,
/** Represents set bindings. */
@@ -60,8 +80,20 @@ abstract class ContributionBinding extends Binding {
}
}
- abstract BindingType bindingType();
-
+ ContributionType contributionType() {
+ switch (provisionType()) {
+ case SET:
+ case SET_VALUES:
+ return ContributionType.SET;
+ case MAP:
+ return ContributionType.MAP;
+ case UNIQUE:
+ return ContributionType.UNIQUE;
+ default:
+ throw new AssertionError("Unknown provision type: " + provisionType());
+ }
+ }
+
/** Returns the type that specifies this' nullability, absent if not nullable. */
abstract Optional<DeclaredType> nullableType();
@@ -77,46 +109,142 @@ abstract class ContributionBinding extends Binding {
* Returns whether this binding is synthetic, i.e., not explicitly tied to code, but generated
* implicitly by the framework.
*/
- // TODO(user): Remove the SYNTHETIC enums from ProvisionBinding and ProductionBinding and make
- // this field the source of truth for synthetic bindings.
- abstract boolean isSyntheticBinding();
+ boolean isSyntheticBinding() {
+ return bindingKind().equals(Kind.SYNTHETIC);
+ }
+
+ /** If this provision requires members injection, this will be the corresponding request. */
+ abstract Optional<DependencyRequest> membersInjectionRequest();
/**
- * Returns the framework class associated with this binding, e.g., {@link Provider} for a
- * ProvisionBinding.
+ * The kind of contribution this binding represents. Defines which elements can specify this kind
+ * of contribution.
*/
- abstract Class<?> frameworkClass();
+ enum Kind {
+ /**
+ * A binding that is not explicitly tied to an element, but generated implicitly by the
+ * framework.
+ */
+ SYNTHETIC,
+
+ // Provision kinds
+
+ /** An {@link Inject}-annotated constructor. */
+ INJECTION,
+
+ /** A {@link Provides}-annotated method. */
+ PROVISION,
+
+ /** An implicit binding to a {@link Component @Component}-annotated type. */
+ COMPONENT,
+
+ /** A provision method on a component's {@linkplain Component#dependencies() dependency}. */
+ COMPONENT_PROVISION,
+
+ /**
+ * A subcomponent builder method on a component or subcomponent.
+ */
+ SUBCOMPONENT_BUILDER,
+
+ // Production kinds
+
+ /** A {@link Produces}-annotated method that doesn't return a {@link ListenableFuture}. */
+ IMMEDIATE,
+
+ /** A {@link Produces}-annotated method that returns a {@link ListenableFuture}. */
+ FUTURE_PRODUCTION,
+
+ /**
+ * A production method on a production component's
+ * {@linkplain ProductionComponent#dependencies() dependency} that returns a
+ * {@link ListenableFuture}. Methods on production component dependencies that don't return a
+ * {@link ListenableFuture} are considered {@linkplain #PROVISION provision bindings}.
+ */
+ COMPONENT_PRODUCTION,
+ }
+
+ /**
+ * The kind of this contribution binding.
+ */
+ protected abstract Kind bindingKind();
+
+ /**
+ * A predicate that passes for bindings of a given kind.
+ */
+ static Predicate<ContributionBinding> isOfKind(final Kind kind) {
+ return new Predicate<ContributionBinding>() {
+ @Override
+ public boolean apply(ContributionBinding binding) {
+ return binding.bindingKind().equals(kind);
+ }};
+ }
+
+ /** The provision type that was used to bind the key. */
+ abstract Provides.Type provisionType();
+
+ /**
+ * The strategy for getting an instance of a factory for a {@link ContributionBinding}.
+ */
+ enum FactoryCreationStrategy {
+ /** The factory class is an enum with one value named {@code INSTANCE}. */
+ ENUM_INSTANCE,
+ /** The factory must be created by calling the constructor. */
+ CLASS_CONSTRUCTOR,
+ }
+
+ /**
+ * Returns {@link FactoryCreationStrategy#ENUM_INSTANCE} if the binding has no dependencies and
+ * is a static provision binding or an {@link Inject @Inject} constructor binding. Otherwise
+ * returns {@link FactoryCreationStrategy#CLASS_CONSTRUCTOR}.
+ */
+ FactoryCreationStrategy factoryCreationStrategy() {
+ switch (bindingKind()) {
+ case PROVISION:
+ return implicitDependencies().isEmpty() && bindingElement().getModifiers().contains(STATIC)
+ ? FactoryCreationStrategy.ENUM_INSTANCE
+ : FactoryCreationStrategy.CLASS_CONSTRUCTOR;
+
+ case INJECTION:
+ return implicitDependencies().isEmpty()
+ ? FactoryCreationStrategy.ENUM_INSTANCE
+ : FactoryCreationStrategy.CLASS_CONSTRUCTOR;
+
+ default:
+ return FactoryCreationStrategy.CLASS_CONSTRUCTOR;
+ }
+ }
/**
- * Returns the set of {@link BindingType} enum values implied by a given
- * {@link ContributionBinding} collection.
+ * Returns the {@link ContributionType}s represented by a given {@link ContributionBinding}
+ * collection.
*/
- static <B extends ContributionBinding> ImmutableListMultimap<BindingType, B> bindingTypesFor(
- Iterable<? extends B> bindings) {
- ImmutableListMultimap.Builder<BindingType, B> builder =
- ImmutableListMultimap.builder();
- builder.orderKeysBy(Ordering.<BindingType>natural());
+ static <B extends ContributionBinding>
+ ImmutableListMultimap<ContributionType, B> contributionTypesFor(
+ Iterable<? extends B> bindings) {
+ ImmutableListMultimap.Builder<ContributionType, B> builder = ImmutableListMultimap.builder();
+ builder.orderKeysBy(Ordering.<ContributionType>natural());
for (B binding : bindings) {
- builder.put(binding.bindingType(), binding);
+ builder.put(binding.contributionType(), binding);
}
return builder.build();
}
/**
- * Returns a single {@code BindingsType} represented by a given collection of
- * {@code ContributionBindings} or throws an IllegalArgumentException if the given bindings
- * are not all of one type.
+ * Returns a single {@link ContributionType} represented by a given collection of
+ * {@link ContributionBinding}s.
+ *
+ * @throws IllegalArgumentException if the given bindings are not all of one type
*/
- static BindingType bindingTypeFor(Iterable<? extends ContributionBinding> bindings) {
+ static ContributionType contributionTypeFor(Iterable<ContributionBinding> bindings) {
checkNotNull(bindings);
checkArgument(!Iterables.isEmpty(bindings), "no bindings");
- Set<BindingType> types = EnumSet.noneOf(BindingType.class);
+ Set<ContributionType> types = EnumSet.noneOf(ContributionType.class);
for (ContributionBinding binding : bindings) {
- types.add(binding.bindingType());
+ types.add(binding.contributionType());
}
if (types.size() > 1) {
throw new IllegalArgumentException(
- String.format(ErrorMessages.MULTIPLE_BINDING_TYPES_FORMAT, types));
+ String.format(ErrorMessages.MULTIPLE_CONTRIBUTION_TYPES_FORMAT, types));
}
return Iterables.getOnlyElement(types);
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionBindingFormatter.java b/compiler/src/main/java/dagger/internal/codegen/ContributionBindingFormatter.java
index e7e7e778a..0d267619b 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ProductionBindingFormatter.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ContributionBindingFormatter.java
@@ -21,26 +21,32 @@ import static com.google.auto.common.MoreElements.asExecutable;
import static com.google.auto.common.MoreTypes.asDeclared;
/**
- * Formats a {@link ProductionBinding} into a {@link String} suitable for use in error messages.
+ * Formats a {@link ContributionBinding} into a {@link String} suitable for use in error messages.
*
- * @author Jesse Beder
+ * @author Christian Gruber
* @since 2.0
*/
-final class ProductionBindingFormatter extends Formatter<ProductionBinding> {
+final class ContributionBindingFormatter extends Formatter<ContributionBinding> {
private final MethodSignatureFormatter methodSignatureFormatter;
-
- ProductionBindingFormatter(MethodSignatureFormatter methodSignatureFormatter) {
+
+ ContributionBindingFormatter(MethodSignatureFormatter methodSignatureFormatter) {
this.methodSignatureFormatter = methodSignatureFormatter;
}
- @Override public String format(ProductionBinding binding) {
+ @Override public String format(ContributionBinding binding) {
switch (binding.bindingKind()) {
+ case COMPONENT_PROVISION:
+ case COMPONENT_PRODUCTION:
+ return methodSignatureFormatter.format(asExecutable(binding.bindingElement()));
+
+ case PROVISION:
+ case SUBCOMPONENT_BUILDER:
case IMMEDIATE:
case FUTURE_PRODUCTION:
- return methodSignatureFormatter.format(asExecutable(binding.bindingElement()),
+ return methodSignatureFormatter.format(
+ asExecutable(binding.bindingElement()),
Optional.of(asDeclared(binding.contributedBy().get().asType())));
- case COMPONENT_PRODUCTION:
- return methodSignatureFormatter.format(asExecutable(binding.bindingElement()));
+
default:
throw new UnsupportedOperationException(
"Not yet supporting " + binding.bindingKind() + " binding types.");
diff --git a/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java b/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java
index 5af9a82c5..49fcd9c9b 100644
--- a/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java
+++ b/compiler/src/main/java/dagger/internal/codegen/DependencyRequest.java
@@ -29,23 +29,27 @@ import dagger.MembersInjector;
import dagger.Provides;
import dagger.producers.Produced;
import dagger.producers.Producer;
+import dagger.producers.internal.AbstractProducer;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
import static com.google.auto.common.MoreTypes.isTypeOf;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
/**
* Represents a request for a key at an injection point. Parameters to {@link Inject} constructors
@@ -57,7 +61,7 @@ import static javax.lang.model.type.TypeKind.DECLARED;
// TODO(gak): Set bindings and the permutations thereof need to be addressed
@AutoValue
abstract class DependencyRequest {
- static Function<DependencyRequest, BindingKey> BINDING_KEY_FUNCTION =
+ static final Function<DependencyRequest, BindingKey> BINDING_KEY_FUNCTION =
new Function<DependencyRequest, BindingKey>() {
@Override public BindingKey apply(DependencyRequest request) {
return request.bindingKey();
@@ -115,15 +119,23 @@ abstract class DependencyRequest {
abstract boolean isNullable();
/**
+ * An optional name for this request when it's referred to in generated code. If absent, it will
+ * use a name derived from {@link #requestElement}.
+ */
+ abstract Optional<String> overriddenVariableName();
+
+ /**
* Factory for {@link DependencyRequest}s.
*
* <p>Any factory method may throw {@link TypeNotPresentException} if a type is not available,
* which may mean that the type will be generated in a later round of processing.
*/
static final class Factory {
+ private final Elements elements;
private final Key.Factory keyFactory;
- Factory(Key.Factory keyFactory) {
+ Factory(Elements elements, Key.Factory keyFactory) {
+ this.elements = elements;
this.keyFactory = keyFactory;
}
@@ -140,33 +152,46 @@ abstract class DependencyRequest {
ImmutableSet<DependencyRequest> forRequiredVariables(
List<? extends VariableElement> variables) {
return FluentIterable.from(variables)
- .transform(new Function<VariableElement, DependencyRequest>() {
- @Override public DependencyRequest apply(VariableElement input) {
- return forRequiredVariable(input);
- }
- })
+ .transform(
+ new Function<VariableElement, DependencyRequest>() {
+ @Override
+ public DependencyRequest apply(VariableElement input) {
+ return forRequiredVariable(input);
+ }
+ })
.toSet();
}
/**
- * Creates a DependencyRequest for implictMapBinding, this request's key will be
- * {@code Map<K, Provider<V>>}, this DependencyRequest is depended by the DependencyRequest
- * whose key is {@code Map<K, V>}
+ * Creates a implicit {@link DependencyRequest} for {@code mapOfFactoryKey}, which will be used
+ * to satisfy the {@code mapOfValueRequest}.
+ *
+ * @param mapOfValueRequest a request for {@code Map<K, V>}
+ * @param mapOfFactoryKey a key equivalent to {@code mapOfValueRequest}'s key, whose type is
+ * {@code Map<K, Provider<V>>} or {@code Map<K, Producer<V>>}
*/
- DependencyRequest forImplicitMapBinding(DependencyRequest delegatingRequest, Key delegateKey) {
- checkNotNull(delegatingRequest);
- return new AutoValue_DependencyRequest(Kind.PROVIDER, delegateKey,
- delegatingRequest.requestElement(),
- delegatingRequest.enclosingType(),
- false /* doesn't allow null */);
+ DependencyRequest forImplicitMapBinding(
+ DependencyRequest mapOfValueRequest, Key mapOfFactoryKey) {
+ checkNotNull(mapOfValueRequest);
+ return new AutoValue_DependencyRequest(
+ Kind.PROVIDER,
+ mapOfFactoryKey,
+ mapOfValueRequest.requestElement(),
+ mapOfValueRequest.enclosingType(),
+ false /* doesn't allow null */,
+ Optional.<String>absent());
}
DependencyRequest forRequiredVariable(VariableElement variableElement) {
+ return forRequiredVariable(variableElement, Optional.<String>absent());
+ }
+
+ DependencyRequest forRequiredVariable(VariableElement variableElement, Optional<String> name) {
checkNotNull(variableElement);
TypeMirror type = variableElement.asType();
Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement);
- return newDependencyRequest(variableElement, type, qualifier,
- getEnclosingType(variableElement));
+ return newDependencyRequest(
+ variableElement, type, qualifier, getEnclosingType(variableElement), name);
}
DependencyRequest forRequiredResolvedVariable(DeclaredType container,
@@ -175,18 +200,25 @@ abstract class DependencyRequest {
checkNotNull(variableElement);
checkNotNull(resolvedType);
Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement);
- return newDependencyRequest(variableElement, resolvedType, qualifier, container);
+ return newDependencyRequest(
+ variableElement, resolvedType, qualifier, container, Optional.<String>absent());
}
DependencyRequest forComponentProvisionMethod(ExecutableElement provisionMethod,
ExecutableType provisionMethodType) {
checkNotNull(provisionMethod);
checkNotNull(provisionMethodType);
- checkArgument(provisionMethod.getParameters().isEmpty(),
- "Component provision methods must be empty: " + provisionMethod);
+ checkArgument(
+ provisionMethod.getParameters().isEmpty(),
+ "Component provision methods must be empty: %s",
+ provisionMethod);
Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(provisionMethod);
- return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier,
- getEnclosingType(provisionMethod));
+ return newDependencyRequest(
+ provisionMethod,
+ provisionMethodType.getReturnType(),
+ qualifier,
+ getEnclosingType(provisionMethod),
+ Optional.<String>absent());
}
DependencyRequest forComponentProductionMethod(ExecutableElement productionMethod,
@@ -203,13 +235,15 @@ abstract class DependencyRequest {
if (isTypeOf(ListenableFuture.class, type)) {
return new AutoValue_DependencyRequest(
Kind.FUTURE,
- keyFactory.forQualifiedType(qualifier,
- Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())),
+ keyFactory.forQualifiedType(
+ qualifier, Iterables.getOnlyElement(((DeclaredType) type).getTypeArguments())),
productionMethod,
container,
- false /* doesn't allow null */);
+ false /* doesn't allow null */,
+ Optional.<String>absent());
} else {
- return newDependencyRequest(productionMethod, type, qualifier, container);
+ return newDependencyRequest(
+ productionMethod, type, qualifier, container, Optional.<String>absent());
}
}
@@ -223,32 +257,53 @@ abstract class DependencyRequest {
TypeMirror returnType = membersInjectionMethodType.getReturnType();
if (returnType.getKind().equals(DECLARED)
&& MoreTypes.isTypeOf(MembersInjector.class, returnType)) {
- return new AutoValue_DependencyRequest(Kind.MEMBERS_INJECTOR,
+ return new AutoValue_DependencyRequest(
+ Kind.MEMBERS_INJECTOR,
keyFactory.forMembersInjectedType(
Iterables.getOnlyElement(((DeclaredType) returnType).getTypeArguments())),
- membersInjectionMethod,
- getEnclosingType(membersInjectionMethod),
- false /* doesn't allow null */);
+ membersInjectionMethod,
+ getEnclosingType(membersInjectionMethod),
+ false /* doesn't allow null */,
+ Optional.<String>absent());
} else {
- return new AutoValue_DependencyRequest(Kind.MEMBERS_INJECTOR,
+ return new AutoValue_DependencyRequest(
+ Kind.MEMBERS_INJECTOR,
keyFactory.forMembersInjectedType(
Iterables.getOnlyElement(membersInjectionMethodType.getParameterTypes())),
- membersInjectionMethod,
- getEnclosingType(membersInjectionMethod),
- false /* doesn't allow null */);
+ membersInjectionMethod,
+ getEnclosingType(membersInjectionMethod),
+ false /* doesn't allow null */,
+ Optional.<String>absent());
}
}
DependencyRequest forMembersInjectedType(DeclaredType type) {
- return new AutoValue_DependencyRequest(Kind.MEMBERS_INJECTOR,
+ return new AutoValue_DependencyRequest(
+ Kind.MEMBERS_INJECTOR,
keyFactory.forMembersInjectedType(type),
type.asElement(),
type,
- false /* doesn't allow null */);
+ false /* doesn't allow null */,
+ Optional.<String>absent());
+ }
+
+ DependencyRequest forProductionComponentMonitorProvider() {
+ TypeElement element = elements.getTypeElement(AbstractProducer.class.getCanonicalName());
+ for (ExecutableElement constructor : constructorsIn(element.getEnclosedElements())) {
+ if (constructor.getParameters().size() == 2) {
+ // the 2-arg constructor has the appropriate dependency as its first arg
+ return forRequiredVariable(constructor.getParameters().get(0), Optional.of("monitor"));
+ }
+ }
+ throw new AssertionError("expected 2-arg constructor in AbstractProducer");
}
- private DependencyRequest newDependencyRequest(Element requestElement,
- TypeMirror type, Optional<AnnotationMirror> qualifier, DeclaredType container) {
+ private DependencyRequest newDependencyRequest(
+ Element requestElement,
+ TypeMirror type,
+ Optional<AnnotationMirror> qualifier,
+ DeclaredType container,
+ Optional<String> name) {
KindAndType kindAndType = extractKindAndType(type);
if (kindAndType.kind().equals(Kind.MEMBERS_INJECTOR)) {
checkArgument(!qualifier.isPresent());
@@ -258,11 +313,13 @@ abstract class DependencyRequest {
// TODO(sameb): should Produced/Producer always require non-nullable?
boolean allowsNull = !kindAndType.kind().equals(Kind.INSTANCE)
|| ConfigurationAnnotations.getNullableType(requestElement).isPresent();
- return new AutoValue_DependencyRequest(kindAndType.kind(),
+ return new AutoValue_DependencyRequest(
+ kindAndType.kind(),
keyFactory.forQualifiedType(qualifier, kindAndType.type()),
requestElement,
container,
- allowsNull);
+ allowsNull,
+ name);
}
@AutoValue
diff --git a/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java b/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java
index 1643adbc4..5ba5635cd 100644
--- a/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java
+++ b/compiler/src/main/java/dagger/internal/codegen/DependencyVariableNamer.java
@@ -33,6 +33,9 @@ import javax.inject.Provider;
final class DependencyVariableNamer implements Function<DependencyRequest, String> {
@Override
public String apply(DependencyRequest dependency) {
+ if (dependency.overriddenVariableName().isPresent()) {
+ return dependency.overriddenVariableName().get();
+ }
String variableName = dependency.requestElement().getSimpleName().toString();
switch (dependency.kind()) {
case INSTANCE:
diff --git a/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java b/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java
index c025eb48a..1f52aca98 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ErrorMessages.java
@@ -185,7 +185,7 @@ final class ErrorMessages {
"Map key annotations with unwrapped values cannot use arrays";
/* collection binding errors */
- static final String MULTIPLE_BINDING_TYPES_FORMAT =
+ static final String MULTIPLE_CONTRIBUTION_TYPES_FORMAT =
"More than one binding present of different types %s";
static final String MULTIPLE_BINDING_TYPES_FOR_KEY_FORMAT =
@@ -224,8 +224,12 @@ final class ErrorMessages {
static final String MALFORMED_MODULE_METHOD_FORMAT =
"Cannot generated a graph because method %s on module %s was malformed";
- static final String NULLABLE_TO_NON_NULLABLE =
- "%s is not nullable, but is being provided by %s";
+ static String nullableToNonNullable(String typeName, String bindingString) {
+ return String.format(
+ "%s is not nullable, but is being provided by %s",
+ typeName,
+ bindingString);
+ }
static final String CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD =
"Cannot return null from a non-@Nullable component method";
@@ -239,6 +243,8 @@ final class ErrorMessages {
return ComponentBuilderMessages.INSTANCE;
case SUBCOMPONENT:
return SubcomponentBuilderMessages.INSTANCE;
+ case PRODUCTION_COMPONENT:
+ return ProductionComponentBuilderMessages.INSTANCE;
default:
throw new IllegalStateException(kind.toString());
}
@@ -370,6 +376,17 @@ final class ErrorMessages {
}
}
+ static final class ProductionComponentBuilderMessages extends ComponentBuilderMessages {
+ @SuppressWarnings("hiding")
+ static final ProductionComponentBuilderMessages INSTANCE =
+ new ProductionComponentBuilderMessages();
+
+ @Override protected String process(String s) {
+ return s.replaceAll("component", "production component")
+ .replaceAll("Component", "ProductionComponent");
+ }
+ }
+
/**
* A regular expression to match a small list of specific packages deemed to
* be unhelpful to display in fully qualified types in error messages.
diff --git a/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java b/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java
index c7eb6326f..a0a48c81e 100644
--- a/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/FactoryGenerator.java
@@ -52,11 +52,11 @@ import javax.tools.Diagnostic;
import static com.google.common.base.Preconditions.checkState;
import static dagger.Provides.Type.SET;
+import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
import static dagger.internal.codegen.ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD;
-import static dagger.internal.codegen.ProvisionBinding.Kind.PROVISION;
-import static dagger.internal.codegen.SourceFiles.factoryNameForProvisionBinding;
import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
-import static dagger.internal.codegen.SourceFiles.parameterizedFactoryNameForProvisionBinding;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
@@ -83,7 +83,7 @@ final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
@Override
ClassName nameGeneratedType(ProvisionBinding binding) {
- return factoryNameForProvisionBinding(binding);
+ return generatedClassNameForBinding(binding);
}
@Override
@@ -156,7 +156,7 @@ final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
getMethodWriter.annotate(Override.class);
getMethodWriter.addModifiers(PUBLIC);
- if (binding.memberInjectionRequest().isPresent()) {
+ if (binding.membersInjectionRequest().isPresent()) {
ParameterizedTypeName membersInjectorType = ParameterizedTypeName.create(
MembersInjector.class, providedTypeName);
factoryWriter.addField(membersInjectorType, "membersInjector").addModifiers(PRIVATE, FINAL);
@@ -211,7 +211,7 @@ final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
break;
case CLASS_CONSTRUCTOR:
createMethodWriter.body().addSnippet("return new %s(%s);",
- parameterizedFactoryNameForProvisionBinding(binding),
+ parameterizedGeneratedTypeNameForBinding(binding),
Joiner.on(", ").join(params.keySet()));
break;
default:
@@ -262,7 +262,7 @@ final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
providesMethodInvocation,
failMsg));
}
- } else if (binding.memberInjectionRequest().isPresent()) {
+ } else if (binding.membersInjectionRequest().isPresent()) {
getMethodWriter.body().addSnippet("%1$s instance = new %1$s(%2$s);",
providedTypeName, parametersSnippet);
getMethodWriter.body().addSnippet("membersInjector.injectMembers(instance);");
diff --git a/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java b/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java
index b92cef796..38e8f026a 100644
--- a/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java
+++ b/compiler/src/main/java/dagger/internal/codegen/FrameworkField.java
@@ -19,20 +19,20 @@ import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import dagger.MembersInjector;
-import dagger.internal.codegen.ContributionBinding.BindingType;
import dagger.internal.codegen.writer.ClassName;
import dagger.internal.codegen.writer.ParameterizedTypeName;
import dagger.internal.codegen.writer.TypeNames;
-import dagger.producers.Producer;
-import javax.inject.Provider;
import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementKindVisitor6;
+import static com.google.common.collect.Iterables.any;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ContributionBinding.contributionTypeFor;
+
/**
* A value object that represents a field used by Dagger-generated code.
*
@@ -64,26 +64,24 @@ abstract class FrameworkField {
}
static FrameworkField createForSyntheticContributionBinding(
- BindingKey bindingKey, int contributionNumber, ContributionBinding contributionBinding) {
- switch (contributionBinding.bindingType()) {
+ int contributionNumber, ContributionBinding contributionBinding) {
+ switch (contributionBinding.contributionType()) {
case MAP:
return createForMapBindingContribution(
contributionBinding.frameworkClass(),
- BindingKey.create(bindingKey.kind(), contributionBinding.key()),
- KeyVariableNamer.INSTANCE.apply(bindingKey.key())
- + "Contribution" + contributionNumber);
+ contributionBinding.bindingKey(),
+ KeyVariableNamer.INSTANCE.apply(contributionBinding.key())
+ + "Contribution"
+ + contributionNumber);
+
case SET:
- return createWithTypeFromKey(
- contributionBinding.frameworkClass(),
- bindingKey,
- KeyVariableNamer.INSTANCE.apply(bindingKey.key())
- + "Contribution" + contributionNumber);
case UNIQUE:
return createWithTypeFromKey(
contributionBinding.frameworkClass(),
- bindingKey,
- KeyVariableNamer.INSTANCE.apply(bindingKey.key())
- + "Contribution" + contributionNumber);
+ contributionBinding.bindingKey(),
+ KeyVariableNamer.INSTANCE.apply(contributionBinding.key())
+ + "Contribution"
+ + contributionNumber);
default:
throw new AssertionError();
}
@@ -93,10 +91,9 @@ abstract class FrameworkField {
BindingKey bindingKey = resolvedBindings.bindingKey();
switch (bindingKey.kind()) {
case CONTRIBUTION:
- ImmutableSet<? extends ContributionBinding> contributionBindings =
+ ImmutableSet<ContributionBinding> contributionBindings =
resolvedBindings.contributionBindings();
- BindingType bindingsType = ProvisionBinding.bindingTypeFor(contributionBindings);
- switch (bindingsType) {
+ switch (contributionTypeFor(contributionBindings)) {
case SET:
case MAP:
return createWithTypeFromKey(
@@ -104,7 +101,7 @@ abstract class FrameworkField {
bindingKey,
KeyVariableNamer.INSTANCE.apply(bindingKey.key()));
case UNIQUE:
- ContributionBinding binding = Iterables.getOnlyElement(contributionBindings);
+ ContributionBinding binding = getOnlyElement(contributionBindings);
return createWithTypeFromKey(
FrameworkField.frameworkClassForResolvedBindings(resolvedBindings),
bindingKey,
@@ -118,7 +115,9 @@ abstract class FrameworkField {
bindingKey,
CaseFormat.UPPER_CAMEL.to(
CaseFormat.LOWER_CAMEL,
- Iterables.getOnlyElement(resolvedBindings.bindings())
+ resolvedBindings
+ .membersInjectionBinding()
+ .get()
.bindingElement()
.getSimpleName()
.toString()));
@@ -149,12 +148,9 @@ abstract class FrameworkField {
static Class<?> frameworkClassForResolvedBindings(ResolvedBindings resolvedBindings) {
switch (resolvedBindings.bindingKey().kind()) {
case CONTRIBUTION:
- for (ContributionBinding binding : resolvedBindings.contributionBindings()) {
- if (binding instanceof ProductionBinding) {
- return Producer.class;
- }
- }
- return Provider.class;
+ return any(resolvedBindings.contributionBindings(), Binding.Type.PRODUCTION)
+ ? Binding.Type.PRODUCTION.frameworkClass()
+ : Binding.Type.PROVISION.frameworkClass();
case MEMBERS_INJECTION:
return MembersInjector.class;
default:
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java b/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java
index b8e302e9a..f7ca429f3 100644
--- a/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java
+++ b/compiler/src/main/java/dagger/internal/codegen/InjectBindingRegistry.java
@@ -45,7 +45,7 @@ import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
/**
* Maintains the collection of provision bindings from {@link Inject} constructors and members
@@ -85,15 +85,6 @@ final class InjectBindingRegistry {
return bindingsByKey.get(key);
}
- /** Caches the binding and pretends a binding is generated without actually generating it. */
- B pretendBindingGenerated(B binding, ClassName factoryName) {
- tryToCacheBinding(binding);
- if (shouldGenerateBinding(binding, factoryName)) {
- materializedBindingKeys.add(binding.key());
- }
- return binding;
- }
-
/** Caches the binding and generates it if it needs generation. */
void tryRegisterBinding(B binding, ClassName factoryName, boolean explicit) {
tryToCacheBinding(binding);
@@ -180,7 +171,7 @@ final class InjectBindingRegistry {
* attempt to register an unresolved version of it.
*/
private ProvisionBinding registerBinding(ProvisionBinding binding, boolean explicit) {
- ClassName factoryName = SourceFiles.factoryNameForProvisionBinding(binding);
+ ClassName factoryName = generatedClassNameForBinding(binding);
provisionBindings.tryRegisterBinding(binding, factoryName, explicit);
if (binding.hasNonDefaultTypeParameters()) {
provisionBindings.tryToGenerateBinding(provisionBindingFactory.unresolve(binding),
@@ -195,21 +186,11 @@ final class InjectBindingRegistry {
*/
private MembersInjectionBinding registerBinding(
MembersInjectionBinding binding, boolean explicit) {
- ClassName membersInjectorName = membersInjectorNameForType(binding.bindingTypeElement());
- if (binding.injectionSites().isEmpty()) {
- // empty members injection bindings are special and don't need source files.
- // so, we just pretend
- membersInjectionBindings.pretendBindingGenerated(binding, membersInjectorName);
- if (binding.hasNonDefaultTypeParameters()) {
- membersInjectionBindings.pretendBindingGenerated(
- membersInjectionBindingFactory.unresolve(binding), membersInjectorName);
- }
- } else {
- membersInjectionBindings.tryRegisterBinding(binding, membersInjectorName, explicit);
- if (binding.hasNonDefaultTypeParameters()) {
- membersInjectionBindings.tryToGenerateBinding(
- membersInjectionBindingFactory.unresolve(binding), membersInjectorName, explicit);
- }
+ ClassName membersInjectorName = generatedClassNameForBinding(binding);
+ membersInjectionBindings.tryRegisterBinding(binding, membersInjectorName, explicit);
+ if (binding.hasNonDefaultTypeParameters()) {
+ membersInjectionBindings.tryToGenerateBinding(
+ membersInjectionBindingFactory.unresolve(binding), membersInjectorName, explicit);
}
return binding;
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java b/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java
index 91143de6e..e30678af7 100644
--- a/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/InjectFieldValidator.java
@@ -16,7 +16,6 @@
package dagger.internal.codegen;
import com.google.common.collect.ImmutableSet;
-
import java.util.Set;
import javax.inject.Inject;
import javax.lang.model.element.AnnotationMirror;
diff --git a/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java
index 61b19c96c..dac904fdc 100644
--- a/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java
+++ b/compiler/src/main/java/dagger/internal/codegen/InjectProcessingStep.java
@@ -20,8 +20,10 @@ import com.google.auto.common.MoreTypes;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
+
import java.lang.annotation.Annotation;
import java.util.Set;
+
import javax.annotation.processing.Messager;
import javax.inject.Inject;
import javax.lang.model.element.Element;
@@ -73,64 +75,71 @@ final class InjectProcessingStep implements BasicAnnotationProcessor.ProcessingS
@Override
public Set<Element> process(
SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
// TODO(gak): add some error handling for bad source files
final ImmutableSet.Builder<ProvisionBinding> provisions = ImmutableSet.builder();
// TODO(gak): instead, we should collect reports by type and check later
final ImmutableSet.Builder<DeclaredType> membersInjectedTypes = ImmutableSet.builder();
for (Element injectElement : elementsByAnnotation.get(Inject.class)) {
- injectElement.accept(
- new ElementKindVisitor6<Void, Void>() {
- @Override
- public Void visitExecutableAsConstructor(ExecutableElement constructorElement, Void v) {
- ValidationReport<TypeElement> report =
- constructorValidator.validate(constructorElement);
-
- report.printMessagesTo(messager);
-
- if (report.isClean()) {
- provisions.add(
- provisionBindingFactory.forInjectConstructor(
- constructorElement, Optional.<TypeMirror>absent()));
- DeclaredType type =
- MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
- if (membersInjectionBindingFactory.hasInjectedMembers(type)) {
- membersInjectedTypes.add(type);
+ try {
+ injectElement.accept(
+ new ElementKindVisitor6<Void, Void>() {
+ @Override
+ public Void visitExecutableAsConstructor(
+ ExecutableElement constructorElement, Void v) {
+ ValidationReport<TypeElement> report =
+ constructorValidator.validate(constructorElement);
+
+ report.printMessagesTo(messager);
+
+ if (report.isClean()) {
+ provisions.add(
+ provisionBindingFactory.forInjectConstructor(
+ constructorElement, Optional.<TypeMirror>absent()));
+ DeclaredType type =
+ MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
+ if (membersInjectionBindingFactory.hasInjectedMembers(type)) {
+ membersInjectedTypes.add(type);
+ }
}
+
+ return null;
}
- return null;
- }
+ @Override
+ public Void visitVariableAsField(VariableElement fieldElement, Void p) {
+ ValidationReport<VariableElement> report = fieldValidator.validate(fieldElement);
- @Override
- public Void visitVariableAsField(VariableElement fieldElement, Void p) {
- ValidationReport<VariableElement> report = fieldValidator.validate(fieldElement);
+ report.printMessagesTo(messager);
- report.printMessagesTo(messager);
+ if (report.isClean()) {
+ membersInjectedTypes.add(
+ MoreTypes.asDeclared(fieldElement.getEnclosingElement().asType()));
+ }
- if (report.isClean()) {
- membersInjectedTypes.add(
- MoreTypes.asDeclared(fieldElement.getEnclosingElement().asType()));
+ return null;
}
- return null;
- }
+ @Override
+ public Void visitExecutableAsMethod(ExecutableElement methodElement, Void p) {
+ ValidationReport<ExecutableElement> report =
+ methodValidator.validate(methodElement);
- @Override
- public Void visitExecutableAsMethod(ExecutableElement methodElement, Void p) {
- ValidationReport<ExecutableElement> report = methodValidator.validate(methodElement);
+ report.printMessagesTo(messager);
- report.printMessagesTo(messager);
+ if (report.isClean()) {
+ membersInjectedTypes.add(
+ MoreTypes.asDeclared(methodElement.getEnclosingElement().asType()));
+ }
- if (report.isClean()) {
- membersInjectedTypes.add(
- MoreTypes.asDeclared(methodElement.getEnclosingElement().asType()));
+ return null;
}
-
- return null;
- }
- },
- null);
+ },
+ null);
+ } catch (TypeNotPresentException e) {
+ rejectedElements.add(injectElement);
+ }
}
for (DeclaredType injectedType : membersInjectedTypes.build()) {
@@ -141,6 +150,6 @@ final class InjectProcessingStep implements BasicAnnotationProcessor.ProcessingS
for (ProvisionBinding binding : provisions.build()) {
injectBindingRegistry.registerBinding(binding);
}
- return ImmutableSet.of();
+ return rejectedElements.build();
}
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/Key.java b/compiler/src/main/java/dagger/internal/codegen/Key.java
index c14cc22c4..f0bd3a04f 100644
--- a/compiler/src/main/java/dagger/internal/codegen/Key.java
+++ b/compiler/src/main/java/dagger/internal/codegen/Key.java
@@ -25,6 +25,7 @@ import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.Provides;
+import dagger.producers.Produced;
import dagger.producers.Producer;
import dagger.producers.Produces;
import java.util.Map;
@@ -45,6 +46,7 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.Types;
+import static com.google.auto.common.MoreTypes.asExecutable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
@@ -200,6 +202,16 @@ abstract class Key {
return forMethod(componentMethod, keyType);
}
+ Key forSubcomponentBuilderMethod(
+ ExecutableElement subcomponentBuilderMethod, DeclaredType declaredContainer) {
+ checkNotNull(subcomponentBuilderMethod);
+ checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
+ ExecutableType resolvedMethod =
+ asExecutable(types.asMemberOf(declaredContainer, subcomponentBuilderMethod));
+ TypeMirror returnType = normalize(types, resolvedMethod.getReturnType());
+ return forMethod(subcomponentBuilderMethod, returnType);
+ }
+
Key forProvidesMethod(ExecutableType executableType, ExecutableElement method) {
checkNotNull(method);
checkArgument(method.getKind().equals(METHOD));
@@ -334,7 +346,7 @@ abstract class Key {
DeclaredType declaredMapType = MoreTypes.asDeclared(possibleMapKey.type());
TypeMirror mapValueType = Util.getValueTypeOfMap(declaredMapType);
if (!MoreTypes.isTypeOf(wrappingClass, mapValueType)) {
- DeclaredType keyType = Util.getKeyTypeOfMap(declaredMapType);
+ TypeMirror keyType = Util.getKeyTypeOfMap(declaredMapType);
TypeElement wrappingElement = getClassElement(wrappingClass);
if (wrappingElement == null) {
// This target might not be compiled with Producers, so wrappingClass might not have an
@@ -350,5 +362,22 @@ abstract class Key {
}
return Optional.absent();
}
+
+ /**
+ * Optionally extract a {@link Key} for a {@code Set<T>} if the given key is for
+ * {@code Set<Produced<T>>}.
+ */
+ Optional<Key> implicitSetKeyFromProduced(Key possibleSetOfProducedKey) {
+ if (MoreTypes.isTypeOf(Set.class, possibleSetOfProducedKey.type())) {
+ TypeMirror argType =
+ MoreTypes.asDeclared(possibleSetOfProducedKey.type()).getTypeArguments().get(0);
+ if (MoreTypes.isTypeOf(Produced.class, argType)) {
+ TypeMirror producedArgType = MoreTypes.asDeclared(argType).getTypeArguments().get(0);
+ TypeMirror setType = types.getDeclaredType(getSetElement(), producedArgType);
+ return Optional.of(possibleSetOfProducedKey.withType(types, setType));
+ }
+ }
+ return Optional.absent();
+ }
}
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java b/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java
index d2e62fcab..6e695f33a 100644
--- a/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java
+++ b/compiler/src/main/java/dagger/internal/codegen/KeyFormatter.java
@@ -26,7 +26,7 @@ final class KeyFormatter extends Formatter<Key> {
@Override public String format(Key request) {
StringBuilder builder = new StringBuilder();
if (request.qualifier().isPresent()) {
- builder.append(request.qualifier()); // TODO(cgruber): Use AnnotationMirrorFormatter.
+ builder.append(request.qualifier().get());
builder.append(' ');
}
builder.append(request.type()); // TODO(cgruber): Use TypeMirrorFormatter.
diff --git a/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java b/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java
index d6aa2f21c..586a1e935 100644
--- a/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/MapKeyValidator.java
@@ -28,7 +28,7 @@ import static dagger.internal.codegen.ErrorMessages.UNWRAPPED_MAP_KEY_WITH_TOO_M
import static javax.lang.model.util.ElementFilter.methodsIn;
/**
- * A {@link Validator} for {@link MapKey} annotations.
+ * A validator for {@link MapKey} annotations.
*
* @author Chenying Hou
* @since 2.0
diff --git a/compiler/src/main/java/dagger/internal/codegen/MapKeys.java b/compiler/src/main/java/dagger/internal/codegen/MapKeys.java
index 4d79a28c8..fbbd8cf82 100644
--- a/compiler/src/main/java/dagger/internal/codegen/MapKeys.java
+++ b/compiler/src/main/java/dagger/internal/codegen/MapKeys.java
@@ -20,7 +20,6 @@ import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import dagger.MapKey;
import dagger.internal.codegen.writer.ClassName;
import dagger.internal.codegen.writer.Snippet;
diff --git a/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java b/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java
index 87b2ab187..7fbcf115d 100644
--- a/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java
+++ b/compiler/src/main/java/dagger/internal/codegen/MembersInjectionBinding.java
@@ -89,6 +89,11 @@ abstract class MembersInjectionBinding extends Binding {
Optional.<DependencyRequest>absent());
}
+ @Override
+ protected Binding.Type bindingType() {
+ return Binding.Type.MEMBERS_INJECTION;
+ }
+
@AutoValue
abstract static class InjectionSite {
enum Kind {
diff --git a/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java b/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java
index f7fcdee9d..3694d2e81 100644
--- a/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/MembersInjectorGenerator.java
@@ -62,7 +62,7 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.SourceFiles.parameterizedMembersInjectorNameForMembersInjectionBinding;
+import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
@@ -109,6 +109,10 @@ final class MembersInjectorGenerator extends SourceFileGenerator<MembersInjectio
@Override
ImmutableSet<JavaWriter> write(ClassName generatedTypeName, MembersInjectionBinding binding) {
+ // Empty members injection bindings are special and don't need source files.
+ if (binding.injectionSites().isEmpty()) {
+ return ImmutableSet.of();
+ }
Set<String> delegateMethods = new HashSet<>();
// We don't want to write out resolved bindings -- we want to write out the generic version.
@@ -197,7 +201,7 @@ final class MembersInjectorGenerator extends SourceFileGenerator<MembersInjectio
.body()
.addSnippet(
" return new %s(%s);",
- parameterizedMembersInjectorNameForMembersInjectionBinding(binding),
+ parameterizedGeneratedTypeNameForBinding(binding),
Joiner.on(", ").join(constructorWriter.parameters().keySet()));
ImmutableMap<BindingKey, FieldWriter> dependencyFields = dependencyFieldsBuilder.build();
diff --git a/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java b/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java
index dcabab52d..4b6f0c33c 100644
--- a/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java
+++ b/compiler/src/main/java/dagger/internal/codegen/MissingBindingSuggestions.java
@@ -15,9 +15,7 @@
*/
package dagger.internal.codegen;
-import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
-
import java.util.ArrayDeque;
import java.util.Deque;
diff --git a/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java b/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java
index f5e33b59d..c938af2c3 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ModuleDescriptor.java
@@ -42,7 +42,7 @@ abstract class ModuleDescriptor {
abstract ImmutableSet<ModuleDescriptor> includedModules();
- abstract ImmutableSet<? extends ContributionBinding> bindings();
+ abstract ImmutableSet<ContributionBinding> bindings();
enum DefaultCreationStrategy {
PASSED,
diff --git a/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleGenerator.java b/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleGenerator.java
new file mode 100644
index 000000000..a4e020b32
--- /dev/null
+++ b/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleGenerator.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.internal.codegen;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.writer.ClassName;
+import dagger.internal.codegen.writer.ClassWriter;
+import dagger.internal.codegen.writer.FieldWriter;
+import dagger.internal.codegen.writer.JavaWriter;
+import dagger.internal.codegen.writer.MethodWriter;
+import dagger.internal.codegen.writer.ParameterizedTypeName;
+import dagger.internal.codegen.writer.TypeName;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import dagger.producers.monitoring.internal.MonitorCache;
+
+import java.util.Set;
+import javax.annotation.Generated;
+import javax.annotation.processing.Filer;
+import javax.inject.Provider;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.element.Modifier.FINAL;
+
+/** Generates a monitoring module for use with production components. */
+final class MonitoringModuleGenerator extends SourceFileGenerator<TypeElement> {
+ private static final TypeName SET_OF_FACTORIES =
+ ParameterizedTypeName.create(
+ Set.class, ClassName.fromClass(ProductionComponentMonitor.Factory.class));
+
+ MonitoringModuleGenerator(Filer filer) {
+ super(filer);
+ }
+
+ @Override
+ ClassName nameGeneratedType(TypeElement componentElement) {
+ return SourceFiles.generatedMonitoringModuleName(componentElement);
+ }
+
+ @Override
+ Iterable<? extends Element> getOriginatingElements(TypeElement componentElement) {
+ return ImmutableSet.of(componentElement);
+ }
+
+ @Override
+ Optional<? extends Element> getElementForErrorReporting(TypeElement componentElement) {
+ return Optional.of(componentElement);
+ }
+
+ @Override
+ ImmutableSet<JavaWriter> write(ClassName generatedTypeName, TypeElement componentElement) {
+ JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
+ ClassWriter classWriter = writer.addClass(generatedTypeName.simpleName());
+ classWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName());
+ classWriter.annotate(Module.class);
+ classWriter.addModifiers(FINAL);
+
+ // TODO(beder): Replace this default set binding with EmptyCollections when it exists.
+ MethodWriter emptySetBindingMethod =
+ classWriter.addMethod(SET_OF_FACTORIES, "defaultSetOfFactories");
+ emptySetBindingMethod.addModifiers(STATIC);
+ emptySetBindingMethod.annotate(Provides.class).setMember("type", Provides.Type.SET_VALUES);
+ emptySetBindingMethod
+ .body()
+ .addSnippet("return %s.of();", ClassName.fromClass(ImmutableSet.class));
+
+ FieldWriter providerField = classWriter.addField(MonitorCache.class, "monitorCache");
+ providerField.addModifiers(PRIVATE, FINAL);
+ providerField.setInitializer("new %s()", ClassName.fromClass(MonitorCache.class));
+ MethodWriter monitorMethod = classWriter.addMethod(ProductionComponentMonitor.class, "monitor");
+ monitorMethod.annotate(Provides.class);
+ monitorMethod.addParameter(
+ ParameterizedTypeName.create(Provider.class, ClassName.fromTypeElement(componentElement)),
+ "component");
+ monitorMethod.addParameter(
+ ParameterizedTypeName.create(Provider.class, SET_OF_FACTORIES), "factories");
+ monitorMethod.body().addSnippet("return monitorCache.monitor(component, factories);");
+
+ return ImmutableSet.of(writer);
+ }
+}
diff --git a/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
new file mode 100644
index 000000000..91b10e7ef
--- /dev/null
+++ b/compiler/src/main/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.internal.codegen;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
+import dagger.producers.ProductionComponent;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+/**
+ * A processing step that is responsible for generating a special module for a
+ * {@link ProductionComponent}.
+ */
+final class MonitoringModuleProcessingStep implements ProcessingStep {
+ private final Messager messager;
+ private final MonitoringModuleGenerator monitoringModuleGenerator;
+
+ MonitoringModuleProcessingStep(
+ Messager messager, MonitoringModuleGenerator monitoringModuleGenerator) {
+ this.messager = messager;
+ this.monitoringModuleGenerator = monitoringModuleGenerator;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(ProductionComponent.class);
+ }
+
+ @Override
+ public Set<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ for (TypeElement element : typesIn(elementsByAnnotation.get(ProductionComponent.class))) {
+ try {
+ monitoringModuleGenerator.generate(element);
+ } catch (SourceFileGenerationException e) {
+ e.printMessageTo(messager);
+ }
+ }
+ return ImmutableSet.of();
+ }
+}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java b/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java
index 9ae154886..90a0337bd 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ProducerFactoryGenerator.java
@@ -44,19 +44,17 @@ import dagger.producers.Producer;
import dagger.producers.Produces;
import dagger.producers.internal.AbstractProducer;
import dagger.producers.internal.Producers;
+import dagger.producers.monitoring.ProducerMonitor;
import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
import java.util.List;
-import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import javax.annotation.Generated;
-import javax.annotation.Nullable;
import javax.annotation.processing.Filer;
import javax.lang.model.element.Element;
import javax.lang.model.type.TypeMirror;
-import static dagger.internal.codegen.SourceFiles.factoryNameForProductionBinding;
import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
+import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
@@ -80,7 +78,7 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
@Override
ClassName nameGeneratedType(ProductionBinding binding) {
- return factoryNameForProductionBinding(binding);
+ return generatedClassNameForBinding(binding);
}
@Override
@@ -107,14 +105,15 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
ConstructorWriter constructorWriter = factoryWriter.addConstructor();
constructorWriter.addModifiers(PUBLIC);
- constructorWriter
- .addParameter(ProductionComponentMonitor.class, "componentMonitor")
- .annotate(Nullable.class);
+ ImmutableMap<BindingKey, FrameworkField> fields =
+ SourceFiles.generateBindingFieldsForDependencies(
+ dependencyRequestMapper, binding.implicitDependencies());
+
constructorWriter
.body()
.addSnippet(
- "super(%s.producerMonitorFor(componentMonitor, %s.create(%s.class)));",
- ClassName.fromClass(Producers.class),
+ "super(%s, %s.create(%s.class));",
+ fields.get(binding.monitorRequest().get().bindingKey()).name(),
ClassName.fromClass(ProducerToken.class),
factoryWriter.name());
@@ -140,13 +139,10 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
factoryWriter.setSuperclass(
ParameterizedTypeName.create(AbstractProducer.class, providedTypeName));
- MethodWriter getMethodWriter = factoryWriter.addMethod(futureTypeName, "compute");
- getMethodWriter.annotate(Override.class);
- getMethodWriter.addModifiers(PROTECTED);
-
- final ImmutableMap<BindingKey, FrameworkField> fields =
- SourceFiles.generateBindingFieldsForDependencies(
- dependencyRequestMapper, binding.dependencies());
+ MethodWriter computeMethodWriter = factoryWriter.addMethod(futureTypeName, "compute");
+ computeMethodWriter.annotate(Override.class);
+ computeMethodWriter.addModifiers(PROTECTED);
+ computeMethodWriter.addParameter(ProducerMonitor.class, "monitor").addModifiers(FINAL);
for (FrameworkField bindingField : fields.values()) {
TypeName fieldType = bindingField.frameworkType();
@@ -158,15 +154,18 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
.addSnippet("this.%1$s = %1$s;", field.name());
}
- boolean returnsFuture = binding.bindingKind().equals(ProductionBinding.Kind.FUTURE_PRODUCTION);
- ImmutableList<DependencyRequest> asyncDependencies = FluentIterable
- .from(binding.dependencies())
- .filter(new Predicate<DependencyRequest>() {
- @Override public boolean apply(DependencyRequest dependency) {
- return isAsyncDependency(dependency);
- }
- })
- .toList();
+ boolean returnsFuture =
+ binding.bindingKind().equals(ContributionBinding.Kind.FUTURE_PRODUCTION);
+ ImmutableList<DependencyRequest> asyncDependencies =
+ FluentIterable.from(binding.implicitDependencies())
+ .filter(
+ new Predicate<DependencyRequest>() {
+ @Override
+ public boolean apply(DependencyRequest dependency) {
+ return isAsyncDependency(dependency);
+ }
+ })
+ .toList();
for (DependencyRequest dependency : asyncDependencies) {
ParameterizedTypeName futureType = ParameterizedTypeName.create(
@@ -174,143 +173,229 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
asyncDependencyType(dependency));
String name = fields.get(dependency.bindingKey()).name();
Snippet futureAccess = Snippet.format("%s.get()", name);
- getMethodWriter.body().addSnippet("%s %sFuture = %s;",
- futureType,
- name,
- dependency.kind().equals(DependencyRequest.Kind.PRODUCED)
- ? Snippet.format("%s.createFutureProduced(%s)",
- ClassName.fromClass(Producers.class), futureAccess)
- : futureAccess);
+ computeMethodWriter
+ .body()
+ .addSnippet(
+ "%s %sFuture = %s;",
+ futureType,
+ name,
+ dependency.kind().equals(DependencyRequest.Kind.PRODUCED)
+ ? Snippet.format(
+ "%s.createFutureProduced(%s)",
+ ClassName.fromClass(Producers.class),
+ futureAccess)
+ : futureAccess);
+ }
+
+ FutureTransform futureTransform = FutureTransform.create(fields, binding, asyncDependencies);
+ Snippet transformSnippet =
+ Snippet.format(
+ Joiner.on('\n')
+ .join(
+ "new %1$s<%2$s, %3$s>() {",
+ " %4$s",
+ " @Override public %5$s apply(%2$s %6$s) %7$s {",
+ " %8$s",
+ " }",
+ "}"),
+ ClassName.fromClass(AsyncFunction.class),
+ futureTransform.applyArgType(),
+ providedTypeName,
+ futureTransform.hasUncheckedCast()
+ ? "@SuppressWarnings(\"unchecked\") // safe by specification"
+ : "",
+ futureTypeName,
+ futureTransform.applyArgName(),
+ getThrowsClause(binding.thrownTypes()),
+ getInvocationSnippet(!returnsFuture, binding, futureTransform.parameterSnippets()));
+ computeMethodWriter
+ .body()
+ .addSnippet(
+ "return %s.transform(%s, %s, executor);",
+ ClassName.fromClass(Futures.class),
+ futureTransform.futureSnippet(),
+ transformSnippet);
+
+ // TODO(gak): write a sensible toString
+ return ImmutableSet.of(writer);
+ }
+
+ /** Represents the transformation of an input future by a producer method. */
+ abstract static class FutureTransform {
+ protected final ImmutableMap<BindingKey, FrameworkField> fields;
+ protected final ProductionBinding binding;
+
+ FutureTransform(ImmutableMap<BindingKey, FrameworkField> fields, ProductionBinding binding) {
+ this.fields = fields;
+ this.binding = binding;
+ }
+
+ /** The snippet representing the future that should be transformed. */
+ abstract Snippet futureSnippet();
+
+ /** The type of the argument to the apply method. */
+ abstract TypeName applyArgType();
+
+ /** The name of the argument to the apply method */
+ abstract String applyArgName();
+
+ /** The snippets to be passed to the produces method itself. */
+ abstract ImmutableList<Snippet> parameterSnippets();
+
+ /** Whether the transform method has an unchecked cast. */
+ boolean hasUncheckedCast() {
+ return false;
+ }
+
+ static FutureTransform create(
+ ImmutableMap<BindingKey, FrameworkField> fields,
+ ProductionBinding binding,
+ ImmutableList<DependencyRequest> asyncDependencies) {
+ if (asyncDependencies.isEmpty()) {
+ return new NoArgFutureTransform(fields, binding);
+ } else if (asyncDependencies.size() == 1) {
+ return new SingleArgFutureTransform(
+ fields, binding, Iterables.getOnlyElement(asyncDependencies));
+ } else {
+ return new MultiArgFutureTransform(fields, binding, asyncDependencies);
+ }
+ }
+ }
+
+ static final class NoArgFutureTransform extends FutureTransform {
+ NoArgFutureTransform(
+ ImmutableMap<BindingKey, FrameworkField> fields, ProductionBinding binding) {
+ super(fields, binding);
+ }
+
+ @Override
+ Snippet futureSnippet() {
+ return Snippet.format(
+ "%s.<%s>immediateFuture(null)",
+ ClassName.fromClass(Futures.class),
+ ClassName.fromClass(Void.class));
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return ClassName.fromClass(Void.class);
+ }
+
+ @Override
+ String applyArgName() {
+ return "ignoredVoidArg";
}
- if (asyncDependencies.isEmpty()) {
+ @Override
+ ImmutableList<Snippet> parameterSnippets() {
ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
for (DependencyRequest dependency : binding.dependencies()) {
- parameterSnippets.add(frameworkTypeUsageStatement(
- Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind()));
+ parameterSnippets.add(
+ frameworkTypeUsageStatement(
+ Snippet.format(
+ "%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
}
- final boolean wrapWithFuture = false; // since submitToExecutor will create the future
- Snippet invocationSnippet =
- getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build());
- TypeName callableReturnType = returnsFuture ? futureTypeName : providedTypeName;
- Snippet throwsClause = getThrowsClause(binding.thrownTypes());
- Snippet callableSnippet =
- Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s>() {",
- " @Override public %2$s call() %3$s{",
- " %4$s",
- " }",
- "}"),
- ClassName.fromClass(Callable.class),
- callableReturnType,
- throwsClause,
- invocationSnippet);
- getMethodWriter
- .body()
- .addSnippet(
- "%s future = %s.submitToExecutor(%s, executor);",
- ParameterizedTypeName.create(
- ClassName.fromClass(ListenableFuture.class), callableReturnType),
- ClassName.fromClass(Producers.class),
- callableSnippet);
- getMethodWriter
- .body()
- .addSnippet(
- "return %s;",
- returnsFuture
- ? Snippet.format("%s.dereference(future)", ClassName.fromClass(Futures.class))
- : "future");
- } else {
- final Snippet futureSnippet;
- final Snippet transformSnippet;
- if (asyncDependencies.size() == 1) {
- DependencyRequest asyncDependency = Iterables.getOnlyElement(asyncDependencies);
- futureSnippet = Snippet.format("%s",
- fields.get(asyncDependency.bindingKey()).name() + "Future");
- String argName = asyncDependency.requestElement().getSimpleName().toString();
- ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
- for (DependencyRequest dependency : binding.dependencies()) {
- // We really want to compare instances here, because asyncDependency is an element in the
- // set binding.dependencies().
- if (dependency == asyncDependency) {
- parameterSnippets.add(Snippet.format("%s", argName));
- } else {
- parameterSnippets.add(frameworkTypeUsageStatement(
- Snippet.format(fields.get(dependency.bindingKey()).name()),
- dependency.kind()));
- }
+ return parameterSnippets.build();
+ }
+ }
+
+ static final class SingleArgFutureTransform extends FutureTransform {
+ private final DependencyRequest asyncDependency;
+
+ SingleArgFutureTransform(
+ ImmutableMap<BindingKey, FrameworkField> fields,
+ ProductionBinding binding,
+ DependencyRequest asyncDependency) {
+ super(fields, binding);
+ this.asyncDependency = asyncDependency;
+ }
+
+ @Override
+ Snippet futureSnippet() {
+ return Snippet.format("%s", fields.get(asyncDependency.bindingKey()).name() + "Future");
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return asyncDependencyType(asyncDependency);
+ }
+
+ @Override
+ String applyArgName() {
+ return asyncDependency.requestElement().getSimpleName().toString();
+ }
+
+ @Override
+ ImmutableList<Snippet> parameterSnippets() {
+ ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
+ for (DependencyRequest dependency : binding.dependencies()) {
+ // We really want to compare instances here, because asyncDependency is an element in the
+ // set binding.dependencies().
+ if (dependency == asyncDependency) {
+ parameterSnippets.add(Snippet.format("%s", applyArgName()));
+ } else {
+ parameterSnippets.add(
+ frameworkTypeUsageStatement(
+ Snippet.format(
+ "%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
}
- boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future
- Snippet invocationSnippet =
- getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build());
- Snippet throwsClause = getThrowsClause(binding.thrownTypes());
- transformSnippet =
- Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s, %3$s>() {",
- " @Override public %4$s apply(%2$s %5$s) %6$s{",
- " %7$s",
- " }",
- "}"),
- ClassName.fromClass(AsyncFunction.class),
- asyncDependencyType(asyncDependency),
- providedTypeName,
- futureTypeName,
- argName,
- throwsClause,
- invocationSnippet);
- } else {
- futureSnippet = Snippet.format("%s.<%s>allAsList(%s)",
- ClassName.fromClass(Futures.class),
- ClassName.fromClass(Object.class),
- Joiner.on(",").join(FluentIterable
- .from(asyncDependencies)
- .transform(DependencyRequest.BINDING_KEY_FUNCTION)
- .transform(new Function<BindingKey, String>() {
- @Override public String apply(BindingKey dependencyBindingKey) {
- return fields.get(dependencyBindingKey).name() + "Future";
- }
- })));
- ImmutableList<Snippet> parameterSnippets = getParameterSnippets(binding, fields, "args");
- boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future
- Snippet invocationSnippet =
- getInvocationSnippet(wrapWithFuture, binding, parameterSnippets);
- ParameterizedTypeName listOfObject =
- ParameterizedTypeName.create(
- ClassName.fromClass(List.class), ClassName.fromClass(Object.class));
- Snippet throwsClause = getThrowsClause(binding.thrownTypes());
- transformSnippet =
- Snippet.format(
- Joiner.on('\n')
- .join(
- "new %1$s<%2$s, %3$s>() {",
- " @SuppressWarnings(\"unchecked\") // safe by specification",
- " @Override public %4$s apply(%2$s args) %5$s{",
- " %6$s",
- " }",
- "}"),
- ClassName.fromClass(AsyncFunction.class),
- listOfObject,
- providedTypeName,
- futureTypeName,
- throwsClause,
- invocationSnippet);
}
- getMethodWriter.body().addSnippet("return %s.%s(%s, %s, executor);",
+ return parameterSnippets.build();
+ }
+ }
+
+ static final class MultiArgFutureTransform extends FutureTransform {
+ private final ImmutableList<DependencyRequest> asyncDependencies;
+
+ MultiArgFutureTransform(
+ ImmutableMap<BindingKey, FrameworkField> fields,
+ ProductionBinding binding,
+ ImmutableList<DependencyRequest> asyncDependencies) {
+ super(fields, binding);
+ this.asyncDependencies = asyncDependencies;
+ }
+
+ @Override
+ Snippet futureSnippet() {
+ return Snippet.format(
+ "%s.<%s>allAsList(%s)",
ClassName.fromClass(Futures.class),
- "transform",
- futureSnippet,
- transformSnippet);
+ ClassName.fromClass(Object.class),
+ makeParametersSnippet(
+ FluentIterable.from(asyncDependencies)
+ .transform(DependencyRequest.BINDING_KEY_FUNCTION)
+ .transform(
+ new Function<BindingKey, Snippet>() {
+ @Override
+ public Snippet apply(BindingKey bindingKey) {
+ return Snippet.format("%s", fields.get(bindingKey).name() + "Future");
+ }
+ })));
}
- // TODO(gak): write a sensible toString
- return ImmutableSet.of(writer);
+ @Override
+ TypeName applyArgType() {
+ return ParameterizedTypeName.create(
+ ClassName.fromClass(List.class), ClassName.fromClass(Object.class));
+ }
+
+ @Override
+ String applyArgName() {
+ return "args";
+ }
+
+ @Override
+ ImmutableList<Snippet> parameterSnippets() {
+ return getParameterSnippets(binding, fields, applyArgName());
+ }
+
+ @Override
+ boolean hasUncheckedCast() {
+ return true;
+ }
}
- private boolean isAsyncDependency(DependencyRequest dependency) {
+ private static boolean isAsyncDependency(DependencyRequest dependency) {
switch (dependency.kind()) {
case INSTANCE:
case PRODUCED:
@@ -320,7 +405,7 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
}
}
- private TypeName asyncDependencyType(DependencyRequest dependency) {
+ private static TypeName asyncDependencyType(DependencyRequest dependency) {
TypeName keyName = TypeNames.forTypeMirror(dependency.key().type());
switch (dependency.kind()) {
case INSTANCE:
@@ -332,7 +417,8 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
}
}
- private ImmutableList<Snippet> getParameterSnippets(ProductionBinding binding,
+ private static ImmutableList<Snippet> getParameterSnippets(
+ ProductionBinding binding,
ImmutableMap<BindingKey, FrameworkField> fields,
String listArgName) {
int argIndex = 0;
@@ -347,7 +433,7 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
argIndex++;
} else {
snippets.add(frameworkTypeUsageStatement(
- Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind()));
+ Snippet.format("%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
}
}
return snippets.build();
@@ -375,11 +461,11 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
// because we'll wrap all monitoring in non-throwing monitors before we pass them to the
// factories.
ImmutableList.Builder<Snippet> snippets = ImmutableList.builder();
- snippets.add(Snippet.format("if (monitor != null) { monitor.methodStarting(); }"));
+ snippets.add(Snippet.format("monitor.methodStarting();"));
final Snippet valueSnippet;
if (binding.productionType().equals(Produces.Type.SET)) {
- if (binding.bindingKind().equals(ProductionBinding.Kind.FUTURE_PRODUCTION)) {
+ if (binding.bindingKind().equals(ContributionBinding.Kind.FUTURE_PRODUCTION)) {
valueSnippet =
Snippet.format(
"%s.createFutureSingletonSet(%s)",
@@ -403,11 +489,11 @@ final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBindi
return Snippet.format(
Joiner.on('\n')
.join(
- "if (monitor != null) { monitor.methodStarting(); }",
+ "monitor.methodStarting();",
"try {",
" return %s;",
"} finally {",
- " if (monitor != null) { monitor.methodFinished(); }",
+ " monitor.methodFinished();",
"}"),
returnSnippet);
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java b/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java
index 38d45e6a2..1666fbf8c 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ProductionBinding.java
@@ -20,9 +20,11 @@ import com.google.auto.value.AutoValue;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Producer;
+import dagger.Provides;
import dagger.producers.Produces;
+import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
@@ -44,33 +46,26 @@ import static javax.lang.model.element.ElementKind.METHOD;
*/
@AutoValue
abstract class ProductionBinding extends ContributionBinding {
+
@Override
- ImmutableSet<DependencyRequest> implicitDependencies() {
- return dependencies();
+ Binding.Type bindingType() {
+ return Binding.Type.PRODUCTION;
}
- enum Kind {
- /** Represents a binding configured by {@link Produces} that doesn't return a future. */
- IMMEDIATE,
- /** Represents a binding configured by {@link Produces} that returns a future. */
- FUTURE_PRODUCTION,
- /**
- * Represents a binding that is not explicitly tied to code, but generated implicitly by the
- * framework.
- */
- SYNTHETIC_PRODUCTION,
- /**
- * Represents a binding from a production method on a component dependency that returns a
- * future. Methods that return immediate values are considered provision bindings.
- */
- COMPONENT_PRODUCTION,
+ @Override
+ Provides.Type provisionType() {
+ return Provides.Type.valueOf(productionType().name());
}
- /**
- * The type of binding (whether the {@link Produces} method returns a future). For the particular
- * type of production, use {@link #productionType}.
- */
- abstract Kind bindingKind();
+ @Override
+ Set<DependencyRequest> implicitDependencies() {
+ // Similar optimizations to ContributionBinding.implicitDependencies().
+ if (!monitorRequest().isPresent()) {
+ return super.implicitDependencies();
+ } else {
+ return Sets.union(monitorRequest().asSet(), super.implicitDependencies());
+ }
+ }
/** Returns provision type that was used to bind the key. */
abstract Produces.Type productionType();
@@ -78,40 +73,31 @@ abstract class ProductionBinding extends ContributionBinding {
/** Returns the list of types in the throws clause of the method. */
abstract ImmutableList<? extends TypeMirror> thrownTypes();
+ /** If this production requires a monitor, this will be the corresponding request. */
+ abstract Optional<DependencyRequest> monitorRequest();
+
@Override
- BindingType bindingType() {
+ ContributionType contributionType() {
switch (productionType()) {
case SET:
case SET_VALUES:
- return BindingType.SET;
+ return ContributionType.SET;
case MAP:
- return BindingType.MAP;
+ return ContributionType.MAP;
case UNIQUE:
- return BindingType.UNIQUE;
+ return ContributionType.UNIQUE;
default:
- throw new IllegalStateException("Unknown production type: " + productionType());
+ throw new AssertionError("Unknown production type: " + productionType());
}
}
- @Override
- boolean isSyntheticBinding() {
- return bindingKind().equals(Kind.SYNTHETIC_PRODUCTION);
- }
-
- @Override
- Class<?> frameworkClass() {
- return Producer.class;
- }
-
static final class Factory {
private final Types types;
private final Key.Factory keyFactory;
private final DependencyRequest.Factory dependencyRequestFactory;
- Factory(Types types,
- Key.Factory keyFactory,
- DependencyRequest.Factory
- dependencyRequestFactory) {
+ Factory(
+ Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory) {
this.types = types;
this.keyFactory = keyFactory;
this.dependencyRequestFactory = dependencyRequestFactory;
@@ -133,6 +119,8 @@ abstract class ProductionBinding extends ContributionBinding {
declaredContainer,
producesMethod.getParameters(),
resolvedMethod.getParameterTypes());
+ DependencyRequest monitorRequest =
+ dependencyRequestFactory.forProductionComponentMonitorProvider();
Kind kind = MoreTypes.isTypeOf(ListenableFuture.class, producesMethod.getReturnType())
? Kind.FUTURE_PRODUCTION
: Kind.IMMEDIATE;
@@ -144,27 +132,35 @@ abstract class ProductionBinding extends ContributionBinding {
false,
ConfigurationAnnotations.getNullableType(producesMethod),
Optional.of(MoreTypes.asTypeElement(declaredContainer)),
+ Optional.<DependencyRequest>absent(),
kind,
producesAnnotation.type(),
- ImmutableList.copyOf(producesMethod.getThrownTypes()));
+ ImmutableList.copyOf(producesMethod.getThrownTypes()),
+ Optional.of(monitorRequest));
}
- ProductionBinding forImplicitMapBinding(DependencyRequest explicitRequest,
- DependencyRequest implicitRequest) {
- checkNotNull(explicitRequest);
- checkNotNull(implicitRequest);
- ImmutableSet<DependencyRequest> dependencies = ImmutableSet.of(implicitRequest);
+ ProductionBinding implicitMapOfProducerBinding(DependencyRequest mapOfValueRequest) {
+ checkNotNull(mapOfValueRequest);
+ Optional<Key> implicitMapOfProducerKey =
+ keyFactory.implicitMapProducerKeyFrom(mapOfValueRequest.key());
+ checkArgument(
+ implicitMapOfProducerKey.isPresent(), "%s is not for a Map<K, V>", mapOfValueRequest);
+ DependencyRequest implicitMapOfProducerRequest =
+ dependencyRequestFactory.forImplicitMapBinding(
+ mapOfValueRequest, implicitMapOfProducerKey.get());
return new AutoValue_ProductionBinding(
- explicitRequest.key(),
- implicitRequest.requestElement(),
- dependencies,
- findBindingPackage(explicitRequest.key()),
+ mapOfValueRequest.key(),
+ implicitMapOfProducerRequest.requestElement(),
+ ImmutableSet.of(implicitMapOfProducerRequest),
+ findBindingPackage(mapOfValueRequest.key()),
false,
Optional.<DeclaredType>absent(),
Optional.<TypeElement>absent(),
- Kind.SYNTHETIC_PRODUCTION,
+ Optional.<DependencyRequest>absent(),
+ Kind.SYNTHETIC,
Produces.Type.MAP,
- ImmutableList.<TypeMirror>of());
+ ImmutableList.<TypeMirror>of(),
+ Optional.<DependencyRequest>absent());
}
ProductionBinding forComponentMethod(ExecutableElement componentMethod) {
@@ -180,9 +176,11 @@ abstract class ProductionBinding extends ContributionBinding {
false,
Optional.<DeclaredType>absent(),
Optional.<TypeElement>absent(),
+ Optional.<DependencyRequest>absent(),
Kind.COMPONENT_PRODUCTION,
Produces.Type.UNIQUE,
- ImmutableList.copyOf(componentMethod.getThrownTypes()));
+ ImmutableList.copyOf(componentMethod.getThrownTypes()),
+ Optional.<DependencyRequest>absent());
}
}
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java
index 56f8ccb17..0581b1bb1 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ProductionComponentProcessingStep.java
@@ -16,10 +16,13 @@
package dagger.internal.codegen;
import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
import dagger.producers.ProductionComponent;
import java.lang.annotation.Annotation;
+import java.util.Map;
import java.util.Set;
import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
@@ -32,11 +35,14 @@ import javax.lang.model.element.TypeElement;
* @author Jesse Beder
*/
final class ProductionComponentProcessingStep extends AbstractComponentProcessingStep {
- private final ComponentElementValidator componentElementValidator;
+ private final Messager messager;
+ private final ProductionComponentValidator componentValidator;
+ private final BuilderValidator componentBuilderValidator;
ProductionComponentProcessingStep(
Messager messager,
- final ProductionComponentValidator componentValidator,
+ ProductionComponentValidator componentValidator,
+ BuilderValidator componentBuilderValidator,
ComponentHierarchyValidator componentHierarchyValidator,
BindingGraphValidator bindingGraphValidator,
ComponentDescriptor.Factory componentDescriptorFactory,
@@ -50,26 +56,48 @@ final class ProductionComponentProcessingStep extends AbstractComponentProcessin
componentDescriptorFactory,
bindingGraphFactory,
componentGenerator);
- this.componentElementValidator =
- new ComponentElementValidator() {
- @Override
- boolean validateComponent(TypeElement componentTypeElement, Messager messager) {
- ValidationReport<TypeElement> validationReport =
- componentValidator.validate(componentTypeElement);
- validationReport.printMessagesTo(messager);
- return validationReport.isClean();
- }
- };
+ this.messager = messager;
+ this.componentValidator = componentValidator;
+ this.componentBuilderValidator = componentBuilderValidator;
}
@Override
public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.<Class<? extends Annotation>>of(ProductionComponent.class);
+ return ImmutableSet.<Class<? extends Annotation>>of(
+ ProductionComponent.class, ProductionComponent.Builder.class);
}
+ // TODO(beder): Move common logic into the AbstractComponentProcessingStep when implementing
+ // production subcomponents.
@Override
protected ComponentElementValidator componentElementValidator(
SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- return componentElementValidator;
+ final Map<Element, ValidationReport<TypeElement>> builderReportsByComponent =
+ processComponentBuilders(elementsByAnnotation.get(ProductionComponent.Builder.class));
+ return new ComponentElementValidator() {
+ @Override
+ boolean validateComponent(TypeElement componentTypeElement, Messager messager) {
+ ValidationReport<TypeElement> validationReport =
+ componentValidator.validate(componentTypeElement);
+ validationReport.printMessagesTo(messager);
+ if (!validationReport.isClean()) {
+ return false;
+ }
+ ValidationReport<?> builderReport = builderReportsByComponent.get(componentTypeElement);
+ return builderReport == null || builderReport.isClean();
+ }
+ };
+ }
+
+ private Map<Element, ValidationReport<TypeElement>> processComponentBuilders(
+ Set<? extends Element> componentBuilderElements) {
+ Map<Element, ValidationReport<TypeElement>> builderReportsByComponent = Maps.newHashMap();
+ for (Element element : componentBuilderElements) {
+ ValidationReport<TypeElement> report =
+ componentBuilderValidator.validate(MoreElements.asType(element));
+ report.printMessagesTo(messager);
+ builderReportsByComponent.put(element.getEnclosingElement(), report);
+ }
+ return builderReportsByComponent;
}
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java b/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java
index 8a3c203c8..b2ac74fb3 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ProvisionBinding.java
@@ -22,9 +22,7 @@ import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import dagger.Provides;
-import java.util.Set;
import javax.inject.Inject;
-import javax.inject.Provider;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
@@ -37,16 +35,15 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asDeclared;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
-import static dagger.internal.codegen.ProvisionBinding.Kind.INJECTION;
-import static dagger.internal.codegen.ProvisionBinding.Kind.PROVISION;
+import static dagger.internal.codegen.Scope.scopeOf;
import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
import static javax.lang.model.element.ElementKind.FIELD;
import static javax.lang.model.element.ElementKind.METHOD;
-import static javax.lang.model.element.Modifier.STATIC;
/**
* A value object representing the mechanism by which a {@link Key} can be provided. New instances
@@ -57,91 +54,14 @@ import static javax.lang.model.element.Modifier.STATIC;
*/
@AutoValue
abstract class ProvisionBinding extends ContributionBinding {
+
@Override
- Set<DependencyRequest> implicitDependencies() {
- // Optimization: If we don't need the memberInjectionRequest, don't create more objects.
- if (!memberInjectionRequest().isPresent()) {
- return dependencies();
- } else {
- // Optimization: Avoid creating an ImmutableSet+Builder just to union two things together.
- return Sets.union(memberInjectionRequest().asSet(), dependencies());
- }
- }
-
- enum Kind {
- /** Represents an {@link Inject} binding. */
- INJECTION,
- /** Represents a binding configured by {@link Provides}. */
- PROVISION,
- /**
- * Represents a binding that is not explicitly tied to code, but generated implicitly by the
- * framework.
- */
- SYNTHETIC_PROVISON,
- /** Represents the implicit binding to the component. */
- COMPONENT,
- /** Represents a binding from a provision method on a component dependency. */
- COMPONENT_PROVISION,
- }
-
- /**
- * The type of binding ({@link Inject} or {@link Provides}). For the particular type of provision,
- * use {@link #provisionType}.
- */
- abstract Kind bindingKind();
-
- /** Returns provision type that was used to bind the key. */
- abstract Provides.Type provisionType();
-
- /**
- * The scope of the provider.
- */
- abstract Scope scope();
-
- /** If this provision requires members injection, this will be the corresponding request. */
- abstract Optional<DependencyRequest> memberInjectionRequest();
-
- @Override
- BindingType bindingType() {
- switch (provisionType()) {
- case SET:
- case SET_VALUES:
- return BindingType.SET;
- case MAP:
- return BindingType.MAP;
- case UNIQUE:
- return BindingType.UNIQUE;
- default:
- throw new IllegalStateException("Unknown provision type: " + provisionType());
- }
+ Binding.Type bindingType() {
+ return Binding.Type.PROVISION;
}
-
- @Override
- boolean isSyntheticBinding() {
- return bindingKind().equals(Kind.SYNTHETIC_PROVISON);
- }
-
+
@Override
- Class<?> frameworkClass() {
- return Provider.class;
- }
-
- enum FactoryCreationStrategy {
- ENUM_INSTANCE,
- CLASS_CONSTRUCTOR,
- }
-
- FactoryCreationStrategy factoryCreationStrategy() {
- if (bindingKind().equals(INJECTION) && implicitDependencies().isEmpty()) {
- return FactoryCreationStrategy.ENUM_INSTANCE;
- }
- if (bindingKind().equals(PROVISION)
- && implicitDependencies().isEmpty()
- && bindingElement().getModifiers().contains(STATIC)) {
- return FactoryCreationStrategy.ENUM_INSTANCE;
- }
- return FactoryCreationStrategy.CLASS_CONSTRUCTOR;
- }
+ abstract Scope scope();
static final class Factory {
private final Elements elements;
@@ -211,10 +131,10 @@ abstract class ProvisionBinding extends ContributionBinding {
hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types),
Optional.<DeclaredType>absent(),
Optional.<TypeElement>absent(),
+ membersInjectionRequest,
Kind.INJECTION,
Provides.Type.UNIQUE,
- scope,
- membersInjectionRequest);
+ scope);
}
private static final ImmutableSet<ElementKind> MEMBER_KINDS =
@@ -259,30 +179,35 @@ abstract class ProvisionBinding extends ContributionBinding {
false /* no non-default parameter types */,
ConfigurationAnnotations.getNullableType(providesMethod),
Optional.of(MoreTypes.asTypeElement(declaredContainer)),
+ Optional.<DependencyRequest>absent(),
Kind.PROVISION,
providesAnnotation.type(),
- scope,
- Optional.<DependencyRequest>absent());
+ scope);
}
- ProvisionBinding forImplicitMapBinding(DependencyRequest explicitRequest,
- DependencyRequest implicitRequest) {
- checkNotNull(explicitRequest);
- checkNotNull(implicitRequest);
- ImmutableSet<DependencyRequest> dependencies = ImmutableSet.of(implicitRequest);
- Scope scope = Scope.scopeOf(implicitRequest.requestElement());
+ ProvisionBinding implicitMapOfProviderBinding(DependencyRequest mapOfValueRequest) {
+ checkNotNull(mapOfValueRequest);
+ Optional<Key> implicitMapOfProviderKey =
+ keyFactory.implicitMapProviderKeyFrom(mapOfValueRequest.key());
+ checkArgument(
+ implicitMapOfProviderKey.isPresent(),
+ "%s is not a request for Map<K, V>",
+ mapOfValueRequest);
+ DependencyRequest implicitMapOfProviderRequest =
+ dependencyRequestFactory.forImplicitMapBinding(
+ mapOfValueRequest, implicitMapOfProviderKey.get());
return new AutoValue_ProvisionBinding(
- explicitRequest.key(),
- implicitRequest.requestElement(),
- dependencies,
- findBindingPackage(explicitRequest.key()),
+ mapOfValueRequest.key(),
+ implicitMapOfProviderRequest.requestElement(),
+ ImmutableSet.of(implicitMapOfProviderRequest),
+ findBindingPackage(mapOfValueRequest.key()),
false /* no non-default parameter types */,
Optional.<DeclaredType>absent(),
Optional.<TypeElement>absent(),
- Kind.SYNTHETIC_PROVISON,
+ Optional.<DependencyRequest>absent(),
+ Kind.SYNTHETIC,
Provides.Type.MAP,
- scope,
- Optional.<DependencyRequest>absent());
+ scopeOf(implicitMapOfProviderRequest.requestElement()));
}
ProvisionBinding forComponent(TypeElement componentDefinitionType) {
@@ -295,10 +220,10 @@ abstract class ProvisionBinding extends ContributionBinding {
false /* no non-default parameter types */,
Optional.<DeclaredType>absent(),
Optional.<TypeElement>absent(),
+ Optional.<DependencyRequest>absent(),
Kind.COMPONENT,
Provides.Type.UNIQUE,
- Scope.unscoped(),
- Optional.<DependencyRequest>absent());
+ Scope.unscoped());
}
ProvisionBinding forComponentMethod(ExecutableElement componentMethod) {
@@ -314,10 +239,30 @@ abstract class ProvisionBinding extends ContributionBinding {
false /* no non-default parameter types */,
ConfigurationAnnotations.getNullableType(componentMethod),
Optional.<TypeElement>absent(),
+ Optional.<DependencyRequest>absent(),
Kind.COMPONENT_PROVISION,
Provides.Type.UNIQUE,
- scope,
- Optional.<DependencyRequest>absent());
+ scope);
+ }
+
+ ProvisionBinding forSubcomponentBuilderMethod(
+ ExecutableElement subcomponentBuilderMethod, TypeElement contributedBy) {
+ checkNotNull(subcomponentBuilderMethod);
+ checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
+ checkArgument(subcomponentBuilderMethod.getParameters().isEmpty());
+ DeclaredType declaredContainer = asDeclared(contributedBy.asType());
+ return new AutoValue_ProvisionBinding(
+ keyFactory.forSubcomponentBuilderMethod(subcomponentBuilderMethod, declaredContainer),
+ subcomponentBuilderMethod,
+ ImmutableSet.<DependencyRequest>of(),
+ Optional.<String>absent(),
+ false /* no non-default parameter types */,
+ Optional.<DeclaredType>absent(),
+ Optional.of(contributedBy),
+ Optional.<DependencyRequest>absent(),
+ Kind.SUBCOMPONENT_BUILDER,
+ Provides.Type.UNIQUE,
+ Scope.unscoped());
}
}
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ProvisionBindingFormatter.java b/compiler/src/main/java/dagger/internal/codegen/ProvisionBindingFormatter.java
deleted file mode 100644
index 92d031042..000000000
--- a/compiler/src/main/java/dagger/internal/codegen/ProvisionBindingFormatter.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 Google, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package dagger.internal.codegen;
-
-import com.google.common.base.Optional;
-
-import static com.google.auto.common.MoreElements.asExecutable;
-import static com.google.auto.common.MoreTypes.asDeclared;
-
-/**
- * Formats a {@link ProvisionBinding} into a {@link String} suitable for use in error messages.
- *
- * @author Christian Gruber
- * @since 2.0
- */
-final class ProvisionBindingFormatter extends Formatter<ProvisionBinding> {
- private final MethodSignatureFormatter methodSignatureFormatter;
-
- ProvisionBindingFormatter(MethodSignatureFormatter methodSignatureFormatter) {
- this.methodSignatureFormatter = methodSignatureFormatter;
- }
-
- @Override public String format(ProvisionBinding binding) {
- switch (binding.bindingKind()) {
- case PROVISION:
- return methodSignatureFormatter.format(asExecutable(binding.bindingElement()),
- Optional.of(asDeclared(binding.contributedBy().get().asType())));
- case COMPONENT_PROVISION:
- return methodSignatureFormatter.format(asExecutable(binding.bindingElement()));
- default:
- throw new UnsupportedOperationException(
- "Not yet supporting " + binding.bindingKind() + " binding types.");
- }
- }
-}
diff --git a/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java b/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java
index 7ef4deb92..024097ef0 100644
--- a/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java
+++ b/compiler/src/main/java/dagger/internal/codegen/ResolvedBindings.java
@@ -16,13 +16,17 @@
package dagger.internal.codegen;
import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
-import java.util.Set;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.ContributionBinding.bindingTypeFor;
+import static dagger.internal.codegen.ContributionBinding.contributionTypeFor;
/**
* The collection of bindings that have been resolved for a binding key.
@@ -31,63 +35,168 @@ import static dagger.internal.codegen.ContributionBinding.bindingTypeFor;
*/
@AutoValue
abstract class ResolvedBindings {
+ /**
+ * The binding key for which the {@link #bindings()} have been resolved.
+ */
abstract BindingKey bindingKey();
+
+ /**
+ * The component in which the bindings in {@link #ownedBindings()},
+ * {@link #ownedContributionBindings()}, and {@link #ownedMembersInjectionBinding()} were
+ * resolved.
+ */
abstract ComponentDescriptor owningComponent();
- abstract ImmutableSet<? extends Binding> ownedBindings();
- abstract ImmutableSetMultimap<ComponentDescriptor, ? extends Binding> inheritedBindings();
- static ResolvedBindings create(
- BindingKey bindingKey,
- ComponentDescriptor owningComponent,
- Set<? extends Binding> ownedBindings,
- Multimap<ComponentDescriptor, ? extends Binding> inheritedBindings) {
- return new AutoValue_ResolvedBindings(
- bindingKey,
- owningComponent,
- ImmutableSet.copyOf(ownedBindings),
- ImmutableSetMultimap.copyOf(inheritedBindings));
+ /**
+ * The contribution bindings for {@link #bindingKey()} that were resolved in
+ * {@link #owningComponent()} or its ancestor components, keyed by the component in which the
+ * binding was resolved. If {@link #bindingKey()}'s kind is not
+ * {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
+ */
+ abstract ImmutableSetMultimap<ComponentDescriptor, ContributionBinding> allContributionBindings();
+
+ /**
+ * The members-injection bindings for {@link #bindingKey()} that were resolved in
+ * {@link #owningComponent()} or its ancestor components, keyed by the component in which the
+ * binding was resolved. If {@link #bindingKey()}'s kind is not
+ * {@link BindingKey.Kind#MEMBERS_INJECTION}, this is empty.
+ */
+ abstract ImmutableMap<ComponentDescriptor, MembersInjectionBinding> allMembersInjectionBindings();
+
+ /**
+ * All bindings for {@link #bindingKey()}, regardless of in which component they were resolved.
+ */
+ ImmutableSet<? extends Binding> bindings() {
+ switch (bindingKey().kind()) {
+ case CONTRIBUTION:
+ return contributionBindings();
+
+ case MEMBERS_INJECTION:
+ return ImmutableSet.copyOf(membersInjectionBinding().asSet());
+
+ default:
+ throw new AssertionError(bindingKey());
+ }
+ }
+
+ /**
+ * All bindings for {@link #bindingKey()} that were resolved in {@link #owningComponent()}.
+ */
+ ImmutableSet<? extends Binding> ownedBindings() {
+ switch (bindingKey().kind()) {
+ case CONTRIBUTION:
+ return ownedContributionBindings();
+
+ case MEMBERS_INJECTION:
+ return ImmutableSet.copyOf(ownedMembersInjectionBinding().asSet());
+
+ default:
+ throw new AssertionError(bindingKey());
+ }
+ }
+
+ /**
+ * All contribution bindings, regardless of owning component.
+ *
+ * @throws IllegalStateException if {@link #bindingKey()} is not a
+ * {@link BindingKey.Kind#CONTRIBUTION}.
+ */
+ ImmutableSet<ContributionBinding> contributionBindings() {
+ checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION));
+ return ImmutableSet.copyOf(allContributionBindings().values());
+ }
+
+ /**
+ * The contribution bindings that were resolved in {@link #owningComponent()}.
+ *
+ * @throws IllegalStateException if {@link #bindingKey()} is not a
+ * {@link BindingKey.Kind#CONTRIBUTION}.
+ */
+ ImmutableSet<ContributionBinding> ownedContributionBindings() {
+ checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION));
+ return allContributionBindings().get(owningComponent());
+ }
+
+ /**
+ * The members-injection binding, regardless of owning component.
+ *
+ * @throws IllegalStateException if {@link #bindingKey()} is not a
+ * {@link BindingKey.Kind#MEMBERS_INJECTION}.
+ */
+ Optional<MembersInjectionBinding> membersInjectionBinding() {
+ checkState(bindingKey().kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
+ ImmutableSet<MembersInjectionBinding> membersInjectionBindings =
+ FluentIterable.from(allMembersInjectionBindings().values()).toSet();
+ return membersInjectionBindings.isEmpty()
+ ? Optional.<MembersInjectionBinding>absent()
+ : Optional.of(Iterables.getOnlyElement(membersInjectionBindings));
+ }
+
+ /**
+ * The members-injection binding that was resolved in {@link #owningComponent()}.
+ *
+ * @throws IllegalStateException if {@link #bindingKey()} is not a
+ * {@link BindingKey.Kind#MEMBERS_INJECTION}.
+ */
+ Optional<MembersInjectionBinding> ownedMembersInjectionBinding() {
+ checkState(bindingKey().kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
+ return Optional.fromNullable(allMembersInjectionBindings().get(owningComponent()));
}
- static ResolvedBindings create(
+ /**
+ * Creates a {@link ResolvedBindings} for contribution bindings.
+ */
+ static ResolvedBindings forContributionBindings(
BindingKey bindingKey,
ComponentDescriptor owningComponent,
- Binding... ownedBindings) {
+ Multimap<ComponentDescriptor, ? extends ContributionBinding> contributionBindings) {
+ checkArgument(bindingKey.kind().equals(BindingKey.Kind.CONTRIBUTION));
return new AutoValue_ResolvedBindings(
bindingKey,
owningComponent,
- ImmutableSet.copyOf(ownedBindings),
- ImmutableSetMultimap.<ComponentDescriptor, Binding>of());
- }
-
- ImmutableSet<? extends Binding> bindings() {
- return new ImmutableSet.Builder<Binding>()
- .addAll(ownedBindings())
- .addAll(inheritedBindings().values())
- .build();
+ ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>copyOf(contributionBindings),
+ ImmutableMap.<ComponentDescriptor, MembersInjectionBinding>of());
}
- @SuppressWarnings("unchecked") // checked by validator
- ImmutableSet<? extends ContributionBinding> ownedContributionBindings() {
- checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION));
- return (ImmutableSet<? extends ContributionBinding>) ownedBindings();
+ /**
+ * Creates a {@link ResolvedBindings} for contribution bindings.
+ */
+ static ResolvedBindings forContributionBindings(
+ BindingKey bindingKey,
+ ComponentDescriptor owningComponent,
+ ContributionBinding... ownedContributionBindings) {
+ return forContributionBindings(
+ bindingKey,
+ owningComponent,
+ ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>builder()
+ .putAll(owningComponent, ownedContributionBindings)
+ .build());
}
- @SuppressWarnings("unchecked") // checked by validator
- ImmutableSet<? extends ContributionBinding> contributionBindings() {
- checkState(bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION));
- return new ImmutableSet.Builder<ContributionBinding>()
- .addAll((Iterable<? extends ContributionBinding>) ownedBindings())
- .addAll((Iterable<? extends ContributionBinding>) inheritedBindings().values())
- .build();
+ /**
+ * Creates a {@link ResolvedBindings} for members injection bindings.
+ */
+ static ResolvedBindings forMembersInjectionBinding(
+ BindingKey bindingKey,
+ ComponentDescriptor owningComponent,
+ MembersInjectionBinding ownedMembersInjectionBinding) {
+ checkArgument(bindingKey.kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
+ return new AutoValue_ResolvedBindings(
+ bindingKey,
+ owningComponent,
+ ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>of(),
+ ImmutableMap.of(owningComponent, ownedMembersInjectionBinding));
}
- @SuppressWarnings("unchecked") // checked by validator
- ImmutableSet<? extends MembersInjectionBinding> membersInjectionBindings() {
- checkState(bindingKey().kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
- return new ImmutableSet.Builder<MembersInjectionBinding>()
- .addAll((Iterable<? extends MembersInjectionBinding>) ownedBindings())
- .addAll((Iterable<? extends MembersInjectionBinding>) inheritedBindings().values())
- .build();
+ /**
+ * Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
+ */
+ static ResolvedBindings noBindings(BindingKey bindingKey, ComponentDescriptor owningComponent) {
+ return new AutoValue_ResolvedBindings(
+ bindingKey,
+ owningComponent,
+ ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>of(),
+ ImmutableMap.<ComponentDescriptor, MembersInjectionBinding>of());
}
/**
@@ -95,14 +204,8 @@ abstract class ResolvedBindings {
* as this one, but no {@link #ownedBindings()}.
*/
ResolvedBindings asInheritedIn(ComponentDescriptor owningComponent) {
- return ResolvedBindings.create(
- bindingKey(),
- owningComponent,
- ImmutableSet.<Binding>of(),
- new ImmutableSetMultimap.Builder<ComponentDescriptor, Binding>()
- .putAll(inheritedBindings())
- .putAll(owningComponent, ownedBindings())
- .build());
+ return new AutoValue_ResolvedBindings(
+ bindingKey(), owningComponent, allContributionBindings(), allMembersInjectionBindings());
}
/**
@@ -111,7 +214,7 @@ abstract class ResolvedBindings {
boolean isMultibindings() {
return bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)
&& !contributionBindings().isEmpty()
- && bindingTypeFor(contributionBindings()).isMultibinding();
+ && contributionTypeFor(contributionBindings()).isMultibinding();
}
/**
@@ -120,6 +223,6 @@ abstract class ResolvedBindings {
boolean isUniqueContribution() {
return bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)
&& !contributionBindings().isEmpty()
- && !bindingTypeFor(contributionBindings()).isMultibinding();
+ && !contributionTypeFor(contributionBindings()).isMultibinding();
}
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java b/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java
index 890d3553b..7ad0acbc4 100644
--- a/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java
+++ b/compiler/src/main/java/dagger/internal/codegen/SourceFiles.java
@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import dagger.internal.DoubleCheckLazy;
-import dagger.internal.codegen.ContributionBinding.BindingType;
import dagger.internal.codegen.writer.ClassName;
import dagger.internal.codegen.writer.ParameterizedTypeName;
import dagger.internal.codegen.writer.Snippet;
@@ -31,7 +30,6 @@ import dagger.internal.codegen.writer.TypeName;
import dagger.internal.codegen.writer.TypeNames;
import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.lang.model.element.ExecutableElement;
@@ -40,6 +38,7 @@ import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
/**
* Utilities for generating files.
@@ -184,83 +183,96 @@ class SourceFiles {
throw new AssertionError();
}
}
-
- static ClassName factoryNameForProvisionBinding(ProvisionBinding binding) {
- TypeElement enclosingTypeElement = binding.bindingTypeElement();
- ClassName enclosingClassName = ClassName.fromTypeElement(enclosingTypeElement);
- switch (binding.bindingKind()) {
- case INJECTION:
+
+ /**
+ * Returns the generated factory or members injector name for a binding.
+ */
+ static ClassName generatedClassNameForBinding(Binding binding) {
+ switch (binding.bindingType()) {
case PROVISION:
- return enclosingClassName.topLevelClassName().peerNamed(
- enclosingClassName.classFileName() + "_" + factoryPrefix(binding) + "Factory");
- case SYNTHETIC_PROVISON:
- throw new IllegalArgumentException();
+ case PRODUCTION:
+ ContributionBinding contribution = (ContributionBinding) binding;
+ checkArgument(!contribution.isSyntheticBinding());
+ ClassName enclosingClassName = ClassName.fromTypeElement(contribution.bindingTypeElement());
+ switch (contribution.bindingKind()) {
+ case INJECTION:
+ case PROVISION:
+ case IMMEDIATE:
+ case FUTURE_PRODUCTION:
+ return enclosingClassName
+ .topLevelClassName()
+ .peerNamed(
+ enclosingClassName.classFileName()
+ + "_"
+ + factoryPrefix(contribution)
+ + "Factory");
+
+ default:
+ throw new AssertionError();
+ }
+
+ case MEMBERS_INJECTION:
+ return membersInjectorNameForType(binding.bindingTypeElement());
+
default:
throw new AssertionError();
}
}
/**
- * Returns the factory name parameterized with the ProvisionBinding's parameters (if necessary).
+ * Returns the generated factory or members injector name parameterized with the proper type
+ * parameters if necessary.
*/
- static TypeName parameterizedFactoryNameForProvisionBinding(
- ProvisionBinding binding) {
- ClassName factoryName = factoryNameForProvisionBinding(binding);
- List<TypeName> parameters = ImmutableList.of();
- if (binding.bindingType().equals(BindingType.UNIQUE)) {
- switch(binding.bindingKind()) {
- case INJECTION:
- TypeName bindingName = TypeNames.forTypeMirror(binding.key().type());
- // If the binding is parameterized, parameterize the factory.
- if (bindingName instanceof ParameterizedTypeName) {
- parameters = ((ParameterizedTypeName) bindingName).parameters();
- }
- break;
- case PROVISION:
- // For provision bindings, we parameterize creation on the types of
- // the module, not the types of the binding.
- // Consider: Module<A, B, C> { @Provides List<B> provideB(B b) { .. }}
- // The binding is just parameterized on <B>, but we need all of <A, B, C>.
- if (!binding.bindingTypeElement().getTypeParameters().isEmpty()) {
- parameters = ((ParameterizedTypeName) TypeNames.forTypeMirror(
- binding.bindingTypeElement().asType())).parameters();
- }
- break;
- default: // fall through.
- }
- }
- return parameters.isEmpty() ? factoryName
- : ParameterizedTypeName.create(factoryName, parameters);
+ static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
+ return generatedClassNameForBinding(binding).withTypeParameters(bindingTypeParameters(binding));
}
+
+ private static ImmutableList<TypeName> bindingTypeParameters(Binding binding)
+ throws AssertionError {
+ TypeMirror bindingType;
+ switch (binding.bindingType()) {
+ case PROVISION:
+ case PRODUCTION:
+ ContributionBinding contributionBinding = (ContributionBinding) binding;
+ if (contributionBinding.contributionType().isMultibinding()) {
+ return ImmutableList.of();
+ }
+ switch (contributionBinding.bindingKind()) {
+ case INJECTION:
+ bindingType = contributionBinding.key().type();
+ break;
+
+ case PROVISION:
+ // For provision bindings, we parameterize creation on the types of
+ // the module, not the types of the binding.
+ // Consider: Module<A, B, C> { @Provides List<B> provideB(B b) { .. }}
+ // The binding is just parameterized on <B>, but we need all of <A, B, C>.
+ bindingType = contributionBinding.bindingTypeElement().asType();
+ break;
+
+ case IMMEDIATE:
+ case FUTURE_PRODUCTION:
+ // TODO(beder): Can these be treated just like PROVISION?
+ throw new UnsupportedOperationException();
+
+ default:
+ return ImmutableList.of();
+ }
+ break;
+
+ case MEMBERS_INJECTION:
+ bindingType = binding.key().type();
+ break;
- static ClassName factoryNameForProductionBinding(ProductionBinding binding) {
- TypeElement enclosingTypeElement = binding.bindingTypeElement();
- ClassName enclosingClassName = ClassName.fromTypeElement(enclosingTypeElement);
- switch (binding.bindingKind()) {
- case IMMEDIATE:
- case FUTURE_PRODUCTION:
- return enclosingClassName.topLevelClassName().peerNamed(
- enclosingClassName.classFileName() + "_" + factoryPrefix(binding) + "Factory");
default:
throw new AssertionError();
}
+ TypeName bindingTypeName = TypeNames.forTypeMirror(bindingType);
+ return bindingTypeName instanceof ParameterizedTypeName
+ ? ((ParameterizedTypeName) bindingTypeName).parameters()
+ : ImmutableList.<TypeName>of();
}
-
- /**
- * Returns the members injector's name parameterized with the binding's parameters (if necessary).
- */
- static TypeName parameterizedMembersInjectorNameForMembersInjectionBinding(
- MembersInjectionBinding binding) {
- ClassName factoryName = membersInjectorNameForType(binding.bindingElement());
- TypeName bindingName = TypeNames.forTypeMirror(binding.key().type());
- // If the binding is parameterized, parameterize the MembersInjector.
- if (bindingName instanceof ParameterizedTypeName) {
- return ParameterizedTypeName.create(factoryName,
- ((ParameterizedTypeName) bindingName).parameters());
- }
- return factoryName;
- }
-
+
static ClassName membersInjectorNameForType(TypeElement typeElement) {
ClassName injectedClassName = ClassName.fromTypeElement(typeElement);
return injectedClassName
@@ -268,24 +280,24 @@ class SourceFiles {
.peerNamed(injectedClassName.classFileName() + "_MembersInjector");
}
- private static String factoryPrefix(ProvisionBinding binding) {
+ static ClassName generatedMonitoringModuleName(TypeElement componentElement) {
+ ClassName componentName = ClassName.fromTypeElement(componentElement);
+ return componentName
+ .topLevelClassName()
+ .peerNamed(componentName.classFileName() + "_MonitoringModule");
+ }
+
+ private static String factoryPrefix(ContributionBinding binding) {
switch (binding.bindingKind()) {
case INJECTION:
return "";
- case PROVISION:
- return CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL,
- ((ExecutableElement) binding.bindingElement()).getSimpleName().toString());
- default:
- throw new IllegalArgumentException();
- }
- }
- private static String factoryPrefix(ProductionBinding binding) {
- switch (binding.bindingKind()) {
+ case PROVISION:
case IMMEDIATE:
case FUTURE_PRODUCTION:
- return CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL,
- ((ExecutableElement) binding.bindingElement()).getSimpleName().toString());
+ return CaseFormat.LOWER_CAMEL.to(
+ UPPER_CAMEL, ((ExecutableElement) binding.bindingElement()).getSimpleName().toString());
+
default:
throw new IllegalArgumentException();
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java b/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java
index 8cb31b92d..128766872 100644
--- a/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java
+++ b/compiler/src/main/java/dagger/internal/codegen/SubcomponentWriter.java
@@ -18,10 +18,7 @@ package dagger.internal.codegen;
import com.google.auto.common.MoreTypes;
import com.google.common.base.CaseFormat;
import com.google.common.base.Optional;
-import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
import dagger.internal.codegen.ComponentGenerator.MemberSelect;
import dagger.internal.codegen.writer.ClassName;
@@ -32,6 +29,7 @@ import dagger.internal.codegen.writer.Snippet;
import dagger.internal.codegen.writer.TypeName;
import dagger.internal.codegen.writer.TypeNames;
import java.util.List;
+import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
@@ -40,6 +38,7 @@ import javax.lang.model.type.TypeMirror;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Sets.difference;
import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.UNINITIALIZED;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
@@ -189,12 +188,9 @@ class SubcomponentWriter extends AbstractComponentWriter {
}
}
- ImmutableSet<TypeElement> uninitializedModules =
- FluentIterable.from(graph.componentDescriptor().transitiveModules())
- .transform(ModuleDescriptor.getModuleElement())
- .filter(Predicates.not(Predicates.in(componentContributionFields.keySet())))
- .toSet();
-
+ Set<TypeElement> uninitializedModules =
+ difference(graph.componentRequirements(), componentContributionFields.keySet());
+
for (TypeElement moduleType : uninitializedModules) {
String preferredModuleName =
CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleType.getSimpleName().toString());
diff --git a/compiler/src/main/java/dagger/internal/codegen/Util.java b/compiler/src/main/java/dagger/internal/codegen/Util.java
index faa0459c4..8c1aba399 100644
--- a/compiler/src/main/java/dagger/internal/codegen/Util.java
+++ b/compiler/src/main/java/dagger/internal/codegen/Util.java
@@ -21,7 +21,7 @@ import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
-import java.util.List;
+import dagger.producers.Produced;
import java.util.Map;
import java.util.Set;
import javax.inject.Provider;
@@ -46,31 +46,29 @@ import static javax.lang.model.element.Modifier.STATIC;
*/
final class Util {
/**
- * Returns the {@code V} type for a {@link Map} type like Map<K, Provider<V>>} if the map
+ * Returns the {@code V} type for a {@link Map} type like {@code Map<K, Provider<V>>} if the map
* includes such a construction
*/
- public static DeclaredType getProvidedValueTypeOfMap(DeclaredType mapType) {
+ public static TypeMirror getProvidedValueTypeOfMap(DeclaredType mapType) {
checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
- return asDeclared(asDeclared(mapType.getTypeArguments().get(1)).getTypeArguments().get(0));
+ return asDeclared(mapType.getTypeArguments().get(1)).getTypeArguments().get(0);
}
// TODO(cgruber): Consider an object that holds and exposes the various parts of a Map type.
/**
* returns the value type for a {@link Map} type like Map<K, V>}.
*/
- public static DeclaredType getValueTypeOfMap(DeclaredType mapType) {
+ public static TypeMirror getValueTypeOfMap(DeclaredType mapType) {
checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
- List<? extends TypeMirror> mapArgs = mapType.getTypeArguments();
- return asDeclared(mapArgs.get(1));
+ return mapType.getTypeArguments().get(1);
}
/**
* Returns the key type for a {@link Map} type like Map<K, Provider<V>>}
*/
- public static DeclaredType getKeyTypeOfMap(DeclaredType mapType) {
+ public static TypeMirror getKeyTypeOfMap(DeclaredType mapType) {
checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
- List<? extends TypeMirror> mapArgs = mapType.getTypeArguments();
- return MoreTypes.asDeclared(mapArgs.get(0));
+ return mapType.getTypeArguments().get(0);
}
/**
@@ -91,6 +89,13 @@ final class Util {
&& MoreTypes.isTypeOf(Provider.class, asDeclared(type).getTypeArguments().get(1));
}
+ /** Returns true if {@code type} is a {@code Set<Produced<T>>}. */
+ static boolean isSetOfProduced(TypeMirror type) {
+ return MoreTypes.isType(type)
+ && MoreTypes.isTypeOf(Set.class, type)
+ && MoreTypes.isTypeOf(Produced.class, MoreTypes.asDeclared(type).getTypeArguments().get(0));
+ }
+
/**
* Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Wrapper} for that type.
*/
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java b/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java
index b2cb45134..8dbf27bc3 100644
--- a/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java
+++ b/compiler/src/main/java/dagger/internal/codegen/writer/AnnotationWriter.java
@@ -15,9 +15,10 @@
*/
package dagger.internal.codegen.writer;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import java.io.IOException;
import java.util.Map.Entry;
import java.util.Set;
@@ -27,6 +28,7 @@ import static dagger.internal.codegen.writer.Writables.toStringWritable;
public final class AnnotationWriter implements Writable, HasClassReferences {
private final ClassName annotationName;
+ private final Set<HasClassReferences> memberReferences = Sets.newLinkedHashSet();
private final SortedMap<String, Writable> memberMap = Maps.newTreeMap();
AnnotationWriter(ClassName annotationName) {
@@ -45,6 +47,12 @@ public final class AnnotationWriter implements Writable, HasClassReferences {
memberMap.put(name, toStringWritable(StringLiteral.forValue(value)));
}
+ public <T extends Enum<T>> void setMember(String name, T value) {
+ Snippet snippet = Snippet.format("%s.%s", ClassName.fromClass(value.getClass()), value);
+ memberMap.put(name, snippet);
+ memberReferences.add(snippet);
+ }
+
@Override
public Appendable write(Appendable appendable, Context context) throws IOException {
appendable.append('@');
@@ -65,6 +73,9 @@ public final class AnnotationWriter implements Writable, HasClassReferences {
@Override
public Set<ClassName> referencedClasses() {
- return ImmutableSet.of(annotationName);
+ return FluentIterable.from(memberReferences)
+ .append(annotationName)
+ .transformAndConcat(HasClassReferences.COMBINER)
+ .toSet();
}
}
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java b/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java
index 5b7776853..bd0791fc6 100644
--- a/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java
+++ b/compiler/src/main/java/dagger/internal/codegen/writer/ClassName.java
@@ -127,7 +127,6 @@ public final class ClassName implements TypeName, Comparable<ClassName> {
public ClassName nestedClassNamed(String memberClassName) {
checkNotNull(memberClassName);
checkArgument(SourceVersion.isIdentifier(memberClassName));
- checkArgument(Ascii.isUpperCase(memberClassName.charAt(0)));
return new ClassName(packageName(),
new ImmutableList.Builder<String>()
.addAll(enclosingSimpleNames())
@@ -139,10 +138,17 @@ public final class ClassName implements TypeName, Comparable<ClassName> {
public ClassName peerNamed(String peerClassName) {
checkNotNull(peerClassName);
checkArgument(SourceVersion.isIdentifier(peerClassName));
- checkArgument(Ascii.isUpperCase(peerClassName.charAt(0)));
return new ClassName(packageName(), enclosingSimpleNames(), peerClassName);
}
+ /**
+ * Returns a parameterized type name with this as its raw type if {@code parameters} is not empty.
+ * If {@code parameters} is empty, returns this object.
+ */
+ public TypeName withTypeParameters(List<? extends TypeName> parameters) {
+ return parameters.isEmpty() ? this : ParameterizedTypeName.create(this, parameters);
+ }
+
private static final ImmutableSet<NestingKind> ACCEPTABLE_NESTING_KINDS =
Sets.immutableEnumSet(TOP_LEVEL, MEMBER);
diff --git a/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java b/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java
index 2d11f60f7..80ab944f8 100644
--- a/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java
+++ b/compiler/src/main/java/dagger/internal/codegen/writer/Snippet.java
@@ -27,28 +27,9 @@ import java.util.Formatter;
import java.util.Iterator;
import java.util.Set;
-public final class Snippet implements HasClassReferences, Writable {
- private final String format;
- private final ImmutableSet<TypeName> types;
- private final ImmutableList<Object> args;
-
- private Snippet(String format, ImmutableSet<TypeName> types, ImmutableList<Object> args) {
- this.format = format;
- this.types = types;
- this.args = args;
- }
-
- public String format() {
- return format;
- }
+public abstract class Snippet implements HasClassReferences, Writable {
- public ImmutableList<Object> args() {
- return args;
- }
-
- public ImmutableSet<TypeName> types() {
- return types;
- }
+ abstract ImmutableSet<TypeName> types();
@Override
public String toString() {
@@ -56,40 +37,96 @@ public final class Snippet implements HasClassReferences, Writable {
}
@Override
- public Set<ClassName> referencedClasses() {
- return FluentIterable.from(types)
- .transformAndConcat(new Function<TypeName, Set<ClassName>>() {
- @Override
- public Set<ClassName> apply(TypeName input) {
- return input.referencedClasses();
- }
- })
+ public final Set<ClassName> referencedClasses() {
+ return FluentIterable.from(types())
+ .transformAndConcat(
+ new Function<TypeName, Set<ClassName>>() {
+ @Override
+ public Set<ClassName> apply(TypeName input) {
+ return input.referencedClasses();
+ }
+ })
.toSet();
}
- @Override
- public Appendable write(Appendable appendable, Context context) throws IOException {
- ImmutableList.Builder<Object> formattedArgsBuilder = ImmutableList.builder();
- for (Object arg : args) {
- if (arg instanceof Writable) {
- formattedArgsBuilder.add(((Writable) arg).write(new StringBuilder(), context).toString());
- } else {
- formattedArgsBuilder.add(arg);
+ private static final class BasicSnippet extends Snippet {
+ final String format;
+ final ImmutableSet<TypeName> types;
+ final ImmutableList<Object> args;
+
+ BasicSnippet(String format, ImmutableSet<TypeName> types, ImmutableList<Object> args) {
+ this.format = format;
+ this.types = types;
+ this.args = args;
+ }
+
+ @Override
+ ImmutableSet<TypeName> types() {
+ return types;
+ }
+
+ @Override
+ public Appendable write(Appendable appendable, Context context) throws IOException {
+ ImmutableList.Builder<Object> formattedArgsBuilder = ImmutableList.builder();
+ for (Object arg : args) {
+ if (arg instanceof Writable) {
+ formattedArgsBuilder.add(((Writable) arg).write(new StringBuilder(), context).toString());
+ } else {
+ formattedArgsBuilder.add(arg);
+ }
}
+
+ @SuppressWarnings("resource") // intentionally don't close the formatter
+ Formatter formatter = new Formatter(appendable);
+ formatter.format(format, Iterables.toArray(formattedArgsBuilder.build(), Object.class));
+
+ return appendable;
+ }
+ }
+
+ private static final class CompoundSnippet extends Snippet {
+ final String joinToken;
+ final ImmutableList<Snippet> snippets;
+
+ CompoundSnippet(String joinToken, ImmutableList<Snippet> snippets) {
+ this.joinToken = joinToken;
+ this.snippets = snippets;
}
- @SuppressWarnings("resource") // intentionally don't close the formatter
- Formatter formatter = new Formatter(appendable);
- formatter.format(format, formattedArgsBuilder.build().toArray(new Object[0]));
+ @Override
+ ImmutableSet<TypeName> types() {
+ return FluentIterable.from(snippets)
+ .transformAndConcat(
+ new Function<Snippet, Iterable<TypeName>>() {
+ @Override
+ public Iterable<TypeName> apply(Snippet input) {
+ return input.types();
+ }
+ })
+ .toSet();
+ }
- return appendable;
+ @Override
+ public Appendable write(Appendable appendable, Context context) throws IOException {
+ Iterator<Snippet> snippetIterator = snippets.iterator();
+ if (snippetIterator.hasNext()) {
+ Snippet firstSnippet = snippetIterator.next();
+ firstSnippet.write(appendable, context);
+ while (snippetIterator.hasNext()) {
+ Snippet nextSnippet = snippetIterator.next();
+ appendable.append(joinToken);
+ nextSnippet.write(appendable, context);
+ }
+ }
+ return appendable;
+ }
}
public static Snippet format(String format, Object... args) {
ImmutableSet.Builder<TypeName> types = ImmutableSet.builder();
for (Object arg : args) {
if (arg instanceof Snippet) {
- types.addAll(((Snippet) arg).types);
+ types.addAll(((Snippet) arg).types());
}
if (arg instanceof TypeName) {
types.add((TypeName) arg);
@@ -98,7 +135,7 @@ public final class Snippet implements HasClassReferences, Writable {
types.add(((HasTypeName) arg).name());
}
}
- return new Snippet(format, types.build(), ImmutableList.copyOf(args));
+ return new BasicSnippet(format, types.build(), ImmutableList.copyOf(args));
}
public static Snippet format(String format, Iterable<? extends Object> args) {
@@ -121,66 +158,20 @@ public final class Snippet implements HasClassReferences, Writable {
}
public static Snippet makeParametersSnippet(Iterable<Snippet> parameterSnippets) {
- Iterator<Snippet> iterator = parameterSnippets.iterator();
- StringBuilder stringBuilder = new StringBuilder();
- ImmutableSet.Builder<TypeName> typesBuilder = ImmutableSet.builder();
- ImmutableList.Builder<Object> argsBuilder = ImmutableList.builder();
- if (iterator.hasNext()) {
- Snippet firstSnippet = iterator.next();
- stringBuilder.append(firstSnippet.format());
- typesBuilder.addAll(firstSnippet.types());
- argsBuilder.addAll(firstSnippet.args());
- }
- while (iterator.hasNext()) {
- Snippet nextSnippet = iterator.next();
- stringBuilder.append(", ").append(nextSnippet.format());
- typesBuilder.addAll(nextSnippet.types());
- argsBuilder.addAll(nextSnippet.args());
- }
- return new Snippet(stringBuilder.toString(), typesBuilder.build(), argsBuilder.build());
+ return join(", ", parameterSnippets);
}
/**
- * A snippet that concatenates its arguments.
+ * A snippet that concatenates its arguments with each snippet separated by a new line.
*/
public static Snippet concat(Iterable<Snippet> snippets) {
- return join(Joiner.on(""), snippets);
+ return join("\n", snippets);
}
/**
* A snippet that joins its arguments with {@code joiner}.
*/
- public static Snippet join(Joiner joiner, Iterable<Snippet> snippets) {
- FluentIterable<Snippet> fluentSnippets = FluentIterable.from(snippets);
- return new Snippet(
- joiner
- .appendTo(
- new StringBuilder(),
- fluentSnippets.transform(
- new Function<Snippet, String>() {
- @Override
- public String apply(Snippet snippet) {
- return snippet.format;
- }
- }))
- .toString(),
- fluentSnippets
- .transformAndConcat(
- new Function<Snippet, ImmutableSet<TypeName>>() {
- @Override
- public ImmutableSet<TypeName> apply(Snippet snippet) {
- return snippet.types;
- }
- })
- .toSet(),
- fluentSnippets
- .transformAndConcat(
- new Function<Snippet, ImmutableList<Object>>() {
- @Override
- public ImmutableList<Object> apply(Snippet snippet) {
- return snippet.args;
- }
- })
- .toList());
+ public static Snippet join(String joinToken, Iterable<Snippet> snippets) {
+ return new CompoundSnippet(joinToken, ImmutableList.copyOf(snippets));
}
}
diff --git a/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java b/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java
index 20bafff65..cf0e69d33 100644
--- a/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/ComponentBuilderTest.java
@@ -81,6 +81,7 @@ public class ComponentBuilderTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" }",
"",
@@ -155,6 +156,7 @@ public class ComponentBuilderTest {
" return builder().create();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.stringProvider = TestModule_StringFactory.create(builder.testModule);",
" }",
@@ -257,6 +259,7 @@ public class ComponentBuilderTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.stringProvider = TestModule1_StringFactory.create(builder.testModule1);",
" this.integerProvider = TestModule2_IntegerFactory.create(builder.testModule2);",
diff --git a/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java
index 94e48e2c8..28e3b4570 100644
--- a/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/ComponentProcessorTest.java
@@ -232,6 +232,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" }",
"",
@@ -318,6 +319,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.someInjectableTypeProvider =",
" ScopedProvider.create(SomeInjectableType_Factory.create());",
@@ -400,6 +402,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.bMembersInjector =",
" OuterType$B_MembersInjector.create(OuterType$A_Factory.create());",
@@ -499,6 +502,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.bProvider = TestModule_BFactory.create(builder.testModule,",
" C_Factory.create());",
@@ -778,6 +782,160 @@ public class ComponentProcessorTest {
.compilesWithoutError();
}
+ @Test
+ public void subcomponentOmitsInheritedBindings() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface Parent {",
+ " Child child();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.mapkeys.StringKey;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "import static dagger.Provides.Type.SET;",
+ "import static dagger.Provides.Type.MAP;",
+ "",
+ "@Module",
+ "class ParentModule {",
+ " @Provides(type = SET) static Object parentObject() {",
+ " return \"parent object\";",
+ " }",
+ "",
+ " @Provides(type = MAP) @StringKey(\"parent key\") Object parentKeyObject() {",
+ " return \"parent value\";",
+ " }",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "test.Child",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " Set<Object> objectSet();",
+ " Map<String, Object> objectMap();",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerParent",
+ "package test;",
+ "",
+ "import dagger.internal.MapFactory;",
+ "import dagger.internal.MapProviderFactory;",
+ "import dagger.internal.SetFactory;",
+ "import java.util.Map;",
+ "import java.util.Set;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "",
+ "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
+ "public final class DaggerParent implements Parent {",
+ " private Provider<Set<Object>> setOfObjectContribution1Provider;",
+ " private Provider<Set<Object>> setOfObjectProvider;",
+ " private Provider<Object> mapOfStringAndProviderOfObjectContribution1;",
+ " private Provider<Map<String, Provider<Object>>>",
+ " mapOfStringAndProviderOfObjectProvider;",
+ "",
+ " private DaggerParent(Builder builder) {",
+ " assert builder != null;",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static Parent create() {",
+ " return builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.setOfObjectContribution1Provider =",
+ " ParentModule_ParentObjectFactory.create();",
+ " this.setOfObjectProvider = SetFactory.create(setOfObjectContribution1Provider);",
+ " this.mapOfStringAndProviderOfObjectContribution1 =",
+ " ParentModule_ParentKeyObjectFactory.create(builder.parentModule);",
+ " this.mapOfStringAndProviderOfObjectProvider =",
+ " MapProviderFactory.<String, Object>builder(1)",
+ " .put(\"parent key\", mapOfStringAndProviderOfObjectContribution1)",
+ " .build();",
+ " }",
+ "",
+ " @Override",
+ " public Child child() {",
+ " return new ChildImpl();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private ParentModule parentModule;",
+ "",
+ " private Builder() {}",
+ "",
+ " public Parent build() {",
+ " if (parentModule == null) {",
+ " this.parentModule = new ParentModule();",
+ " }",
+ " return new DaggerParent(this);",
+ " }",
+ "",
+ " public Builder parentModule(ParentModule parentModule) {",
+ " if (parentModule == null) {",
+ " throw new NullPointerException();",
+ " }",
+ " this.parentModule = parentModule;",
+ " return this;",
+ " }",
+ " }",
+ "",
+ " private final class ChildImpl implements Child {",
+ " private Provider<Map<String, Object>> mapOfStringAndObjectProvider;",
+ "",
+ " private ChildImpl() {",
+ " initialize();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.mapOfStringAndObjectProvider = MapFactory.create(",
+ " DaggerParent.this.mapOfStringAndProviderOfObjectProvider);",
+ " }",
+ "",
+ " @Override",
+ " public Set<Object> objectSet() {",
+ " return DaggerParent.this.setOfObjectProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Map<String, Object> objectMap() {",
+ " return mapOfStringAndObjectProvider.get();",
+ " }",
+ " }",
+ "}");
+ assertAbout(javaSources())
+ .that(ImmutableList.of(parent, parentModule, child))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expected);
+ }
+
@Test public void testDefaultPackage() {
JavaFileObject aClass = JavaFileObjects.forSourceLines("AClass", "class AClass {}");
JavaFileObject bClass = JavaFileObjects.forSourceLines("BClass",
@@ -875,6 +1033,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.setOfStringContribution1Provider =",
" EmptySetModule_EmptySetFactory.create(builder.emptySetModule);",
@@ -985,6 +1144,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.someInjectedTypeMembersInjector =",
" SomeInjectedType_MembersInjector.create(SomeInjectableType_Factory.create());",
@@ -1064,6 +1224,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.simpleComponentProvider = InstanceFactory.<SimpleComponent>create(this);",
" this.someInjectableTypeProvider =",
@@ -1143,6 +1304,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.someInjectedTypeMembersInjector =",
" SomeInjectedType_MembersInjector.create(SomeInjectableType_Factory.create());",
@@ -1220,6 +1382,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.someInjectableTypeProvider =",
" SomeInjectableType_Factory.create((MembersInjector) MembersInjectors.noOp());",
@@ -1309,6 +1472,7 @@ public class ComponentProcessorTest {
" return new Builder();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.aProvider = new Factory<A>() {",
" private final AComponent aComponent = builder.aComponent;",
@@ -1429,6 +1593,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.aProvider = test.TestModule_AFactory.create(builder.testModule);",
" this.aProvider1 = TestModule_AFactory.create(builder.testModule1);",
@@ -1558,6 +1723,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.bProvider = B_Factory.create(C_Factory.create());",
" this.aProvider = A_Factory.create(bProvider);",
@@ -1660,6 +1826,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {}",
"",
" @Override",
@@ -1748,6 +1915,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {}",
"",
" @Override",
@@ -1957,6 +2125,7 @@ public class ComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.dProvider = new D_Factory(B_Factory.INSTANCE);",
" }",
diff --git a/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java b/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java
index 7766f244a..b47a43c1c 100644
--- a/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/DependencyRequestMapperTest.java
@@ -57,7 +57,7 @@ public class DependencyRequestMapperTest {
this.types = compilationRule.getTypes();
this.elements = compilationRule.getElements();
this.keyFactory = new Key.Factory(types, elements);
- this.dependencyRequestFactory = new DependencyRequest.Factory(keyFactory);
+ this.dependencyRequestFactory = new DependencyRequest.Factory(elements, keyFactory);
}
private List<? extends VariableElement> sampleProviderParameters() {
diff --git a/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java
index 36303f1e9..6001ea713 100644
--- a/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/GraphValidationTest.java
@@ -28,8 +28,7 @@ import org.junit.runners.JUnit4;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static dagger.internal.codegen.ErrorMessages.NULLABLE_TO_NON_NULLABLE;
-import static java.util.Arrays.asList;
+import static dagger.internal.codegen.ErrorMessages.nullableToNonNullable;
@RunWith(JUnit4.class)
public class GraphValidationTest {
@@ -69,25 +68,38 @@ public class GraphValidationTest {
}
@Test public void componentProvisionWithNoDependencyChain() {
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "final class TestClass {",
- " interface A {}",
- "",
- " @Component()",
- " interface AComponent {",
- " A getA();",
- " }",
- "}");
- String expectedError =
- "test.TestClass.A cannot be provided without an @Provides-annotated method.";
- assertAbout(javaSource()).that(component)
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Qualifier;",
+ "",
+ "final class TestClass {",
+ " @Qualifier @interface Q {}",
+ " interface A {}",
+ "",
+ " @Component()",
+ " interface AComponent {",
+ " A getA();",
+ " @Q A qualifiedA();",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(component)
.processedWith(new ComponentProcessor())
.failsToCompile()
- .withErrorContaining(expectedError).in(component).onLine(10);
+ .withErrorContaining(
+ "test.TestClass.A cannot be provided without an @Provides-annotated method.")
+ .in(component)
+ .onLine(12)
+ .and()
+ .withErrorContaining(
+ "@test.TestClass.Q test.TestClass.A "
+ + "cannot be provided without an @Provides-annotated method.")
+ .in(component)
+ .onLine(13);
}
@Test public void constructorInjectionWithoutAnnotation() {
@@ -923,8 +935,10 @@ public class GraphValidationTest {
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
.processedWith(new ComponentProcessor())
.failsToCompile()
- .withErrorContaining(String.format(NULLABLE_TO_NON_NULLABLE, "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
+ .withErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
// but if we disable the validation, then it compiles fine.
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
@@ -965,8 +979,10 @@ public class GraphValidationTest {
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
.processedWith(new ComponentProcessor())
.failsToCompile()
- .withErrorContaining(String.format(NULLABLE_TO_NON_NULLABLE, "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
+ .withErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
// but if we disable the validation, then it compiles fine.
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
@@ -1007,8 +1023,10 @@ public class GraphValidationTest {
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
.processedWith(new ComponentProcessor())
.failsToCompile()
- .withErrorContaining(String.format(NULLABLE_TO_NON_NULLABLE, "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
+ .withErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
// but if we disable the validation, then it compiles fine.
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, a, module, component))
@@ -1040,8 +1058,10 @@ public class GraphValidationTest {
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, module, component))
.processedWith(new ComponentProcessor())
.failsToCompile()
- .withErrorContaining(String.format(NULLABLE_TO_NON_NULLABLE, "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
+ .withErrorContaining(
+ nullableToNonNullable(
+ "java.lang.String",
+ "@test.Nullable @Provides String test.TestModule.provideString()"));
// but if we disable the validation, then it compiles fine.
assertAbout(javaSources()).that(ImmutableList.of(NULLABLE, module, component))
diff --git a/compiler/src/test/java/dagger/internal/codegen/PackageProxyTest.java b/compiler/src/test/java/dagger/internal/codegen/InaccessibleTypeTest.java
index 8df80d19b..635592214 100644
--- a/compiler/src/test/java/dagger/internal/codegen/PackageProxyTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/InaccessibleTypeTest.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
@@ -7,12 +22,12 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
@RunWith(JUnit4.class)
-public class PackageProxyTest {
- @Test public void basicPackageProxy() {
+public class InaccessibleTypeTest {
+ @Test public void basicInjectedType() {
JavaFileObject noDepClassFile = JavaFileObjects.forSourceLines("foreign.NoDepClass",
"package foreign;",
"",
@@ -62,7 +77,6 @@ public class PackageProxyTest {
"test.DaggerTestComponent",
"package test;",
"",
- "import foreign.DaggerTestComponent_PackageProxy;",
"import foreign.NoDepClass_Factory;",
"import foreign.NonPublicClass1_Factory;",
"import foreign.NonPublicClass2_Factory;",
@@ -73,8 +87,10 @@ public class PackageProxyTest {
"",
"@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
"public final class DaggerTestComponent implements TestComponent {",
- " private final DaggerTestComponent_PackageProxy foreign_Proxy =",
- " new DaggerTestComponent_PackageProxy();",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider nonPublicClass1Provider;",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider nonPublicClass2Provider;",
" private Provider<PublicClass> publicClassProvider;",
"",
" private DaggerTestComponent(Builder builder) {",
@@ -90,14 +106,15 @@ public class PackageProxyTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
- " this.foreign_Proxy.nonPublicClass1Provider =",
+ " this.nonPublicClass1Provider =",
" NonPublicClass1_Factory.create(NoDepClass_Factory.create());",
- " this.foreign_Proxy.nonPublicClass2Provider =",
+ " this.nonPublicClass2Provider =",
" NonPublicClass2_Factory.create(NoDepClass_Factory.create());",
" this.publicClassProvider = PublicClass_Factory.create(",
- " foreign_Proxy.nonPublicClass1Provider,",
- " foreign_Proxy.nonPublicClass2Provider,",
+ " nonPublicClass1Provider,",
+ " nonPublicClass2Provider,",
" NoDepClass_Factory.create());",
" }",
"",
@@ -115,19 +132,20 @@ public class PackageProxyTest {
" }",
" }",
"}");
- assert_().about(javaSources())
+ assertAbout(javaSources())
.that(ImmutableList.of(
noDepClassFile,
publicClassFile,
nonPublicClass1File,
nonPublicClass2File,
componentFile))
+ .withCompilerOptions("-Xlint:rawtypes", "-Xlint:unchecked", "-Werror")
.processedWith(new ComponentProcessor())
.compilesWithoutError()
.and().generatesSources(generatedComponent);
}
- @Test public void memberInjectionPackageProxy() {
+ @Test public void memberInjectedType() {
JavaFileObject noDepClassFile = JavaFileObjects.forSourceLines("test.NoDepClass",
"package test;",
"",
@@ -209,6 +227,7 @@ public class PackageProxyTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.aMembersInjector = A_MembersInjector.create(NoDepClass_Factory.create());",
" }",
@@ -227,7 +246,7 @@ public class PackageProxyTest {
" }",
" }",
"}");
- assert_().about(javaSources())
+ assertAbout(javaSources())
.that(ImmutableList.of(
noDepClassFile,
aClassFile,
@@ -235,6 +254,7 @@ public class PackageProxyTest {
cClassFile,
dClassFile,
componentFile))
+ .withCompilerOptions("-Xlint:rawtypes", "-Xlint:unchecked", "-Werror")
.processedWith(new ComponentProcessor())
.compilesWithoutError()
.and().generatesSources(generatedComponent);
diff --git a/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java
index 5f488c814..9e1b6dccd 100644
--- a/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/MapBindingComponentProcessorTest.java
@@ -137,6 +137,7 @@ public class MapBindingComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.mapOfPathEnumAndProviderOfHandlerContribution1 =",
" MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
@@ -296,6 +297,7 @@ public class MapBindingComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.mapOfStringAndProviderOfHandlerContribution1 =",
" MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
@@ -462,6 +464,7 @@ public class MapBindingComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.mapOfWrappedClassKeyAndProviderOfHandlerContribution1 =",
" MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
@@ -634,6 +637,7 @@ public class MapBindingComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.mapOfPathEnumAndProviderOfHandlerContribution1 =",
" MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
@@ -755,6 +759,7 @@ public class MapBindingComponentProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.provideAMapProvider = MapModule_ProvideAMapFactory.create(builder.mapModule);",
" }",
diff --git a/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java
index bc8a2660e..191ee6c12 100644
--- a/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/MapKeyProcessorTest.java
@@ -226,6 +226,7 @@ public class MapKeyProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.mapOfPathKeyAndProviderOfHandlerContribution1 =",
" MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
@@ -401,6 +402,7 @@ public class MapKeyProcessorTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.mapOfPathKeyAndProviderOfHandlerContribution1 =",
" MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
diff --git a/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java b/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java
index 7925bd222..52be72ae9 100644
--- a/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/MembersInjectionTest.java
@@ -33,6 +33,7 @@ import org.junit.runners.JUnit4;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
@RunWith(JUnit4.class)
public class MembersInjectionTest {
@@ -87,6 +88,7 @@ public class MembersInjectionTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.childProvider =",
" Child_Factory.create((MembersInjector) MembersInjectors.noOp());",
@@ -176,6 +178,7 @@ public class MembersInjectionTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.childMembersInjector = Child_MembersInjector.create(Dep_Factory.create());",
" this.childProvider = Child_Factory.create(childMembersInjector);",
@@ -872,4 +875,47 @@ public class MembersInjectionTest {
.and()
.generatesSources(bMembersInjector);
}
+
+ @Test
+ public void lowerCaseNamedMembersInjector_forLowerCaseType() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class foo {",
+ " @Inject String string;",
+ "}");
+ JavaFileObject fooModule =
+ JavaFileObjects.forSourceLines(
+ "test.fooModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class fooModule {",
+ " @Provides String string() { return \"foo\"; }",
+ "}");
+ JavaFileObject fooComponent =
+ JavaFileObjects.forSourceLines(
+ "test.fooComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = fooModule.class)",
+ "interface fooComponent {",
+ " void inject(foo target);",
+ "}");
+
+ assertAbout(javaSources())
+ .that(ImmutableList.of(foo, fooModule, fooComponent))
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and().generatesFileNamed(CLASS_OUTPUT, "test", "foo_MembersInjector.class");
+ }
}
diff --git a/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java b/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java
index 0ae01b40c..03ec35d9b 100644
--- a/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/MissingBindingSuggestionsTest.java
@@ -15,17 +15,14 @@
*/
package dagger.internal.codegen;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.JavaFileObjects;
-import java.util.Arrays;
import javax.tools.JavaFileObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
@RunWith(JUnit4.class)
diff --git a/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java b/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
index 0041f5d89..f9c287859 100644
--- a/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
@@ -471,53 +471,52 @@ public class ProducerModuleFactoryGeneratorTest {
"TestModule_ProduceStringFactory",
"package test;",
"",
+ "import com.google.common.util.concurrent.AsyncFunction;",
"import com.google.common.util.concurrent.Futures;",
"import com.google.common.util.concurrent.ListenableFuture;",
"import dagger.producers.internal.AbstractProducer;",
- "import dagger.producers.internal.Producers;",
+ "import dagger.producers.monitoring.ProducerMonitor;",
"import dagger.producers.monitoring.ProducerToken;",
"import dagger.producers.monitoring.ProductionComponentMonitor;",
- "import java.util.concurrent.Callable;",
"import java.util.concurrent.Executor;",
"import javax.annotation.Generated;",
- "import javax.annotation.Nullable;",
+ "import javax.inject.Provider;",
"",
"@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
"public final class TestModule_ProduceStringFactory extends AbstractProducer<String> {",
" private final TestModule module;",
" private final Executor executor;",
+ " private final Provider<ProductionComponentMonitor> monitorProvider;",
"",
" public TestModule_ProduceStringFactory(",
- " @Nullable ProductionComponentMonitor componentMonitor,",
" TestModule module,",
- " Executor executor) {",
+ " Executor executor,",
+ " Provider<ProductionComponentMonitor> monitorProvider) {",
" super(",
- " Producers.producerMonitorFor(",
- " componentMonitor,",
- " ProducerToken.create(TestModule_ProduceStringFactory.class)));",
+ " monitorProvider,",
+ " ProducerToken.create(TestModule_ProduceStringFactory.class));",
" assert module != null;",
" this.module = module;",
" assert executor != null;",
" this.executor = executor;",
+ " assert monitorProvider != null;",
+ " this.monitorProvider = monitorProvider;",
" }",
"",
- " @Override protected ListenableFuture<String> compute() {",
- " ListenableFuture<ListenableFuture<String>> future = Producers.submitToExecutor(",
- " new Callable<ListenableFuture<String>>() {",
- " @Override public ListenableFuture<String> call() {",
- " if (monitor != null) {",
- " monitor.methodStarting();",
- " }",
+ " @Override protected ListenableFuture<String> compute(",
+ " final ProducerMonitor monitor) {",
+ " return Futures.transform(",
+ " Futures.<Void>immediateFuture(null),",
+ " new AsyncFunction<Void, String>() {",
+ " @Override public ListenableFuture<String> apply(Void ignoredVoidArg) {",
+ " monitor.methodStarting();",
" try {",
" return module.produceString();",
" } finally {",
- " if (monitor != null) {",
- " monitor.methodFinished();",
- " }",
+ " monitor.methodFinished();",
" }",
" }",
" }, executor);",
- " return Futures.dereference(future);",
" }",
"}");
assertAbout(javaSource())
diff --git a/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java b/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java
index 92bbb752c..a8e39b28a 100644
--- a/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/ProductionComponentProcessorTest.java
@@ -123,98 +123,136 @@ public class ProductionComponentProcessorTest {
" ListenableFuture<A> a();",
" }",
"}");
- JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
- "test.DaggerTestClass_SimpleComponent",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.Producer;",
- "import dagger.producers.internal.Producers;",
- "import java.util.concurrent.Executor;",
- "import javax.annotation.Generated;",
- "import javax.inject.Provider;",
- "import test.TestClass.A;",
- "import test.TestClass.AModule;",
- "import test.TestClass.B;",
- "import test.TestClass.BModule;",
- "import test.TestClass.SimpleComponent;",
- "",
- "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
- "public final class DaggerTestClass_SimpleComponent implements SimpleComponent {",
- " private Provider<B> bProvider;",
- " private Producer<A> aProducer;",
- "",
- " private DaggerTestClass_SimpleComponent(Builder builder) {",
- " assert builder != null;",
- " initialize(builder);",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " private void initialize(final Builder builder) {",
- " this.bProvider = TestClass$BModule_BFactory.create(",
- " builder.bModule, TestClass$C_Factory.create());",
- " this.aProducer = new TestClass$AModule_AFactory(",
- " null,",
- " builder.aModule,",
- " builder.executor,",
- " Producers.producerFromProvider(bProvider));",
- " }",
- "",
- " @Override",
- " public ListenableFuture<A> a() {",
- " return aProducer.get();",
- " }",
- "",
- " public static final class Builder {",
- " private BModule bModule;",
- " private AModule aModule;",
- " private Executor executor;",
- "",
- " private Builder() {",
- " }",
- "",
- " public SimpleComponent build() {",
- " if (bModule == null) {",
- " this.bModule = new BModule();",
- " }",
- " if (aModule == null) {",
- " this.aModule = new AModule();",
- " }",
- " if (executor == null) {",
- " throw new IllegalStateException(Executor.class.getCanonicalName()",
- " + \" must be set\");",
- " }",
- " return new DaggerTestClass_SimpleComponent(this);",
- " }",
- "",
- " public Builder aModule(AModule aModule) {",
- " if (aModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.aModule = aModule;",
- " return this;",
- " }",
- "",
- " public Builder bModule(BModule bModule) {",
- " if (bModule == null) {",
- " throw new NullPointerException();",
- " }",
- " this.bModule = bModule;",
- " return this;",
- " }",
- "",
- " public Builder executor(Executor executor) {",
- " if (executor == null) {",
- " throw new NullPointerException();",
- " }",
- " this.executor = executor;",
- " return this;",
- " }",
- " }",
- "}");
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestClass_SimpleComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.internal.InstanceFactory;",
+ "import dagger.internal.SetFactory;",
+ "import dagger.producers.Producer;",
+ "import dagger.producers.internal.Producers;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor.Factory;",
+ "import java.util.Set;",
+ "import java.util.concurrent.Executor;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "import test.TestClass.A;",
+ "import test.TestClass.AModule;",
+ "import test.TestClass.B;",
+ "import test.TestClass.BModule;",
+ "import test.TestClass.SimpleComponent;",
+ "",
+ "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
+ "public final class DaggerTestClass_SimpleComponent implements SimpleComponent {",
+ " private Provider<SimpleComponent> simpleComponentProvider;",
+ " private Provider<Set<Factory>> setOfFactoryContribution1Provider;",
+ " private Provider<Set<Factory>> setOfFactoryProvider;",
+ " private Provider<ProductionComponentMonitor> monitorProvider;",
+ " private Provider<B> bProvider;",
+ " private Producer<A> aProducer;",
+ "",
+ " private DaggerTestClass_SimpleComponent(Builder builder) {",
+ " assert builder != null;",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.simpleComponentProvider = InstanceFactory.<SimpleComponent>create(this);",
+ " this.setOfFactoryContribution1Provider =",
+ " TestClass$SimpleComponent_MonitoringModule_DefaultSetOfFactoriesFactory",
+ " .create();",
+ " this.setOfFactoryProvider = SetFactory.create(setOfFactoryContribution1Provider);",
+ " this.monitorProvider =",
+ " TestClass$SimpleComponent_MonitoringModule_MonitorFactory.create(",
+ " builder.testClass$SimpleComponent_MonitoringModule,",
+ " simpleComponentProvider,",
+ " setOfFactoryProvider);",
+ " this.bProvider = TestClass$BModule_BFactory.create(",
+ " builder.bModule, TestClass$C_Factory.create());",
+ " this.aProducer = new TestClass$AModule_AFactory(",
+ " builder.aModule,",
+ " builder.executor,",
+ " monitorProvider,",
+ " Producers.producerFromProvider(bProvider));",
+ " }",
+ "",
+ " @Override",
+ " public ListenableFuture<A> a() {",
+ " return aProducer.get();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private TestClass$SimpleComponent_MonitoringModule",
+ " testClass$SimpleComponent_MonitoringModule;",
+ " private BModule bModule;",
+ " private AModule aModule;",
+ " private Executor executor;",
+ "",
+ " private Builder() {",
+ " }",
+ "",
+ " public SimpleComponent build() {",
+ " if (testClass$SimpleComponent_MonitoringModule == null) {",
+ " this.testClass$SimpleComponent_MonitoringModule =",
+ " new TestClass$SimpleComponent_MonitoringModule();",
+ " }",
+ " if (bModule == null) {",
+ " this.bModule = new BModule();",
+ " }",
+ " if (aModule == null) {",
+ " this.aModule = new AModule();",
+ " }",
+ " if (executor == null) {",
+ " throw new IllegalStateException(Executor.class.getCanonicalName()",
+ " + \" must be set\");",
+ " }",
+ " return new DaggerTestClass_SimpleComponent(this);",
+ " }",
+ "",
+ " public Builder aModule(AModule aModule) {",
+ " if (aModule == null) {",
+ " throw new NullPointerException();",
+ " }",
+ " this.aModule = aModule;",
+ " return this;",
+ " }",
+ "",
+ " public Builder bModule(BModule bModule) {",
+ " if (bModule == null) {",
+ " throw new NullPointerException();",
+ " }",
+ " this.bModule = bModule;",
+ " return this;",
+ " }",
+ "",
+ " public Builder testClass$SimpleComponent_MonitoringModule(",
+ " TestClass$SimpleComponent_MonitoringModule",
+ " testClass$SimpleComponent_MonitoringModule) {",
+ " if (testClass$SimpleComponent_MonitoringModule == null) {",
+ " throw new NullPointerException();",
+ " }",
+ " this.testClass$SimpleComponent_MonitoringModule =",
+ " testClass$SimpleComponent_MonitoringModule;",
+ " return this;",
+ " }",
+ "",
+ " public Builder executor(Executor executor) {",
+ " if (executor == null) {",
+ " throw new NullPointerException();",
+ " }",
+ " this.executor = executor;",
+ " return this;",
+ " }",
+ " }",
+ "}");
assertAbout(javaSource()).that(component)
.processedWith(new ComponentProcessor())
.compilesWithoutError()
diff --git a/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java
index 9f577b722..6fc871684 100644
--- a/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/ProductionGraphValidationTest.java
@@ -161,4 +161,110 @@ public class ProductionGraphValidationTest {
.failsToCompile()
.withErrorContaining(expectedError).in(component).onLine(20);
}
+
+ @Test
+ public void monitoringDependsOnUnboundType() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.ProductionComponent;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "",
+ "import static dagger.Provides.Type.SET;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " @Module",
+ " final class MonitoringModule {",
+ " @Provides(type = SET)",
+ " ProductionComponentMonitor.Factory monitorFactory(A unbound) {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " final class StringModule {",
+ " @Produces ListenableFuture<String> str() {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(modules = {MonitoringModule.class, StringModule.class})",
+ " interface StringComponent {",
+ " ListenableFuture<String> getString();",
+ " }",
+ "}");
+ String expectedError =
+ "test.TestClass.A cannot be provided without an @Provides-annotated method.";
+ assertAbout(javaSource())
+ .that(component)
+ .processedWith(new ComponentProcessor())
+ .failsToCompile()
+ .withErrorContaining(expectedError)
+ .in(component)
+ .onLine(33);
+ }
+
+ @Test
+ public void monitoringDependsOnProduction() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.producers.ProducerModule;",
+ "import dagger.producers.Produces;",
+ "import dagger.producers.ProductionComponent;",
+ "import dagger.producers.monitoring.ProductionComponentMonitor;",
+ "",
+ "import static dagger.Provides.Type.SET;",
+ "",
+ "final class TestClass {",
+ " interface A {}",
+ "",
+ " @Module",
+ " final class MonitoringModule {",
+ " @Provides(type = SET) ProductionComponentMonitor.Factory monitorFactory(A a) {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProducerModule",
+ " final class StringModule {",
+ " @Produces A a() {",
+ " return null;",
+ " }",
+ "",
+ " @Produces ListenableFuture<String> str() {",
+ " return null;",
+ " }",
+ " }",
+ "",
+ " @ProductionComponent(modules = {MonitoringModule.class, StringModule.class})",
+ " interface StringComponent {",
+ " ListenableFuture<String> getString();",
+ " }",
+ "}");
+ String expectedError =
+ "java.util.Set<dagger.producers.monitoring.ProductionComponentMonitor.Factory> is a"
+ + " provision, which cannot depend on a production.";
+ assertAbout(javaSource())
+ .that(component)
+ .processedWith(new ComponentProcessor())
+ .failsToCompile()
+ .withErrorContaining(expectedError)
+ .in(component)
+ .onLine(36);
+ }
}
diff --git a/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java b/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java
index 05ef5f37a..d47c32839 100644
--- a/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java
+++ b/compiler/src/test/java/dagger/internal/codegen/SubcomponentValidationTest.java
@@ -43,15 +43,26 @@ public final class SubcomponentValidationTest {
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = ModuleWithParameters.class)",
- "interface ChildComponent {}");
+ "interface ChildComponent {",
+ " Object object();",
+ "}");
JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters",
"package test;",
"",
"import dagger.Module;",
+ "import dagger.Provides;",
"",
"@Module",
"final class ModuleWithParameters {",
- " ModuleWithParameters(Object whatever) {}",
+ " private final Object object;",
+ "",
+ " ModuleWithParameters(Object object) {",
+ " this.object = object;",
+ " }",
+ "",
+ " @Provides Object object() {",
+ " return object;",
+ " }",
"}");
assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile, moduleFile))
.processedWith(new ComponentProcessor())
@@ -244,7 +255,7 @@ public final class SubcomponentValidationTest {
.failsToCompile()
.withErrorContaining("@Singleton");
}
-
+
@Test
public void delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent() {
JavaFileObject parentComponentFile =
@@ -356,6 +367,7 @@ public final class SubcomponentValidationTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) { ",
" this.dep1MembersInjector = Dep1_MembersInjector.create();",
" this.dep1Provider = Dep1_Factory.create(dep1MembersInjector);",
@@ -393,14 +405,13 @@ public final class SubcomponentValidationTest {
" private Provider<NeedsDep1> needsDep1Provider;",
" private Provider<A> aProvider;",
" private Provider<Object> provideObjectProvider;",
- " private MembersInjector<Dep1> dep1MembersInjector;",
- " private MembersInjector<Dep2> dep2MembersInjector;",
" ",
" private ChildComponentImpl() { ",
" this.childModule = new ChildModule();",
" initialize();",
" }",
- " ",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize() { ",
" this.aMembersInjector = A_MembersInjector.create();",
" this.needsDep1Provider = NeedsDep1_Factory.create(",
@@ -412,8 +423,6 @@ public final class SubcomponentValidationTest {
" DaggerParentComponent.this.dep2Provider);",
" this.provideObjectProvider = ChildModule_ProvideObjectFactory.create(",
" childModule, aProvider);",
- " this.dep1MembersInjector = Dep1_MembersInjector.create();",
- " this.dep2MembersInjector = Dep2_MembersInjector.create();",
" }",
" ",
" @Override",
diff --git a/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java b/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java
index 40545039c..58fa26307 100644
--- a/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java
+++ b/compiler/src/test/java/dagger/tests/integration/operation/PrimitiveInjectionTest.java
@@ -92,6 +92,7 @@ public final class PrimitiveInjectionTest {
" return builder().build();",
" }",
"",
+ " @SuppressWarnings(\"unchecked\")",
" private void initialize(final Builder builder) {",
" this.primitiveIntProvider =",
" PrimitiveModule_PrimitiveIntFactory.create(builder.primitiveModule);",
diff --git a/core/src/main/java/dagger/Subcomponent.java b/core/src/main/java/dagger/Subcomponent.java
index 0ef47011f..988f17b49 100644
--- a/core/src/main/java/dagger/Subcomponent.java
+++ b/core/src/main/java/dagger/Subcomponent.java
@@ -16,9 +16,11 @@
package dagger;
import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* A subcomponent that inherits the bindings from a parent {@link Component} or
@@ -28,6 +30,7 @@ import static java.lang.annotation.ElementType.TYPE;
* @author Gregory Kick
* @since 2.0
*/
+@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
@Target(TYPE)
@Documented
public @interface Subcomponent {
diff --git a/dagger2_annotation_processor.mk b/dagger2_annotation_processor.mk
new file mode 100644
index 000000000..f80b83cb2
--- /dev/null
+++ b/dagger2_annotation_processor.mk
@@ -0,0 +1,19 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DAGGER2_PROCESSOR_LIBRARIES := \
+ dagger2-compiler-host
+
+DAGGER2_PROCESSOR_CLASSES := \
+ dagger.internal.codegen.ComponentProcessor
diff --git a/examples/android-activity-graphs/pom.xml b/examples/android-activity-graphs/pom.xml
index 53de228ff..340c59c54 100644
--- a/examples/android-activity-graphs/pom.xml
+++ b/examples/android-activity-graphs/pom.xml
@@ -37,6 +37,7 @@
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>${project.version}</version>
+ <scope>provided</scope>
<optional>true</optional>
</dependency>
@@ -54,7 +55,7 @@
<build>
<plugins>
<plugin>
- <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+ <groupId>com.simpligility.maven.plugins</groupId>
<artifactId>android-maven-plugin</artifactId>
<extensions>true</extensions>
</plugin>
diff --git a/examples/android-activity-graphs/AndroidManifest.xml b/examples/android-activity-graphs/src/main/AndroidManifest.xml
index 234406dfa..234406dfa 100644
--- a/examples/android-activity-graphs/AndroidManifest.xml
+++ b/examples/android-activity-graphs/src/main/AndroidManifest.xml
diff --git a/examples/android-simple/pom.xml b/examples/android-simple/pom.xml
index 332aeb66b..fb934e6d6 100644
--- a/examples/android-simple/pom.xml
+++ b/examples/android-simple/pom.xml
@@ -37,6 +37,7 @@
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>${project.version}</version>
+ <scope>provided</scope>
<optional>true</optional>
</dependency>
@@ -50,7 +51,7 @@
<build>
<plugins>
<plugin>
- <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+ <groupId>com.simpligility.maven.plugins</groupId>
<artifactId>android-maven-plugin</artifactId>
<extensions>true</extensions>
</plugin>
diff --git a/examples/android-simple/AndroidManifest.xml b/examples/android-simple/src/main/AndroidManifest.xml
index 53c83bfd3..53c83bfd3 100644
--- a/examples/android-simple/AndroidManifest.xml
+++ b/examples/android-simple/src/main/AndroidManifest.xml
diff --git a/examples/pom.xml b/examples/pom.xml
index 708635f1a..eb4685aa0 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -62,12 +62,13 @@
</configuration>
</plugin>
<plugin>
- <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+ <groupId>com.simpligility.maven.plugins</groupId>
<artifactId>android-maven-plugin</artifactId>
- <version>3.8.2</version>
+ <version>4.3.0</version>
<configuration>
<sdk>
- <platform>16</platform>
+ <platform>23</platform>
+ <path>${env.ANDROID_HOME}</path>
</sdk>
</configuration>
</plugin>
diff --git a/java_annotation_processors.mk b/java_annotation_processors.mk
new file mode 100644
index 000000000..18ef29ba1
--- /dev/null
+++ b/java_annotation_processors.mk
@@ -0,0 +1,69 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Manages use of annotation processors.
+#
+# At the moment both the -processorpath and the -processor
+# flags must be specified in order to use annotation processors
+# as a code indexing tool that wraps javac doesn't as yet support
+# the same behaviour as standard javac with regard to use of
+# annotation processors. In particular it:
+# - doesn't default -processorpath to be the same as -classpath
+# - doesn't scan processorpath to automatically discover processors
+# - doesn't support a comma separated list of processor class names
+# on a single -processor option so need on option per class name.
+#
+# Input variables:
+#
+# PROCESSOR_LIBRARIES := <list of library names>
+# Similar to names added to LOCAL_JAVA_LIBRARIES.
+#
+# PROCESSOR_CLASSES := <list of processor class names>
+#
+# Upon exit various LOCAL_ variables have been updated and the
+# input variables have been cleared.
+
+# Map the library names to actual JARs.
+PROCESSOR_JARS := $(call java-lib-deps, $(PROCESSOR_LIBRARIES), true)
+
+# Add a javac -processorpath flag.
+LOCAL_JAVACFLAGS += -processorpath $(call normalize-path-list,$(PROCESSOR_JARS))
+
+# Specify only one processor class per -processor option as
+# the indexing tool does not parse the -processor value as a
+# comma separated list.
+LOCAL_JAVACFLAGS += $(foreach class,$(PROCESSOR_CLASSES),-processor $(class))
+
+# Create a source directory into which the code will be generated.
+GENERATED_SOURCE_DIR := $(local-generated-sources-dir)/annotation_processor_output/
+
+# Tell javac to generate source files in the source directory.
+LOCAL_JAVACFLAGS += -s $(GENERATED_SOURCE_DIR)
+LOCAL_GENERATED_SOURCES := $(GENERATED_SOURCE_DIR)
+
+# Add dependency between the jar being built and the processor jars so that
+# they are built before this one.
+LOCAL_ADDITIONAL_DEPENDENCIES += $(PROCESSOR_JARS) $(GENERATED_SOURCE_DIR)
+
+$(GENERATED_SOURCE_DIR):
+ mkdir -p $@
+
+LOCAL_JACK_ENABLED := disabled
+
+# Clean up all the extra variables to make sure that they don't escape to
+# another module.
+PROCESSOR_LIBRARIES :=
+PROCESSOR_CLASSES :=
+PROCESSOR_JARS :=
+GENERATED_SOURCE_DIR :=
diff --git a/pom.xml b/pom.xml
index d584d49d0..fcdfe0c7a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,15 +44,17 @@
<!-- Compilation -->
<java.version>1.7</java.version>
<javax.inject.version>1</javax.inject.version>
+ <javax.annotation.version>2.0.1</javax.annotation.version>
<javawriter.version>2.5.0</javawriter.version>
<auto.common.version>1.0-SNAPSHOT</auto.common.version>
- <auto.factory.version>1.0-SNAPSHOT</auto.factory.version>
+ <auto.factory.version>1.0-beta3</auto.factory.version>
<auto.service.version>1.0-rc2</auto.service.version>
<auto.value.version>1.0</auto.value.version>
<guava.version>18.0</guava.version>
+ <google.java.format.version>0.1-SNAPSHOT</google.java.format.version>
<!-- Test Dependencies -->
- <compile-testing.version>0.7</compile-testing.version>
+ <compile-testing.version>1.0-SNAPSHOT</compile-testing.version>
<junit.version>4.11</junit.version>
<mockito.version>1.9.5</mockito.version>
<truth.version>0.26</truth.version>
@@ -89,6 +91,16 @@
<artifactId>javax.inject</artifactId>
<version>${javax.inject.version}</version>
</dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject-tck</artifactId>
+ <version>${javax.inject.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <version>${javax.annotation.version}</version>
+ </dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javawriter</artifactId>
@@ -105,6 +117,11 @@
<version>${guava.version}</version>
</dependency>
<dependency>
+ <groupId>com.google.googlejavaformat</groupId>
+ <artifactId>google-java-format</artifactId>
+ <version>${google.java.format.version}</version>
+ </dependency>
+ <dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
<version>${auto.common.version}</version>
diff --git a/producers/pom.xml b/producers/pom.xml
index 1ee555b97..edaeca428 100644
--- a/producers/pom.xml
+++ b/producers/pom.xml
@@ -36,6 +36,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
@@ -50,6 +55,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava-testlib</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
diff --git a/producers/src/main/java/dagger/producers/Produced.java b/producers/src/main/java/dagger/producers/Produced.java
index 7edfee314..db5c133ff 100644
--- a/producers/src/main/java/dagger/producers/Produced.java
+++ b/producers/src/main/java/dagger/producers/Produced.java
@@ -64,7 +64,7 @@ public abstract class Produced<T> {
/** Returns a successful {@code Produced}, whose {@link #get} will return the given value. */
public static <T> Produced<T> successful(@Nullable T value) {
- return new Successful(value);
+ return new Successful<T>(value);
}
/**
@@ -72,7 +72,7 @@ public abstract class Produced<T> {
* {@code ExecutionException} with the given cause.
*/
public static <T> Produced<T> failed(Throwable throwable) {
- return new Failed(checkNotNull(throwable));
+ return new Failed<T>(checkNotNull(throwable));
}
private static final class Successful<T> extends Produced<T> {
@@ -90,7 +90,7 @@ public abstract class Produced<T> {
if (o == this) {
return true;
} else if (o instanceof Successful) {
- Successful that = (Successful) o;
+ Successful<?> that = (Successful<?>) o;
return Objects.equal(this.value, that.value);
} else {
return false;
@@ -117,7 +117,7 @@ public abstract class Produced<T> {
if (o == this) {
return true;
} else if (o instanceof Failed) {
- Failed that = (Failed) o;
+ Failed<?> that = (Failed<?>) o;
return this.throwable.equals(that.throwable);
} else {
return false;
diff --git a/producers/src/main/java/dagger/producers/ProductionComponent.java b/producers/src/main/java/dagger/producers/ProductionComponent.java
index a6009c5b3..8ccdb4433 100644
--- a/producers/src/main/java/dagger/producers/ProductionComponent.java
+++ b/producers/src/main/java/dagger/producers/ProductionComponent.java
@@ -80,4 +80,42 @@ public @interface ProductionComponent {
* A list of types that are to be used as component dependencies.
*/
Class<?>[] dependencies() default {};
+
+ /**
+ * A builder for a component. Components may have a single nested static abstract class or
+ * interface annotated with {@code @ProductionComponent.Builder}. If they do, then the component's
+ * generated builder will match the API in the type. Builders must follow some rules:
+ * <ul>
+ * <li> A single abstract method with no arguments must exist, and must return the component.
+ * (This is typically the {@code build()} method.)
+ * <li> All other abstract methods must take a single argument and must return void,
+ * the builder type, or a supertype of the builder.
+ * <li> There <b>must</b> be an abstract method whose parameter is
+ * {@link java.util.concurrent.Executor}.
+ * <li> Each component dependency <b>must</b> have an abstract setter method.
+ * <li> Each module dependency that Dagger can't instantiate itself (i.e., the module
+ * doesn't have a visible no-args constructor) <b>must</b> have an abstract setter method.
+ * Other module dependencies (ones that Dagger can instantiate) are allowed, but not
+ * required.
+ * <li> Non-abstract methods are allowed, but ignored as far as validation and builder generation
+ * are concerned.
+ * </ul>
+ *
+ * For example, this could be a valid {@code ProductionComponent} with a builder: <pre><code>
+ * {@literal @}ProductionComponent(modules = {BackendModule.class, FrontendModule.class})
+ * interface MyComponent {
+ * {@literal ListenableFuture<MyWidget>} myWidget();
+ *
+ * {@literal @}ProductionComponent.Builder
+ * interface Builder {
+ * MyComponent build();
+ * Builder executor(Executor executor);
+ * Builder backendModule(BackendModule bm);
+ * Builder frontendModule(FrontendModule fm);
+ * }
+ * }</code></pre>
+ */
+ @Target(TYPE)
+ @Documented
+ @interface Builder {}
}
diff --git a/producers/src/main/java/dagger/producers/internal/AbstractProducer.java b/producers/src/main/java/dagger/producers/internal/AbstractProducer.java
index 7843b2094..f7e8ec0e5 100644
--- a/producers/src/main/java/dagger/producers/internal/AbstractProducer.java
+++ b/producers/src/main/java/dagger/producers/internal/AbstractProducer.java
@@ -15,12 +15,17 @@
*/
package dagger.producers.internal;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.Producer;
import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import dagger.producers.monitoring.internal.Monitors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
import javax.annotation.Nullable;
+import javax.inject.Provider;
/**
* An abstract {@link Producer} implementation that memoizes the result of its compute method.
@@ -29,19 +34,22 @@ import javax.annotation.Nullable;
* @since 2.0
*/
public abstract class AbstractProducer<T> implements Producer<T> {
- @Nullable protected final ProducerMonitor monitor;
+ private final Provider<ProductionComponentMonitor> monitorProvider;
+ @Nullable private final ProducerToken token;
private volatile ListenableFuture<T> instance = null;
protected AbstractProducer() {
- this(null);
+ this(Monitors.noOpProductionComponentMonitorProvider(), null);
}
- protected AbstractProducer(@Nullable ProducerMonitor monitor) {
- this.monitor = monitor;
+ protected AbstractProducer(
+ Provider<ProductionComponentMonitor> monitorProvider, @Nullable ProducerToken token) {
+ this.monitorProvider = checkNotNull(monitorProvider);
+ this.token = token;
}
/** Computes this producer's future, which is then cached in {@link #get}. */
- protected abstract ListenableFuture<T> compute();
+ protected abstract ListenableFuture<T> compute(ProducerMonitor monitor);
@Override
public final ListenableFuture<T> get() {
@@ -51,25 +59,12 @@ public abstract class AbstractProducer<T> implements Producer<T> {
synchronized (this) {
result = instance;
if (result == null) {
- instance = result = compute();
+ ProducerMonitor monitor = monitorProvider.get().producerMonitorFor(token);
+ instance = result = compute(monitor);
if (result == null) {
throw new NullPointerException("compute returned null");
}
- if (monitor != null) {
- Futures.addCallback(
- result,
- new FutureCallback<T>() {
- @Override
- public void onSuccess(T value) {
- monitor.succeeded(value);
- }
-
- @Override
- public void onFailure(Throwable t) {
- monitor.failed(t);
- }
- });
- }
+ monitor.addCallbackTo(result);
}
}
}
diff --git a/producers/src/main/java/dagger/producers/internal/Producers.java b/producers/src/main/java/dagger/producers/internal/Producers.java
index 6cd06d40e..7052c3508 100644
--- a/producers/src/main/java/dagger/producers/internal/Producers.java
+++ b/producers/src/main/java/dagger/producers/internal/Producers.java
@@ -20,17 +20,10 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FutureFallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListenableFutureTask;
import dagger.producers.Produced;
import dagger.producers.Producer;
import dagger.producers.monitoring.ProducerMonitor;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-import javax.annotation.Nullable;
import javax.inject.Provider;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -55,12 +48,17 @@ public final class Producers {
// TODO(user): Document what happens with an InterruptedException after you figure out how to
// trigger one in a test.
public static <T> ListenableFuture<Produced<T>> createFutureProduced(ListenableFuture<T> future) {
+ // TODO(dpb): Switch to Futures.catchAsync once guava_jdk5 gets to v19.
return Futures.withFallback(
- Futures.transform(future, new Function<T, Produced<T>>() {
- @Override public Produced<T> apply(final T value) {
- return Produced.successful(value);
- }
- }), Producers.<T>futureFallbackForProduced());
+ Futures.transform(
+ future,
+ new Function<T, Produced<T>>() {
+ @Override
+ public Produced<T> apply(final T value) {
+ return Produced.successful(value);
+ }
+ }),
+ Producers.<T>futureFallbackForProduced());
}
@@ -90,19 +88,6 @@ public final class Producers {
}
/**
- * Submits a callable to an executor, returning the future representing the task. This mirrors
- * {@link com.google.common.util.concurrent.ListeningExecutorService#submit}, but only requires an
- * {@link Executor}.
- *
- * @throws RejectedExecutionException if this task cannot be accepted for execution.
- */
- public static <T> ListenableFuture<T> submitToExecutor(Callable<T> callable, Executor executor) {
- ListenableFutureTask<T> future = ListenableFutureTask.create(callable);
- executor.execute(future);
- return future;
- }
-
- /**
* Returns a producer that immediately executes the binding logic for the given provider every
* time it is called.
*/
@@ -110,20 +95,30 @@ public final class Producers {
checkNotNull(provider);
return new AbstractProducer<T>() {
@Override
- protected ListenableFuture<T> compute() {
+ protected ListenableFuture<T> compute(ProducerMonitor unusedMonitor) {
return Futures.immediateFuture(provider.get());
}
};
}
- /** Lifts {@link ProductionComponentMonitor#producerMonitorFor} to nullable types. */
- @Nullable
- public static ProducerMonitor producerMonitorFor(
- @Nullable ProductionComponentMonitor componentMonitor, ProducerToken token) {
- if (componentMonitor != null) {
- return componentMonitor.producerMonitorFor(token);
- }
- return null;
+ /** Returns a producer that succeeds with the given value. */
+ public static <T> Producer<T> immediateProducer(final T value) {
+ return new Producer<T>() {
+ @Override
+ public ListenableFuture<T> get() {
+ return Futures.immediateFuture(value);
+ }
+ };
+ }
+
+ /** Returns a producer that fails with the given exception. */
+ public static <T> Producer<T> immediateFailedProducer(final Throwable throwable) {
+ return new Producer<T>() {
+ @Override
+ public ListenableFuture<T> get() {
+ return Futures.immediateFailedFuture(throwable);
+ }
+ };
}
private Producers() {}
diff --git a/producers/src/main/java/dagger/producers/internal/SetOfProducedProducer.java b/producers/src/main/java/dagger/producers/internal/SetOfProducedProducer.java
new file mode 100644
index 000000000..e51bcad42
--- /dev/null
+++ b/producers/src/main/java/dagger/producers/internal/SetOfProducedProducer.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.producers.internal;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.monitoring.ProducerMonitor;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns a
+ * future {@code Set<Produced<T>>} whose elements are populated by subsequent calls to the delegate
+ * {@link Producer#get} methods.
+ *
+ * @author Jesse Beder
+ * @since 2.0
+ */
+public final class SetOfProducedProducer<T> extends AbstractProducer<Set<Produced<T>>> {
+ /**
+ * Returns a new producer that creates {@link Set} futures from the union of the given
+ * {@link Producer} instances.
+ */
+ @SafeVarargs
+ public static <T> Producer<Set<Produced<T>>> create(Producer<Set<T>>... producers) {
+ return new SetOfProducedProducer<T>(ImmutableSet.copyOf(producers));
+ }
+
+ private final ImmutableSet<Producer<Set<T>>> contributingProducers;
+
+ private SetOfProducedProducer(ImmutableSet<Producer<Set<T>>> contributingProducers) {
+ this.contributingProducers = contributingProducers;
+ }
+
+ /**
+ * Returns a future {@link Set} of {@link Produced} values whose iteration order is that of the
+ * elements given by each of the producers, which are invoked in the order given at creation.
+ *
+ * <p>If any of the delegate sets, or any elements therein, are null, then that corresponding
+ * {@code Produced} element will fail with a NullPointerException.
+ *
+ * <p>Canceling this future will attempt to cancel all of the component futures; but if any of the
+ * delegate futures fail or are canceled, this future succeeds, with the appropriate failed
+ * {@link Produced}.
+ *
+ * @throws NullPointerException if any of the delegate producers return null
+ */
+ @Override
+ public ListenableFuture<Set<Produced<T>>> compute(ProducerMonitor unusedMonitor) {
+ List<ListenableFuture<Produced<Set<T>>>> futureProducedSets =
+ new ArrayList<ListenableFuture<Produced<Set<T>>>>(contributingProducers.size());
+ for (Producer<Set<T>> producer : contributingProducers) {
+ ListenableFuture<Set<T>> futureSet = producer.get();
+ if (futureSet == null) {
+ throw new NullPointerException(producer + " returned null");
+ }
+ futureProducedSets.add(Producers.createFutureProduced(futureSet));
+ }
+ return Futures.transform(
+ Futures.allAsList(futureProducedSets),
+ new Function<List<Produced<Set<T>>>, Set<Produced<T>>>() {
+ @Override
+ public Set<Produced<T>> apply(List<Produced<Set<T>>> producedSets) {
+ ImmutableSet.Builder<Produced<T>> builder = ImmutableSet.builder();
+ for (Produced<Set<T>> producedSet : producedSets) {
+ try {
+ Set<T> set = producedSet.get();
+ if (set == null) {
+ // TODO(beder): This is a vague exception. Can we somehow point to the failing
+ // producer? See the similar comment in the component writer about null
+ // provisions.
+ builder.add(
+ Produced.<T>failed(
+ new NullPointerException(
+ "Cannot contribute a null set into a producer set binding when it's"
+ + " injected as Set<Produced<T>>.")));
+ } else {
+ for (T value : set) {
+ if (value == null) {
+ builder.add(
+ Produced.<T>failed(
+ new NullPointerException(
+ "Cannot contribute a null element into a producer set binding"
+ + " when it's injected as Set<Produced<T>>.")));
+ } else {
+ builder.add(Produced.successful(value));
+ }
+ }
+ }
+ } catch (ExecutionException e) {
+ builder.add(Produced.<T>failed(e.getCause()));
+ }
+ }
+ return builder.build();
+ }
+ });
+ }
+}
diff --git a/producers/src/main/java/dagger/producers/internal/SetProducer.java b/producers/src/main/java/dagger/producers/internal/SetProducer.java
index ba3312882..5b3c0902d 100644
--- a/producers/src/main/java/dagger/producers/internal/SetProducer.java
+++ b/producers/src/main/java/dagger/producers/internal/SetProducer.java
@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.Producer;
+import dagger.producers.monitoring.ProducerMonitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -37,8 +38,8 @@ public final class SetProducer<T> extends AbstractProducer<Set<T>> {
* Returns a new producer that creates {@link Set} futures from the union of the given
* {@link Producer} instances.
*/
- public static <T> Producer<Set<T>> create(
- @SuppressWarnings("unchecked") Producer<Set<T>>... producers) {
+ @SafeVarargs
+ public static <T> Producer<Set<T>> create(Producer<Set<T>>... producers) {
return new SetProducer<T>(ImmutableSet.copyOf(producers));
}
@@ -62,7 +63,7 @@ public final class SetProducer<T> extends AbstractProducer<Set<T>> {
* @throws NullPointerException if any of the delegate producers return null
*/
@Override
- public ListenableFuture<Set<T>> compute() {
+ public ListenableFuture<Set<T>> compute(ProducerMonitor unusedMonitor) {
List<ListenableFuture<Set<T>>> futureSets =
new ArrayList<ListenableFuture<Set<T>>>(contributingProducers.size());
for (Producer<Set<T>> producer : contributingProducers) {
diff --git a/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java b/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java
index 8e4e9b28b..20551c3db 100644
--- a/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java
+++ b/producers/src/main/java/dagger/producers/monitoring/ProducerMonitor.java
@@ -15,27 +15,37 @@
*/
package dagger.producers.monitoring;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.Produces;
+
/**
* A hook for monitoring the execution of individual {@linkplain Produces producer methods}. See
* {@link ProductionComponentMonitor} for how to install these monitors.
*
- * <p>The lifecycle of the monitor is:
+ * <p>The lifecycle of the monitor, under normal conditions, is:
* <ul>
- * <li>{@link #methodStarting}
+ * <li>{@link #methodStarting()}
* <li>The method is called
- * <li>{@link #methodFinished}
+ * <li>{@link #methodFinished()}
* <li>If the method returns a value, then:
* <ul>
- * <li>{#succeeded} if the method returned normally; or
- * <li>{#failed} if the method threw an exception.
+ * <li>{@link #succeeded(Object)} if the method returned normally; or
+ * <li>{@link #failed(Throwable)} if the method threw an exception.
* </ul>
* <li>If the method returns a future, then:
* <ul>
- * <li>{#succeeded} if the method returned normally, and the future succeeded; or
- * <li>{#failed} if the method threw an exception, or returned normally and the future failed.
+ * <li>{@link #succeeded(Object)} if the method returned normally, and the future succeeded; or
+ * <li>{@link #failed(Throwable)} if the method threw an exception, or returned normally and the
+ * future failed.
* </ul>
* </ul>
*
+ * <p>If any input to the monitored producer fails, {@link #failed(Throwable)} will be called
+ * immediately with the failed input's exception. If more than one input fails, an arbitrary failed
+ * input's exception is used.
+ *
* <p>If any of the monitor's methods throw, then the exception will be logged and processing will
* continue unaffected.
*
@@ -45,33 +55,52 @@ public abstract class ProducerMonitor {
/**
* Called when the producer method is about to start executing.
*
- * <p>When multiple monitors are installed, the order that each monitor will call
- * {@code methodWillStart} is unspecified, but will remain consistent throughout the course of the
- * execution of a component.
+ * <p>When multiple monitors are installed, the order that each monitor will call this method is
+ * unspecified, but will remain consistent throughout the course of the execution of a component.
*/
public void methodStarting() {}
/**
* Called when the producer method has finished executing.
*
- * <p>When multiple monitors are installed, the {@code methodFinished} calls will be in the
- * reverse order from the {@link #methodWillStart} calls.
+ * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
+ * calls to {@link #methodStarting()}.
*/
public void methodFinished() {}
/**
* Called when the producer’s future has completed successfully with a value.
*
- * <p>When multiple monitors are installed, the {@code futureSucceeded} calls will be in the
- * reverse order from the {@link #methodWillStart} calls.
+ * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
+ * calls to {@link #methodStarting()}.
*/
public void succeeded(Object o) {}
/**
* Called when the producer's future has failed with an exception.
*
- * <p>When multiple monitors are installed, the {@code futureFailed} calls will be in the reverse
- * order from the {@link #methodWillStart} calls.
+ * <p>When multiple monitors are installed, calls to this method will be in the reverse order from
+ * calls to {@link #methodStarting()}.
*/
public void failed(Throwable t) {}
+
+ /**
+ * Adds this monitor's completion methods as a callback to the future. This is only intended to be
+ * overridden in the framework!
+ */
+ public <T> void addCallbackTo(ListenableFuture<T> future) {
+ Futures.addCallback(
+ future,
+ new FutureCallback<T>() {
+ @Override
+ public void onSuccess(T value) {
+ succeeded(value);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ failed(t);
+ }
+ });
+ }
}
diff --git a/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java b/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java
index 126a40d4e..5834206ee 100644
--- a/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java
+++ b/producers/src/main/java/dagger/producers/monitoring/ProducerToken.java
@@ -15,6 +15,8 @@
*/
package dagger.producers.monitoring;
+import dagger.producers.Produces;
+
import static com.google.common.base.Preconditions.checkNotNull;
/** A token that represents an individual {@linkplain Produces producer method}. */
diff --git a/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java b/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java
index 1a62dfa1f..4dc2903fa 100644
--- a/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java
+++ b/producers/src/main/java/dagger/producers/monitoring/ProductionComponentMonitor.java
@@ -15,6 +15,9 @@
*/
package dagger.producers.monitoring;
+import dagger.producers.Produces;
+import dagger.producers.ProductionComponent;
+
/**
* A hook for monitoring execution of {@linkplain ProductionComponent production components}. To
* install a {@code ProductionComponentMonitor}, contribute to a set binding of
diff --git a/producers/src/main/java/dagger/producers/monitoring/internal/MonitorCache.java b/producers/src/main/java/dagger/producers/monitoring/internal/MonitorCache.java
new file mode 100644
index 000000000..2a76c5f71
--- /dev/null
+++ b/producers/src/main/java/dagger/producers/monitoring/internal/MonitorCache.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.producers.monitoring.internal;
+
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.inject.Provider;
+
+/**
+ * A class that provides a {@link ProductionComponentMonitor} for use in production components.
+ *
+ * <p>This caches the underlying the monitor, since we want a single instance for each component.
+ */
+public final class MonitorCache {
+ private static final Logger logger = Logger.getLogger(MonitorCache.class.getName());
+
+ private volatile ProductionComponentMonitor monitor;
+
+ /**
+ * Returns the underlying monitor. This will only actually compute the monitor the first time it
+ * is called; subsequent calls will simply return the cached value, so the arguments to this
+ * method are ignored. It is expected (though not checked) that this method is called with
+ * equivalent arguments each time (like a {@link dagger.Provides @Provides} method would).
+ */
+ public ProductionComponentMonitor monitor(
+ Provider<?> componentProvider,
+ Provider<Set<ProductionComponentMonitor.Factory>> monitorFactorySetProvider) {
+ ProductionComponentMonitor result = monitor;
+ if (result == null) {
+ synchronized (this) {
+ result = monitor;
+ if (result == null) {
+ try {
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ monitorFactorySetProvider.get());
+ result = monitor = factory.create(componentProvider.get());
+ } catch (RuntimeException e) {
+ logger.log(Level.SEVERE, "RuntimeException while constructing monitor factories.", e);
+ result = monitor = Monitors.noOpProductionComponentMonitor();
+ }
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/producers/src/main/java/dagger/producers/monitoring/internal/Monitors.java b/producers/src/main/java/dagger/producers/monitoring/internal/Monitors.java
new file mode 100644
index 000000000..f27ce37b1
--- /dev/null
+++ b/producers/src/main/java/dagger/producers/monitoring/internal/Monitors.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.producers.monitoring.internal;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.inject.Provider;
+
+/**
+ * Utility methods relating to monitoring, for use in generated producers code.
+ *
+ * @author Jesse Beder
+ */
+public final class Monitors {
+ private static final Logger logger = Logger.getLogger(Monitors.class.getName());
+
+ /**
+ * Returns a monitor factory that delegates to the given factories, and ensures that any method
+ * called on this object, even transitively, does not throw a {@link RuntimeException} or return
+ * null.
+ *
+ * <p>If the delegate monitors throw an {@link Error}, then that will escape this monitor
+ * implementation. Errors are treated as unrecoverable conditions, and may cause the entire
+ * component's execution to fail.
+ */
+ public static ProductionComponentMonitor.Factory delegatingProductionComponentMonitorFactory(
+ Collection<? extends ProductionComponentMonitor.Factory> factories) {
+ if (factories.isEmpty()) {
+ return noOpProductionComponentMonitorFactory();
+ } else if (factories.size() == 1) {
+ return new NonThrowingProductionComponentMonitor.Factory(Iterables.getOnlyElement(factories));
+ } else {
+ return new DelegatingProductionComponentMonitor.Factory(factories);
+ }
+ }
+
+ /**
+ * A component monitor that delegates to a single monitor, and catches and logs all exceptions
+ * that the delegate throws.
+ */
+ private static final class NonThrowingProductionComponentMonitor
+ implements ProductionComponentMonitor {
+ private final ProductionComponentMonitor delegate;
+
+ NonThrowingProductionComponentMonitor(ProductionComponentMonitor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ try {
+ ProducerMonitor monitor = delegate.producerMonitorFor(token);
+ return monitor == null ? noOpProducerMonitor() : new NonThrowingProducerMonitor(monitor);
+ } catch (RuntimeException e) {
+ logProducerMonitorForException(e, delegate, token);
+ return noOpProducerMonitor();
+ }
+ }
+
+ static final class Factory implements ProductionComponentMonitor.Factory {
+ private final ProductionComponentMonitor.Factory delegate;
+
+ Factory(ProductionComponentMonitor.Factory delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ProductionComponentMonitor create(Object component) {
+ try {
+ ProductionComponentMonitor monitor = delegate.create(component);
+ return monitor == null
+ ? noOpProductionComponentMonitor()
+ : new NonThrowingProductionComponentMonitor(monitor);
+ } catch (RuntimeException e) {
+ logCreateException(e, delegate, component);
+ return noOpProductionComponentMonitor();
+ }
+ }
+ }
+ }
+
+ /**
+ * A producer monitor that delegates to a single monitor, and catches and logs all exceptions
+ * that the delegate throws.
+ */
+ private static final class NonThrowingProducerMonitor extends ProducerMonitor {
+ private final ProducerMonitor delegate;
+
+ NonThrowingProducerMonitor(ProducerMonitor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void methodStarting() {
+ try {
+ delegate.methodStarting();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodStarting");
+ }
+ }
+
+ @Override
+ public void methodFinished() {
+ try {
+ delegate.methodFinished();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodFinished");
+ }
+ }
+
+ @Override
+ public void succeeded(Object o) {
+ try {
+ delegate.succeeded(o);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "succeeded", o);
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ try {
+ delegate.failed(t);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "failed", t);
+ }
+ }
+ }
+
+ /**
+ * A component monitor that delegates to several monitors, and catches and logs all exceptions
+ * that the delegates throw.
+ */
+ private static final class DelegatingProductionComponentMonitor
+ implements ProductionComponentMonitor {
+ private final ImmutableList<ProductionComponentMonitor> delegates;
+
+ DelegatingProductionComponentMonitor(ImmutableList<ProductionComponentMonitor> delegates) {
+ this.delegates = delegates;
+ }
+
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ ImmutableList.Builder<ProducerMonitor> monitorsBuilder = ImmutableList.builder();
+ for (ProductionComponentMonitor delegate : delegates) {
+ try {
+ ProducerMonitor monitor = delegate.producerMonitorFor(token);
+ if (monitor != null) {
+ monitorsBuilder.add(monitor);
+ }
+ } catch (RuntimeException e) {
+ logProducerMonitorForException(e, delegate, token);
+ }
+ }
+ ImmutableList<ProducerMonitor> monitors = monitorsBuilder.build();
+ if (monitors.isEmpty()) {
+ return noOpProducerMonitor();
+ } else if (monitors.size() == 1) {
+ return new NonThrowingProducerMonitor(Iterables.getOnlyElement(monitors));
+ } else {
+ return new DelegatingProducerMonitor(monitors);
+ }
+ }
+
+ static final class Factory implements ProductionComponentMonitor.Factory {
+ private final ImmutableList<? extends ProductionComponentMonitor.Factory> delegates;
+
+ Factory(Iterable<? extends ProductionComponentMonitor.Factory> delegates) {
+ this.delegates = ImmutableList.copyOf(delegates);
+ }
+
+ @Override
+ public ProductionComponentMonitor create(Object component) {
+ ImmutableList.Builder<ProductionComponentMonitor> monitorsBuilder = ImmutableList.builder();
+ for (ProductionComponentMonitor.Factory delegate : delegates) {
+ try {
+ ProductionComponentMonitor monitor = delegate.create(component);
+ if (monitor != null) {
+ monitorsBuilder.add(monitor);
+ }
+ } catch (RuntimeException e) {
+ logCreateException(e, delegate, component);
+ }
+ }
+ ImmutableList<ProductionComponentMonitor> monitors = monitorsBuilder.build();
+ if (monitors.isEmpty()) {
+ return noOpProductionComponentMonitor();
+ } else if (monitors.size() == 1) {
+ return new NonThrowingProductionComponentMonitor(Iterables.getOnlyElement(monitors));
+ } else {
+ return new DelegatingProductionComponentMonitor(monitors);
+ }
+ }
+ }
+ }
+
+ /**
+ * A producer monitor that delegates to several monitors, and catches and logs all exceptions
+ * that the delegates throw.
+ */
+ private static final class DelegatingProducerMonitor extends ProducerMonitor {
+ private final ImmutableList<ProducerMonitor> delegates;
+
+ DelegatingProducerMonitor(ImmutableList<ProducerMonitor> delegates) {
+ this.delegates = delegates;
+ }
+
+ @Override
+ public void methodStarting() {
+ for (ProducerMonitor delegate : delegates) {
+ try {
+ delegate.methodStarting();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodStarting");
+ }
+ }
+ }
+
+ @Override
+ public void methodFinished() {
+ for (ProducerMonitor delegate : delegates.reverse()) {
+ try {
+ delegate.methodFinished();
+ } catch (RuntimeException e) {
+ logProducerMonitorMethodException(e, delegate, "methodFinished");
+ }
+ }
+ }
+
+ @Override
+ public void succeeded(Object o) {
+ for (ProducerMonitor delegate : delegates.reverse()) {
+ try {
+ delegate.succeeded(o);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "succeeded", o);
+ }
+ }
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ for (ProducerMonitor delegate : delegates.reverse()) {
+ try {
+ delegate.failed(t);
+ } catch (RuntimeException e) {
+ logProducerMonitorArgMethodException(e, delegate, "failed", t);
+ }
+ }
+ }
+ }
+
+ /** Returns a monitor factory that returns no-op component monitors. */
+ public static ProductionComponentMonitor.Factory noOpProductionComponentMonitorFactory() {
+ return NO_OP_PRODUCTION_COMPONENT_MONITOR_FACTORY;
+ }
+
+ /** Returns a component monitor that returns no-op producer monitors. */
+ public static ProductionComponentMonitor noOpProductionComponentMonitor() {
+ return NO_OP_PRODUCTION_COMPONENT_MONITOR;
+ }
+
+ /** Returns a producer monitor that does nothing. */
+ public static ProducerMonitor noOpProducerMonitor() {
+ return NO_OP_PRODUCER_MONITOR;
+ }
+
+ /** Returns a provider of a no-op component monitor. */
+ public static Provider<ProductionComponentMonitor> noOpProductionComponentMonitorProvider() {
+ return NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER;
+ }
+
+ private static final ProductionComponentMonitor.Factory
+ NO_OP_PRODUCTION_COMPONENT_MONITOR_FACTORY =
+ new ProductionComponentMonitor.Factory() {
+ @Override
+ public ProductionComponentMonitor create(Object component) {
+ return noOpProductionComponentMonitor();
+ }
+ };
+
+ private static final ProductionComponentMonitor NO_OP_PRODUCTION_COMPONENT_MONITOR =
+ new ProductionComponentMonitor() {
+ @Override
+ public ProducerMonitor producerMonitorFor(ProducerToken token) {
+ return noOpProducerMonitor();
+ }
+ };
+
+ private static final ProducerMonitor NO_OP_PRODUCER_MONITOR =
+ new ProducerMonitor() {
+ @Override
+ public <T> void addCallbackTo(ListenableFuture<T> future) {
+ // overridden to avoid adding a do-nothing callback
+ }
+ };
+
+ private static final Provider<ProductionComponentMonitor>
+ NO_OP_PRODUCTION_COMPONENT_MONITOR_PROVIDER =
+ new Provider() {
+ @Override
+ public ProductionComponentMonitor get() {
+ return noOpProductionComponentMonitor();
+ }
+ };
+
+ private static void logCreateException(
+ RuntimeException e, ProductionComponentMonitor.Factory factory, Object component) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProductionComponentMonitor.Factory.create on factory "
+ + factory
+ + " with component "
+ + component,
+ e);
+ }
+
+ private static void logProducerMonitorForException(
+ RuntimeException e, ProductionComponentMonitor monitor, ProducerToken token) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProductionComponentMonitor.producerMonitorFor on monitor "
+ + monitor
+ + " with token "
+ + token,
+ e);
+ }
+
+ private static void logProducerMonitorMethodException(
+ RuntimeException e, ProducerMonitor monitor, String method) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProducerMonitor." + method + " on monitor " + monitor,
+ e);
+ }
+
+ private static void logProducerMonitorArgMethodException(
+ RuntimeException e, ProducerMonitor monitor, String method, Object arg) {
+ logger.log(
+ Level.SEVERE,
+ "RuntimeException while calling ProducerMonitor."
+ + method
+ + " on monitor "
+ + monitor
+ + " with "
+ + arg,
+ e);
+ }
+
+ private Monitors() {}
+}
diff --git a/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java b/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java
index 15a64bd1f..923ea70a1 100644
--- a/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java
+++ b/producers/src/test/java/dagger/producers/internal/AbstractProducerTest.java
@@ -20,35 +20,51 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import dagger.producers.Producer;
import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
import java.util.concurrent.ExecutionException;
+import javax.inject.Provider;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
/**
* Tests {@link AbstractProducer}.
*/
@RunWith(JUnit4.class)
public class AbstractProducerTest {
- @Mock private ProducerMonitor monitor;
+ @Mock private ProductionComponentMonitor componentMonitor;
+ private ProducerMonitor monitor;
+ private Provider<ProductionComponentMonitor> componentMonitorProvider;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
+ monitor = Mockito.mock(ProducerMonitor.class, Mockito.CALLS_REAL_METHODS);
+ when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor);
+ componentMonitorProvider =
+ new Provider<ProductionComponentMonitor>() {
+ @Override
+ public ProductionComponentMonitor get() {
+ return componentMonitor;
+ }
+ };
}
@Test
public void get_nullPointerException() {
- Producer<Object> producer = new DelegateProducer<>(monitor, null);
+ Producer<Object> producer = new DelegateProducer<>(componentMonitorProvider, null);
try {
producer.get();
fail();
@@ -58,11 +74,11 @@ public class AbstractProducerTest {
@Test public void get() throws Exception {
Producer<Integer> producer =
- new AbstractProducer<Integer>(monitor) {
+ new AbstractProducer<Integer>(componentMonitorProvider, null) {
int i = 0;
@Override
- public ListenableFuture<Integer> compute() {
+ public ListenableFuture<Integer> compute(ProducerMonitor unusedMonitor) {
return Futures.immediateFuture(i++);
}
};
@@ -74,11 +90,11 @@ public class AbstractProducerTest {
@Test
public void monitor_success() throws Exception {
SettableFuture<Integer> delegateFuture = SettableFuture.create();
- Producer<Integer> producer = new DelegateProducer<>(monitor, delegateFuture);
+ Producer<Integer> producer = new DelegateProducer<>(componentMonitorProvider, delegateFuture);
ListenableFuture<Integer> future = producer.get();
assertThat(future.isDone()).isFalse();
- verifyZeroInteractions(monitor);
+ verify(monitor).addCallbackTo(any(ListenableFuture.class));
delegateFuture.set(-42);
assertThat(future.get()).isEqualTo(-42);
verify(monitor).succeeded(-42);
@@ -88,11 +104,11 @@ public class AbstractProducerTest {
@Test
public void monitor_failure() throws Exception {
SettableFuture<Integer> delegateFuture = SettableFuture.create();
- Producer<Integer> producer = new DelegateProducer<>(monitor, delegateFuture);
+ Producer<Integer> producer = new DelegateProducer<>(componentMonitorProvider, delegateFuture);
ListenableFuture<Integer> future = producer.get();
assertThat(future.isDone()).isFalse();
- verifyZeroInteractions(monitor);
+ verify(monitor).addCallbackTo(any(ListenableFuture.class));
Throwable t = new RuntimeException("monkey");
delegateFuture.setException(t);
try {
@@ -105,22 +121,23 @@ public class AbstractProducerTest {
verifyNoMoreInteractions(monitor);
}
- @Test
+ @Test(expected = NullPointerException.class)
public void monitor_null() throws Exception {
- Producer<Integer> producer = new DelegateProducer<>(null, Futures.immediateFuture(42));
- assertThat(producer.get().get()).isEqualTo(42);
+ new DelegateProducer<>(null, Futures.immediateFuture(42));
}
static final class DelegateProducer<T> extends AbstractProducer<T> {
private final ListenableFuture<T> delegate;
- DelegateProducer(ProducerMonitor monitor, ListenableFuture<T> delegate) {
- super(monitor);
+ DelegateProducer(
+ Provider<ProductionComponentMonitor> componentMonitorProvider,
+ ListenableFuture<T> delegate) {
+ super(componentMonitorProvider, null);
this.delegate = delegate;
}
@Override
- public ListenableFuture<T> compute() {
+ public ListenableFuture<T> compute(ProducerMonitor unusedMonitor) {
return delegate;
}
}
diff --git a/producers/src/test/java/dagger/producers/internal/ProducersTest.java b/producers/src/test/java/dagger/producers/internal/ProducersTest.java
index 43564089f..e2707b342 100644
--- a/producers/src/test/java/dagger/producers/internal/ProducersTest.java
+++ b/producers/src/test/java/dagger/producers/internal/ProducersTest.java
@@ -17,12 +17,10 @@ package dagger.producers.internal;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import dagger.producers.Produced;
import dagger.producers.Producer;
import java.util.Set;
-import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.inject.Provider;
@@ -98,16 +96,6 @@ public class ProducersTest {
}
}
- @Test public void submitToExecutor() throws Exception {
- ListenableFuture<Integer> future = Producers.submitToExecutor(new Callable<Integer>() {
- @Override public Integer call() {
- return 42;
- }
- }, MoreExecutors.directExecutor());
- assertThat(future.isDone()).isTrue();
- assertThat(future.get()).isEqualTo(42);
- }
-
@Test public void producerFromProvider() throws Exception {
Producer<Integer> producer = Producers.producerFromProvider(new Provider<Integer>() {
int i = 0;
diff --git a/producers/src/test/java/dagger/producers/internal/SetOfProducedProducerTest.java b/producers/src/test/java/dagger/producers/internal/SetOfProducedProducerTest.java
new file mode 100644
index 000000000..e36ba05a4
--- /dev/null
+++ b/producers/src/test/java/dagger/producers/internal/SetOfProducedProducerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.producers.internal;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+
+/**
+ * Tests {@link SetOfProducedProducer}.
+ */
+@RunWith(JUnit4.class)
+public class SetOfProducedProducerTest {
+ @Test
+ public void success() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.create(
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(5, 7)));
+ assertThat(producer.get().get())
+ .containsExactly(
+ Produced.successful(1),
+ Produced.successful(2),
+ Produced.successful(5),
+ Produced.successful(7));
+ }
+
+ @Test
+ public void failure() throws Exception {
+ RuntimeException e = new RuntimeException("monkey");
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.create(
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
+ Producers.<Set<Integer>>immediateFailedProducer(e));
+ assertThat(producer.get().get())
+ .containsExactly(
+ Produced.successful(1), Produced.successful(2), Produced.<Integer>failed(e));
+ }
+
+ @Test
+ public void delegateSetNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.create(Producers.<Set<Integer>>immediateProducer(null));
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).isEmpty();
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures).getCause())
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void oneOfDelegateSetNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.create(
+ Producers.<Set<Integer>>immediateProducer(null),
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(7, 3)));
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).containsExactly(3, 7);
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures).getCause())
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void delegateElementNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.create(
+ Producers.<Set<Integer>>immediateProducer(Collections.<Integer>singleton(null)));
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).isEmpty();
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures).getCause())
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void oneOfDelegateElementNpe() throws Exception {
+ Producer<Set<Produced<Integer>>> producer =
+ SetOfProducedProducer.create(
+ Producers.<Set<Integer>>immediateProducer(Sets.newHashSet(Arrays.asList(5, 2, null))));
+ Results<Integer> results = Results.create(producer.get().get());
+ assertThat(results.successes).containsExactly(2, 5);
+ assertThat(results.failures).hasSize(1);
+ assertThat(Iterables.getOnlyElement(results.failures).getCause())
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ static final class Results<T> {
+ final ImmutableSet<T> successes;
+ final ImmutableSet<ExecutionException> failures;
+
+ private Results(ImmutableSet<T> successes, ImmutableSet<ExecutionException> failures) {
+ this.successes = successes;
+ this.failures = failures;
+ }
+
+ static <T> Results<T> create(Set<Produced<T>> setOfProduced) {
+ ImmutableSet.Builder<T> successes = ImmutableSet.builder();
+ ImmutableSet.Builder<ExecutionException> failures = ImmutableSet.builder();
+ for (Produced<T> produced : setOfProduced) {
+ try {
+ successes.add(produced.get());
+ } catch (ExecutionException e) {
+ failures.add(e);
+ }
+ }
+ return new Results<T>(successes.build(), failures.build());
+ }
+ }
+}
diff --git a/producers/src/test/java/dagger/producers/internal/SetProducerTest.java b/producers/src/test/java/dagger/producers/internal/SetProducerTest.java
index 1f8ff7c3a..a38830f0a 100644
--- a/producers/src/test/java/dagger/producers/internal/SetProducerTest.java
+++ b/producers/src/test/java/dagger/producers/internal/SetProducerTest.java
@@ -16,7 +16,6 @@
package dagger.producers.internal;
import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.producers.Producer;
import java.util.Collections;
@@ -35,16 +34,18 @@ import static org.junit.Assert.fail;
@RunWith(JUnit4.class)
public class SetProducerTest {
@Test public void success() throws Exception {
- Producer<Set<Integer>> producer = SetProducer.create(
- new ImmediateProducer<Set<Integer>>(ImmutableSet.of(1, 2)),
- new ImmediateProducer<Set<Integer>>(ImmutableSet.of(5, 7)));
+ Producer<Set<Integer>> producer =
+ SetProducer.create(
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(5, 7)));
assertThat(producer.get().get()).containsExactly(1, 2, 5, 7);
}
@Test public void delegateSetNpe() throws Exception {
- Producer<Set<Integer>> producer = SetProducer.create(
- new ImmediateProducer<Set<Integer>>(ImmutableSet.of(1, 2)),
- new ImmediateProducer<Set<Integer>>(null));
+ Producer<Set<Integer>> producer =
+ SetProducer.create(
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
+ Producers.<Set<Integer>>immediateProducer(null));
ListenableFuture<Set<Integer>> future = producer.get();
try {
future.get();
@@ -55,10 +56,10 @@ public class SetProducerTest {
}
@Test public void delegateElementNpe() throws Exception {
- Producer<Set<Integer>> producer = SetProducer.create(
- new ImmediateProducer<Set<Integer>>(ImmutableSet.of(1, 2)),
- new ImmediateProducer<Set<Integer>>(
- Collections.<Integer>singleton(null)));
+ Producer<Set<Integer>> producer =
+ SetProducer.create(
+ Producers.<Set<Integer>>immediateProducer(ImmutableSet.of(1, 2)),
+ Producers.<Set<Integer>>immediateProducer(Collections.<Integer>singleton(null)));
ListenableFuture<Set<Integer>> future = producer.get();
try {
future.get();
@@ -67,16 +68,4 @@ public class SetProducerTest {
assertThat(e.getCause()).isInstanceOf(NullPointerException.class);
}
}
-
- private static final class ImmediateProducer<T> implements Producer<T> {
- private final T value;
-
- ImmediateProducer(T value) {
- this.value = value;
- }
-
- @Override public ListenableFuture<T> get() {
- return Futures.immediateFuture(value);
- }
- }
}
diff --git a/producers/src/test/java/dagger/producers/monitoring/internal/MonitorsTest.java b/producers/src/test/java/dagger/producers/monitoring/internal/MonitorsTest.java
new file mode 100644
index 000000000..e7f42746b
--- /dev/null
+++ b/producers/src/test/java/dagger/producers/monitoring/internal/MonitorsTest.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.producers.monitoring.internal;
+
+import com.google.common.collect.ImmutableList;
+import dagger.producers.monitoring.ProducerMonitor;
+import dagger.producers.monitoring.ProducerToken;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(JUnit4.class)
+public final class MonitorsTest {
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactory;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitor;
+ @Mock private ProducerMonitor mockProducerMonitor;
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryA;
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryB;
+ @Mock private ProductionComponentMonitor.Factory mockProductionComponentMonitorFactoryC;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitorA;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitorB;
+ @Mock private ProductionComponentMonitor mockProductionComponentMonitorC;
+ @Mock private ProducerMonitor mockProducerMonitorA;
+ @Mock private ProducerMonitor mockProducerMonitorB;
+ @Mock private ProducerMonitor mockProducerMonitorC;
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void zeroMonitorsReturnsNoOp() {
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.<ProductionComponentMonitor.Factory>of());
+ assertThat(factory).isSameAs(Monitors.noOpProductionComponentMonitorFactory());
+ }
+
+ @Test
+ public void singleMonitor_nullProductionComponentMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class))).thenReturn(null);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
+ }
+
+ @Test
+ public void singleMonitor_throwingProductionComponentMonitorFactory() {
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactory)
+ .create(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
+ }
+
+ @Test
+ public void singleMonitor_nullProducerMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitor);
+ when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(null);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
+ .isSameAs(Monitors.noOpProducerMonitor());
+ }
+
+ @Test
+ public void singleMonitor_throwingProductionComponentMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitor);
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitor)
+ .producerMonitorFor(any(ProducerToken.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
+ .isSameAs(Monitors.noOpProducerMonitor());
+ }
+
+ @Test
+ public void singleMonitor_normalProducerMonitorSuccess() {
+ setUpNormalSingleMonitor();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Object o = new Object();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void singleMonitor_normalProducerMonitorFailure() {
+ setUpNormalSingleMonitor();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Throwable t = new RuntimeException("monkey");
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void singleMonitor_throwingProducerMonitorSuccess() {
+ setUpNormalSingleMonitor();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).succeeded(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Object o = new Object();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void singleMonitor_throwingProducerMonitorFailure() {
+ setUpNormalSingleMonitor();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).failed(any(Throwable.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(mockProductionComponentMonitorFactory));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+ Throwable t = new RuntimeException("gorilla");
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitor);
+ order.verify(mockProducerMonitor).methodStarting();
+ order.verify(mockProducerMonitor).methodFinished();
+ order.verify(mockProducerMonitor).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitor);
+ }
+
+ @Test
+ public void multipleMonitors_nullProductionComponentMonitors() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorFactoryB.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorFactoryC.create(any(Object.class))).thenReturn(null);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
+ }
+
+ @Test
+ public void multipleMonitors_throwingProductionComponentMonitorFactories() {
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryA)
+ .create(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryB)
+ .create(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryC)
+ .create(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ assertThat(factory.create(new Object())).isSameAs(Monitors.noOpProductionComponentMonitor());
+ }
+
+ @Test
+ public void multipleMonitors_someNullProductionComponentMonitors() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorA);
+ when(mockProductionComponentMonitorFactoryB.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorFactoryC.create(any(Object.class))).thenReturn(null);
+ when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorA);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA);
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA);
+ }
+
+ @Test
+ public void multipleMonitors_someThrowingProductionComponentMonitorFactories() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorA);
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryB)
+ .create(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProductionComponentMonitorFactoryC)
+ .create(any(Object.class));
+ when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorA);
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA);
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA);
+ }
+
+ @Test
+ public void multipleMonitors_normalProductionComponentMonitorSuccess() {
+ setUpNormalMultipleMonitors();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).succeeded(o);
+ order.verify(mockProducerMonitorB).succeeded(o);
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ @Test
+ public void multipleMonitors_normalProductionComponentMonitorFailure() {
+ setUpNormalMultipleMonitors();
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Throwable t = new RuntimeException("chimpanzee");
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).failed(t);
+ order.verify(mockProducerMonitorB).failed(t);
+ order.verify(mockProducerMonitorA).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ @Test
+ public void multipleMonitors_someThrowingProducerMonitorsSuccess() {
+ setUpNormalMultipleMonitors();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorB).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorC).succeeded(any(Object.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Object o = new Object();
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.succeeded(o);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).succeeded(o);
+ order.verify(mockProducerMonitorB).succeeded(o);
+ order.verify(mockProducerMonitorA).succeeded(o);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ @Test
+ public void multipleMonitors_someThrowingProducerMonitorsFailure() {
+ setUpNormalMultipleMonitors();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).methodStarting();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorB).methodFinished();
+ doThrow(new RuntimeException("monkey")).when(mockProducerMonitorC).failed(any(Throwable.class));
+ ProductionComponentMonitor.Factory factory =
+ Monitors.delegatingProductionComponentMonitorFactory(
+ ImmutableList.of(
+ mockProductionComponentMonitorFactoryA,
+ mockProductionComponentMonitorFactoryB,
+ mockProductionComponentMonitorFactoryC));
+ ProductionComponentMonitor monitor = factory.create(new Object());
+ ProducerMonitor producerMonitor =
+ monitor.producerMonitorFor(ProducerToken.create(Object.class));
+
+ Throwable t = new RuntimeException("chimpanzee");
+ producerMonitor.methodStarting();
+ producerMonitor.methodFinished();
+ producerMonitor.failed(t);
+
+ InOrder order = inOrder(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ order.verify(mockProducerMonitorA).methodStarting();
+ order.verify(mockProducerMonitorB).methodStarting();
+ order.verify(mockProducerMonitorC).methodStarting();
+ order.verify(mockProducerMonitorC).methodFinished();
+ order.verify(mockProducerMonitorB).methodFinished();
+ order.verify(mockProducerMonitorA).methodFinished();
+ order.verify(mockProducerMonitorC).failed(t);
+ order.verify(mockProducerMonitorB).failed(t);
+ order.verify(mockProducerMonitorA).failed(t);
+ verifyNoMoreInteractions(mockProducerMonitorA, mockProducerMonitorB, mockProducerMonitorC);
+ }
+
+ private void setUpNormalSingleMonitor() {
+ when(mockProductionComponentMonitorFactory.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitor);
+ when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitor);
+ }
+
+ private void setUpNormalMultipleMonitors() {
+ when(mockProductionComponentMonitorFactoryA.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorA);
+ when(mockProductionComponentMonitorFactoryB.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorB);
+ when(mockProductionComponentMonitorFactoryC.create(any(Object.class)))
+ .thenReturn(mockProductionComponentMonitorC);
+ when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorA);
+ when(mockProductionComponentMonitorB.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorB);
+ when(mockProductionComponentMonitorC.producerMonitorFor(any(ProducerToken.class)))
+ .thenReturn(mockProducerMonitorC);
+ }
+}
diff --git a/resources/META-INF/services/javax.annotation.processing.Processor b/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 000000000..069807e64
--- /dev/null
+++ b/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+dagger.internal.codegen.ComponentProcessor