| /* |
| * Copyright (C) 2015 The Dagger Authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package dagger.internal.codegen; |
| |
| import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; |
| import static com.google.auto.common.MoreTypes.asDeclared; |
| import static com.google.common.base.Preconditions.checkState; |
| import static com.squareup.javapoet.MethodSpec.constructorBuilder; |
| import static com.squareup.javapoet.MethodSpec.methodBuilder; |
| import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED; |
| import static dagger.internal.codegen.BindingRequest.bindingRequest; |
| import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock; |
| import static dagger.internal.codegen.DaggerStreams.toImmutableList; |
| import static dagger.internal.codegen.GeneratedComponentModel.MethodSpecKind.BUILDER_METHOD; |
| import static dagger.internal.codegen.GeneratedComponentModel.MethodSpecKind.CANCELLATION_LISTENER_METHOD; |
| import static dagger.internal.codegen.GeneratedComponentModel.MethodSpecKind.COMPONENT_METHOD; |
| import static dagger.internal.codegen.GeneratedComponentModel.MethodSpecKind.CONSTRUCTOR; |
| import static dagger.internal.codegen.GeneratedComponentModel.MethodSpecKind.INITIALIZE_METHOD; |
| import static dagger.internal.codegen.GeneratedComponentModel.TypeSpecKind.COMPONENT_BUILDER; |
| import static dagger.internal.codegen.GeneratedComponentModel.TypeSpecKind.SUBCOMPONENT; |
| import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE; |
| import static javax.lang.model.element.Modifier.FINAL; |
| import static javax.lang.model.element.Modifier.PRIVATE; |
| import static javax.lang.model.element.Modifier.PROTECTED; |
| import static javax.lang.model.element.Modifier.PUBLIC; |
| import static javax.lang.model.element.Modifier.STATIC; |
| |
| import com.google.auto.common.MoreTypes; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableListMultimap; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Multimaps; |
| import com.squareup.javapoet.ClassName; |
| import com.squareup.javapoet.CodeBlock; |
| import com.squareup.javapoet.MethodSpec; |
| import com.squareup.javapoet.ParameterSpec; |
| import com.squareup.javapoet.TypeSpec; |
| import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; |
| import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod; |
| import dagger.model.Key; |
| import dagger.producers.internal.CancellationListener; |
| import dagger.producers.internal.Producers; |
| import java.util.List; |
| import java.util.Optional; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.type.DeclaredType; |
| |
| /** Builds the model for an implementation of a component or subcomponent. */ |
| abstract class ComponentModelBuilder { |
| private static final String MAY_INTERRUPT_IF_RUNNING = "mayInterruptIfRunning"; |
| |
| static GeneratedComponentModel buildComponentModel( |
| DaggerTypes types, |
| DaggerElements elements, |
| KeyFactory keyFactory, |
| CompilerOptions compilerOptions, |
| ClassName name, |
| BindingGraph graph, |
| BindingGraphFactory bindingGraphFactory) { |
| GeneratedComponentModel generatedComponentModel = |
| GeneratedComponentModel.create(name, graph, keyFactory); |
| OptionalFactories optionalFactories = new OptionalFactories(generatedComponentModel); |
| Optional<GeneratedComponentBuilderModel> generatedComponentBuilderModel = |
| GeneratedComponentBuilderModel.create(generatedComponentModel, graph, elements, types); |
| ComponentRequirementFields componentRequirementFields = |
| new ComponentRequirementFields( |
| graph, generatedComponentModel, generatedComponentBuilderModel); |
| ComponentBindingExpressions bindingExpressions = |
| new ComponentBindingExpressions( |
| graph, |
| generatedComponentModel, |
| componentRequirementFields, |
| optionalFactories, |
| types, |
| elements, |
| compilerOptions); |
| if (generatedComponentModel.isAbstract()) { |
| checkState( |
| compilerOptions.aheadOfTimeSubcomponents(), |
| "Calling 'buildComponentModel()' on %s when not generating ahead-of-time subcomponents.", |
| graph.componentDescriptor().componentDefinitionType()); |
| return new SubComponentModelBuilder( |
| Optional.empty(), /* parent */ |
| types, |
| elements, |
| keyFactory, |
| graph, |
| generatedComponentModel, |
| optionalFactories, |
| bindingExpressions, |
| componentRequirementFields, |
| generatedComponentBuilderModel, |
| bindingGraphFactory, |
| compilerOptions) |
| .build(); |
| } else { |
| return new RootComponentModelBuilder( |
| types, |
| elements, |
| keyFactory, |
| graph, |
| generatedComponentModel, |
| optionalFactories, |
| bindingExpressions, |
| componentRequirementFields, |
| generatedComponentBuilderModel, |
| bindingGraphFactory, |
| compilerOptions) |
| .build(); |
| } |
| } |
| |
| private final DaggerElements elements; |
| private final DaggerTypes types; |
| private final KeyFactory keyFactory; |
| private final BindingGraph graph; |
| private final ComponentBindingExpressions bindingExpressions; |
| private final ComponentRequirementFields componentRequirementFields; |
| private final GeneratedComponentModel generatedComponentModel; |
| private final OptionalFactories optionalFactories; |
| private final Optional<GeneratedComponentBuilderModel> generatedComponentBuilderModel; |
| private final BindingGraphFactory bindingGraphFactory; |
| private final CompilerOptions compilerOptions; |
| private boolean done; |
| |
| private ComponentModelBuilder( |
| DaggerTypes types, |
| DaggerElements elements, |
| KeyFactory keyFactory, |
| BindingGraph graph, |
| GeneratedComponentModel generatedComponentModel, |
| OptionalFactories optionalFactories, |
| ComponentBindingExpressions bindingExpressions, |
| ComponentRequirementFields componentRequirementFields, |
| Optional<GeneratedComponentBuilderModel> generatedComponentBuilderModel, |
| BindingGraphFactory bindingGraphFactory, |
| CompilerOptions compilerOptions) { |
| this.types = types; |
| this.elements = elements; |
| this.keyFactory = keyFactory; |
| this.graph = graph; |
| this.generatedComponentModel = generatedComponentModel; |
| this.optionalFactories = optionalFactories; |
| this.bindingExpressions = bindingExpressions; |
| this.componentRequirementFields = componentRequirementFields; |
| this.generatedComponentBuilderModel = generatedComponentBuilderModel; |
| this.bindingGraphFactory = bindingGraphFactory; |
| this.compilerOptions = compilerOptions; |
| } |
| |
| /** |
| * Returns a {@link GeneratedComponentModel} for this component. This is only intended to be |
| * called once (and will throw on successive invocations). If the component must be regenerated, |
| * use a new instance. |
| */ |
| protected final GeneratedComponentModel build() { |
| checkState( |
| !done, |
| "ComponentModelBuilder has already built the GeneratedComponentModel for [%s].", |
| generatedComponentModel.name()); |
| setSupertype(); |
| generatedComponentBuilderModel |
| .map(GeneratedComponentBuilderModel::typeSpec) |
| .ifPresent(this::addBuilderClass); |
| |
| getLocalAndInheritedMethods( |
| graph.componentDescriptor().componentDefinitionType(), types, elements) |
| .forEach(method -> generatedComponentModel.claimMethodName(method.getSimpleName())); |
| |
| addFactoryMethods(); |
| addInterfaceMethods(); |
| addSubcomponents(); |
| addConstructor(); |
| |
| if (graph.componentDescriptor().kind().isProducer()) { |
| addCancellationListenerImplementation(); |
| } |
| |
| done = true; |
| return generatedComponentModel; |
| } |
| |
| /** Set the supertype for this generated class. */ |
| private void setSupertype() { |
| if (generatedComponentModel.supermodel().isPresent()) { |
| generatedComponentModel.addSuperclass(generatedComponentModel.supermodel().get().name()); |
| } else { |
| generatedComponentModel.addSupertype(graph.componentType()); |
| } |
| } |
| |
| /** |
| * Adds {@code builder} as a nested builder class. Root components and subcomponents will nest |
| * this in different classes. |
| */ |
| protected abstract void addBuilderClass(TypeSpec builder); |
| |
| /** Adds component factory methods. */ |
| protected abstract void addFactoryMethods(); |
| |
| protected void addInterfaceMethods() { |
| /* Each component method may have been declared by several supertypes. We want to implement only |
| * one method for each distinct signature.*/ |
| ImmutableListMultimap<MethodSignature, ComponentMethodDescriptor> componentMethodsBySignature = |
| Multimaps.index(graph.componentDescriptor().entryPointMethods(), this::getMethodSignature); |
| for (List<ComponentMethodDescriptor> methodsWithSameSignature : |
| Multimaps.asMap(componentMethodsBySignature).values()) { |
| ComponentMethodDescriptor anyOneMethod = methodsWithSameSignature.stream().findAny().get(); |
| MethodSpec methodSpec = bindingExpressions.getComponentMethod(anyOneMethod); |
| |
| // If the binding for the component method is modifiable, register it as such. |
| ModifiableBindingType modifiableBindingType = |
| bindingExpressions |
| .modifiableBindingExpressions() |
| .registerComponentMethodIfModifiable(anyOneMethod, methodSpec); |
| |
| // If the method should be implemented in this component, implement it. |
| if (modifiableBindingType.hasBaseClassImplementation()) { |
| generatedComponentModel.addMethod(COMPONENT_METHOD, methodSpec); |
| } |
| } |
| } |
| |
| private static final int STATEMENTS_PER_METHOD = 100; |
| |
| private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled"; |
| |
| private void addCancellationListenerImplementation() { |
| generatedComponentModel.addSupertype(elements.getTypeElement(CancellationListener.class)); |
| generatedComponentModel.claimMethodName(CANCELLATION_LISTENER_METHOD_NAME); |
| |
| MethodSpec.Builder methodBuilder = |
| methodBuilder(CANCELLATION_LISTENER_METHOD_NAME) |
| .addModifiers(PUBLIC) |
| .addAnnotation(Override.class) |
| .addParameter(boolean.class, MAY_INTERRUPT_IF_RUNNING); |
| if (generatedComponentModel.supermodel().isPresent()) { |
| methodBuilder.addStatement( |
| "super.$L($L)", CANCELLATION_LISTENER_METHOD_NAME, MAY_INTERRUPT_IF_RUNNING); |
| } |
| |
| ImmutableList<CodeBlock> cancellationStatements = cancellationStatements(); |
| if (cancellationStatements.isEmpty() && generatedComponentModel.supermodel().isPresent()) { |
| // Partial subcomponent implementations that have no new cancellations don't need to override |
| // the method just to call super(). |
| return; |
| } |
| |
| if (cancellationStatements.size() < STATEMENTS_PER_METHOD) { |
| methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build(); |
| } else { |
| List<List<CodeBlock>> partitions = |
| Lists.partition(cancellationStatements, STATEMENTS_PER_METHOD); |
| for (List<CodeBlock> partition : partitions) { |
| String methodName = generatedComponentModel.getUniqueMethodName("cancelProducers"); |
| MethodSpec method = |
| MethodSpec.methodBuilder(methodName) |
| .addModifiers(PRIVATE) |
| .addParameter(boolean.class, MAY_INTERRUPT_IF_RUNNING) |
| .addCode(CodeBlocks.concat(partition)) |
| .build(); |
| methodBuilder.addStatement("$N($L)", method, MAY_INTERRUPT_IF_RUNNING); |
| generatedComponentModel.addMethod(CANCELLATION_LISTENER_METHOD, method); |
| } |
| } |
| |
| addCancelParentStatement(methodBuilder); |
| |
| generatedComponentModel.addMethod(CANCELLATION_LISTENER_METHOD, methodBuilder.build()); |
| } |
| |
| private ImmutableList<CodeBlock> cancellationStatements() { |
| // Reversing should order cancellations starting from entry points and going down to leaves |
| // rather than the other way around. This shouldn't really matter but seems *slightly* |
| // preferable because: |
| // When a future that another future depends on is cancelled, that cancellation will propagate |
| // up the future graph toward the entry point. Cancelling in reverse order should ensure that |
| // everything that depends on a particular node has already been cancelled when that node is |
| // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might |
| // propagate through most of the graph, making most of the cancel calls that follow in the |
| // onProducerFutureCancelled method do nothing. |
| ImmutableList<Key> cancellationKeys = |
| generatedComponentModel.getCancellableProducerKeys().reverse(); |
| |
| ImmutableList.Builder<CodeBlock> cancellationStatements = ImmutableList.builder(); |
| for (Key cancellationKey : cancellationKeys) { |
| cancellationStatements.add( |
| CodeBlock.of( |
| "$T.cancel($L, $N);", |
| Producers.class, |
| bindingExpressions |
| .getDependencyExpression( |
| bindingRequest(cancellationKey, FrameworkType.PRODUCER_NODE), |
| generatedComponentModel.name()) |
| .codeBlock(), |
| MAY_INTERRUPT_IF_RUNNING)); |
| } |
| return cancellationStatements.build(); |
| } |
| |
| protected void addCancelParentStatement(MethodSpec.Builder methodBuilder) { |
| // Does nothing by default. Overridden in subclass(es) to add a statement if and only if the |
| // component being generated is a concrete subcomponent implementation with a parent that allows |
| // cancellation to propagate to it from subcomponents. |
| } |
| |
| private MethodSignature getMethodSignature(ComponentMethodDescriptor method) { |
| return MethodSignature.forComponentMethod( |
| method, MoreTypes.asDeclared(graph.componentType().asType()), types); |
| } |
| |
| private void addSubcomponents() { |
| for (BindingGraph subgraph : graph.subgraphs()) { |
| // TODO(b/117833324): Can an abstract inner subcomponent implementation be elided if it's |
| // totally empty? |
| generatedComponentModel.addSubcomponent( |
| subgraph.componentDescriptor(), buildSubcomponentModel(subgraph)); |
| } |
| } |
| |
| private GeneratedComponentModel getSubcomponentSupermodel(ComponentDescriptor subcomponent) { |
| // If the current model is for a subcomponent that has a defined supermodel, that supermodel |
| // should contain a reference to a model for `subcomponent` |
| if (generatedComponentModel.supermodel().isPresent()) { |
| Optional<GeneratedComponentModel> supermodel = |
| generatedComponentModel.supermodel().get().subcomponentModel(subcomponent); |
| checkState( |
| supermodel.isPresent(), |
| "Attempting to generate an implementation of a subcomponent [%s] whose parent is a " |
| + "subcomponent [%s], but whose supermodel is not present on the parent's " |
| + "supermodel.", |
| subcomponent.componentDefinitionType(), |
| graph.componentType()); |
| return supermodel.get(); |
| } |
| |
| // Otherwise, the enclosing component is top-level, so we must generate the supermodel for the |
| // subcomponent. We do so by building the model for the abstract base class for the |
| // subcomponent. This is done by truncating the binding graph at the subcomponent. |
| BindingGraph truncatedBindingGraph = bindingGraphFactory.create(subcomponent); |
| return buildComponentModel( |
| // TODO(ronshapiro): extract a factory class here so that we don't need to pass around |
| // types, elements, keyFactory, etc... |
| types, |
| elements, |
| keyFactory, |
| compilerOptions, |
| ComponentGenerator.componentName(truncatedBindingGraph.componentType()), |
| truncatedBindingGraph, |
| bindingGraphFactory); |
| } |
| |
| private GeneratedComponentModel buildSubcomponentModel(BindingGraph childGraph) { |
| GeneratedComponentModel childModel; |
| if (compilerOptions.aheadOfTimeSubcomponents()) { |
| childModel = |
| GeneratedComponentModel.forSubcomponent( |
| childGraph.componentDescriptor(), |
| generatedComponentModel, |
| getSubcomponentSupermodel(childGraph.componentDescriptor())); |
| } else { |
| childModel = |
| GeneratedComponentModel.forSubcomponent( |
| childGraph.componentDescriptor(), generatedComponentModel); |
| } |
| Optional<GeneratedComponentBuilderModel> childBuilderModel = |
| GeneratedComponentBuilderModel.create(childModel, childGraph, elements, types); |
| ComponentRequirementFields childComponentRequirementFields = |
| componentRequirementFields.forChildComponent(childGraph, childModel, childBuilderModel); |
| ComponentBindingExpressions childBindingExpressions = |
| bindingExpressions.forChildComponent( |
| childGraph, childModel, childComponentRequirementFields); |
| return new SubComponentModelBuilder( |
| Optional.of(this), |
| types, |
| elements, |
| keyFactory, |
| childGraph, |
| childModel, |
| optionalFactories, |
| childBindingExpressions, |
| childComponentRequirementFields, |
| childBuilderModel, |
| bindingGraphFactory, |
| compilerOptions) |
| .build(); |
| } |
| |
| private void addConstructor() { |
| List<List<CodeBlock>> partitions = |
| Lists.partition(generatedComponentModel.getInitializations(), STATEMENTS_PER_METHOD); |
| |
| ImmutableList<ParameterSpec> constructorParameters = constructorParameters(); |
| MethodSpec.Builder constructor = |
| constructorBuilder() |
| .addModifiers(generatedComponentModel.isAbstract() ? PROTECTED : PRIVATE) |
| .addParameters(constructorParameters); |
| generatedComponentModel.setConstructorParameters(constructorParameters); |
| generatedComponentModel |
| .supermodel() |
| .ifPresent( |
| supermodel -> |
| constructor.addStatement( |
| CodeBlock.of( |
| "super($L)", |
| supermodel.constructorParameters().stream() |
| .map(param -> CodeBlock.of("$N", param)) |
| .collect(toParametersCodeBlock())))); |
| |
| ImmutableList<ParameterSpec> initializeParameters = initializeParameters(); |
| CodeBlock initializeParametersCodeBlock = |
| constructorParameters |
| .stream() |
| .map(param -> CodeBlock.of("$N", param)) |
| .collect(toParametersCodeBlock()); |
| |
| for (List<CodeBlock> partition : partitions) { |
| String methodName = generatedComponentModel.getUniqueMethodName("initialize"); |
| MethodSpec.Builder initializeMethod = |
| methodBuilder(methodName) |
| .addModifiers(PRIVATE) |
| /* TODO(gak): Strictly speaking, we only need the suppression here if we are also |
| * initializing a raw field in this method, but the structure of this code makes it |
| * awkward to pass that bit through. This will be cleaned up when we no longer |
| * separate fields and initialization as we do now. */ |
| .addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED)) |
| .addCode(CodeBlocks.concat(partition)); |
| initializeMethod.addParameters(initializeParameters); |
| constructor.addStatement("$L($L)", methodName, initializeParametersCodeBlock); |
| generatedComponentModel.addMethod(INITIALIZE_METHOD, initializeMethod.build()); |
| } |
| generatedComponentModel.addMethod(CONSTRUCTOR, constructor.build()); |
| } |
| |
| /** Returns the list of {@link ParameterSpec}s for the initialize methods. */ |
| private ImmutableList<ParameterSpec> initializeParameters() { |
| return constructorParameters() |
| .stream() |
| .map(param -> param.toBuilder().addModifiers(FINAL).build()) |
| .collect(toImmutableList()); |
| } |
| |
| /** Returns the list of {@link ParameterSpec}s for the constructor. */ |
| private ImmutableList<ParameterSpec> constructorParameters() { |
| if (generatedComponentBuilderModel.isPresent()) { |
| return ImmutableList.of( |
| ParameterSpec.builder(generatedComponentBuilderModel.get().name(), "builder").build()); |
| } else if (generatedComponentModel.isAbstract() && generatedComponentModel.isNested()) { |
| // If we're generating an abstract inner subcomponent, then we are not implementing module |
| // instance bindings and have no need for factory method parameters. |
| return ImmutableList.of(); |
| } else if (graph.factoryMethod().isPresent()) { |
| return getFactoryMethodParameterSpecs(graph); |
| } else if (generatedComponentModel.isAbstract()) { |
| // If we're generating an abstract base implementation of a subcomponent it's acceptable to |
| // have neither a builder nor factory method. |
| return ImmutableList.of(); |
| } else { |
| throw new AssertionError( |
| "Expected either a component builder or factory method but found neither."); |
| } |
| } |
| |
| /** Builds the model for the root component. */ |
| private static final class RootComponentModelBuilder extends ComponentModelBuilder { |
| RootComponentModelBuilder( |
| DaggerTypes types, |
| DaggerElements elements, |
| KeyFactory keyFactory, |
| BindingGraph graph, |
| GeneratedComponentModel generatedComponentModel, |
| OptionalFactories optionalFactories, |
| ComponentBindingExpressions bindingExpressions, |
| ComponentRequirementFields componentRequirementFields, |
| Optional<GeneratedComponentBuilderModel> generatedComponentBuilderModel, |
| BindingGraphFactory bindingGraphFactory, |
| CompilerOptions compilerOptions) { |
| super( |
| types, |
| elements, |
| keyFactory, |
| graph, |
| generatedComponentModel, |
| optionalFactories, |
| bindingExpressions, |
| componentRequirementFields, |
| generatedComponentBuilderModel, |
| bindingGraphFactory, |
| compilerOptions); |
| } |
| |
| @Override |
| protected void addBuilderClass(TypeSpec builder) { |
| super.generatedComponentModel.addType(COMPONENT_BUILDER, builder); |
| } |
| |
| @Override |
| protected void addFactoryMethods() { |
| // Only top-level components have the factory builder() method. |
| // Mirror the user's builder API type if they had one. |
| MethodSpec builderFactoryMethod = |
| methodBuilder("builder") |
| .addModifiers(PUBLIC, STATIC) |
| .returns( |
| builderSpec().isPresent() |
| ? ClassName.get(builderSpec().get().builderDefinitionType()) |
| : super.generatedComponentBuilderModel.get().name()) |
| .addStatement("return new $T()", super.generatedComponentBuilderModel.get().name()) |
| .build(); |
| super.generatedComponentModel.addMethod(BUILDER_METHOD, builderFactoryMethod); |
| if (canInstantiateAllRequirements()) { |
| CharSequence buildMethodName = |
| builderSpec().isPresent() ? builderSpec().get().buildMethod().getSimpleName() : "build"; |
| super.generatedComponentModel.addMethod( |
| BUILDER_METHOD, |
| methodBuilder("create") |
| .returns(ClassName.get(super.graph.componentType())) |
| .addModifiers(PUBLIC, STATIC) |
| .addStatement("return new Builder().$L()", buildMethodName) |
| .build()); |
| } |
| } |
| |
| private Optional<ComponentDescriptor.BuilderSpec> builderSpec() { |
| return super.graph.componentDescriptor().builderSpec(); |
| } |
| |
| /** {@code true} if all of the graph's required dependencies can be automatically constructed */ |
| private boolean canInstantiateAllRequirements() { |
| return !Iterables.any( |
| super.graph.componentRequirements(), |
| dependency -> dependency.requiresAPassedInstance(super.elements, super.types)); |
| } |
| } |
| |
| /** |
| * Builds the model for a subcomponent. If generating ahead-of-time subcomponents this model may |
| * be for an abstract base class implementation, an abstract inner implementation, or a concrete |
| * implementation that extends an abstract base implementation. Otherwise it represents a private, |
| * inner, concrete, final implementation of a subcomponent which extends a user defined type. |
| */ |
| private static final class SubComponentModelBuilder extends ComponentModelBuilder { |
| private final Optional<ComponentModelBuilder> parent; |
| private final GeneratedComponentModel generatedComponentModel; |
| private final ComponentBindingExpressions bindingExpressions; |
| |
| SubComponentModelBuilder( |
| Optional<ComponentModelBuilder> parent, |
| DaggerTypes types, |
| DaggerElements elements, |
| KeyFactory keyFactory, |
| BindingGraph graph, |
| GeneratedComponentModel generatedComponentModel, |
| OptionalFactories optionalFactories, |
| ComponentBindingExpressions bindingExpressions, |
| ComponentRequirementFields componentRequirementFields, |
| Optional<GeneratedComponentBuilderModel> builder, |
| BindingGraphFactory bindingGraphFactory, |
| CompilerOptions compilerOptions) { |
| super( |
| types, |
| elements, |
| keyFactory, |
| graph, |
| generatedComponentModel, |
| optionalFactories, |
| bindingExpressions, |
| componentRequirementFields, |
| builder, |
| bindingGraphFactory, |
| compilerOptions); |
| this.parent = parent; |
| this.generatedComponentModel = generatedComponentModel; |
| this.bindingExpressions = bindingExpressions; |
| } |
| |
| @Override |
| protected void addBuilderClass(TypeSpec builder) { |
| if (parent.isPresent()) { |
| // In an inner implementation of a subcomponent the builder is a peer class. |
| parent.get().generatedComponentModel.addType(SUBCOMPONENT, builder); |
| } else { |
| generatedComponentModel.addType(SUBCOMPONENT, builder); |
| } |
| } |
| |
| @Override |
| protected void addFactoryMethods() { |
| // Only construct instances of subcomponents that have concrete implementations. |
| if (!generatedComponentModel.isAbstract()) { |
| // Use the parent's factory method to create this subcomponent if the |
| // subcomponent was not added via {@link dagger.Module#subcomponents()}. |
| super.graph.factoryMethod().ifPresent(this::createSubcomponentFactoryMethod); |
| } |
| } |
| |
| private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) { |
| checkState(parent.isPresent()); |
| parent |
| .get() |
| .generatedComponentModel |
| .addMethod( |
| COMPONENT_METHOD, |
| MethodSpec.overriding(factoryMethod, parentType(), super.types) |
| .addStatement( |
| "return new $T($L)", |
| generatedComponentModel.name(), |
| getFactoryMethodParameterSpecs(super.graph).stream() |
| .map(param -> CodeBlock.of("$N", param)) |
| .collect(toParametersCodeBlock())) |
| .build()); |
| } |
| |
| private DeclaredType parentType() { |
| return asDeclared(parent.get().graph.componentType().asType()); |
| } |
| |
| @Override |
| protected void addInterfaceMethods() { |
| if (generatedComponentModel.supermodel().isPresent()) { |
| // Since we're overriding a subcomponent implementation we add to its implementation given |
| // an expanded binding graph. |
| |
| // Override modifiable binding methods. |
| for (ModifiableBindingMethod modifiableBindingMethod : |
| generatedComponentModel.getModifiableBindingMethods()) { |
| bindingExpressions |
| .modifiableBindingExpressions() |
| .getModifiableBindingMethod(modifiableBindingMethod) |
| .ifPresent( |
| method -> generatedComponentModel.addImplementedModifiableBindingMethod(method)); |
| } |
| } else { |
| super.addInterfaceMethods(); |
| } |
| } |
| |
| @Override |
| protected void addCancelParentStatement(MethodSpec.Builder methodBuilder) { |
| if (shouldPropagateCancellationToParent()) { |
| methodBuilder.addStatement( |
| "$T.this.$L($L)", |
| parent.get().generatedComponentModel.name(), |
| CANCELLATION_LISTENER_METHOD_NAME, |
| MAY_INTERRUPT_IF_RUNNING); |
| } |
| } |
| |
| private boolean shouldPropagateCancellationToParent() { |
| return parent.isPresent() |
| && parent |
| .get() |
| .generatedComponentModel |
| .componentDescriptor() |
| .cancellationPolicy() |
| .map(policy -> policy.fromSubcomponents().equals(PROPAGATE)) |
| .orElse(false); |
| } |
| } |
| |
| /** Returns the list of {@link ParameterSpec}s for the corresponding graph's factory method. */ |
| private static ImmutableList<ParameterSpec> getFactoryMethodParameterSpecs(BindingGraph graph) { |
| return graph |
| .factoryMethodParameters() |
| .values() |
| .stream() |
| .map(ParameterSpec::get) |
| .collect(toImmutableList()); |
| } |
| } |