blob: 5d6e0c0ac58f0605a66bd90a2cd3eb05b2894a22 [file] [log] [blame]
ronshapiroe05f9212017-08-08 11:22:11 -07001/*
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
17package dagger.internal.codegen;
18
19import static com.google.common.base.CaseFormat.LOWER_CAMEL;
20import static com.google.common.base.CaseFormat.UPPER_CAMEL;
21import static com.squareup.javapoet.MethodSpec.methodBuilder;
22import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
23import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
24import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
25import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
26import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
27import static dagger.internal.codegen.CodeBlocks.toConcatenatedCodeBlock;
ronshapiroe05f9212017-08-08 11:22:11 -070028import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
dpb639bd4d2017-10-24 15:15:17 -070029import static dagger.internal.codegen.DaggerStreams.toImmutableList;
ronshapiro5f059592017-12-07 07:18:33 -080030import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
ronshapiro120abc62017-12-15 09:57:09 -080031import static dagger.internal.codegen.RequestKinds.requestTypeName;
ronshapiroe05f9212017-08-08 11:22:11 -070032import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
33import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
34import static dagger.internal.codegen.TypeNames.rawTypeName;
35import static java.util.stream.Collectors.toList;
36import static javax.lang.model.element.Modifier.PUBLIC;
37import static javax.lang.model.element.Modifier.STATIC;
38import static javax.lang.model.type.TypeKind.VOID;
39
40import com.google.auto.common.MoreElements;
41import com.google.common.collect.ImmutableList;
42import com.google.common.collect.ImmutableSet;
43import com.squareup.javapoet.ClassName;
44import com.squareup.javapoet.CodeBlock;
45import com.squareup.javapoet.MethodSpec;
46import com.squareup.javapoet.ParameterSpec;
47import com.squareup.javapoet.TypeName;
48import com.squareup.javapoet.TypeVariableName;
ronshapiroe05f9212017-08-08 11:22:11 -070049import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
ronshapiroa5023ca2018-01-02 12:55:01 -080050import dagger.model.DependencyRequest;
ronshapiro120abc62017-12-15 09:57:09 -080051import dagger.model.RequestKind;
ronshapiroe05f9212017-08-08 11:22:11 -070052import java.util.ArrayList;
53import java.util.List;
54import java.util.Optional;
55import java.util.function.Function;
56import javax.lang.model.element.ExecutableElement;
57import javax.lang.model.element.Parameterizable;
58import javax.lang.model.element.TypeElement;
59import javax.lang.model.element.TypeParameterElement;
60import javax.lang.model.element.VariableElement;
61import javax.lang.model.type.TypeKind;
62import javax.lang.model.type.TypeMirror;
ronshapiro55cd3922017-08-10 09:48:08 -070063import javax.lang.model.util.Types;
ronshapiroe05f9212017-08-08 11:22:11 -070064
65/**
66 * Injection methods are static methods that implement provision and/or injection in one step:
67 *
68 * <ul>
69 * <li>methods that invoke {@code @Inject} constructors and do members injection if necessary
70 * <li>methods that call {@code @Provides} module methods
71 * <li>methods that perform members injection
72 * </ul>
73 */
74// TODO(ronshapiro): add examples for each class of injection method
75final class InjectionMethods {
76 /**
77 * A static method that returns an object from a {@code @Provides} method or an {@code @Inject}ed
78 * constructor. Its parameters match the dependency requests for constructor and members
79 * injection.
80 *
81 * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". If the
82 * {@code @Provides} method and its raw parameter types are publicly accessible, no method is
83 * necessary and this method returns {@link Optional#empty()}.
84 *
85 * <p>TODO(ronshapiro): At the moment non-static {@code @Provides} methods are not supported.
86 *
87 * <p>Example:
88 *
89 * <pre><code>
90 * abstract class FooModule {
91 * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
92 * }
93 *
94 * public static proxyProvideFoo(Bar bar, Baz baz) { … }
95 * </code></pre>
96 *
97 * <p>For {@code @Inject}ed constructors, the method name is "newFoo". If the constructor and its
98 * raw parameter types are publicly accessible, no method is necessary and this method returns
99 * {@code Optional#empty()}.
100 *
101 * <p>Example:
102 *
103 * <pre><code>
104 * class Foo {
105 * {@literal @Inject} Foo(Bar bar) {}
106 * }
107 *
108 * public static Foo newFoo(Bar bar) { … }
109 * </code></pre>
110 */
111 static final class ProvisionMethod {
112
113 /**
114 * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement()
bcorso472e2682018-04-09 14:51:34 -0700115 * constructor} and injects the instance's members.
ronshapiroe05f9212017-08-08 11:22:11 -0700116 */
bcorso472e2682018-04-09 14:51:34 -0700117 static MethodSpec create(ProvisionBinding binding, CompilerOptions compilerOptions) {
ronshapiroe05f9212017-08-08 11:22:11 -0700118 ExecutableElement element = MoreElements.asExecutable(binding.bindingElement().get());
119 switch (element.getKind()) {
120 case CONSTRUCTOR:
bcorso472e2682018-04-09 14:51:34 -0700121 return constructorProxy(element);
ronshapiroe05f9212017-08-08 11:22:11 -0700122 case METHOD:
bcorso472e2682018-04-09 14:51:34 -0700123 return methodProxy(
124 element,
125 methodName(element),
126 ReceiverAccessibility.IGNORE,
127 CheckNotNullPolicy.get(binding, compilerOptions));
ronshapiroe05f9212017-08-08 11:22:11 -0700128 default:
129 throw new AssertionError(element);
130 }
131 }
132
133 /**
134 * Invokes the injection method for {@code binding}, with the dependencies transformed with the
135 * {@code dependencyUsage} function.
136 */
137 static CodeBlock invoke(
138 ProvisionBinding binding,
139 Function<DependencyRequest, CodeBlock> dependencyUsage,
ronshapirodc07ed52017-08-23 08:52:10 -0700140 ClassName requestingClass,
ronshapiro5f059592017-12-07 07:18:33 -0800141 Optional<CodeBlock> moduleReference,
142 CompilerOptions compilerOptions) {
ronshapirodc07ed52017-08-23 08:52:10 -0700143 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
144 moduleReference.ifPresent(arguments::add);
145 arguments.addAll(
146 injectionMethodArguments(
147 binding.provisionDependencies(), dependencyUsage, requestingClass));
ronshapiroe05f9212017-08-08 11:22:11 -0700148 return callInjectionMethod(
bcorso472e2682018-04-09 14:51:34 -0700149 create(binding, compilerOptions).name,
ronshapirodc07ed52017-08-23 08:52:10 -0700150 arguments.build(),
ronshapiroe05f9212017-08-08 11:22:11 -0700151 generatedClassNameForBinding(binding),
152 requestingClass);
153 }
154
155 private static MethodSpec constructorProxy(ExecutableElement constructor) {
156 UniqueNameSet names = new UniqueNameSet();
157 TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
158 MethodSpec.Builder method =
159 methodBuilder(methodName(constructor))
160 .returns(TypeName.get(enclosingType.asType()))
161 .addModifiers(PUBLIC, STATIC);
162
163 copyTypeParameters(enclosingType, method);
164 copyThrows(constructor, method);
165
166 return method
167 .addStatement(
168 "return new $T($L)", enclosingType, copyParameters(constructor, method, names))
169 .build();
170 }
171
172 /**
173 * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage}
174 * requires the use of an injection method.
175 */
ronshapiro5f059592017-12-07 07:18:33 -0800176 static boolean requiresInjectionMethod(
177 ProvisionBinding binding, CompilerOptions compilerOptions, String callingPackage) {
ronshapiroe05f9212017-08-08 11:22:11 -0700178 ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
179 return !binding.injectionSites().isEmpty()
ronshapiro5f059592017-12-07 07:18:33 -0800180 || binding.shouldCheckForNull(compilerOptions)
ronshapiroe05f9212017-08-08 11:22:11 -0700181 || !isElementAccessibleFrom(method, callingPackage)
182 || method
ronshapiro5f059592017-12-07 07:18:33 -0800183 .getParameters()
184 .stream()
185 .map(VariableElement::asType)
186 .anyMatch(type -> !isRawTypeAccessible(type, callingPackage));
ronshapiroe05f9212017-08-08 11:22:11 -0700187 }
188
ronshapiroe05f9212017-08-08 11:22:11 -0700189 /**
190 * Returns the name of the {@code static} method that wraps {@code method}. For methods that are
191 * associated with {@code @Inject} constructors, the method will also inject all {@link
192 * InjectionSite}s.
193 */
194 private static String methodName(ExecutableElement method) {
195 switch (method.getKind()) {
196 case CONSTRUCTOR:
197 return "new" + method.getEnclosingElement().getSimpleName();
198 case METHOD:
199 return "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
200 default:
201 throw new AssertionError(method);
202 }
203 }
204 }
205
206 /**
207 * A static method that injects one member of an instance of a type. Its first parameter is an
208 * instance of the type to be injected. The remaining parameters match the dependency requests for
209 * the injection site.
210 *
211 * <p>Example:
212 *
213 * <pre><code>
214 * class Foo {
215 * {@literal @Inject} Bar bar;
216 * {@literal @Inject} void setThings(Baz baz, Qux qux) {}
217 * }
218 *
219 * public static injectBar(Foo instance, Bar bar) { … }
220 * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … }
221 * </code></pre>
222 */
223 static final class InjectionSiteMethod {
224 /**
225 * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent
226 * that's in a different package), a method in the supertype's package must be generated to give
227 * the subclass's members injector a way to inject it. Each potentially inaccessible member
228 * receives its own method, as the subclass may need to inject them in a different order from
229 * the parent class.
230 */
231 static MethodSpec create(InjectionSite injectionSite) {
232 String methodName = methodName(injectionSite);
233 switch (injectionSite.kind()) {
234 case METHOD:
235 return methodProxy(
236 MoreElements.asExecutable(injectionSite.element()),
237 methodName,
ronshapiro5f059592017-12-07 07:18:33 -0800238 ReceiverAccessibility.CAST_IF_NOT_PUBLIC,
239 CheckNotNullPolicy.IGNORE);
ronshapiroe05f9212017-08-08 11:22:11 -0700240 case FIELD:
241 return fieldProxy(MoreElements.asVariable(injectionSite.element()), methodName);
242 default:
243 throw new AssertionError(injectionSite);
244 }
245 }
246
247 /**
248 * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
249 * transformed using the {@code dependencyUsage} function.
ronshapiro55cd3922017-08-10 09:48:08 -0700250 *
251 * @param instanceType the type of the {@code instance} parameter
ronshapiroe05f9212017-08-08 11:22:11 -0700252 */
253 static CodeBlock invokeAll(
254 ImmutableSet<InjectionSite> injectionSites,
255 ClassName generatedTypeName,
256 CodeBlock instanceCodeBlock,
ronshapiro55cd3922017-08-10 09:48:08 -0700257 TypeMirror instanceType,
258 Types types,
ronshapiroe05f9212017-08-08 11:22:11 -0700259 Function<DependencyRequest, CodeBlock> dependencyUsage) {
260 return injectionSites
261 .stream()
262 .map(
ronshapiro55cd3922017-08-10 09:48:08 -0700263 injectionSite -> {
264 TypeMirror injectSiteType =
265 types.erasure(injectionSite.element().getEnclosingElement().asType());
266
267 // If instance has been declared as Object because it is not accessible from the
268 // component, but the injectionSite is in a supertype of instanceType that is
269 // publicly accessible, the InjectionSiteMethod will request the actual type and not
270 // Object as the first parameter. If so, cast to the supertype which is accessible
271 // from within generatedTypeName
272 CodeBlock maybeCastedInstance =
273 !types.isSubtype(instanceType, injectSiteType)
274 && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName())
275 ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock)
276 : instanceCodeBlock;
277 return CodeBlock.of(
278 "$L;",
279 invoke(injectionSite, generatedTypeName, maybeCastedInstance, dependencyUsage));
280 })
ronshapiroe05f9212017-08-08 11:22:11 -0700281 .collect(toConcatenatedCodeBlock());
282 }
283
284 /**
285 * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
286 * using the {@code dependencyUsage} function.
287 */
288 private static CodeBlock invoke(
289 InjectionSite injectionSite,
290 ClassName generatedTypeName,
291 CodeBlock instanceCodeBlock,
292 Function<DependencyRequest, CodeBlock> dependencyUsage) {
293 List<CodeBlock> arguments = new ArrayList<>();
294 arguments.add(instanceCodeBlock);
295 if (!injectionSite.dependencies().isEmpty()) {
296 arguments.addAll(
297 injectionSite
298 .dependencies()
299 .stream()
300 .map(dependencyUsage)
301 .collect(toList()));
302 }
303 return callInjectionMethod(
304 create(injectionSite).name,
305 arguments,
306 membersInjectorNameForType(
307 MoreElements.asType(injectionSite.element().getEnclosingElement())),
308 generatedTypeName);
309 }
310
311 /*
312 * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
313 *
314 * - @Inject void members() {} will generate a method that conflicts with the instance
315 * method `injectMembers(T)`
316 * - Adding the index could conflict with another member:
317 * @Inject void a(Object o) {}
318 * @Inject void a(String s) {}
319 * @Inject void a1(String s) {}
320 *
321 * Here, Method a(String) will add the suffix "1", which will conflict with the method
322 * generated for a1(String)
323 * - Members named "members" or "methods" could also conflict with the {@code static} injection
324 * method.
325 */
326 private static String methodName(InjectionSite injectionSite) {
327 int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
328 String indexString = index == 0 ? "" : String.valueOf(index + 1);
329 return "inject"
330 + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
331 + indexString;
332 }
333 }
334
335 /**
336 * Returns an argument list suitable for calling an injection method. Down-casts any arguments
337 * that are {@code Object} (or {@code Provider<Object>}) at the caller but not the method.
338 *
339 * @param dependencies the dependencies used by the method
340 * @param dependencyUsage function to apply on each of {@code dependencies} before casting
341 * @param requestingClass the class calling the injection method
342 */
ronshapirodc07ed52017-08-23 08:52:10 -0700343 private static ImmutableList<CodeBlock> injectionMethodArguments(
ronshapiroe05f9212017-08-08 11:22:11 -0700344 ImmutableSet<DependencyRequest> dependencies,
345 Function<DependencyRequest, CodeBlock> dependencyUsage,
346 ClassName requestingClass) {
347 return dependencies.stream()
348 .map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass))
ronshapirodc07ed52017-08-23 08:52:10 -0700349 .collect(toImmutableList());
ronshapiroe05f9212017-08-08 11:22:11 -0700350 }
351
352 private static CodeBlock injectionMethodArgument(
353 DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) {
354 TypeMirror keyType = dependency.key().type();
355 CodeBlock.Builder codeBlock = CodeBlock.builder();
356 if (!isRawTypeAccessible(keyType, generatedTypeName.packageName())
357 && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) {
ronshapiro120abc62017-12-15 09:57:09 -0800358 if (!dependency.kind().equals(RequestKind.INSTANCE)) {
ronshapiroe05f9212017-08-08 11:22:11 -0700359 TypeName usageTypeName = accessibleType(dependency);
360 codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName));
361 } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) {
362 codeBlock.add("($T)", keyType);
363 }
364 }
365 return codeBlock.add(argument).build();
366 }
367
368 /**
369 * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns
370 * {@link Object}.
371 */
372 private static TypeName accessibleType(DependencyRequest dependency) {
ronshapiro120abc62017-12-15 09:57:09 -0800373 TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type()));
ronshapiroa5023ca2018-01-02 12:55:01 -0800374 return dependency
375 .requestElement()
376 .map(element -> element.asType().getKind().isPrimitive())
377 .orElse(false)
378 ? typeName.unbox()
379 : typeName;
ronshapiroe05f9212017-08-08 11:22:11 -0700380 }
381
382 /**
383 * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
384 * Object}.
385 */
386 private static TypeName accessibleType(TypeMirror type) {
387 return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT;
388 }
389
390 private static CodeBlock callInjectionMethod(
391 String methodName,
392 List<CodeBlock> arguments,
393 ClassName enclosingClass,
394 ClassName requestingClass) {
395 CodeBlock.Builder invocation = CodeBlock.builder();
396 if (!enclosingClass.equals(requestingClass)) {
397 invocation.add("$T.", enclosingClass);
398 }
399 return invocation.add("$L($L)", methodName, makeParametersCodeBlock(arguments)).build();
400 }
401
402 private enum ReceiverAccessibility {
403 CAST_IF_NOT_PUBLIC {
404 @Override
405 TypeName parameterType(TypeMirror type) {
406 return accessibleType(type);
407 }
408
409 @Override
410 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
411 return instanceWithPotentialCast(instance, instanceType);
412 }
413 },
414 IGNORE {
415 @Override
416 TypeName parameterType(TypeMirror type) {
417 return TypeName.get(type);
418 }
419
420 @Override
421 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
422 return instance;
423 }
424 },
425 ;
426
427 abstract TypeName parameterType(TypeMirror type);
428 abstract CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType);
429 }
430
431 private static CodeBlock instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType) {
432 return isRawTypePubliclyAccessible(instanceType)
433 ? instance
434 : CodeBlock.of("(($T) $L)", instanceType, instance);
435 }
436
ronshapiro5f059592017-12-07 07:18:33 -0800437 private enum CheckNotNullPolicy {
438 IGNORE, CHECK_FOR_NULL;
439 CodeBlock checkForNull(CodeBlock maybeNull) {
440 if (this.equals(IGNORE)) {
441 return maybeNull;
442 }
443 return checkNotNullProvidesMethod(maybeNull);
444 }
445
446 static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) {
447 return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE;
448 }
449 }
450
ronshapiroe05f9212017-08-08 11:22:11 -0700451 private static MethodSpec methodProxy(
ronshapiro5f059592017-12-07 07:18:33 -0800452 ExecutableElement method,
453 String methodName,
454 ReceiverAccessibility receiverAccessibility,
455 CheckNotNullPolicy checkNotNullPolicy) {
ronshapiroe05f9212017-08-08 11:22:11 -0700456 TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
457 MethodSpec.Builder methodBuilder = methodBuilder(methodName).addModifiers(PUBLIC, STATIC);
458
459 UniqueNameSet nameSet = new UniqueNameSet();
460 if (!method.getModifiers().contains(STATIC)) {
461 methodBuilder.addParameter(
462 receiverAccessibility.parameterType(enclosingType.asType()),
463 nameSet.getUniqueName("instance"));
464 }
465 CodeBlock arguments = copyParameters(method, methodBuilder, nameSet);
466 if (!method.getReturnType().getKind().equals(VOID)) {
467 methodBuilder.returns(TypeName.get(method.getReturnType()));
468 getNullableType(method)
469 .ifPresent(nullableType -> CodeBlocks.addAnnotation(methodBuilder, nullableType));
470 methodBuilder.addCode("return ");
471 }
ronshapiro5f059592017-12-07 07:18:33 -0800472 CodeBlock.Builder proxyInvocation = CodeBlock.builder();
ronshapiroe05f9212017-08-08 11:22:11 -0700473 if (method.getModifiers().contains(STATIC)) {
ronshapiro5f059592017-12-07 07:18:33 -0800474 proxyInvocation.add("$T", rawTypeName(TypeName.get(enclosingType.asType())));
ronshapiroe05f9212017-08-08 11:22:11 -0700475 } else {
476 copyTypeParameters(enclosingType, methodBuilder);
477 // "instance" is guaranteed b/c it was the first name into the UniqueNameSet
ronshapiro5f059592017-12-07 07:18:33 -0800478 proxyInvocation.add(
ronshapiroe05f9212017-08-08 11:22:11 -0700479 receiverAccessibility.potentiallyCast(CodeBlock.of("instance"), enclosingType.asType()));
480 }
481 copyTypeParameters(method, methodBuilder);
482 copyThrows(method, methodBuilder);
483
ronshapiro5f059592017-12-07 07:18:33 -0800484 proxyInvocation.add(".$N($L)", method.getSimpleName(), arguments);
485 methodBuilder.addCode(checkNotNullPolicy.checkForNull(proxyInvocation.build())).addCode(";");
ronshapiroe05f9212017-08-08 11:22:11 -0700486 return methodBuilder.build();
487 }
488
489 private static MethodSpec fieldProxy(VariableElement field, String methodName) {
490 TypeElement enclosingType = MoreElements.asType(field.getEnclosingElement());
491 MethodSpec.Builder methodBuilder = methodBuilder(methodName).addModifiers(PUBLIC, STATIC);
492 copyTypeParameters(enclosingType, methodBuilder);
493
494 UniqueNameSet nameSet = new UniqueNameSet();
495 String instanceName = nameSet.getUniqueName("instance");
496 methodBuilder.addParameter(accessibleType(enclosingType.asType()), instanceName);
497 return methodBuilder
498 .addCode(
499 "$L.$L = $L;",
500 instanceWithPotentialCast(CodeBlock.of(instanceName), enclosingType.asType()),
501 field.getSimpleName(),
502 copyParameter(field, methodBuilder, nameSet))
503 .build();
504 }
505
506 private static void copyThrows(ExecutableElement method, MethodSpec.Builder methodBuilder) {
507 for (TypeMirror thrownType : method.getThrownTypes()) {
508 methodBuilder.addException(TypeName.get(thrownType));
509 }
510 }
511
512 private static CodeBlock copyParameters(
513 ExecutableElement method, MethodSpec.Builder methodBuilder, UniqueNameSet nameSet) {
514 ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder();
515 for (VariableElement parameter : method.getParameters()) {
516 argumentsBuilder.add(copyParameter(parameter, methodBuilder, nameSet));
517 }
518 methodBuilder.varargs(method.isVarArgs());
519 return makeParametersCodeBlock(argumentsBuilder.build());
520 }
521
522 private static CodeBlock copyParameter(
523 VariableElement element, MethodSpec.Builder methodBuilder, UniqueNameSet nameSet) {
524 TypeMirror elementType = element.asType();
525 boolean useObject = !isRawTypePubliclyAccessible(elementType);
526 TypeName typeName = useObject ? TypeName.OBJECT : TypeName.get(elementType);
527 String name = nameSet.getUniqueName(element.getSimpleName().toString());
528 ParameterSpec parameter =
529 ParameterSpec.builder(typeName, name).build();
530 methodBuilder.addParameter(parameter);
531 return useObject
532 ? CodeBlock.of("($T) $N", elementType, parameter)
533 : CodeBlock.of("$N", parameter);
534 }
535
536 private static void copyTypeParameters(
537 Parameterizable parameterizable, MethodSpec.Builder methodBuilder) {
538 for (TypeParameterElement typeParameterElement : parameterizable.getTypeParameters()) {
539 methodBuilder.addTypeVariable(TypeVariableName.get(typeParameterElement));
540 }
541 }
542}