dagger.internal.codegen.binding.ComponentCreatorDescriptor Maven / Gradle / Ivy
Show all versions of dagger-compiler Show documentation
/*
* Copyright (C) 2014 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.binding;
import static com.google.common.base.Verify.verify;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.ComponentCreatorAnnotation.getCreatorAnnotations;
import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotations;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
import static dagger.internal.codegen.xprocessing.XTypes.isSubtype;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XMethodType;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import dagger.internal.codegen.base.ComponentCreatorAnnotation;
import dagger.internal.codegen.base.ComponentCreatorKind;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.DependencyRequest;
import dagger.internal.codegen.xprocessing.XElements;
import java.util.List;
/**
* A descriptor for a component creator type: that is, a type annotated with
* {@code @Component.Builder} (or one of the corresponding production or subcomponent versions).
*/
@AutoValue
public abstract class ComponentCreatorDescriptor {
/** Returns the annotation marking this creator. */
public abstract ComponentCreatorAnnotation annotation();
/** The kind of this creator. */
public final ComponentCreatorKind kind() {
return annotation().creatorKind();
}
/** The annotated creator type. */
public abstract XTypeElement typeElement();
/** The method that creates and returns a component instance. */
public abstract XMethodElement factoryMethod();
/**
* Multimap of component requirements to setter methods that set that requirement.
*
* In a valid creator, there will be exactly one element per component requirement, so this
* method should only be called when validating the descriptor.
*/
abstract ImmutableSetMultimap unvalidatedSetterMethods();
/**
* Multimap of component requirements to factory method parameters that set that requirement.
*
* In a valid creator, there will be exactly one element per component requirement, so this
* method should only be called when validating the descriptor.
*/
abstract ImmutableSetMultimap
unvalidatedFactoryParameters();
/**
* Multimap of component requirements to elements (methods or parameters) that set that
* requirement.
*
* In a valid creator, there will be exactly one element per component requirement, so this
* method should only be called when validating the descriptor.
*/
public final ImmutableSetMultimap
unvalidatedRequirementElements() {
// ComponentCreatorValidator ensures that there are either setter methods or factory method
// parameters, but not both, so we can cheat a little here since we know that only one of
// the two multimaps will be non-empty.
return ImmutableSetMultimap.copyOf( // no actual copy
unvalidatedSetterMethods().isEmpty()
? unvalidatedFactoryParameters()
: unvalidatedSetterMethods());
}
/**
* Map of component requirements to elements (setter methods or factory method parameters) that
* set them.
*/
@Memoized
ImmutableMap requirementElements() {
return flatten(unvalidatedRequirementElements());
}
/** Map of component requirements to setter methods for those requirements. */
@Memoized
public ImmutableMap setterMethods() {
return flatten(unvalidatedSetterMethods());
}
/** Map of component requirements to factory method parameters for those requirements. */
@Memoized
public ImmutableMap factoryParameters() {
return flatten(unvalidatedFactoryParameters());
}
private static ImmutableMap flatten(Multimap multimap) {
return ImmutableMap.copyOf(
Maps.transformValues(multimap.asMap(), values -> getOnlyElement(values)));
}
/** Returns the set of component requirements this creator allows the user to set. */
public final ImmutableSet userSettableRequirements() {
// Note: they should have been validated at the point this is used, so this set is valid.
return unvalidatedRequirementElements().keySet();
}
/** Returns the set of requirements for modules and component dependencies for this creator. */
public final ImmutableSet moduleAndDependencyRequirements() {
return userSettableRequirements().stream()
.filter(requirement -> !requirement.isBoundInstance())
.collect(toImmutableSet());
}
/** Returns the set of bound instance requirements for this creator. */
final ImmutableSet boundInstanceRequirements() {
return userSettableRequirements().stream()
.filter(ComponentRequirement::isBoundInstance)
.collect(toImmutableSet());
}
/** Returns the element in this creator that sets the given {@code requirement}. */
final XElement elementForRequirement(ComponentRequirement requirement) {
return requirementElements().get(requirement);
}
/** Creates a new {@link ComponentCreatorDescriptor} for the given creator {@code type}. */
public static ComponentCreatorDescriptor create(
XTypeElement creator, DependencyRequestFactory dependencyRequestFactory) {
XType componentType = creator.getEnclosingTypeElement().getType();
ImmutableSetMultimap.Builder setterMethods =
ImmutableSetMultimap.builder();
XMethodElement factoryMethod = null;
for (XMethodElement method : getAllUnimplementedMethods(creator)) {
XMethodType resolvedMethodType = method.asMemberOf(creator.getType());
if (isSubtype(componentType, resolvedMethodType.getReturnType())) {
verify(
factoryMethod == null,
"Expected a single factory method for %s but found multiple: [%s, %s]",
XElements.toStableString(creator),
XElements.toStableString(factoryMethod),
XElements.toStableString(method));
factoryMethod = method;
} else {
XExecutableParameterElement parameter = getOnlyElement(method.getParameters());
XType parameterType = getOnlyElement(resolvedMethodType.getParameterTypes());
setterMethods.put(
requirement(method, parameter, parameterType, dependencyRequestFactory, method),
method);
}
}
verify(
factoryMethod != null,
"Expected a single factory method for %s but found none.",
XElements.toStableString(creator));
ImmutableSetMultimap.Builder
factoryParameters = ImmutableSetMultimap.builder();
XMethodType resolvedFactoryMethodType = factoryMethod.asMemberOf(creator.getType());
List parameters = factoryMethod.getParameters();
List parameterTypes = resolvedFactoryMethodType.getParameterTypes();
for (int i = 0; i < parameters.size(); i++) {
XExecutableParameterElement parameter = parameters.get(i);
XType parameterType = parameterTypes.get(i);
factoryParameters.put(
requirement(
factoryMethod,
parameter,
parameterType,
dependencyRequestFactory,
parameter),
parameter);
}
// Validation should have ensured exactly one creator annotation is present on the type.
ComponentCreatorAnnotation annotation = getOnlyElement(getCreatorAnnotations(creator));
return new AutoValue_ComponentCreatorDescriptor(
annotation, creator, factoryMethod, setterMethods.build(), factoryParameters.build());
}
private static ComponentRequirement requirement(
XMethodElement method,
XExecutableParameterElement parameter,
XType parameterType,
DependencyRequestFactory dependencyRequestFactory,
XElement elementForVariableName) {
if (method.hasAnnotation(TypeNames.BINDS_INSTANCE)
|| parameter.hasAnnotation(TypeNames.BINDS_INSTANCE)) {
DependencyRequest request =
dependencyRequestFactory.forRequiredResolvedVariable(parameter, parameterType);
return ComponentRequirement.forBoundInstance(
request.key(), request.isNullable(), elementForVariableName);
}
return parameterType.getTypeElement().hasAnyAnnotation(moduleAnnotations())
? ComponentRequirement.forModule(parameterType)
: ComponentRequirement.forDependency(parameterType);
}
}