dagger.internal.codegen.ComponentModelBuilder.java Source code

Java tutorial

Introduction

Here is the source code for dagger.internal.codegen.ComponentModelBuilder.java

Source

/*
 * 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.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.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 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.MissingBindingMethods.MissingBindingMethod;
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 {
    static GeneratedComponentModel buildComponentModel(DaggerTypes types, DaggerElements elements,
            KeyFactory keyFactory, CompilerOptions compilerOptions, ClassName name, BindingGraph graph,
            BindingGraphFactory bindingGraphFactory) {
        GeneratedComponentModel generatedComponentModel;
        if (graph.componentDescriptor().kind().isTopLevel()) {
            generatedComponentModel = GeneratedComponentModel.forComponent(name);
        } else {
            generatedComponentModel = GeneratedComponentModel.forBaseSubcomponent(name);
        }
        SubcomponentNames subcomponentNames = new SubcomponentNames(graph, keyFactory);
        OptionalFactories optionalFactories = new OptionalFactories(generatedComponentModel);
        Optional<ComponentBuilder> builder = ComponentBuilder.create(generatedComponentModel, graph,
                subcomponentNames, elements, types);
        ComponentRequirementFields componentRequirementFields = new ComponentRequirementFields(graph,
                generatedComponentModel, builder);
        ComponentBindingExpressions bindingExpressions = new ComponentBindingExpressions(graph,
                generatedComponentModel, subcomponentNames, 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 AbstractSubcomponentModelBuilder(Optional.empty(), /* parent */
                    types, elements, keyFactory, graph, generatedComponentModel, subcomponentNames,
                    optionalFactories, bindingExpressions, componentRequirementFields, builder, bindingGraphFactory,
                    compilerOptions).build();
        } else {
            return new RootComponentModelBuilder(types, elements, keyFactory, graph, generatedComponentModel,
                    subcomponentNames, optionalFactories, bindingExpressions, componentRequirementFields, builder,
                    bindingGraphFactory, compilerOptions).build();
        }
    }

    private GeneratedComponentModel buildSubcomponentModel(BindingGraph childGraph) {
        ClassName parentName = generatedComponentModel.name();
        ClassName childName = parentName
                .nestedClass(subcomponentNames.get(childGraph.componentDescriptor()) + "Impl");
        GeneratedComponentModel childModel = GeneratedComponentModel.forSubcomponent(childName);
        Optional<ComponentBuilder> childBuilder = ComponentBuilder.create(childModel, childGraph, subcomponentNames,
                elements, types);
        ComponentRequirementFields childComponentRequirementFields = componentRequirementFields
                .forChildComponent(childGraph, childModel, childBuilder);
        ComponentBindingExpressions childBindingExpressions = bindingExpressions.forChildComponent(childGraph,
                childModel, childComponentRequirementFields);
        return new SubComponentModelBuilder(this, childGraph, childModel, childBindingExpressions,
                childComponentRequirementFields, childBuilder).build();
    }

    private GeneratedComponentModel buildAbstractInnerSubcomponentModel(BindingGraph childGraph) {
        ClassName childName = generatedComponentModel.name()
                .nestedClass(subcomponentNames.get(childGraph.componentDescriptor()) + "Impl");
        GeneratedComponentModel supermodel = getSubcomponentSupermodel(childGraph.componentDescriptor());
        GeneratedComponentModel childModel = GeneratedComponentModel.forAbstractSubcomponent(childName, supermodel);
        Optional<ComponentBuilder> childBuilder = ComponentBuilder.create(childModel, childGraph, subcomponentNames,
                elements, types);
        ComponentRequirementFields childComponentRequirementFields = componentRequirementFields
                .forChildComponent(childGraph, childModel, childBuilder);
        ComponentBindingExpressions childBindingExpressions = bindingExpressions.forChildComponent(childGraph,
                childModel, childComponentRequirementFields);
        return new AbstractSubcomponentModelBuilder(Optional.of(this), types, elements, keyFactory, childGraph,
                childModel, subcomponentNames, optionalFactories, childBindingExpressions,
                childComponentRequirementFields, childBuilder, bindingGraphFactory, compilerOptions).build();
    }

    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 final DaggerElements elements;
    private final DaggerTypes types;
    private final KeyFactory keyFactory;
    private final BindingGraph graph;
    private final SubcomponentNames subcomponentNames;
    private final ComponentBindingExpressions bindingExpressions;
    private final ComponentRequirementFields componentRequirementFields;
    private final GeneratedComponentModel generatedComponentModel;
    private final OptionalFactories optionalFactories;
    private final Optional<ComponentBuilder> builder;
    private final BindingGraphFactory bindingGraphFactory;
    private final CompilerOptions compilerOptions;
    private boolean done;

    private ComponentModelBuilder(DaggerTypes types, DaggerElements elements, KeyFactory keyFactory,
            BindingGraph graph, GeneratedComponentModel generatedComponentModel,
            SubcomponentNames subcomponentNames, OptionalFactories optionalFactories,
            ComponentBindingExpressions bindingExpressions, ComponentRequirementFields componentRequirementFields,
            Optional<ComponentBuilder> builder, BindingGraphFactory bindingGraphFactory,
            CompilerOptions compilerOptions) {
        this.types = types;
        this.elements = elements;
        this.keyFactory = keyFactory;
        this.graph = graph;
        this.subcomponentNames = subcomponentNames;
        this.generatedComponentModel = generatedComponentModel;
        this.optionalFactories = optionalFactories;
        this.bindingExpressions = bindingExpressions;
        this.componentRequirementFields = componentRequirementFields;
        this.builder = builder;
        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();
        builder.map(ComponentBuilder::typeSpec).ifPresent(this::addBuilderClass);

        getLocalAndInheritedMethods(graph.componentDescriptor().componentDefinitionType(), types, elements)
                .forEach(method -> generatedComponentModel.claimMethodName(method.getSimpleName()));

        addFactoryMethods();
        addInterfaceMethods();
        addSubcomponents();
        addConstructor();

        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();
            generatedComponentModel.addMethod(COMPONENT_METHOD,
                    bindingExpressions.getComponentMethod(anyOneMethod));
        }
    }

    private MethodSignature getMethodSignature(ComponentMethodDescriptor method) {
        return MethodSignature.forComponentMethod(method, MoreTypes.asDeclared(graph.componentType().asType()),
                types);
    }

    private void addSubcomponents() {
        for (BindingGraph subgraph : graph.subgraphs()) {
            generatedComponentModel.addSubcomponent(subgraph.componentDescriptor(),
                    generatedComponentModel.isAbstract() ? buildAbstractInnerSubcomponentModel(subgraph)
                            : buildSubcomponentModel(subgraph));
        }
    }

    private static final int INITIALIZATIONS_PER_INITIALIZE_METHOD = 100;

    private void addConstructor() {
        List<List<CodeBlock>> partitions = Lists.partition(generatedComponentModel.getInitializations(),
                INITIALIZATIONS_PER_INITIALIZE_METHOD);

        ImmutableList<ParameterSpec> constructorParameters = constructorParameters();
        MethodSpec.Builder constructor = constructorBuilder()
                .addModifiers(generatedComponentModel.isAbstract() ? PROTECTED : PRIVATE)
                .addParameters(constructorParameters);

        if (generatedComponentModel.supermodel().isPresent()) {
            constructor.addStatement(CodeBlock.of("super($L)", 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());

        UniqueNameSet methodNames = new UniqueNameSet();
        for (List<CodeBlock> partition : partitions) {
            String methodName = methodNames.getUniqueName("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 initilization 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 initialze 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 (builder.isPresent()) {
            return ImmutableList.of(ParameterSpec.builder(builder.get().name(), "builder").build());
        } else if (graph.factoryMethod().isPresent()) {
            return getFactoryMethodParameterSpecs(graph);
        } else if (generatedComponentModel.isAbstract() && !generatedComponentModel.isNested()) {
            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,
                SubcomponentNames subcomponentNames, OptionalFactories optionalFactories,
                ComponentBindingExpressions bindingExpressions,
                ComponentRequirementFields componentRequirementFields, Optional<ComponentBuilder> builder,
                BindingGraphFactory bindingGraphFactory, CompilerOptions compilerOptions) {
            super(types, elements, keyFactory, graph, generatedComponentModel, subcomponentNames, optionalFactories,
                    bindingExpressions, componentRequirementFields, builder, 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.builder.get().name())
                    .addStatement("return new $T()", super.builder.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 nested subcomponent. This is used when ahead-of-time components are not
     * enabled (current default mode).
     */
    private static final class SubComponentModelBuilder extends ComponentModelBuilder {
        private final ComponentModelBuilder parent;

        SubComponentModelBuilder(ComponentModelBuilder parent, BindingGraph graph,
                GeneratedComponentModel generatedComponentModel, ComponentBindingExpressions bindingExpressions,
                ComponentRequirementFields componentRequirementFields, Optional<ComponentBuilder> builder) {
            super(parent.types, parent.elements, parent.keyFactory, graph, generatedComponentModel,
                    parent.subcomponentNames, parent.optionalFactories, bindingExpressions,
                    componentRequirementFields, builder, parent.bindingGraphFactory, parent.compilerOptions);
            this.parent = parent;
        }

        @Override
        protected void addBuilderClass(TypeSpec builder) {
            parent.generatedComponentModel.addType(SUBCOMPONENT, builder);
        }

        @Override
        protected void addFactoryMethods() {
            // 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) {
            parent.generatedComponentModel.addMethod(COMPONENT_METHOD, MethodSpec
                    .overriding(factoryMethod, parentType(), super.types)
                    .addStatement("return new $T($L)", super.generatedComponentModel.name(),
                            getFactoryMethodParameterSpecs(super.graph).stream()
                                    .map(param -> CodeBlock.of("$N", param)).collect(toParametersCodeBlock()))
                    .build());
        }

        private DeclaredType parentType() {
            return asDeclared(parent.graph.componentType().asType());
        }
    }

    /** Builds the model for abstract implementations of a subcomponent. */
    private static final class AbstractSubcomponentModelBuilder extends ComponentModelBuilder {
        private final Optional<ComponentModelBuilder> parent;
        private final GeneratedComponentModel generatedComponentModel;
        private final ComponentBindingExpressions bindingExpressions;

        AbstractSubcomponentModelBuilder(Optional<ComponentModelBuilder> parent, DaggerTypes types,
                DaggerElements elements, KeyFactory keyFactory, BindingGraph graph,
                GeneratedComponentModel generatedComponentModel, SubcomponentNames subcomponentNames,
                OptionalFactories optionalFactories, ComponentBindingExpressions bindingExpressions,
                ComponentRequirementFields componentRequirementFields, Optional<ComponentBuilder> builder,
                BindingGraphFactory bindingGraphFactory, CompilerOptions compilerOptions) {
            super(types, elements, keyFactory, graph, generatedComponentModel, subcomponentNames, optionalFactories,
                    bindingExpressions, componentRequirementFields, builder, bindingGraphFactory, compilerOptions);
            this.parent = parent;
            this.generatedComponentModel = generatedComponentModel;
            this.bindingExpressions = bindingExpressions;
        }

        @Override
        protected void addBuilderClass(TypeSpec builder) {
            if (parent.isPresent()) {
                // If 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.
        }

        @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.

                // Implement missing binding methods.
                for (MissingBindingMethod missingBindingMethod : generatedComponentModel
                        .getMissingBindingMethods()) {
                    bindingExpressions.getMissingBindingMethodImplementation(missingBindingMethod)
                            .ifPresent(method -> generatedComponentModel
                                    .addImplementedMissingBindingMethod(missingBindingMethod, method));
                }
            } else {
                super.addInterfaceMethods();
            }
        }
    }

    /** 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());
    }
}