• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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