Inline requests for bindings that were provided with a @BindInstance method, and enable SimpleMethodRequestFulfillment to operate on instance @Provides methods
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=166206675
diff --git a/java/dagger/internal/codegen/AbstractComponentWriter.java b/java/dagger/internal/codegen/AbstractComponentWriter.java
index 3eb0c0a..58475ec 100644
--- a/java/dagger/internal/codegen/AbstractComponentWriter.java
+++ b/java/dagger/internal/codegen/AbstractComponentWriter.java
@@ -69,7 +69,6 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
@@ -125,12 +124,14 @@
private final UniqueNameSet componentFieldNames = new UniqueNameSet();
private final UniqueNameSet componentMethodNames = new UniqueNameSet();
private final ComponentBindingExpressions bindingExpressions;
+ protected final ComponentRequirementFields componentRequirementFields;
// TODO(user): Merge into ComponentBindingExpressions after we refactor BindingKey.
private final Map<BindingKey, FrameworkInstanceBindingExpression>
producerFromProviderBindingExpressions = new LinkedHashMap<>();
private final List<CodeBlock> initializations = new ArrayList<>();
protected final List<MethodSpec> interfaceMethods = new ArrayList<>();
private final BindingExpression.Factory bindingExpressionFactory;
+ private final ComponentRequirementField.Factory componentRequirementFieldFactory;
private final Map<Key, MethodSpec> membersInjectionMethods = new LinkedHashMap<>();
protected final MethodSpec.Builder constructor = constructorBuilder().addModifiers(PRIVATE);
@@ -142,16 +143,7 @@
* For each component requirement, the builder field. This map is empty for subcomponents that do
* not use a builder.
*/
- private ImmutableMap<ComponentRequirement, FieldSpec> builderFields = ImmutableMap.of();
-
- /**
- * For each component requirement, the member select for the component field that holds it.
- *
- * <p>Fields are written for all requirements for subcomponents that do not use a builder, and for
- * any requirement that is reused from a subcomponent of this component.
- */
- protected final Map<ComponentRequirement, MemberSelect> componentContributionFields =
- Maps.newHashMap();
+ private final ImmutableMap<ComponentRequirement, FieldSpec> builderFields;
/**
* The member-selects for {@link dagger.internal.ReferenceReleasingProviderManager} fields,
@@ -168,7 +160,8 @@
BindingGraph graph,
ImmutableMap<ComponentDescriptor, String> subcomponentNames,
OptionalFactories optionalFactories,
- ComponentBindingExpressions bindingExpressions) {
+ ComponentBindingExpressions bindingExpressions,
+ ComponentRequirementFields componentRequirementFields) {
this.types = types;
this.elements = elements;
this.keyFactory = keyFactory;
@@ -179,16 +172,27 @@
this.subcomponentNames = subcomponentNames;
this.optionalFactories = optionalFactories;
this.bindingExpressions = bindingExpressions;
+ // TODO(dpb): Allow ComponentBuilder.create to return a no-op object
+ if (hasBuilder(graph)) {
+ builder = ComponentBuilder.create(name, graph, subcomponentNames, elements, types);
+ builderFields = builder.builderFields();
+ } else {
+ builderFields = ImmutableMap.of();
+ }
+ this.componentRequirementFields = componentRequirementFields;
this.bindingExpressionFactory =
new BindingExpression.Factory(
compilerOptions,
name,
componentFieldNames,
bindingExpressions,
+ componentRequirementFields,
this,
childComponentNames(keyFactory, subcomponentNames),
graph,
elements);
+ this.componentRequirementFieldFactory =
+ new ComponentRequirementField.Factory(this, componentFieldNames, name, builderFields);
}
private static ImmutableMap<BindingKey, String> childComponentNames(
@@ -216,7 +220,8 @@
graph,
parent.subcomponentNames,
parent.optionalFactories,
- parent.bindingExpressions.forChildComponent());
+ parent.bindingExpressions.forChildComponent(),
+ parent.componentRequirementFields.forChildComponent());
}
protected final ClassName componentDefinitionTypeName() {
@@ -224,48 +229,6 @@
}
/**
- * Returns an expression that evaluates to an instance of the requirement, looking for either a
- * builder field or a component field.
- */
- private CodeBlock getComponentContributionExpression(ComponentRequirement componentRequirement) {
- if (builderFields.containsKey(componentRequirement)) {
- return CodeBlock.of("builder.$N", builderFields.get(componentRequirement));
- } else {
- Optional<CodeBlock> codeBlock =
- getOrCreateComponentRequirementFieldExpression(componentRequirement);
- checkState(
- codeBlock.isPresent(), "no builder or component field for %s", componentRequirement);
- return codeBlock.get();
- }
- }
-
- /**
- * Returns an expression for a component requirement field. Adds a field the first time one is
- * requested for a requirement if this component's builder has a field for it.
- */
- protected Optional<CodeBlock> getOrCreateComponentRequirementFieldExpression(
- ComponentRequirement componentRequirement) {
- MemberSelect fieldSelect = componentContributionFields.get(componentRequirement);
- if (fieldSelect == null) {
- if (!builderFields.containsKey(componentRequirement)) {
- return Optional.empty();
- }
- FieldSpec componentField =
- componentField(
- TypeName.get(componentRequirement.type()),
- simpleVariableName(componentRequirement.typeElement()))
- .addModifiers(PRIVATE, FINAL)
- .build();
- component.addField(componentField);
- constructor.addCode(
- "this.$N = builder.$N;", componentField, builderFields.get(componentRequirement));
- fieldSelect = localField(name, componentField.name);
- componentContributionFields.put(componentRequirement, fieldSelect);
- }
- return Optional.of(fieldSelect.getExpressionFor(name));
- }
-
- /**
* Creates a {@link FieldSpec.Builder} with a unique name based off of {@code name}.
*/
protected final FieldSpec.Builder componentField(TypeName type, String name) {
@@ -300,7 +263,7 @@
final TypeSpec.Builder write() {
checkState(!done, "ComponentWriter has already been generated.");
decorateComponent();
- if (hasBuilder()) {
+ if (hasBuilder(graph)) {
addBuilder();
}
@@ -311,6 +274,7 @@
addFactoryMethods();
addReferenceReleasingProviderManagerFields();
createBindingExpressions();
+ createComponentRequirementFields();
implementInterfaceMethods();
addSubcomponents();
writeInitializeAndInterfaceMethods();
@@ -329,7 +293,7 @@
*/
protected abstract void decorateComponent();
- private boolean hasBuilder() {
+ private static boolean hasBuilder(BindingGraph graph) {
ComponentDescriptor component = graph.componentDescriptor();
return component.kind().isTopLevel() || component.builderSpec().isPresent();
}
@@ -338,9 +302,6 @@
* Adds a builder type.
*/
private void addBuilder() {
- builder = ComponentBuilder.create(name, graph, subcomponentNames, elements, types);
- builderFields = builder.builderFields();
-
addBuilderClass(builder.typeSpec());
constructor.addParameter(builderName(), "builder");
@@ -457,6 +418,14 @@
bindingExpressions.addBindingExpression(bindingExpressionFactory.forField(resolvedBindings));
}
+ private void createComponentRequirementFields() {
+ builderFields
+ .keySet()
+ .stream()
+ .map(componentRequirementFieldFactory::forBuilderField)
+ .forEach(componentRequirementFields::add);
+ }
+
private boolean useRawType(Binding binding) {
return useRawType(binding.bindingPackage());
}
@@ -578,7 +547,7 @@
* separate fields and initilization as we do now. */
.addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED))
.addCode(CodeBlocks.concat(partition));
- if (hasBuilder()) {
+ if (hasBuilder(graph)) {
initializeMethod.addParameter(builderName(), "builder", FINAL);
constructor.addStatement("$L(builder)", methodName);
} else {
@@ -720,14 +689,15 @@
TypeName bindingKeyTypeName = TypeName.get(binding.key().type());
switch (binding.bindingKind()) {
case COMPONENT:
+ // This bindingKeyTypeName type parameter can be removed when we drop java 7 source support
+ return CodeBlock.of("$T.<$T>create(this)", INSTANCE_FACTORY, bindingKeyTypeName);
+
+ case COMPONENT_DEPENDENCY:
return CodeBlock.of(
- "$T.<$T>create($L)",
+ "$T.create($L)",
INSTANCE_FACTORY,
- bindingKeyTypeName,
- bindingKeyTypeName.equals(componentDefinitionTypeName())
- ? "this"
- : getComponentContributionExpression(
- ComponentRequirement.forDependency(binding.key().type())));
+ componentRequirementFields.getExpressionDuringInitialization(
+ ComponentRequirement.forDependency(binding.key().type()), name));
case COMPONENT_PROVISION:
{
@@ -778,8 +748,8 @@
return CodeBlock.of(
"new $L($L)",
factoryName,
- getComponentContributionExpression(
- ComponentRequirement.forDependency(dependencyType.asType())));
+ componentRequirementFields.getExpressionDuringInitialization(
+ ComponentRequirement.forDependency(dependencyType.asType()), name));
}
case SUBCOMPONENT_BUILDER:
@@ -807,7 +777,8 @@
"$T.$L($L)",
InstanceFactory.class,
binding.nullableType().isPresent() ? "createNullable" : "create",
- getComponentContributionExpression(ComponentRequirement.forBinding(binding)));
+ componentRequirementFields.getExpressionDuringInitialization(
+ ComponentRequirement.forBinding(binding), name));
case INJECTION:
case PROVISION:
@@ -816,8 +787,9 @@
Lists.newArrayListWithCapacity(binding.explicitDependencies().size() + 1);
if (binding.requiresModuleInstance()) {
arguments.add(
- getComponentContributionExpression(
- ComponentRequirement.forModule(binding.contributingModule().get().asType())));
+ componentRequirementFields.getExpressionDuringInitialization(
+ ComponentRequirement.forModule(binding.contributingModule().get().asType()),
+ name));
}
arguments.addAll(getDependencyArguments(binding));
@@ -853,8 +825,8 @@
/* 1 */ PRODUCER,
/* 2 */ binding.key().type(),
/* 3 */ LISTENABLE_FUTURE,
- /* 4 */ getComponentContributionExpression(
- ComponentRequirement.forDependency(dependencyType.asType())),
+ /* 4 */ componentRequirementFields.getExpressionDuringInitialization(
+ ComponentRequirement.forDependency(dependencyType.asType()), name),
/* 5 */ binding.bindingElement().get().getSimpleName(),
/* 6 */ dependencyType,
/* 7 */ simpleVariableName(dependencyType));
@@ -866,8 +838,9 @@
Lists.newArrayListWithCapacity(binding.dependencies().size() + 2);
if (binding.requiresModuleInstance()) {
arguments.add(
- getComponentContributionExpression(
- ComponentRequirement.forModule(binding.contributingModule().get().asType())));
+ componentRequirementFields.getExpressionDuringInitialization(
+ ComponentRequirement.forModule(binding.contributingModule().get().asType()),
+ name));
}
arguments.addAll(getDependencyArguments(binding));
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index 4d06d38..2206e66 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -59,6 +59,7 @@
private final ClassName componentName;
private final UniqueNameSet componentFieldNames;
private final ComponentBindingExpressions componentBindingExpressions;
+ private final ComponentRequirementFields componentRequirementFields;
private final GeneratedComponentModel generatedComponentModel;
private final ImmutableMap<BindingKey, String> subcomponentNames;
private final BindingGraph graph;
@@ -69,6 +70,7 @@
ClassName componentName,
UniqueNameSet componentFieldNames,
ComponentBindingExpressions componentBindingExpressions,
+ ComponentRequirementFields componentRequirementFields,
GeneratedComponentModel generatedComponentModel,
ImmutableMap<BindingKey, String> subcomponentNames,
BindingGraph graph,
@@ -77,6 +79,7 @@
this.componentName = checkNotNull(componentName);
this.componentFieldNames = checkNotNull(componentFieldNames);
this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.componentRequirementFields = checkNotNull(componentRequirementFields);
this.generatedComponentModel = checkNotNull(generatedComponentModel);
this.subcomponentNames = checkNotNull(subcomponentNames);
this.graph = checkNotNull(graph);
@@ -151,6 +154,13 @@
ProvisionBinding provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding();
switch (provisionBinding.bindingKind()) {
+ case COMPONENT:
+ return new ComponentInstanceBindingExpression(bindingExpression, componentName);
+ case COMPONENT_DEPENDENCY:
+ return new BoundInstanceBindingExpression(
+ bindingExpression,
+ ComponentRequirement.forDependency(provisionBinding.key().type()),
+ componentRequirementFields);
case SUBCOMPONENT_BUILDER:
return new SubcomponentBuilderBindingExpression(
bindingExpression, subcomponentNames.get(resolvedBindings.bindingKey()));
@@ -160,17 +170,29 @@
case SYNTHETIC_OPTIONAL_BINDING:
return new OptionalBindingExpression(
provisionBinding, bindingExpression, componentBindingExpressions);
- case INJECTION:
+ case BUILDER_BINDING:
+ return new BoundInstanceBindingExpression(
+ bindingExpression,
+ ComponentRequirement.forBinding(provisionBinding),
+ componentRequirementFields);
+ case INJECTION:
case PROVISION:
if (!provisionBinding.scope().isPresent()
- && !provisionBinding.requiresModuleInstance()
&& provisionBinding.bindingElement().isPresent()) {
+ Optional<ComponentRequirement> moduleRequirement =
+ provisionBinding.requiresModuleInstance()
+ ? Optional.of(
+ ComponentRequirement.forModule(
+ provisionBinding.contributingModule().get().asType()))
+ : Optional.empty();
return new SimpleMethodBindingExpression(
compilerOptions,
provisionBinding,
bindingExpression,
componentBindingExpressions,
- generatedComponentModel);
+ generatedComponentModel,
+ moduleRequirement,
+ componentRequirementFields);
}
// fall through
default:
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
index 25feb5b..ba01bb7 100644
--- a/java/dagger/internal/codegen/BindingGraph.java
+++ b/java/dagger/internal/codegen/BindingGraph.java
@@ -230,7 +230,8 @@
// Collect Component dependencies.
for (TypeElement componentDependency : componentDescriptor.dependencies()) {
- explicitBindingsBuilder.add(provisionBindingFactory.forComponent(componentDependency));
+ explicitBindingsBuilder.add(
+ provisionBindingFactory.forComponentDependency(componentDependency));
List<ExecutableElement> dependencyMethods =
ElementFilter.methodsIn(elements.getAllMembers(componentDependency));
for (ExecutableElement method : dependencyMethods) {
diff --git a/java/dagger/internal/codegen/BoundInstanceBindingExpression.java b/java/dagger/internal/codegen/BoundInstanceBindingExpression.java
new file mode 100644
index 0000000..2f3cd5b
--- /dev/null
+++ b/java/dagger/internal/codegen/BoundInstanceBindingExpression.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.Component;
+
+/**
+ * A binding expression for instances bound with {@link dagger.BindsInstance} and instances of
+ * {@link Component#dependencies() component dependencies}.
+ */
+final class BoundInstanceBindingExpression extends SimpleInvocationBindingExpression {
+ private final ComponentRequirement componentRequirement;
+ private final ComponentRequirementFields componentRequirementFields;
+
+ BoundInstanceBindingExpression(
+ BindingExpression delegate,
+ ComponentRequirement componentRequirement,
+ ComponentRequirementFields componentRequirementFields) {
+ super(delegate);
+ this.componentRequirement = componentRequirement;
+ this.componentRequirementFields = componentRequirementFields;
+ }
+
+ @Override
+ CodeBlock getInstanceDependencyExpression(
+ DependencyRequest.Kind requestKind, ClassName requestingClass) {
+ return componentRequirementFields.getExpression(componentRequirement, requestingClass);
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
index 32f053c..8e45306 100644
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -29,11 +29,12 @@
import java.util.Map;
import javax.lang.model.type.TypeMirror;
-/** A factory of code expressions used to access any binding available to a component. */
+/** A central repository of code expressions used to access any binding available to a component. */
final class ComponentBindingExpressions {
- // TODO(dpb): Can this use a flattened ImmutableMap, built from its parents? Maybe make
- // BindingExpression.Factory create it.
+ // TODO(dpb,ronshapiro): refactor this and ComponentRequirementFields into a
+ // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
+ // parents? If so, maybe make BindingExpression.Factory create it.
/**
* A list of binding expression maps. The first element contains the bindings owned by this
diff --git a/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java b/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
new file mode 100644
index 0000000..7523cf3
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+
+/** A binding expression for the instance of the component itself, i.e. {@code this}. */
+final class ComponentInstanceBindingExpression extends SimpleInvocationBindingExpression {
+ private final ClassName componentName;
+
+ ComponentInstanceBindingExpression(BindingExpression delegate, ClassName componentName) {
+ super(delegate);
+ this.componentName = componentName;
+ }
+
+ @Override
+ CodeBlock getInstanceDependencyExpression(
+ DependencyRequest.Kind requestKind, ClassName requestingClass) {
+ return componentName.equals(requestingClass)
+ ? CodeBlock.of("this")
+ : CodeBlock.of("$T.this", componentName);
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentRequirementField.java b/java/dagger/internal/codegen/ComponentRequirementField.java
new file mode 100644
index 0000000..e8d721d
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentRequirementField.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+
+/**
+ * A factory for expressions of {@link ComponentRequirement}s in the generated component. This is
+ * <em>not</em> a {@link BindingExpression}, since {@link ComponentRequirement}s do not have a
+ * {@link BindingKey}. See {@link BoundInstanceBindingExpression} for binding expressions that are
+ * themselves a binding.
+ */
+abstract class ComponentRequirementField {
+ private final ComponentRequirement componentRequirement;
+
+ private ComponentRequirementField(ComponentRequirement componentRequirement) {
+ this.componentRequirement = checkNotNull(componentRequirement);
+ }
+
+ final ComponentRequirement componentRequirement() {
+ return componentRequirement;
+ }
+
+ /**
+ * Returns an expression for the {@link ComponentRequirement} to be used when implementing a
+ * component method. This may add a field to the component in order to reference the component
+ * requirement outside of the {@code initialize()} methods.
+ */
+ abstract CodeBlock getExpression(ClassName requestingClass);
+
+ /**
+ * Returns an expression for the {@link ComponentRequirement} to be used only within {@code
+ * initialize()} methods, where the component builder is available.
+ *
+ * <p>When accessing this field from a subcomponent, this may cause a field to be initialized in
+ * the component that owns this {@link ComponentRequirement}.
+ */
+ abstract CodeBlock getExpressionDuringInitialization(ClassName requestingClass);
+
+ /**
+ * A {@link ComponentRequirementField} for {@link ComponentRequirement}s that have a corresponding
+ * field on the component builder.
+ */
+ private static final class BuilderField extends ComponentRequirementField {
+ private final GeneratedComponentModel generatedComponentModel;
+ private final UniqueNameSet componentFieldNames;
+ private final ClassName owningComponent;
+ private final FieldSpec builderField;
+ private MemberSelect field;
+
+ private BuilderField(
+ ComponentRequirement componentRequirement,
+ GeneratedComponentModel generatedComponentModel,
+ UniqueNameSet componentFieldNames,
+ ClassName owningComponent,
+ FieldSpec builderField) {
+ super(componentRequirement);
+ this.generatedComponentModel = checkNotNull(generatedComponentModel);
+ this.componentFieldNames = checkNotNull(componentFieldNames);
+ this.owningComponent = checkNotNull(owningComponent);
+ this.builderField = checkNotNull(builderField);
+ }
+
+ @Override
+ CodeBlock getExpression(ClassName requestingClass) {
+ return getField().getExpressionFor(requestingClass);
+ }
+
+ @Override
+ CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
+ if (owningComponent.equals(requestingClass)) {
+ return CodeBlock.of("builder.$N", builderField);
+ } else {
+ // requesting this component requirement during initialization of a child component requires
+ // the it to be access from a field and not the builder (since it is no longer available)
+ return getExpression(requestingClass);
+ }
+ }
+
+ private MemberSelect getField() {
+ if (field == null) {
+ // TODO(dpb,ronshapiro): think about whether GeneratedComponentModel.addField should make a
+ // unique name for the field.
+ String fieldName = componentFieldNames.getUniqueName(componentRequirement().variableName());
+ FieldSpec componentField =
+ FieldSpec.builder(TypeName.get(componentRequirement().type()), fieldName, PRIVATE)
+ .build();
+ generatedComponentModel.addField(componentField);
+ generatedComponentModel.addInitialization(
+ CodeBlock.of("this.$N = builder.$N;", componentField, builderField));
+ field = MemberSelect.localField(owningComponent, fieldName);
+ }
+ return field;
+ }
+ }
+
+ /**
+ * A {@link ComponentRequirementField} for {@link ComponentRequirement}s that have a corresponding
+ * field already added on the component.
+ */
+ private static final class ComponentField extends ComponentRequirementField {
+ private final MemberSelect memberSelect;
+
+ private ComponentField(
+ ComponentRequirement componentRequirement,
+ FieldSpec componentField,
+ ClassName owningComponent) {
+ super(componentRequirement);
+ this.memberSelect = MemberSelect.localField(owningComponent, componentField.name);
+ }
+
+ @Override
+ CodeBlock getExpression(ClassName requestingClass) {
+ return memberSelect.getExpressionFor(requestingClass);
+ }
+
+ @Override
+ CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
+ return getExpression(requestingClass);
+ }
+ }
+
+ static final class Factory {
+ private final GeneratedComponentModel generatedComponentModel;
+ private final UniqueNameSet componentFieldNames;
+ private final ClassName owningComponent;
+ private final ImmutableMap<ComponentRequirement, FieldSpec> builderFields;
+
+ Factory(
+ GeneratedComponentModel generatedComponentModel,
+ UniqueNameSet componentFieldNames,
+ ClassName owningComponent,
+ ImmutableMap<ComponentRequirement, FieldSpec> builderFields) {
+ this.generatedComponentModel = checkNotNull(generatedComponentModel);
+ this.componentFieldNames = checkNotNull(componentFieldNames);
+ this.owningComponent = checkNotNull(owningComponent);
+ this.builderFields = checkNotNull(builderFields);
+ }
+
+ /**
+ * Returns a {@link ComponentRequirementField} for {@link ComponentRequirement}s that have a
+ * corresponding field on the component builder.
+ */
+ ComponentRequirementField forBuilderField(ComponentRequirement componentRequirement) {
+ return new BuilderField(
+ componentRequirement,
+ generatedComponentModel,
+ componentFieldNames,
+ owningComponent,
+ builderFields.get(componentRequirement));
+ }
+ }
+
+ /**
+ * Returns a {@link ComponentRequirementField} for {@link ComponentRequirement}s that have a
+ * corresponding field already added on the component.
+ */
+ static ComponentRequirementField componentField(
+ ComponentRequirement componentRequirement,
+ FieldSpec componentField,
+ ClassName owningComponent) {
+ return new ComponentField(componentRequirement, componentField, owningComponent);
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentRequirementFields.java b/java/dagger/internal/codegen/ComponentRequirementFields.java
new file mode 100644
index 0000000..afa3382
--- /dev/null
+++ b/java/dagger/internal/codegen/ComponentRequirementFields.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A central repository of fields used to access any {@link ComponentRequirement} available to a
+ * component.
+ */
+final class ComponentRequirementFields {
+
+ // TODO(dpb,ronshapiro): refactor this and ComponentBindingExpressions into a
+ // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
+ // parents? If so, maybe make ComponentRequirementField.Factory create it.
+
+ /**
+ * A list of component requirement field maps. The first element contains the fields on this
+ * component; the second contains the fields owned by its parent; and so on.
+ */
+ private final ImmutableList<Map<ComponentRequirement, ComponentRequirementField>>
+ componentRequirementFieldsMaps;
+
+ private ComponentRequirementFields(
+ ImmutableList<Map<ComponentRequirement, ComponentRequirementField>>
+ componentRequirementFieldsMaps) {
+ this.componentRequirementFieldsMaps = componentRequirementFieldsMaps;
+ }
+
+ ComponentRequirementFields() {
+ this(ImmutableList.of(newComponentRequirementsMap()));
+ }
+
+ /**
+ * Returns an expression for the {@code componentRequirement} to be used when implementing a
+ * component method. This may add a field to the component in order to reference the component
+ * requirement outside of the {@code initialize()} methods.
+ */
+ CodeBlock getExpression(ComponentRequirement componentRequirement, ClassName requestingClass) {
+ return getField(componentRequirement).getExpression(requestingClass);
+ }
+
+ /**
+ * Returns an expression for the {@code componentRequirement} to be used only within {@code
+ * initialize()} methods, where the component builder is available.
+ *
+ * <p>When accessing this field from a subcomponent, this may cause a field to be initialized in
+ * the component that owns this {@link ComponentRequirement}.
+ */
+ CodeBlock getExpressionDuringInitialization(
+ ComponentRequirement componentRequirement, ClassName requestingClass) {
+ return getField(componentRequirement).getExpressionDuringInitialization(requestingClass);
+ }
+
+ private ComponentRequirementField getField(ComponentRequirement componentRequirement) {
+ for (Map<ComponentRequirement, ComponentRequirementField> componentRequirementFieldsMap :
+ componentRequirementFieldsMaps) {
+ ComponentRequirementField field = componentRequirementFieldsMap.get(componentRequirement);
+ if (field != null) {
+ return field;
+ }
+ }
+ throw new IllegalStateException(
+ "no component requirement field found for " + componentRequirement);
+ }
+
+ /**
+ * Adds a component requirement field for a single component requirement owned by this component.
+ */
+ void add(ComponentRequirementField field) {
+ componentRequirementFieldsMaps.get(0).put(field.componentRequirement(), field);
+ }
+
+ /**
+ * Returns {@code true} if the component that owns this {@link ComponentRequirementFields} has a
+ * registered {@link ComponentRequirementField} for {@code componentRequirement}.
+ */
+ boolean contains(ComponentRequirement componentRequirement) {
+ return componentRequirementFieldsMaps
+ .stream()
+ .anyMatch(map -> map.containsKey(componentRequirement));
+ }
+
+ private static Map<ComponentRequirement, ComponentRequirementField>
+ newComponentRequirementsMap() {
+ return new HashMap<>();
+ }
+
+ /**
+ * Returns a new object representing the fields available from a child component of this one.
+ */
+ ComponentRequirementFields forChildComponent() {
+ return new ComponentRequirementFields(
+ FluentIterable.of(newComponentRequirementsMap())
+ .append(componentRequirementFieldsMaps)
+ .toList());
+ }
+}
diff --git a/java/dagger/internal/codegen/ComponentWriter.java b/java/dagger/internal/codegen/ComponentWriter.java
index 53b10c7..8bb273a 100644
--- a/java/dagger/internal/codegen/ComponentWriter.java
+++ b/java/dagger/internal/codegen/ComponentWriter.java
@@ -65,7 +65,8 @@
graph,
new UniqueSubcomponentNamesGenerator(graph).generate(),
new OptionalFactories(),
- new ComponentBindingExpressions());
+ new ComponentBindingExpressions(),
+ new ComponentRequirementFields());
}
/**
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
index 81eb8d9..8891e67 100644
--- a/java/dagger/internal/codegen/ContributionBinding.java
+++ b/java/dagger/internal/codegen/ContributionBinding.java
@@ -129,6 +129,9 @@
/** A provision method on a component's {@linkplain Component#dependencies() dependency}. */
COMPONENT_PROVISION,
+ /** An instance of a {@linkplain Component#dependencies() dependency}. */
+ COMPONENT_DEPENDENCY,
+
/**
* A subcomponent builder method on a component or subcomponent.
*/
diff --git a/java/dagger/internal/codegen/InjectionMethods.java b/java/dagger/internal/codegen/InjectionMethods.java
index d2ebf4e..6eff4dd 100644
--- a/java/dagger/internal/codegen/InjectionMethods.java
+++ b/java/dagger/internal/codegen/InjectionMethods.java
@@ -25,11 +25,11 @@
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.CodeBlocks.toConcatenatedCodeBlock;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
import static dagger.internal.codegen.TypeNames.rawTypeName;
+import static dagger.internal.codegen.Util.toImmutableList;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
@@ -136,13 +136,16 @@
static CodeBlock invoke(
ProvisionBinding binding,
Function<DependencyRequest, CodeBlock> dependencyUsage,
- ClassName requestingClass) {
+ ClassName requestingClass,
+ Optional<CodeBlock> moduleReference) {
+ ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+ moduleReference.ifPresent(arguments::add);
+ arguments.addAll(
+ injectionMethodArguments(
+ binding.provisionDependencies(), dependencyUsage, requestingClass));
return callInjectionMethod(
create(binding).get().name,
- // TODO(dpb): would this be simpler if injectionMethodArguments returned a List?
- ImmutableList.of(
- injectionMethodArguments(
- binding.provisionDependencies(), dependencyUsage, requestingClass)),
+ arguments.build(),
generatedClassNameForBinding(binding),
requestingClass);
}
@@ -336,13 +339,13 @@
* @param dependencyUsage function to apply on each of {@code dependencies} before casting
* @param requestingClass the class calling the injection method
*/
- private static CodeBlock injectionMethodArguments(
+ private static ImmutableList<CodeBlock> injectionMethodArguments(
ImmutableSet<DependencyRequest> dependencies,
Function<DependencyRequest, CodeBlock> dependencyUsage,
ClassName requestingClass) {
return dependencies.stream()
.map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass))
- .collect(toParametersCodeBlock());
+ .collect(toImmutableList());
}
private static CodeBlock injectionMethodArgument(
diff --git a/java/dagger/internal/codegen/Key.java b/java/dagger/internal/codegen/Key.java
index 50d12bc..3c4cecc 100644
--- a/java/dagger/internal/codegen/Key.java
+++ b/java/dagger/internal/codegen/Key.java
@@ -503,7 +503,8 @@
return builder(type).build();
}
- Key forComponent(TypeMirror type) {
+ // TODO(ronshapiro): Remove these conveniences which are simple wrappers around Key.Builder
+ Key forType(TypeMirror type) {
return builder(type).build();
}
diff --git a/java/dagger/internal/codegen/ProvisionBinding.java b/java/dagger/internal/codegen/ProvisionBinding.java
index 5eeca8d..12f43da 100644
--- a/java/dagger/internal/codegen/ProvisionBinding.java
+++ b/java/dagger/internal/codegen/ProvisionBinding.java
@@ -272,11 +272,21 @@
return ProvisionBinding.builder()
.contributionType(ContributionType.UNIQUE)
.bindingElement(componentDefinitionType)
- .key(keyFactory.forComponent(componentDefinitionType.asType()))
+ .key(keyFactory.forType(componentDefinitionType.asType()))
.bindingKind(Kind.COMPONENT)
.build();
}
+ ProvisionBinding forComponentDependency(TypeElement dependencyType) {
+ checkNotNull(dependencyType);
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(dependencyType)
+ .key(keyFactory.forType(dependencyType.asType()))
+ .bindingKind(Kind.COMPONENT_DEPENDENCY)
+ .build();
+ }
+
ProvisionBinding forComponentMethod(ExecutableElement componentMethod) {
checkNotNull(componentMethod);
checkArgument(componentMethod.getKind().equals(METHOD));
diff --git a/java/dagger/internal/codegen/SimpleMethodBindingExpression.java b/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
index ec9d208..de83e7b 100644
--- a/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
@@ -18,20 +18,19 @@
import static com.google.auto.common.MoreElements.asExecutable;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod;
import static dagger.internal.codegen.TypeNames.rawTypeName;
-import static javax.lang.model.element.Modifier.STATIC;
import com.google.auto.common.MoreTypes;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.TypeName;
import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
+import java.util.Optional;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.DeclaredType;
@@ -44,24 +43,29 @@
private final ProvisionBinding provisionBinding;
private final ComponentBindingExpressions componentBindingExpressions;
private final GeneratedComponentModel generatedComponentModel;
+ private final Optional<ComponentRequirement> moduleRequirement;
+ private final ComponentRequirementFields componentRequirementFields;
SimpleMethodBindingExpression(
CompilerOptions compilerOptions,
ProvisionBinding provisionBinding,
BindingExpression delegate,
ComponentBindingExpressions componentBindingExpressions,
- GeneratedComponentModel generatedComponentModel) {
+ GeneratedComponentModel generatedComponentModel,
+ Optional<ComponentRequirement> moduleRequirement,
+ ComponentRequirementFields componentRequirementFields) {
super(delegate);
checkArgument(
provisionBinding.implicitDependencies().isEmpty(),
"framework deps are not currently supported");
checkArgument(!provisionBinding.scope().isPresent());
- checkArgument(!provisionBinding.requiresModuleInstance());
checkArgument(provisionBinding.bindingElement().isPresent());
this.compilerOptions = compilerOptions;
this.provisionBinding = provisionBinding;
this.componentBindingExpressions = componentBindingExpressions;
this.generatedComponentModel = generatedComponentModel;
+ this.moduleRequirement = moduleRequirement;
+ this.componentRequirementFields = componentRequirementFields;
}
@Override
@@ -85,13 +89,11 @@
case CONSTRUCTOR:
return CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments);
case METHOD:
- checkState(method.getModifiers().contains(STATIC));
+ CodeBlock module =
+ moduleReference(requestingClass)
+ .orElse(CodeBlock.of("$T", provisionBinding.bindingTypeElement().get()));
return maybeCheckForNulls(
- CodeBlock.of(
- "$T.$L($L)",
- provisionBinding.bindingTypeElement().get(),
- method.getSimpleName(),
- arguments));
+ CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments));
default:
throw new IllegalStateException();
}
@@ -114,7 +116,8 @@
ProvisionMethod.invoke(
provisionBinding,
request -> dependencyArgument(request, requestingClass),
- requestingClass)));
+ requestingClass,
+ moduleReference(requestingClass))));
}
private CodeBlock dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
@@ -145,4 +148,9 @@
generatedComponentModel.getMembersInjectionMethod(provisionBinding.key()),
instance);
}
+
+ private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
+ return moduleRequirement.map(
+ requirement -> componentRequirementFields.getExpression(requirement, requestingClass));
+ }
}
diff --git a/java/dagger/internal/codegen/SubcomponentWriter.java b/java/dagger/internal/codegen/SubcomponentWriter.java
index 48e674f..739e3c4 100644
--- a/java/dagger/internal/codegen/SubcomponentWriter.java
+++ b/java/dagger/internal/codegen/SubcomponentWriter.java
@@ -18,10 +18,8 @@
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Sets.difference;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
-import static dagger.internal.codegen.MemberSelect.localField;
import static dagger.internal.codegen.TypeSpecs.addSupertype;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
@@ -29,7 +27,9 @@
import com.google.auto.common.MoreTypes;
import com.google.common.base.CaseFormat;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
@@ -74,16 +74,6 @@
}
@Override
- protected Optional<CodeBlock> getOrCreateComponentRequirementFieldExpression(
- ComponentRequirement componentRequirement) {
- Optional<CodeBlock> expression =
- super.getOrCreateComponentRequirementFieldExpression(componentRequirement);
- return expression.isPresent()
- ? expression
- : parent.getOrCreateComponentRequirementFieldExpression(componentRequirement);
- }
-
- @Override
protected CodeBlock getReferenceReleasingProviderManagerExpression(Scope scope) {
return parent.getReferenceReleasingProviderManagerExpression(scope);
}
@@ -147,7 +137,7 @@
ComponentRequirement.forModule(moduleTypeElement.asType());
TypeName moduleType = TypeName.get(paramTypes.get(i));
componentMethod.addParameter(moduleType, moduleVariable.getSimpleName().toString());
- if (!componentContributionFields.containsKey(componentRequirement)) {
+ if (!componentRequirementFields.contains(componentRequirement)) {
String preferredModuleName =
CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleTypeElement.getSimpleName().toString());
FieldSpec contributionField =
@@ -156,23 +146,23 @@
.build();
component.addField(contributionField);
- String actualModuleName = contributionField.name;
constructor
- .addParameter(moduleType, actualModuleName)
+ .addParameter(moduleType, contributionField.name)
.addStatement(
- "this.$1L = $2T.checkNotNull($1L)",
- actualModuleName,
- Preconditions.class);
+ "this.$1N = $2T.checkNotNull($1N)", contributionField, Preconditions.class);
- MemberSelect moduleSelect = localField(name, actualModuleName);
- componentContributionFields.put(componentRequirement, moduleSelect);
+ componentRequirementFields.add(
+ ComponentRequirementField.componentField(
+ componentRequirement, contributionField, name));
subcomponentConstructorParameters.add(
CodeBlock.of("$L", moduleVariable.getSimpleName()));
}
}
Set<ComponentRequirement> uninitializedModules =
- difference(graph.componentRequirements(), componentContributionFields.keySet());
+ Sets.filter(
+ graph.componentRequirements(),
+ Predicates.not(componentRequirementFields::contains));
for (ComponentRequirement componentRequirement : uninitializedModules) {
checkState(componentRequirement.kind().equals(ComponentRequirement.Kind.MODULE));
@@ -184,11 +174,10 @@
.addModifiers(PRIVATE, FINAL)
.build();
component.addField(contributionField);
- String actualModuleName = contributionField.name;
- constructor.addStatement(
- "this.$L = new $T()", actualModuleName, ClassName.get(moduleType));
- MemberSelect moduleSelect = localField(name, actualModuleName);
- componentContributionFields.put(componentRequirement, moduleSelect);
+ constructor.addStatement("this.$N = new $T()", contributionField, ClassName.get(moduleType));
+ componentRequirementFields.add(
+ ComponentRequirementField.componentField(
+ componentRequirement, contributionField, name));
}
componentMethod.addStatement("return new $T($L)",