/*
* 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 extends VariableElement> parameters = factoryMethod.getParameters();
List extends TypeMirror> 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);
}
}