| /* |
| * 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.common.base.CaseFormat.LOWER_CAMEL; |
| import static com.google.common.base.Preconditions.checkState; |
| import static com.squareup.javapoet.MethodSpec.methodBuilder; |
| import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock; |
| import static dagger.internal.codegen.GeneratedComponentModel.FieldSpecKind.COMPONENT_REQUIREMENT_FIELD; |
| import static dagger.internal.codegen.GeneratedComponentModel.MethodSpecKind.COMPONENT_METHOD; |
| 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.PUBLIC; |
| |
| import com.google.auto.common.MoreTypes; |
| import com.google.common.base.CaseFormat; |
| import com.google.common.base.Predicates; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Sets; |
| import com.squareup.javapoet.ClassName; |
| import com.squareup.javapoet.CodeBlock; |
| import com.squareup.javapoet.FieldSpec; |
| import com.squareup.javapoet.MethodSpec; |
| import com.squareup.javapoet.TypeName; |
| import com.squareup.javapoet.TypeSpec; |
| import dagger.internal.Preconditions; |
| import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; |
| import java.util.List; |
| import java.util.Optional; |
| import java.util.Set; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.ExecutableType; |
| import javax.lang.model.type.TypeMirror; |
| |
| /** |
| * Creates the nested implementation class for a subcomponent. |
| */ |
| final class SubcomponentWriter extends AbstractComponentWriter { |
| |
| private final AbstractComponentWriter parent; |
| |
| /** |
| * The parent's factory method to create this subcomponent, or {@link Optional#empty()} if the |
| * subcomponent was added via {@link dagger.Module#subcomponents()}. |
| */ |
| private final Optional<ComponentMethodDescriptor> subcomponentFactoryMethod; |
| |
| SubcomponentWriter(AbstractComponentWriter parent, BindingGraph graph) { |
| super( |
| parent, |
| subcomponentModel(parent, graph), |
| graph, |
| parent.componentRequirementFields.forChildComponent()); |
| this.parent = parent; |
| this.subcomponentFactoryMethod = |
| Optional.ofNullable( |
| parent |
| .graph |
| .componentDescriptor() |
| .subcomponentsByFactoryMethod() |
| .inverse() |
| .get(graph.componentDescriptor())); |
| } |
| |
| private static GeneratedComponentModel subcomponentModel( |
| AbstractComponentWriter parent, BindingGraph graph) { |
| ClassName parentName = parent.generatedComponentModel.name(); |
| ClassName name = |
| parentName.nestedClass(parent.subcomponentNames.get(graph.componentDescriptor()) + "Impl"); |
| return GeneratedComponentModel.forSubcomponent(name); |
| } |
| |
| private ExecutableType resolvedSubcomponentFactoryMethod() { |
| checkState( |
| subcomponentFactoryMethod.isPresent(), |
| "%s does not have a factory method for %s", |
| parent.graph.componentType(), |
| graph.componentType()); |
| return MoreTypes.asExecutable( |
| types.asMemberOf( |
| MoreTypes.asDeclared(parent.graph.componentType().asType()), |
| subcomponentFactoryMethod.get().methodElement())); |
| } |
| |
| @Override |
| protected void addBuilderClass(TypeSpec builder) { |
| parent.generatedComponentModel.addType(SUBCOMPONENT, builder); |
| } |
| |
| @Override |
| protected void addFactoryMethods() { |
| if (!subcomponentFactoryMethod.isPresent() |
| || !subcomponentFactoryMethod.get().kind().isSubcomponentKind()) { |
| // subcomponent builder methods are implemented in |
| // AbstractComponentWriter.implementInterfaceMethods |
| return; |
| } |
| MethodSpec.Builder componentMethod = |
| methodBuilder(subcomponentFactoryMethod.get().methodElement().getSimpleName().toString()) |
| .addModifiers(PUBLIC) |
| .addAnnotation(Override.class); |
| ExecutableType resolvedMethod = resolvedSubcomponentFactoryMethod(); |
| componentMethod.returns(ClassName.get(resolvedMethod.getReturnType())); |
| writeSubcomponentWithoutBuilder(componentMethod, resolvedMethod); |
| parent.generatedComponentModel.addMethod(COMPONENT_METHOD, componentMethod.build()); |
| } |
| |
| private void writeSubcomponentWithoutBuilder( |
| MethodSpec.Builder componentMethod, ExecutableType resolvedMethod) { |
| ImmutableList.Builder<CodeBlock> subcomponentConstructorParameters = ImmutableList.builder(); |
| List<? extends VariableElement> params = |
| subcomponentFactoryMethod.get().methodElement().getParameters(); |
| List<? extends TypeMirror> paramTypes = resolvedMethod.getParameterTypes(); |
| for (int i = 0; i < params.size(); i++) { |
| VariableElement moduleVariable = params.get(i); |
| TypeElement moduleTypeElement = MoreTypes.asTypeElement(paramTypes.get(i)); |
| ComponentRequirement componentRequirement = |
| ComponentRequirement.forModule(moduleTypeElement.asType()); |
| TypeName moduleType = TypeName.get(paramTypes.get(i)); |
| componentMethod.addParameter(moduleType, moduleVariable.getSimpleName().toString()); |
| if (!componentRequirementFields.contains(componentRequirement)) { |
| String preferredModuleName = |
| CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleTypeElement.getSimpleName().toString()); |
| FieldSpec contributionField = |
| componentField(ClassName.get(moduleTypeElement), preferredModuleName) |
| .addModifiers(PRIVATE, FINAL) |
| .build(); |
| generatedComponentModel.addField(COMPONENT_REQUIREMENT_FIELD, contributionField); |
| |
| constructor |
| .addParameter(moduleType, contributionField.name) |
| .addStatement( |
| "this.$1N = $2T.checkNotNull($1N)", contributionField, Preconditions.class); |
| |
| componentRequirementFields.add( |
| ComponentRequirementField.componentField( |
| componentRequirement, contributionField, generatedComponentModel.name())); |
| subcomponentConstructorParameters.add( |
| CodeBlock.of("$L", moduleVariable.getSimpleName())); |
| } |
| } |
| |
| Set<ComponentRequirement> uninitializedModules = |
| Sets.filter( |
| graph.componentRequirements(), |
| Predicates.not(componentRequirementFields::contains)); |
| |
| for (ComponentRequirement componentRequirement : uninitializedModules) { |
| checkState(componentRequirement.kind().equals(ComponentRequirement.Kind.MODULE)); |
| TypeElement moduleType = componentRequirement.typeElement(); |
| String preferredModuleName = |
| CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleType.getSimpleName().toString()); |
| FieldSpec contributionField = |
| componentField(ClassName.get(moduleType), preferredModuleName) |
| .addModifiers(PRIVATE, FINAL) |
| .build(); |
| generatedComponentModel.addField(COMPONENT_REQUIREMENT_FIELD, contributionField); |
| constructor.addStatement("this.$N = new $T()", contributionField, ClassName.get(moduleType)); |
| componentRequirementFields.add( |
| ComponentRequirementField.componentField( |
| componentRequirement, contributionField, generatedComponentModel.name())); |
| } |
| |
| componentMethod.addStatement( |
| "return new $T($L)", |
| generatedComponentModel.name(), |
| makeParametersCodeBlock(subcomponentConstructorParameters.build())); |
| } |
| |
| /** Creates a {@link FieldSpec.Builder} with a unique name based off of {@code name}. */ |
| private final FieldSpec.Builder componentField(TypeName type, String name) { |
| return FieldSpec.builder(type, generatedComponentModel.getUniqueFieldName(name)); |
| } |
| } |