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