blob: 66ee2979952367c3d658dc8cfb1c8c6d8fa62963 [file] [log] [blame]
/*
* Copyright (C) 2016 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.squareup.javapoet.TypeSpec.classBuilder;
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.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.ReferenceReleasingProviderManager;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
/** The model of the component being generated. */
final class GeneratedComponentModel {
/** A type of field that this component model can generate. */
// TODO(user, dpb): Move component requirements and reference managers to top? The order should
// be component requirements, referencemanagers, framework fields, private method fields, ... etc
static enum FieldSpecKind {
/**
* A field for the lock and cached value for {@linkplain PrivateMethodBindingExpression
* private-method scoped bindings}.
*/
PRIVATE_METHOD_SCOPED_FIELD,
/** A field required by the component, e.g. module instances. */
COMPONENT_REQUIREMENT_FIELD,
/** A framework field for type T, e.g. Provider<T>. */
FRAMEWORK_FIELD,
/** A field for a {@link ReferenceReleasingProviderManager}. */
REFERENCE_RELEASING_MANAGER_FIELD,
/** A static field that always returns an absent {@code Optional} value for the binding. */
ABSENT_OPTIONAL_FIELD
}
/** A type of method that this component model can generate. */
// TODO(user, dpb): Change the oder to constructor, initialize, component, then private
// (including MIM and AOM—why treat those separately?).
static enum MethodSpecKind {
/** The component constructor. */
CONSTRUCTOR,
/** A builder method for the component. (Only used by the root component.) */
BUILDER_METHOD,
/** A private method that wraps depenency expressions. */
PRIVATE_METHOD,
/** An initialization method that initializes component requirements and framework types. */
INITIALIZE_METHOD,
/** An implementation of a component interface method. */
COMPONENT_METHOD,
/** A private method that encapsulates members injection logic for a binding. */
MEMBERS_INJECTION_METHOD,
/** A static method that always returns an absent {@code Optional} value for the binding. */
ABSENT_OPTIONAL_METHOD
}
/** A type of nested class that this component model can generate. */
static enum TypeSpecKind {
/** A factory class for a present optional binding. */
PRESENT_FACTORY,
/** A class for the component builder (Only used by the root component.) */
COMPONENT_BUILDER,
/** A provider class for a component provision. */
COMPONENT_PROVISION_FACTORY,
/** A class for the subcomponent or subcomponent builder. */
SUBCOMPONENT
}
private final ClassName name;
// TODO(user): This is only non-private to ease migration with AbstractComponentWriter!
private final TypeSpec.Builder component;
private final UniqueNameSet componentFieldNames = new UniqueNameSet();
private final UniqueNameSet componentMethodNames = new UniqueNameSet();
private final List<CodeBlock> initializations = new ArrayList<>();
private final ListMultimap<FieldSpecKind, FieldSpec> fieldSpecsMap =
MultimapBuilder.enumKeys(FieldSpecKind.class).arrayListValues().build();
private final ListMultimap<MethodSpecKind, MethodSpec> methodSpecsMap =
MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build();
private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
private GeneratedComponentModel(ClassName name, Modifier... modifiers) {
this.name = name;
this.component = classBuilder(name).addModifiers(modifiers);
}
static GeneratedComponentModel forComponent(ClassName name) {
return new GeneratedComponentModel(name, PUBLIC, FINAL);
}
static GeneratedComponentModel forSubcomponent(ClassName name) {
return new GeneratedComponentModel(name, PRIVATE, FINAL);
}
/** Returns the name of the component. */
ClassName name() {
return name;
}
/** Adds the given super type to the component. */
void addSupertype(TypeElement supertype) {
TypeSpecs.addSupertype(component, supertype);
}
/** Adds the given field to the component. */
void addField(FieldSpecKind fieldKind, FieldSpec fieldSpec) {
fieldSpecsMap.put(fieldKind, fieldSpec);
}
/** Adds the given fields to the component. */
void addFields(FieldSpecKind fieldKind, Iterable<FieldSpec> fieldSpecs) {
fieldSpecsMap.putAll(fieldKind, fieldSpecs);
}
/** Adds the given method to the component. */
void addMethod(MethodSpecKind methodKind, MethodSpec methodSpec) {
methodSpecsMap.put(methodKind, methodSpec);
}
/** Adds the given methods to the component. */
void addMethods(MethodSpecKind methodKind, Iterable<MethodSpec> methodSpecs) {
methodSpecsMap.putAll(methodKind, methodSpecs);
}
/** Adds the given type to the component. */
void addType(TypeSpecKind typeKind, TypeSpec typeSpec) {
typeSpecsMap.put(typeKind, typeSpec);
}
/** Adds the given types to the component. */
void addTypes(TypeSpecKind typeKind, Iterable<TypeSpec> typeSpecs) {
typeSpecsMap.putAll(typeKind, typeSpecs);
}
/** Adds the given code block to the initialize methods of the component. */
void addInitialization(CodeBlock codeBlock) {
initializations.add(codeBlock);
}
/** Returns a new, unique field name for the component based on the given name. */
String getUniqueFieldName(String name) {
return componentFieldNames.getUniqueName(name);
}
/** Returns a new, unique method name for the component based on the given name. */
String getUniqueMethodName(String name) {
return componentMethodNames.getUniqueName(name);
}
/** Claims a new method name for the component. Does nothing if method name already exists. */
void claimMethodName(Name name) {
componentMethodNames.claim(name);
}
/** Returns the list of {@link CodeBlock}s that need to go in the initialize method. */
ImmutableList<CodeBlock> getInitializations() {
return ImmutableList.copyOf(initializations);
}
/** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
TypeSpec.Builder generate() {
fieldSpecsMap.asMap().values().forEach(component::addFields);
methodSpecsMap.asMap().values().forEach(component::addMethods);
typeSpecsMap.asMap().values().forEach(component::addTypes);
return component;
}
}