/* * 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.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.common.MoreTypes.asTypeElement; import static com.google.common.base.Verify.verify; import static com.google.common.collect.Iterables.getOnlyElement; import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation; import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.getCreatorAnnotations; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import com.google.auto.common.MoreTypes; 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.BindsInstance; import dagger.internal.codegen.langmodel.DaggerElements; import dagger.internal.codegen.langmodel.DaggerTypes; import dagger.model.DependencyRequest; import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; /** * 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 TypeElement typeElement(); /** The method that creates and returns a component instance. */ public abstract ExecutableElement 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 Element elementForRequirement(ComponentRequirement requirement) { return requirementElements().get(requirement); } /** Creates a new {@link ComponentCreatorDescriptor} for the given creator {@code type}. */ public static ComponentCreatorDescriptor create( DeclaredType type, DaggerElements elements, DaggerTypes types, DependencyRequestFactory dependencyRequestFactory) { TypeElement typeElement = asTypeElement(type); TypeMirror componentType = typeElement.getEnclosingElement().asType(); ImmutableSetMultimap.Builder setterMethods = ImmutableSetMultimap.builder(); ExecutableElement factoryMethod = null; for (ExecutableElement method : elements.getUnimplementedMethods(typeElement)) { ExecutableType resolvedMethodType = MoreTypes.asExecutable(types.asMemberOf(type, method)); if (types.isSubtype(componentType, resolvedMethodType.getReturnType())) { factoryMethod = method; } else { VariableElement parameter = getOnlyElement(method.getParameters()); TypeMirror parameterType = getOnlyElement(resolvedMethodType.getParameterTypes()); setterMethods.put( requirement(method, parameter, parameterType, dependencyRequestFactory, method), method); } } verify(factoryMethod != null); // validation should have ensured this. ImmutableSetMultimap.Builder factoryParameters = ImmutableSetMultimap.builder(); ExecutableType resolvedFactoryMethodType = MoreTypes.asExecutable(types.asMemberOf(type, factoryMethod)); List parameters = factoryMethod.getParameters(); List parameterTypes = resolvedFactoryMethodType.getParameterTypes(); for (int i = 0; i < parameters.size(); i++) { VariableElement parameter = parameters.get(i); TypeMirror 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(typeElement)); return new AutoValue_ComponentCreatorDescriptor( annotation, typeElement, factoryMethod, setterMethods.build(), factoryParameters.build()); } private static ComponentRequirement requirement( ExecutableElement method, VariableElement parameter, TypeMirror type, DependencyRequestFactory dependencyRequestFactory, Element elementForVariableName) { if (isAnnotationPresent(method, BindsInstance.class) || isAnnotationPresent(parameter, BindsInstance.class)) { DependencyRequest request = dependencyRequestFactory.forRequiredResolvedVariable(parameter, type); String variableName = elementForVariableName.getSimpleName().toString(); return ComponentRequirement.forBoundInstance( request.key(), request.isNullable(), variableName); } return moduleAnnotation(asTypeElement(type)).isPresent() ? ComponentRequirement.forModule(type) : ComponentRequirement.forDependency(type); } }