| /* |
| * Copyright (C) 2018 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.google.common.base.Preconditions.checkArgument; |
| import static com.squareup.javapoet.MethodSpec.methodBuilder; |
| import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible; |
| import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock; |
| import static javax.lang.model.element.Modifier.PUBLIC; |
| import static javax.lang.model.element.Modifier.STATIC; |
| |
| import com.google.auto.value.AutoValue; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import com.google.errorprone.annotations.CheckReturnValue; |
| import com.squareup.javapoet.ClassName; |
| import com.squareup.javapoet.CodeBlock; |
| import com.squareup.javapoet.MethodSpec; |
| import com.squareup.javapoet.ParameterSpec; |
| import com.squareup.javapoet.TypeName; |
| import com.squareup.javapoet.TypeVariableName; |
| import java.util.List; |
| import java.util.Optional; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.Parameterizable; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.DeclaredType; |
| import javax.lang.model.type.TypeMirror; |
| |
| /** |
| * A static method that implements provision and/or injection in one step: |
| * |
| * <ul> |
| * <li>methods that invoke {@code @Inject} constructors and do members injection if necessary |
| * <li>methods that call {@code @Provides} module methods |
| * <li>methods that perform members injection |
| * </ul> |
| * |
| * <p>Note that although this type uses {@code @AutoValue}, it uses instance equality. It uses |
| * {@code @AutoValue} to avoid the boilerplate of writing a correct builder, but is not intended to |
| * actually be a value type. |
| */ |
| @AutoValue |
| abstract class InjectionMethod { |
| abstract String name(); |
| |
| abstract boolean varargs(); |
| |
| abstract ImmutableList<TypeVariableName> typeVariables(); |
| |
| abstract ImmutableMap<ParameterSpec, TypeMirror> parameters(); |
| |
| abstract Optional<TypeMirror> returnType(); |
| |
| abstract Optional<DeclaredType> nullableAnnotation(); |
| |
| abstract ImmutableList<TypeMirror> exceptions(); |
| |
| abstract CodeBlock methodBody(); |
| |
| abstract ClassName enclosingClass(); |
| |
| MethodSpec toMethodSpec() { |
| MethodSpec.Builder builder = |
| methodBuilder(name()) |
| .addModifiers(PUBLIC, STATIC) |
| .varargs(varargs()) |
| .addTypeVariables(typeVariables()) |
| .addParameters(parameters().keySet()) |
| .addCode(methodBody()); |
| returnType().map(TypeName::get).ifPresent(builder::returns); |
| nullableAnnotation() |
| .ifPresent(nullableType -> CodeBlocks.addAnnotation(builder, nullableType)); |
| exceptions().stream().map(TypeName::get).forEach(builder::addException); |
| return builder.build(); |
| } |
| |
| CodeBlock invoke(List<CodeBlock> arguments, ClassName requestingClass) { |
| checkArgument(arguments.size() == parameters().size()); |
| CodeBlock.Builder invocation = CodeBlock.builder(); |
| if (!enclosingClass().equals(requestingClass)) { |
| invocation.add("$T.", enclosingClass()); |
| } |
| return invocation.add("$L($L)", name(), makeParametersCodeBlock(arguments)).build(); |
| } |
| |
| @Override |
| public final int hashCode() { |
| return System.identityHashCode(this); |
| } |
| |
| @Override |
| public final boolean equals(Object obj) { |
| return this == obj; |
| } |
| |
| static Builder builder(DaggerElements elements) { |
| Builder builder = new AutoValue_InjectionMethod.Builder(); |
| builder.elements = elements; |
| builder.varargs(false).exceptions(ImmutableList.of()).nullableAnnotation(Optional.empty()); |
| return builder; |
| } |
| |
| @CanIgnoreReturnValue |
| @AutoValue.Builder |
| abstract static class Builder { |
| private final UniqueNameSet parameterNames = new UniqueNameSet(); |
| private final CodeBlock.Builder methodBody = CodeBlock.builder(); |
| private DaggerElements elements; |
| |
| abstract ImmutableMap.Builder<ParameterSpec, TypeMirror> parametersBuilder(); |
| abstract ImmutableList.Builder<TypeVariableName> typeVariablesBuilder(); |
| abstract Builder name(String name); |
| abstract Builder varargs(boolean varargs); |
| abstract Builder returnType(TypeMirror returnType); |
| abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions); |
| abstract Builder nullableAnnotation(Optional<DeclaredType> nullableAnnotation); |
| abstract Builder methodBody(CodeBlock methodBody); |
| |
| final CodeBlock.Builder methodBodyBuilder() { |
| return methodBody; |
| } |
| |
| abstract Builder enclosingClass(ClassName enclosingClass); |
| |
| /** |
| * Adds a parameter for the given name and type. If another parameter has already been added |
| * with the same name, the name is disambiguated. |
| */ |
| ParameterSpec addParameter(String name, TypeMirror type) { |
| ParameterSpec parameter = |
| ParameterSpec.builder(TypeName.get(type), parameterNames.getUniqueName(name)).build(); |
| parametersBuilder().put(parameter, type); |
| return parameter; |
| } |
| |
| /** |
| * Calls {@link #copyParameter(VariableElement)} for each parameter of of {@code method}, and |
| * concatenates the results of each call, {@link CodeBlocks#makeParametersCodeBlock(Iterable) |
| * separated with commas}. |
| */ |
| CodeBlock copyParameters(ExecutableElement method) { |
| ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder(); |
| for (VariableElement parameter : method.getParameters()) { |
| argumentsBuilder.add(copyParameter(parameter)); |
| } |
| varargs(method.isVarArgs()); |
| return makeParametersCodeBlock(argumentsBuilder.build()); |
| } |
| |
| /** |
| * Adds {@code parameter} as a parameter of this method, using a publicly accessible version of |
| * the parameter's type. Returns a {@link CodeBlock} of the usage of this parameter within the |
| * injection method's {@link #methodBody()}. |
| */ |
| CodeBlock copyParameter(VariableElement parameter) { |
| TypeMirror elementType = parameter.asType(); |
| boolean useObject = !isRawTypePubliclyAccessible(elementType); |
| TypeMirror publicType = useObject ? objectType() : elementType; |
| ParameterSpec parameterSpec = addParameter(parameter.getSimpleName().toString(), publicType); |
| return useObject |
| ? CodeBlock.of("($T) $N", elementType, parameterSpec) |
| : CodeBlock.of("$N", parameterSpec); |
| } |
| |
| private TypeMirror objectType() { |
| return elements.getTypeElement(Object.class).asType(); |
| } |
| |
| /** |
| * Adds each type parameter of {@code parameterizable} as a type parameter of this injection |
| * method. |
| */ |
| Builder copyTypeParameters(Parameterizable parameterizable) { |
| parameterizable.getTypeParameters().stream() |
| .map(TypeVariableName::get) |
| .forEach(typeVariablesBuilder()::add); |
| return this; |
| } |
| |
| Builder copyThrows(ExecutableElement element) { |
| exceptions(element.getThrownTypes()); |
| return this; |
| } |
| |
| @CheckReturnValue |
| final InjectionMethod build() { |
| return methodBody(methodBody.build()).buildInternal(); |
| } |
| |
| abstract InjectionMethod buildInternal(); |
| } |
| } |