1 /* 2 * Copyright (C) 2017 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.writing; 18 19 import static com.google.auto.common.MoreElements.asExecutable; 20 import static com.google.auto.common.MoreElements.asType; 21 import static com.google.auto.common.MoreElements.asVariable; 22 import static com.google.common.base.CaseFormat.LOWER_CAMEL; 23 import static com.google.common.base.CaseFormat.UPPER_CAMEL; 24 import static com.google.common.base.Preconditions.checkArgument; 25 import static com.squareup.javapoet.MethodSpec.methodBuilder; 26 import static dagger.internal.codegen.base.RequestKinds.requestTypeName; 27 import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType; 28 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; 29 import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable; 30 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType; 31 import static dagger.internal.codegen.binding.SourceFiles.protectAgainstKeywords; 32 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; 33 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock; 34 import static dagger.internal.codegen.javapoet.CodeBlocks.toConcatenatedCodeBlock; 35 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 36 import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName; 37 import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom; 38 import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible; 39 import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible; 40 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 41 import static java.util.stream.Collectors.toList; 42 import static javax.lang.model.element.Modifier.PUBLIC; 43 import static javax.lang.model.element.Modifier.STATIC; 44 import static javax.lang.model.type.TypeKind.VOID; 45 46 import com.google.auto.common.MoreElements; 47 import com.google.common.collect.ImmutableList; 48 import com.google.common.collect.ImmutableMap; 49 import com.google.common.collect.ImmutableSet; 50 import com.squareup.javapoet.AnnotationSpec; 51 import com.squareup.javapoet.ClassName; 52 import com.squareup.javapoet.CodeBlock; 53 import com.squareup.javapoet.MethodSpec; 54 import com.squareup.javapoet.ParameterSpec; 55 import com.squareup.javapoet.TypeName; 56 import com.squareup.javapoet.TypeVariableName; 57 import dagger.internal.Preconditions; 58 import dagger.internal.codegen.base.UniqueNameSet; 59 import dagger.internal.codegen.binding.AssistedInjectionAnnotations; 60 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite; 61 import dagger.internal.codegen.binding.ProvisionBinding; 62 import dagger.internal.codegen.compileroption.CompilerOptions; 63 import dagger.internal.codegen.extension.DaggerCollectors; 64 import dagger.internal.codegen.javapoet.CodeBlocks; 65 import dagger.internal.codegen.javapoet.TypeNames; 66 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 67 import dagger.internal.codegen.langmodel.DaggerTypes; 68 import dagger.model.DependencyRequest; 69 import dagger.model.RequestKind; 70 import java.util.List; 71 import java.util.Optional; 72 import java.util.function.Function; 73 import javax.lang.model.SourceVersion; 74 import javax.lang.model.element.AnnotationMirror; 75 import javax.lang.model.element.ExecutableElement; 76 import javax.lang.model.element.Parameterizable; 77 import javax.lang.model.element.TypeElement; 78 import javax.lang.model.element.VariableElement; 79 import javax.lang.model.type.TypeKind; 80 import javax.lang.model.type.TypeMirror; 81 82 /** Convenience methods for creating and invoking {@link InjectionMethod}s. */ 83 final class InjectionMethods { 84 85 /** 86 * A method that returns an object from a {@code @Provides} method or an {@code @Inject}ed 87 * constructor. Its parameters match the dependency requests for constructor and members 88 * injection. 89 * 90 * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". For example: 91 * 92 * <pre><code> 93 * abstract class FooModule { 94 * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … } 95 * } 96 * 97 * public static proxyProvideFoo(Bar bar, Baz baz) { … } 98 * </code></pre> 99 * 100 * <p>For {@code @Inject}ed constructors, the method name is "newFoo". For example: 101 * 102 * <pre><code> 103 * class Foo { 104 * {@literal @Inject} Foo(Bar bar) {} 105 * } 106 * 107 * public static Foo newFoo(Bar bar) { … } 108 * </code></pre> 109 */ 110 static final class ProvisionMethod { 111 // These names are already defined in factories and shouldn't be used for the proxy method name. 112 private static final ImmutableSet<String> BANNED_PROXY_NAMES = ImmutableSet.of("get", "create"); 113 114 /** 115 * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement() 116 * constructor} and injects the instance's members. 117 */ create( ProvisionBinding binding, CompilerOptions compilerOptions, KotlinMetadataUtil metadataUtil)118 static MethodSpec create( 119 ProvisionBinding binding, 120 CompilerOptions compilerOptions, 121 KotlinMetadataUtil metadataUtil) { 122 ExecutableElement element = asExecutable(binding.bindingElement().get()); 123 switch (element.getKind()) { 124 case CONSTRUCTOR: 125 return constructorProxy(element); 126 case METHOD: 127 return methodProxy( 128 element, 129 methodName(element), 130 InstanceCastPolicy.IGNORE, 131 CheckNotNullPolicy.get(binding, compilerOptions), 132 metadataUtil); 133 default: 134 throw new AssertionError(element); 135 } 136 } 137 138 /** 139 * Invokes the injection method for {@code binding}, with the dependencies transformed with the 140 * {@code dependencyUsage} function. 141 */ invoke( ProvisionBinding binding, Function<DependencyRequest, CodeBlock> dependencyUsage, ClassName requestingClass, Optional<CodeBlock> moduleReference, CompilerOptions compilerOptions, KotlinMetadataUtil metadataUtil)142 static CodeBlock invoke( 143 ProvisionBinding binding, 144 Function<DependencyRequest, CodeBlock> dependencyUsage, 145 ClassName requestingClass, 146 Optional<CodeBlock> moduleReference, 147 CompilerOptions compilerOptions, 148 KotlinMetadataUtil metadataUtil) { 149 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder(); 150 moduleReference.ifPresent(arguments::add); 151 invokeArguments(binding, dependencyUsage, requestingClass).forEach(arguments::add); 152 153 ClassName enclosingClass = generatedClassNameForBinding(binding); 154 MethodSpec methodSpec = create(binding, compilerOptions, metadataUtil); 155 return invokeMethod(methodSpec, arguments.build(), enclosingClass, requestingClass); 156 } 157 invokeArguments( ProvisionBinding binding, Function<DependencyRequest, CodeBlock> dependencyUsage, ClassName requestingClass)158 static ImmutableList<CodeBlock> invokeArguments( 159 ProvisionBinding binding, 160 Function<DependencyRequest, CodeBlock> dependencyUsage, 161 ClassName requestingClass) { 162 ImmutableMap<VariableElement, DependencyRequest> dependencyRequestMap = 163 binding.provisionDependencies().stream() 164 .collect( 165 toImmutableMap( 166 request -> MoreElements.asVariable(request.requestElement().get()), 167 request -> request)); 168 169 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder(); 170 for (VariableElement parameter : 171 asExecutable(binding.bindingElement().get()).getParameters()) { 172 if (AssistedInjectionAnnotations.isAssistedParameter(parameter)) { 173 arguments.add(CodeBlock.of("$L", parameter.getSimpleName())); 174 } else if (dependencyRequestMap.containsKey(parameter)) { 175 DependencyRequest request = dependencyRequestMap.get(parameter); 176 arguments.add( 177 injectionMethodArgument(request, dependencyUsage.apply(request), requestingClass)); 178 } else { 179 throw new AssertionError("Unexpected parameter: " + parameter); 180 } 181 } 182 183 return arguments.build(); 184 } 185 constructorProxy(ExecutableElement constructor)186 private static MethodSpec constructorProxy(ExecutableElement constructor) { 187 TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement()); 188 MethodSpec.Builder builder = 189 methodBuilder(methodName(constructor)) 190 .addModifiers(PUBLIC, STATIC) 191 .varargs(constructor.isVarArgs()) 192 .returns(TypeName.get(enclosingType.asType())); 193 194 copyTypeParameters(builder, enclosingType); 195 copyThrows(builder, constructor); 196 197 CodeBlock arguments = 198 copyParameters(builder, new UniqueNameSet(), constructor.getParameters()); 199 return builder.addStatement("return new $T($L)", enclosingType, arguments).build(); 200 } 201 202 /** 203 * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage} 204 * requires the use of an injection method. 205 */ requiresInjectionMethod( ProvisionBinding binding, CompilerOptions compilerOptions, ClassName requestingClass)206 static boolean requiresInjectionMethod( 207 ProvisionBinding binding, CompilerOptions compilerOptions, ClassName requestingClass) { 208 ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get()); 209 return !binding.injectionSites().isEmpty() 210 || binding.shouldCheckForNull(compilerOptions) 211 || !isElementAccessibleFrom(method, requestingClass.packageName()) 212 // This check should be removable once we drop support for -source 7 213 || method.getParameters().stream() 214 .map(VariableElement::asType) 215 .anyMatch(type -> !isRawTypeAccessible(type, requestingClass.packageName())); 216 } 217 218 /** 219 * Returns the name of the {@code static} method that wraps {@code method}. For methods that are 220 * associated with {@code @Inject} constructors, the method will also inject all {@link 221 * InjectionSite}s. 222 */ methodName(ExecutableElement method)223 private static String methodName(ExecutableElement method) { 224 switch (method.getKind()) { 225 case CONSTRUCTOR: 226 return "newInstance"; 227 case METHOD: 228 String methodName = method.getSimpleName().toString(); 229 return BANNED_PROXY_NAMES.contains(methodName) 230 ? "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, methodName) 231 : methodName; 232 default: 233 throw new AssertionError(method); 234 } 235 } 236 } 237 238 /** 239 * A static method that injects one member of an instance of a type. Its first parameter is an 240 * instance of the type to be injected. The remaining parameters match the dependency requests for 241 * the injection site. 242 * 243 * <p>Example: 244 * 245 * <pre><code> 246 * class Foo { 247 * {@literal @Inject} Bar bar; 248 * {@literal @Inject} void setThings(Baz baz, Qux qux) {} 249 * } 250 * 251 * public static injectBar(Foo instance, Bar bar) { … } 252 * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … } 253 * </code></pre> 254 */ 255 static final class InjectionSiteMethod { 256 /** 257 * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent 258 * that's in a different package), a method in the supertype's package must be generated to give 259 * the subclass's members injector a way to inject it. Each potentially inaccessible member 260 * receives its own method, as the subclass may need to inject them in a different order from 261 * the parent class. 262 */ create(InjectionSite injectionSite, KotlinMetadataUtil metadataUtil)263 static MethodSpec create(InjectionSite injectionSite, KotlinMetadataUtil metadataUtil) { 264 String methodName = methodName(injectionSite); 265 switch (injectionSite.kind()) { 266 case METHOD: 267 return methodProxy( 268 asExecutable(injectionSite.element()), 269 methodName, 270 InstanceCastPolicy.CAST_IF_NOT_PUBLIC, 271 CheckNotNullPolicy.IGNORE, 272 metadataUtil); 273 case FIELD: 274 Optional<AnnotationMirror> qualifier = 275 injectionSite.dependencies().stream() 276 // methods for fields have a single dependency request 277 .collect(DaggerCollectors.onlyElement()) 278 .key() 279 .qualifier(); 280 return fieldProxy(asVariable(injectionSite.element()), methodName, qualifier); 281 } 282 throw new AssertionError(injectionSite); 283 } 284 285 /** 286 * Invokes each of the injection methods for {@code injectionSites}, with the dependencies 287 * transformed using the {@code dependencyUsage} function. 288 * 289 * @param instanceType the type of the {@code instance} parameter 290 */ invokeAll( ImmutableSet<InjectionSite> injectionSites, ClassName generatedTypeName, CodeBlock instanceCodeBlock, TypeMirror instanceType, Function<DependencyRequest, CodeBlock> dependencyUsage, DaggerTypes types, KotlinMetadataUtil metadataUtil)291 static CodeBlock invokeAll( 292 ImmutableSet<InjectionSite> injectionSites, 293 ClassName generatedTypeName, 294 CodeBlock instanceCodeBlock, 295 TypeMirror instanceType, 296 Function<DependencyRequest, CodeBlock> dependencyUsage, 297 DaggerTypes types, 298 KotlinMetadataUtil metadataUtil) { 299 return injectionSites.stream() 300 .map( 301 injectionSite -> { 302 TypeMirror injectSiteType = 303 types.erasure(injectionSite.element().getEnclosingElement().asType()); 304 305 // If instance has been declared as Object because it is not accessible from the 306 // component, but the injectionSite is in a supertype of instanceType that is 307 // publicly accessible, the InjectionSiteMethod will request the actual type and not 308 // Object as the first parameter. If so, cast to the supertype which is accessible 309 // from within generatedTypeName 310 CodeBlock maybeCastedInstance = 311 !types.isSubtype(instanceType, injectSiteType) 312 && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName()) 313 ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock) 314 : instanceCodeBlock; 315 return CodeBlock.of( 316 "$L;", 317 invoke( 318 injectionSite, 319 generatedTypeName, 320 maybeCastedInstance, 321 dependencyUsage, 322 metadataUtil)); 323 }) 324 .collect(toConcatenatedCodeBlock()); 325 } 326 327 /** 328 * Invokes the injection method for {@code injectionSite}, with the dependencies transformed 329 * using the {@code dependencyUsage} function. 330 */ invoke( InjectionSite injectionSite, ClassName generatedTypeName, CodeBlock instanceCodeBlock, Function<DependencyRequest, CodeBlock> dependencyUsage, KotlinMetadataUtil metadataUtil)331 private static CodeBlock invoke( 332 InjectionSite injectionSite, 333 ClassName generatedTypeName, 334 CodeBlock instanceCodeBlock, 335 Function<DependencyRequest, CodeBlock> dependencyUsage, 336 KotlinMetadataUtil metadataUtil) { 337 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder(); 338 arguments.add(instanceCodeBlock); 339 if (!injectionSite.dependencies().isEmpty()) { 340 arguments.addAll( 341 injectionSite.dependencies().stream().map(dependencyUsage).collect(toList())); 342 } 343 344 ClassName enclosingClass = 345 membersInjectorNameForType(asType(injectionSite.element().getEnclosingElement())); 346 MethodSpec methodSpec = create(injectionSite, metadataUtil); 347 return invokeMethod(methodSpec, arguments.build(), enclosingClass, generatedTypeName); 348 } 349 350 /* 351 * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples: 352 * 353 * - @Inject void members() {} will generate a method that conflicts with the instance 354 * method `injectMembers(T)` 355 * - Adding the index could conflict with another member: 356 * @Inject void a(Object o) {} 357 * @Inject void a(String s) {} 358 * @Inject void a1(String s) {} 359 * 360 * Here, Method a(String) will add the suffix "1", which will conflict with the method 361 * generated for a1(String) 362 * - Members named "members" or "methods" could also conflict with the {@code static} injection 363 * method. 364 */ methodName(InjectionSite injectionSite)365 private static String methodName(InjectionSite injectionSite) { 366 int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName(); 367 String indexString = index == 0 ? "" : String.valueOf(index + 1); 368 return "inject" 369 + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString()) 370 + indexString; 371 } 372 } 373 injectionMethodArgument( DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName)374 private static CodeBlock injectionMethodArgument( 375 DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) { 376 TypeMirror keyType = dependency.key().type(); 377 CodeBlock.Builder codeBlock = CodeBlock.builder(); 378 if (!isRawTypeAccessible(keyType, generatedTypeName.packageName()) 379 && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) { 380 if (!dependency.kind().equals(RequestKind.INSTANCE)) { 381 TypeName usageTypeName = accessibleType(dependency); 382 codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName)); 383 } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) { 384 codeBlock.add("($T)", keyType); 385 } 386 } 387 return codeBlock.add(argument).build(); 388 } 389 390 /** 391 * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns 392 * {@link Object}. 393 */ accessibleType(DependencyRequest dependency)394 private static TypeName accessibleType(DependencyRequest dependency) { 395 TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type())); 396 return dependency 397 .requestElement() 398 .map(element -> element.asType().getKind().isPrimitive()) 399 .orElse(false) 400 ? typeName.unbox() 401 : typeName; 402 } 403 404 /** 405 * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link 406 * Object}. 407 */ accessibleType(TypeMirror type)408 private static TypeName accessibleType(TypeMirror type) { 409 return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT; 410 } 411 412 private enum InstanceCastPolicy { 413 CAST_IF_NOT_PUBLIC, IGNORE; 414 useObjectType(TypeMirror instanceType)415 boolean useObjectType(TypeMirror instanceType) { 416 return this == CAST_IF_NOT_PUBLIC && !isRawTypePubliclyAccessible(instanceType); 417 } 418 } 419 420 private enum CheckNotNullPolicy { 421 IGNORE, CHECK_FOR_NULL; 422 checkForNull(CodeBlock maybeNull)423 CodeBlock checkForNull(CodeBlock maybeNull) { 424 return this.equals(IGNORE) 425 ? maybeNull 426 : CodeBlock.of("$T.checkNotNullFromProvides($L)", Preconditions.class, maybeNull); 427 } 428 get(ProvisionBinding binding, CompilerOptions compilerOptions)429 static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) { 430 return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE; 431 } 432 } 433 methodProxy( ExecutableElement method, String methodName, InstanceCastPolicy instanceCastPolicy, CheckNotNullPolicy checkNotNullPolicy, KotlinMetadataUtil metadataUtil)434 private static MethodSpec methodProxy( 435 ExecutableElement method, 436 String methodName, 437 InstanceCastPolicy instanceCastPolicy, 438 CheckNotNullPolicy checkNotNullPolicy, 439 KotlinMetadataUtil metadataUtil) { 440 MethodSpec.Builder builder = 441 methodBuilder(methodName).addModifiers(PUBLIC, STATIC).varargs(method.isVarArgs()); 442 443 TypeElement enclosingType = asType(method.getEnclosingElement()); 444 boolean isMethodInKotlinObject = metadataUtil.isObjectClass(enclosingType); 445 boolean isMethodInKotlinCompanionObject = metadataUtil.isCompanionObjectClass(enclosingType); 446 UniqueNameSet parameterNameSet = new UniqueNameSet(); 447 CodeBlock instance; 448 if (isMethodInKotlinCompanionObject || method.getModifiers().contains(STATIC)) { 449 instance = CodeBlock.of("$T", rawTypeName(TypeName.get(enclosingType.asType()))); 450 } else if (isMethodInKotlinObject) { 451 // Call through the singleton instance. 452 // See: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods 453 instance = CodeBlock.of("$T.INSTANCE", rawTypeName(TypeName.get(enclosingType.asType()))); 454 } else { 455 copyTypeParameters(builder, enclosingType); 456 boolean useObject = instanceCastPolicy.useObjectType(enclosingType.asType()); 457 instance = copyInstance(builder, parameterNameSet, enclosingType.asType(), useObject); 458 } 459 CodeBlock arguments = copyParameters(builder, parameterNameSet, method.getParameters()); 460 CodeBlock invocation = 461 checkNotNullPolicy.checkForNull( 462 CodeBlock.of("$L.$L($L)", instance, method.getSimpleName(), arguments)); 463 464 copyTypeParameters(builder, method); 465 copyThrows(builder, method); 466 467 if (method.getReturnType().getKind().equals(VOID)) { 468 return builder.addStatement("$L", invocation).build(); 469 } else { 470 getNullableType(method) 471 .ifPresent(annotation -> CodeBlocks.addAnnotation(builder, annotation)); 472 return builder 473 .returns(TypeName.get(method.getReturnType())) 474 .addStatement("return $L", invocation).build(); 475 } 476 } 477 fieldProxy( VariableElement field, String methodName, Optional<AnnotationMirror> qualifierAnnotation)478 private static MethodSpec fieldProxy( 479 VariableElement field, String methodName, Optional<AnnotationMirror> qualifierAnnotation) { 480 MethodSpec.Builder builder = 481 methodBuilder(methodName) 482 .addModifiers(PUBLIC, STATIC) 483 .addAnnotation( 484 AnnotationSpec.builder(TypeNames.INJECTED_FIELD_SIGNATURE) 485 .addMember("value", "$S", memberInjectedFieldSignatureForVariable(field)) 486 .build()); 487 488 qualifierAnnotation.map(AnnotationSpec::get).ifPresent(builder::addAnnotation); 489 490 TypeElement enclosingType = asType(field.getEnclosingElement()); 491 copyTypeParameters(builder, enclosingType); 492 493 boolean useObject = !isRawTypePubliclyAccessible(enclosingType.asType()); 494 UniqueNameSet parameterNameSet = new UniqueNameSet(); 495 CodeBlock instance = copyInstance(builder, parameterNameSet, enclosingType.asType(), useObject); 496 CodeBlock argument = copyParameters(builder, parameterNameSet, ImmutableList.of(field)); 497 return builder.addStatement("$L.$L = $L", instance, field.getSimpleName(), argument).build(); 498 } 499 invokeMethod( MethodSpec methodSpec, ImmutableList<CodeBlock> parameters, ClassName enclosingClass, ClassName requestingClass)500 private static CodeBlock invokeMethod( 501 MethodSpec methodSpec, 502 ImmutableList<CodeBlock> parameters, 503 ClassName enclosingClass, 504 ClassName requestingClass) { 505 checkArgument(methodSpec.parameters.size() == parameters.size()); 506 CodeBlock parameterBlock = makeParametersCodeBlock(parameters); 507 return enclosingClass.equals(requestingClass) 508 ? CodeBlock.of("$L($L)", methodSpec.name, parameterBlock) 509 : CodeBlock.of("$T.$L($L)", enclosingClass, methodSpec.name, parameterBlock); 510 } 511 copyTypeParameters( MethodSpec.Builder methodBuilder, Parameterizable element)512 private static void copyTypeParameters( 513 MethodSpec.Builder methodBuilder, Parameterizable element) { 514 element.getTypeParameters().stream() 515 .map(TypeVariableName::get) 516 .forEach(methodBuilder::addTypeVariable); 517 } 518 copyThrows(MethodSpec.Builder methodBuilder, ExecutableElement method)519 private static void copyThrows(MethodSpec.Builder methodBuilder, ExecutableElement method) { 520 method.getThrownTypes().stream().map(TypeName::get).forEach(methodBuilder::addException); 521 } 522 copyParameters( MethodSpec.Builder methodBuilder, UniqueNameSet parameterNameSet, List<? extends VariableElement> parameters)523 private static CodeBlock copyParameters( 524 MethodSpec.Builder methodBuilder, 525 UniqueNameSet parameterNameSet, 526 List<? extends VariableElement> parameters) { 527 return parameters.stream() 528 .map( 529 parameter -> { 530 String name = 531 parameterNameSet.getUniqueName(validJavaName(parameter.getSimpleName())); 532 TypeMirror type = parameter.asType(); 533 boolean useObject = !isRawTypePubliclyAccessible(type); 534 return copyParameter(methodBuilder, type, name, useObject); 535 }) 536 .collect(toParametersCodeBlock()); 537 } 538 539 private static CodeBlock copyParameter( 540 MethodSpec.Builder methodBuilder, TypeMirror type, String name, boolean useObject) { 541 TypeName typeName = useObject ? TypeName.OBJECT : TypeName.get(type); 542 methodBuilder.addParameter(ParameterSpec.builder(typeName, name).build()); 543 return useObject ? CodeBlock.of("($T) $L", type, name) : CodeBlock.of("$L", name); 544 } 545 546 private static CodeBlock copyInstance( 547 MethodSpec.Builder methodBuilder, 548 UniqueNameSet parameterNameSet, 549 TypeMirror type, 550 boolean useObject) { 551 CodeBlock instance = 552 copyParameter(methodBuilder, type, parameterNameSet.getUniqueName("instance"), useObject); 553 // If we had to cast the instance add an extra parenthesis incase we're calling a method on it. 554 return useObject ? CodeBlock.of("($L)", instance) : instance; 555 } 556 557 private static String validJavaName(CharSequence name) { 558 if (SourceVersion.isIdentifier(name)) { 559 return protectAgainstKeywords(name.toString()); 560 } 561 562 StringBuilder newName = new StringBuilder(name.length()); 563 char firstChar = name.charAt(0); 564 if (!Character.isJavaIdentifierStart(firstChar)) { 565 newName.append('_'); 566 } 567 568 name.chars().forEach(c -> newName.append(Character.isJavaIdentifierPart(c) ? c : '_')); 569 return newName.toString(); 570 } 571 } 572