1 /* 2 * Copyright (C) 2018 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.squareup.javapoet.MethodSpec.methodBuilder; 21 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock; 22 import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible; 23 import static javax.lang.model.element.Modifier.PUBLIC; 24 import static javax.lang.model.element.Modifier.STATIC; 25 26 import com.google.auto.value.AutoValue; 27 import com.google.common.collect.ImmutableList; 28 import com.google.common.collect.ImmutableMap; 29 import com.google.errorprone.annotations.CanIgnoreReturnValue; 30 import com.google.errorprone.annotations.CheckReturnValue; 31 import com.squareup.javapoet.ClassName; 32 import com.squareup.javapoet.CodeBlock; 33 import com.squareup.javapoet.MethodSpec; 34 import com.squareup.javapoet.ParameterSpec; 35 import com.squareup.javapoet.TypeName; 36 import com.squareup.javapoet.TypeVariableName; 37 import dagger.internal.codegen.javapoet.CodeBlocks; 38 import dagger.internal.codegen.langmodel.DaggerElements; 39 import java.util.List; 40 import java.util.Optional; 41 import javax.lang.model.element.ExecutableElement; 42 import javax.lang.model.element.Parameterizable; 43 import javax.lang.model.element.VariableElement; 44 import javax.lang.model.type.DeclaredType; 45 import javax.lang.model.type.TypeMirror; 46 47 /** 48 * A static method that implements provision and/or injection in one step: 49 * 50 * <ul> 51 * <li>methods that invoke {@code @Inject} constructors and do members injection if necessary 52 * <li>methods that call {@code @Provides} module methods 53 * <li>methods that perform members injection 54 * </ul> 55 * 56 * <p>Note that although this type uses {@code @AutoValue}, it uses instance equality. It uses 57 * {@code @AutoValue} to avoid the boilerplate of writing a correct builder, but is not intended to 58 * actually be a value type. 59 */ 60 @AutoValue 61 abstract class InjectionMethod { name()62 abstract String name(); 63 varargs()64 abstract boolean varargs(); 65 typeVariables()66 abstract ImmutableList<TypeVariableName> typeVariables(); 67 parameters()68 abstract ImmutableMap<ParameterSpec, TypeMirror> parameters(); 69 returnType()70 abstract Optional<TypeMirror> returnType(); 71 nullableAnnotation()72 abstract Optional<DeclaredType> nullableAnnotation(); 73 exceptions()74 abstract ImmutableList<TypeMirror> exceptions(); 75 methodBody()76 abstract CodeBlock methodBody(); 77 enclosingClass()78 abstract ClassName enclosingClass(); 79 toMethodSpec()80 MethodSpec toMethodSpec() { 81 MethodSpec.Builder builder = 82 methodBuilder(name()) 83 .addModifiers(PUBLIC, STATIC) 84 .varargs(varargs()) 85 .addTypeVariables(typeVariables()) 86 .addParameters(parameters().keySet()) 87 .addCode(methodBody()); 88 returnType().map(TypeName::get).ifPresent(builder::returns); 89 nullableAnnotation() 90 .ifPresent(nullableType -> CodeBlocks.addAnnotation(builder, nullableType)); 91 exceptions().stream().map(TypeName::get).forEach(builder::addException); 92 return builder.build(); 93 } 94 invoke(List<CodeBlock> arguments, ClassName requestingClass)95 CodeBlock invoke(List<CodeBlock> arguments, ClassName requestingClass) { 96 checkArgument(arguments.size() == parameters().size()); 97 CodeBlock.Builder invocation = CodeBlock.builder(); 98 if (!enclosingClass().equals(requestingClass)) { 99 invocation.add("$T.", enclosingClass()); 100 } 101 return invocation.add("$L($L)", name(), makeParametersCodeBlock(arguments)).build(); 102 } 103 104 @Override hashCode()105 public final int hashCode() { 106 return System.identityHashCode(this); 107 } 108 109 @Override equals(Object obj)110 public final boolean equals(Object obj) { 111 return this == obj; 112 } 113 builder(DaggerElements elements)114 static Builder builder(DaggerElements elements) { 115 Builder builder = new AutoValue_InjectionMethod.Builder(); 116 builder.elements = elements; 117 builder.varargs(false).exceptions(ImmutableList.of()).nullableAnnotation(Optional.empty()); 118 return builder; 119 } 120 121 @CanIgnoreReturnValue 122 @AutoValue.Builder 123 abstract static class Builder { 124 private final UniqueNameSet parameterNames = new UniqueNameSet(); 125 private final CodeBlock.Builder methodBody = CodeBlock.builder(); 126 private DaggerElements elements; 127 parametersBuilder()128 abstract ImmutableMap.Builder<ParameterSpec, TypeMirror> parametersBuilder(); typeVariablesBuilder()129 abstract ImmutableList.Builder<TypeVariableName> typeVariablesBuilder(); name(String name)130 abstract Builder name(String name); varargs(boolean varargs)131 abstract Builder varargs(boolean varargs); returnType(TypeMirror returnType)132 abstract Builder returnType(TypeMirror returnType); exceptions(Iterable<? extends TypeMirror> exceptions)133 abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions); nullableAnnotation(Optional<DeclaredType> nullableAnnotation)134 abstract Builder nullableAnnotation(Optional<DeclaredType> nullableAnnotation); methodBody(CodeBlock methodBody)135 abstract Builder methodBody(CodeBlock methodBody); 136 methodBodyBuilder()137 final CodeBlock.Builder methodBodyBuilder() { 138 return methodBody; 139 } 140 enclosingClass(ClassName enclosingClass)141 abstract Builder enclosingClass(ClassName enclosingClass); 142 143 /** 144 * Adds a parameter for the given name and type. If another parameter has already been added 145 * with the same name, the name is disambiguated. 146 */ addParameter(String name, TypeMirror type)147 ParameterSpec addParameter(String name, TypeMirror type) { 148 ParameterSpec parameter = 149 ParameterSpec.builder(TypeName.get(type), parameterNames.getUniqueName(name)).build(); 150 parametersBuilder().put(parameter, type); 151 return parameter; 152 } 153 154 /** 155 * Calls {@link #copyParameter(VariableElement)} for each parameter of of {@code method}, and 156 * concatenates the results of each call, {@link CodeBlocks#makeParametersCodeBlock(Iterable) 157 * separated with commas}. 158 */ copyParameters(ExecutableElement method)159 CodeBlock copyParameters(ExecutableElement method) { 160 ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder(); 161 for (VariableElement parameter : method.getParameters()) { 162 argumentsBuilder.add(copyParameter(parameter)); 163 } 164 varargs(method.isVarArgs()); 165 return makeParametersCodeBlock(argumentsBuilder.build()); 166 } 167 168 /** 169 * Adds {@code parameter} as a parameter of this method, using a publicly accessible version of 170 * the parameter's type. Returns a {@link CodeBlock} of the usage of this parameter within the 171 * injection method's {@link #methodBody()}. 172 */ copyParameter(VariableElement parameter)173 CodeBlock copyParameter(VariableElement parameter) { 174 TypeMirror elementType = parameter.asType(); 175 boolean useObject = !isRawTypePubliclyAccessible(elementType); 176 TypeMirror publicType = useObject ? objectType() : elementType; 177 ParameterSpec parameterSpec = addParameter(parameter.getSimpleName().toString(), publicType); 178 return useObject 179 ? CodeBlock.of("($T) $N", elementType, parameterSpec) 180 : CodeBlock.of("$N", parameterSpec); 181 } 182 objectType()183 private TypeMirror objectType() { 184 return elements.getTypeElement(Object.class).asType(); 185 } 186 187 /** 188 * Adds each type parameter of {@code parameterizable} as a type parameter of this injection 189 * method. 190 */ copyTypeParameters(Parameterizable parameterizable)191 Builder copyTypeParameters(Parameterizable parameterizable) { 192 parameterizable.getTypeParameters().stream() 193 .map(TypeVariableName::get) 194 .forEach(typeVariablesBuilder()::add); 195 return this; 196 } 197 copyThrows(ExecutableElement element)198 Builder copyThrows(ExecutableElement element) { 199 exceptions(element.getThrownTypes()); 200 return this; 201 } 202 203 @CheckReturnValue build()204 final InjectionMethod build() { 205 return methodBody(methodBody.build()).buildInternal(); 206 } 207 buildInternal()208 abstract InjectionMethod buildInternal(); 209 } 210 } 211