blob: 48e674f47f6db8c6930baed258473a2f21245e27 [file] [log] [blame]
/*
* 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.google.common.collect.Sets.difference;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.MemberSelect.localField;
import static dagger.internal.codegen.TypeSpecs.addSupertype;
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.collect.ImmutableList;
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,
Optional<ComponentMethodDescriptor> subcomponentFactoryMethod,
BindingGraph subgraph) {
super(parent, subcomponentName(parent, subgraph), subgraph);
this.parent = parent;
this.subcomponentFactoryMethod = subcomponentFactoryMethod;
}
private static ClassName subcomponentName(AbstractComponentWriter parent, BindingGraph subgraph) {
return parent.name.nestedClass(
parent.subcomponentNames.get(subgraph.componentDescriptor()) + "Impl");
}
@Override
protected Optional<CodeBlock> getOrCreateComponentRequirementFieldExpression(
ComponentRequirement componentRequirement) {
Optional<CodeBlock> expression =
super.getOrCreateComponentRequirementFieldExpression(componentRequirement);
return expression.isPresent()
? expression
: parent.getOrCreateComponentRequirementFieldExpression(componentRequirement);
}
@Override
protected CodeBlock getReferenceReleasingProviderManagerExpression(Scope scope) {
return parent.getReferenceReleasingProviderManagerExpression(scope);
}
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 decorateComponent() {
component.addModifiers(PRIVATE, FINAL);
addSupertype(
component,
MoreTypes.asTypeElement(
graph.componentDescriptor().builderSpec().isPresent()
? graph.componentDescriptor().builderSpec().get().componentType()
: resolvedSubcomponentFactoryMethod().getReturnType()));
}
@Override
protected void addBuilderClass(TypeSpec builder) {
parent.component.addType(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.interfaceMethods.add(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 (!componentContributionFields.containsKey(componentRequirement)) {
String preferredModuleName =
CaseFormat.UPPER_CAMEL.to(LOWER_CAMEL, moduleTypeElement.getSimpleName().toString());
FieldSpec contributionField =
componentField(ClassName.get(moduleTypeElement), preferredModuleName)
.addModifiers(PRIVATE, FINAL)
.build();
component.addField(contributionField);
String actualModuleName = contributionField.name;
constructor
.addParameter(moduleType, actualModuleName)
.addStatement(
"this.$1L = $2T.checkNotNull($1L)",
actualModuleName,
Preconditions.class);
MemberSelect moduleSelect = localField(name, actualModuleName);
componentContributionFields.put(componentRequirement, moduleSelect);
subcomponentConstructorParameters.add(
CodeBlock.of("$L", moduleVariable.getSimpleName()));
}
}
Set<ComponentRequirement> uninitializedModules =
difference(graph.componentRequirements(), componentContributionFields.keySet());
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();
component.addField(contributionField);
String actualModuleName = contributionField.name;
constructor.addStatement(
"this.$L = new $T()", actualModuleName, ClassName.get(moduleType));
MemberSelect moduleSelect = localField(name, actualModuleName);
componentContributionFields.put(componentRequirement, moduleSelect);
}
componentMethod.addStatement("return new $T($L)",
name, makeParametersCodeBlock(subcomponentConstructorParameters.build()));
}
}