Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2015 Google, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package dagger.internal.codegen;
     17 
     18 import com.google.auto.common.MoreTypes;
     19 import com.google.common.base.CaseFormat;
     20 import com.google.common.base.Optional;
     21 import com.google.common.collect.ImmutableList;
     22 import dagger.internal.codegen.ComponentDescriptor.BuilderSpec;
     23 import dagger.internal.codegen.ComponentGenerator.MemberSelect;
     24 import dagger.internal.codegen.writer.ClassName;
     25 import dagger.internal.codegen.writer.ClassWriter;
     26 import dagger.internal.codegen.writer.FieldWriter;
     27 import dagger.internal.codegen.writer.MethodWriter;
     28 import dagger.internal.codegen.writer.Snippet;
     29 import dagger.internal.codegen.writer.TypeName;
     30 import dagger.internal.codegen.writer.TypeNames;
     31 import java.util.List;
     32 import java.util.Set;
     33 import javax.lang.model.element.ExecutableElement;
     34 import javax.lang.model.element.TypeElement;
     35 import javax.lang.model.element.VariableElement;
     36 import javax.lang.model.type.ExecutableType;
     37 import javax.lang.model.type.TypeMirror;
     38 
     39 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
     40 import static com.google.common.base.Verify.verify;
     41 import static com.google.common.collect.Sets.difference;
     42 import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.UNINITIALIZED;
     43 import static javax.lang.model.element.Modifier.FINAL;
     44 import static javax.lang.model.element.Modifier.PRIVATE;
     45 import static javax.lang.model.element.Modifier.PUBLIC;
     46 
     47 /**
     48  * Creates the nested implementation class for a subcomponent.
     49  */
     50 class SubcomponentWriter extends AbstractComponentWriter {
     51 
     52   private AbstractComponentWriter parent;
     53   private ExecutableElement subcomponentFactoryMethod;
     54 
     55   public SubcomponentWriter(
     56       AbstractComponentWriter parent,
     57       ExecutableElement subcomponentFactoryMethod,
     58       BindingGraph subgraph) {
     59     super(
     60         parent.types,
     61         parent.elements,
     62         parent.keyFactory,
     63         parent.nullableValidationType,
     64         parent.name.nestedClassNamed(subcomponentSimpleName(subgraph)),
     65         subgraph);
     66     this.parent = parent;
     67     this.subcomponentFactoryMethod = subcomponentFactoryMethod;
     68   }
     69 
     70   private static String subcomponentSimpleName(BindingGraph subgraph) {
     71     return subgraph.componentDescriptor().componentDefinitionType().getSimpleName() + "Impl";
     72   }
     73 
     74   @Override
     75   protected InitializationState getInitializationState(BindingKey bindingKey) {
     76     InitializationState initializationState = super.getInitializationState(bindingKey);
     77     return initializationState.equals(UNINITIALIZED)
     78         ? parent.getInitializationState(bindingKey)
     79         : initializationState;
     80   }
     81 
     82   @Override
     83   protected Optional<Snippet> getOrCreateComponentContributionFieldSnippet(
     84       TypeElement contributionType) {
     85     return super.getOrCreateComponentContributionFieldSnippet(contributionType)
     86         .or(parent.getOrCreateComponentContributionFieldSnippet(contributionType));
     87   }
     88 
     89   @Override
     90   protected MemberSelect getMemberSelect(BindingKey key) {
     91     MemberSelect memberSelect = super.getMemberSelect(key);
     92     return memberSelect == null ? parent.getMemberSelect(key) : memberSelect;
     93   }
     94 
     95   @Override
     96   protected Optional<MemberSelect> getMultibindingContributionSnippet(ContributionBinding binding) {
     97     return super.getMultibindingContributionSnippet(binding)
     98         .or(parent.getMultibindingContributionSnippet(binding));
     99   }
    100 
    101   private ExecutableType resolvedSubcomponentFactoryMethod() {
    102     return MoreTypes.asExecutable(
    103         types.asMemberOf(
    104             MoreTypes.asDeclared(parent.componentDefinitionType().asType()),
    105             subcomponentFactoryMethod));
    106   }
    107 
    108   @Override
    109   protected ClassWriter createComponentClass() {
    110     ClassWriter componentWriter = parent.componentWriter.addNestedClass(name.simpleName());
    111     componentWriter.addModifiers(PRIVATE, FINAL);
    112     componentWriter.setSupertype(
    113         MoreTypes.asTypeElement(
    114             graph.componentDescriptor().builderSpec().isPresent()
    115                 ? graph
    116                     .componentDescriptor()
    117                     .builderSpec()
    118                     .get()
    119                     .componentType()
    120                 : resolvedSubcomponentFactoryMethod().getReturnType()));
    121     return componentWriter;
    122   }
    123 
    124   @Override
    125   protected void addBuilder() {
    126     // Only write subcomponent builders if there is a spec.
    127     if (graph.componentDescriptor().builderSpec().isPresent()) {
    128       super.addBuilder();
    129     }
    130   }
    131 
    132   @Override
    133   protected ClassWriter createBuilder() {
    134     // Only write subcomponent builders if there is a spec.
    135     verify(graph.componentDescriptor().builderSpec().isPresent());
    136     return parent.componentWriter.addNestedClass(
    137         componentDefinitionTypeName().simpleName() + "Builder");
    138   }
    139 
    140   @Override
    141   protected void addFactoryMethods() {
    142     MethodWriter componentMethod;
    143     if (graph.componentDescriptor().builderSpec().isPresent()) {
    144       BuilderSpec spec = graph.componentDescriptor().builderSpec().get();
    145       componentMethod =
    146           parent.componentWriter.addMethod(
    147               spec.builderDefinitionType().asType(),
    148               subcomponentFactoryMethod.getSimpleName().toString());
    149       componentMethod.body().addSnippet("return new %s();", builderName.get());
    150     } else {
    151       ExecutableType resolvedMethod = resolvedSubcomponentFactoryMethod();
    152       componentMethod =
    153           parent.componentWriter.addMethod(
    154               resolvedMethod.getReturnType(), subcomponentFactoryMethod.getSimpleName().toString());
    155       writeSubcomponentWithoutBuilder(componentMethod, resolvedMethod);
    156     }
    157     componentMethod.addModifiers(PUBLIC);
    158     componentMethod.annotate(Override.class);
    159   }
    160 
    161   private void writeSubcomponentWithoutBuilder(
    162       MethodWriter componentMethod, ExecutableType resolvedMethod) {
    163     ImmutableList.Builder<Snippet> subcomponentConstructorParameters = ImmutableList.builder();
    164     List<? extends VariableElement> params = subcomponentFactoryMethod.getParameters();
    165     List<? extends TypeMirror> paramTypes = resolvedMethod.getParameterTypes();
    166     for (int i = 0; i < params.size(); i++) {
    167       VariableElement moduleVariable = params.get(i);
    168       TypeElement moduleTypeElement = MoreTypes.asTypeElement(paramTypes.get(i));
    169       TypeName moduleType = TypeNames.forTypeMirror(paramTypes.get(i));
    170       componentMethod.addParameter(moduleType, moduleVariable.getSimpleName().toString());
    171       if (!componentContributionFields.containsKey(moduleTypeElement)) {
    172         String preferredModuleName =
    173             CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleTypeElement.getSimpleName().toString());
    174         FieldWriter contributionField =
    175             componentWriter.addField(moduleTypeElement, preferredModuleName);
    176         contributionField.addModifiers(PRIVATE, FINAL);
    177         String actualModuleName = contributionField.name();
    178         constructorWriter.addParameter(moduleType, actualModuleName);
    179         constructorWriter.body()
    180             .addSnippet("if (%s == null) {", actualModuleName)
    181             .addSnippet("  throw new NullPointerException();")
    182             .addSnippet("}");
    183         constructorWriter.body().addSnippet("this.%1$s = %1$s;", actualModuleName);
    184         MemberSelect moduleSelect =
    185             MemberSelect.instanceSelect(name, Snippet.format(actualModuleName));
    186         componentContributionFields.put(moduleTypeElement, moduleSelect);
    187         subcomponentConstructorParameters.add(Snippet.format("%s", moduleVariable.getSimpleName()));
    188       }
    189     }
    190 
    191     Set<TypeElement> uninitializedModules =
    192         difference(graph.componentRequirements(), componentContributionFields.keySet());
    193 
    194     for (TypeElement moduleType : uninitializedModules) {
    195       String preferredModuleName =
    196           CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleType.getSimpleName().toString());
    197       FieldWriter contributionField = componentWriter.addField(moduleType, preferredModuleName);
    198       contributionField.addModifiers(PRIVATE, FINAL);
    199       String actualModuleName = contributionField.name();
    200       constructorWriter.body().addSnippet("this.%s = new %s();",
    201           actualModuleName, ClassName.fromTypeElement(moduleType));
    202       MemberSelect moduleSelect =
    203           MemberSelect.instanceSelect(name, Snippet.format(actualModuleName));
    204       componentContributionFields.put(moduleType, moduleSelect);
    205     }
    206 
    207     componentMethod.body().addSnippet("return new %s(%s);",
    208         name, Snippet.makeParametersSnippet(subcomponentConstructorParameters.build()));
    209   }
    210 }
    211