• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SubcomponentWriter( AbstractComponentWriter parent, ExecutableElement subcomponentFactoryMethod, BindingGraph subgraph)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 
subcomponentSimpleName(BindingGraph subgraph)70   private static String subcomponentSimpleName(BindingGraph subgraph) {
71     return subgraph.componentDescriptor().componentDefinitionType().getSimpleName() + "Impl";
72   }
73 
74   @Override
getInitializationState(BindingKey bindingKey)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
getOrCreateComponentContributionFieldSnippet( TypeElement contributionType)83   protected Optional<Snippet> getOrCreateComponentContributionFieldSnippet(
84       TypeElement contributionType) {
85     return super.getOrCreateComponentContributionFieldSnippet(contributionType)
86         .or(parent.getOrCreateComponentContributionFieldSnippet(contributionType));
87   }
88 
89   @Override
getMemberSelect(BindingKey key)90   protected MemberSelect getMemberSelect(BindingKey key) {
91     MemberSelect memberSelect = super.getMemberSelect(key);
92     return memberSelect == null ? parent.getMemberSelect(key) : memberSelect;
93   }
94 
95   @Override
getMultibindingContributionSnippet(ContributionBinding binding)96   protected Optional<MemberSelect> getMultibindingContributionSnippet(ContributionBinding binding) {
97     return super.getMultibindingContributionSnippet(binding)
98         .or(parent.getMultibindingContributionSnippet(binding));
99   }
100 
resolvedSubcomponentFactoryMethod()101   private ExecutableType resolvedSubcomponentFactoryMethod() {
102     return MoreTypes.asExecutable(
103         types.asMemberOf(
104             MoreTypes.asDeclared(parent.componentDefinitionType().asType()),
105             subcomponentFactoryMethod));
106   }
107 
108   @Override
createComponentClass()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
addBuilder()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
createBuilder()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
addFactoryMethods()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 
writeSubcomponentWithoutBuilder( MethodWriter componentMethod, ExecutableType resolvedMethod)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