/*
* 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<Snippet> 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<MemberSelect> 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<Snippet> subcomponentConstructorParameters = ImmutableList.builder();
List<? extends VariableElement> params = subcomponentFactoryMethod.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));
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<TypeElement> 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()));
}
}