/* * Copyright (C) 2015 Google, Inc. * * 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 com.google.auto.common.MoreTypes; import com.google.common.base.CaseFormat; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import dagger.internal.codegen.ComponentDescriptor.BuilderSpec; import dagger.internal.codegen.ComponentGenerator.MemberSelect; import dagger.internal.codegen.writer.ClassName; import dagger.internal.codegen.writer.ClassWriter; import dagger.internal.codegen.writer.FieldWriter; import dagger.internal.codegen.writer.MethodWriter; import dagger.internal.codegen.writer.Snippet; import dagger.internal.codegen.writer.TypeName; import dagger.internal.codegen.writer.TypeNames; import java.util.List; import java.util.Set; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import static com.google.common.base.CaseFormat.LOWER_CAMEL; import static com.google.common.base.Verify.verify; import static com.google.common.collect.Sets.difference; import static dagger.internal.codegen.AbstractComponentWriter.InitializationState.UNINITIALIZED; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; /** * Creates the nested implementation class for a subcomponent. */ class SubcomponentWriter extends AbstractComponentWriter { private AbstractComponentWriter parent; private ExecutableElement subcomponentFactoryMethod; public SubcomponentWriter( AbstractComponentWriter parent, ExecutableElement subcomponentFactoryMethod, BindingGraph subgraph) { super( parent.types, parent.elements, parent.keyFactory, parent.nullableValidationType, parent.name.nestedClassNamed(subcomponentSimpleName(subgraph)), subgraph); this.parent = parent; this.subcomponentFactoryMethod = subcomponentFactoryMethod; } private static String subcomponentSimpleName(BindingGraph subgraph) { return subgraph.componentDescriptor().componentDefinitionType().getSimpleName() + "Impl"; } @Override protected InitializationState getInitializationState(BindingKey bindingKey) { InitializationState initializationState = super.getInitializationState(bindingKey); return initializationState.equals(UNINITIALIZED) ? parent.getInitializationState(bindingKey) : initializationState; } @Override protected Optional getOrCreateComponentContributionFieldSnippet( TypeElement contributionType) { return super.getOrCreateComponentContributionFieldSnippet(contributionType) .or(parent.getOrCreateComponentContributionFieldSnippet(contributionType)); } @Override protected MemberSelect getMemberSelect(BindingKey key) { MemberSelect memberSelect = super.getMemberSelect(key); return memberSelect == null ? parent.getMemberSelect(key) : memberSelect; } @Override protected Optional getMultibindingContributionSnippet(ContributionBinding binding) { return super.getMultibindingContributionSnippet(binding) .or(parent.getMultibindingContributionSnippet(binding)); } private ExecutableType resolvedSubcomponentFactoryMethod() { return MoreTypes.asExecutable( types.asMemberOf( MoreTypes.asDeclared(parent.componentDefinitionType().asType()), subcomponentFactoryMethod)); } @Override protected ClassWriter createComponentClass() { ClassWriter componentWriter = parent.componentWriter.addNestedClass(name.simpleName()); componentWriter.addModifiers(PRIVATE, FINAL); componentWriter.setSupertype( MoreTypes.asTypeElement( graph.componentDescriptor().builderSpec().isPresent() ? graph .componentDescriptor() .builderSpec() .get() .componentType() : resolvedSubcomponentFactoryMethod().getReturnType())); return componentWriter; } @Override protected void addBuilder() { // Only write subcomponent builders if there is a spec. if (graph.componentDescriptor().builderSpec().isPresent()) { super.addBuilder(); } } @Override protected ClassWriter createBuilder() { // Only write subcomponent builders if there is a spec. verify(graph.componentDescriptor().builderSpec().isPresent()); return parent.componentWriter.addNestedClass( componentDefinitionTypeName().simpleName() + "Builder"); } @Override protected void addFactoryMethods() { MethodWriter componentMethod; if (graph.componentDescriptor().builderSpec().isPresent()) { BuilderSpec spec = graph.componentDescriptor().builderSpec().get(); componentMethod = parent.componentWriter.addMethod( spec.builderDefinitionType().asType(), subcomponentFactoryMethod.getSimpleName().toString()); componentMethod.body().addSnippet("return new %s();", builderName.get()); } else { ExecutableType resolvedMethod = resolvedSubcomponentFactoryMethod(); componentMethod = parent.componentWriter.addMethod( resolvedMethod.getReturnType(), subcomponentFactoryMethod.getSimpleName().toString()); writeSubcomponentWithoutBuilder(componentMethod, resolvedMethod); } componentMethod.addModifiers(PUBLIC); componentMethod.annotate(Override.class); } private void writeSubcomponentWithoutBuilder( MethodWriter componentMethod, ExecutableType resolvedMethod) { ImmutableList.Builder subcomponentConstructorParameters = ImmutableList.builder(); List params = subcomponentFactoryMethod.getParameters(); List paramTypes = resolvedMethod.getParameterTypes(); for (int i = 0; i < params.size(); i++) { VariableElement moduleVariable = params.get(i); TypeElement moduleTypeElement = MoreTypes.asTypeElement(paramTypes.get(i)); TypeName moduleType = TypeNames.forTypeMirror(paramTypes.get(i)); componentMethod.addParameter(moduleType, moduleVariable.getSimpleName().toString()); if (!componentContributionFields.containsKey(moduleTypeElement)) { String preferredModuleName = CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleTypeElement.getSimpleName().toString()); FieldWriter contributionField = componentWriter.addField(moduleTypeElement, preferredModuleName); contributionField.addModifiers(PRIVATE, FINAL); String actualModuleName = contributionField.name(); constructorWriter.addParameter(moduleType, actualModuleName); constructorWriter.body() .addSnippet("if (%s == null) {", actualModuleName) .addSnippet(" throw new NullPointerException();") .addSnippet("}"); constructorWriter.body().addSnippet("this.%1$s = %1$s;", actualModuleName); MemberSelect moduleSelect = MemberSelect.instanceSelect(name, Snippet.format(actualModuleName)); componentContributionFields.put(moduleTypeElement, moduleSelect); subcomponentConstructorParameters.add(Snippet.format("%s", moduleVariable.getSimpleName())); } } Set uninitializedModules = difference(graph.componentRequirements(), componentContributionFields.keySet()); for (TypeElement moduleType : uninitializedModules) { String preferredModuleName = CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleType.getSimpleName().toString()); FieldWriter contributionField = componentWriter.addField(moduleType, preferredModuleName); contributionField.addModifiers(PRIVATE, FINAL); String actualModuleName = contributionField.name(); constructorWriter.body().addSnippet("this.%s = new %s();", actualModuleName, ClassName.fromTypeElement(moduleType)); MemberSelect moduleSelect = MemberSelect.instanceSelect(name, Snippet.format(actualModuleName)); componentContributionFields.put(moduleType, moduleSelect); } componentMethod.body().addSnippet("return new %s(%s);", name, Snippet.makeParametersSnippet(subcomponentConstructorParameters.build())); } }