blob: a4190247d8a1cce9f546788244b31c0802465946 [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;
ronshapiro1ae4c002018-11-16 08:23:34 -080021import static com.google.common.base.Preconditions.checkArgument;
ronshapiroe05f9212017-08-08 11:22:11 -070022import static com.squareup.javapoet.MethodSpec.methodBuilder;
23import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
24import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
25import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
26import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
27import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
28import static dagger.internal.codegen.CodeBlocks.toConcatenatedCodeBlock;
ronshapiroe05f9212017-08-08 11:22:11 -070029import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
dpb639bd4d2017-10-24 15:15:17 -070030import static dagger.internal.codegen.DaggerStreams.toImmutableList;
ronshapiro5f059592017-12-07 07:18:33 -080031import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
ronshapiro120abc62017-12-15 09:57:09 -080032import static dagger.internal.codegen.RequestKinds.requestTypeName;
ronshapiroe05f9212017-08-08 11:22:11 -070033import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
34import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
35import static dagger.internal.codegen.TypeNames.rawTypeName;
36import static java.util.stream.Collectors.toList;
37import static javax.lang.model.element.Modifier.PUBLIC;
38import static javax.lang.model.element.Modifier.STATIC;
39import static javax.lang.model.type.TypeKind.VOID;
40
41import com.google.auto.common.MoreElements;
42import com.google.common.collect.ImmutableList;
43import com.google.common.collect.ImmutableSet;
44import com.squareup.javapoet.ClassName;
45import com.squareup.javapoet.CodeBlock;
46import com.squareup.javapoet.MethodSpec;
47import com.squareup.javapoet.ParameterSpec;
48import com.squareup.javapoet.TypeName;
49import com.squareup.javapoet.TypeVariableName;
ronshapiroe05f9212017-08-08 11:22:11 -070050import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
ronshapiroa5023ca2018-01-02 12:55:01 -080051import dagger.model.DependencyRequest;
ronshapiro120abc62017-12-15 09:57:09 -080052import dagger.model.RequestKind;
ronshapiroe05f9212017-08-08 11:22:11 -070053import java.util.ArrayList;
54import java.util.List;
55import java.util.Optional;
56import java.util.function.Function;
57import javax.lang.model.element.ExecutableElement;
58import javax.lang.model.element.Parameterizable;
59import javax.lang.model.element.TypeElement;
60import javax.lang.model.element.TypeParameterElement;
61import javax.lang.model.element.VariableElement;
62import javax.lang.model.type.TypeKind;
63import javax.lang.model.type.TypeMirror;
ronshapiro55cd3922017-08-10 09:48:08 -070064import javax.lang.model.util.Types;
ronshapiroe05f9212017-08-08 11:22:11 -070065
66/**
67 * Injection methods are static methods that implement provision and/or injection in one step:
68 *
69 * <ul>
70 * <li>methods that invoke {@code @Inject} constructors and do members injection if necessary
71 * <li>methods that call {@code @Provides} module methods
72 * <li>methods that perform members injection
73 * </ul>
74 */
75// TODO(ronshapiro): add examples for each class of injection method
76final class InjectionMethods {
77 /**
78 * A static method that returns an object from a {@code @Provides} method or an {@code @Inject}ed
79 * constructor. Its parameters match the dependency requests for constructor and members
80 * injection.
81 *
82 * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". If the
83 * {@code @Provides} method and its raw parameter types are publicly accessible, no method is
84 * necessary and this method returns {@link Optional#empty()}.
85 *
86 * <p>TODO(ronshapiro): At the moment non-static {@code @Provides} methods are not supported.
87 *
88 * <p>Example:
89 *
90 * <pre><code>
91 * abstract class FooModule {
92 * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
93 * }
94 *
95 * public static proxyProvideFoo(Bar bar, Baz baz) { … }
96 * </code></pre>
97 *
98 * <p>For {@code @Inject}ed constructors, the method name is "newFoo". If the constructor and its
99 * raw parameter types are publicly accessible, no method is necessary and this method returns
100 * {@code Optional#empty()}.
101 *
102 * <p>Example:
103 *
104 * <pre><code>
105 * class Foo {
106 * {@literal @Inject} Foo(Bar bar) {}
107 * }
108 *
109 * public static Foo newFoo(Bar bar) { … }
110 * </code></pre>
111 */
112 static final class ProvisionMethod {
113
114 /**
115 * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement()
bcorso472e2682018-04-09 14:51:34 -0700116 * constructor} and injects the instance's members.
ronshapiroe05f9212017-08-08 11:22:11 -0700117 */
bcorso472e2682018-04-09 14:51:34 -0700118 static MethodSpec create(ProvisionBinding binding, CompilerOptions compilerOptions) {
ronshapiroe05f9212017-08-08 11:22:11 -0700119 ExecutableElement element = MoreElements.asExecutable(binding.bindingElement().get());
120 switch (element.getKind()) {
121 case CONSTRUCTOR:
bcorso472e2682018-04-09 14:51:34 -0700122 return constructorProxy(element);
ronshapiroe05f9212017-08-08 11:22:11 -0700123 case METHOD:
bcorso472e2682018-04-09 14:51:34 -0700124 return methodProxy(
125 element,
126 methodName(element),
127 ReceiverAccessibility.IGNORE,
128 CheckNotNullPolicy.get(binding, compilerOptions));
ronshapiroe05f9212017-08-08 11:22:11 -0700129 default:
130 throw new AssertionError(element);
131 }
132 }
133
134 /**
135 * Invokes the injection method for {@code binding}, with the dependencies transformed with the
136 * {@code dependencyUsage} function.
137 */
138 static CodeBlock invoke(
139 ProvisionBinding binding,
140 Function<DependencyRequest, CodeBlock> dependencyUsage,
ronshapirodc07ed52017-08-23 08:52:10 -0700141 ClassName requestingClass,
ronshapiro5f059592017-12-07 07:18:33 -0800142 Optional<CodeBlock> moduleReference,
143 CompilerOptions compilerOptions) {
ronshapirodc07ed52017-08-23 08:52:10 -0700144 ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
145 moduleReference.ifPresent(arguments::add);
146 arguments.addAll(
147 injectionMethodArguments(
148 binding.provisionDependencies(), dependencyUsage, requestingClass));
ronshapiroe05f9212017-08-08 11:22:11 -0700149 return callInjectionMethod(
bcorso472e2682018-04-09 14:51:34 -0700150 create(binding, compilerOptions).name,
ronshapirodc07ed52017-08-23 08:52:10 -0700151 arguments.build(),
ronshapiroe05f9212017-08-08 11:22:11 -0700152 generatedClassNameForBinding(binding),
153 requestingClass);
154 }
155
156 private static MethodSpec constructorProxy(ExecutableElement constructor) {
157 UniqueNameSet names = new UniqueNameSet();
158 TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
159 MethodSpec.Builder method =
160 methodBuilder(methodName(constructor))
161 .returns(TypeName.get(enclosingType.asType()))
162 .addModifiers(PUBLIC, STATIC);
163
164 copyTypeParameters(enclosingType, method);
165 copyThrows(constructor, method);
166
167 return method
168 .addStatement(
169 "return new $T($L)", enclosingType, copyParameters(constructor, method, names))
170 .build();
171 }
172
173 /**
174 * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage}
175 * requires the use of an injection method.
176 */
ronshapiro5f059592017-12-07 07:18:33 -0800177 static boolean requiresInjectionMethod(
ronshapiro1ae4c002018-11-16 08:23:34 -0800178 ProvisionBinding binding,
179 ImmutableList<Expression> arguments,
180 CompilerOptions compilerOptions,
181 String callingPackage,
182 DaggerTypes types) {
ronshapiroe05f9212017-08-08 11:22:11 -0700183 ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
184 return !binding.injectionSites().isEmpty()
ronshapiro5f059592017-12-07 07:18:33 -0800185 || binding.shouldCheckForNull(compilerOptions)
ronshapiroe05f9212017-08-08 11:22:11 -0700186 || !isElementAccessibleFrom(method, callingPackage)
ronshapiro1ae4c002018-11-16 08:23:34 -0800187 || !areParametersAssignable(method, arguments, types)
188 // This check should be removable once we drop support for -source 7
189 || method.getParameters().stream()
190 .map(VariableElement::asType)
191 .anyMatch(type -> !isRawTypeAccessible(type, callingPackage));
192 }
193
194 private static boolean areParametersAssignable(
195 ExecutableElement element, ImmutableList<Expression> arguments, DaggerTypes types) {
196 List<? extends VariableElement> parameters = element.getParameters();
197 checkArgument(parameters.size() == arguments.size());
198 for (int i = 0; i < parameters.size(); i++) {
199 if (!types.isAssignable(arguments.get(i).type(), parameters.get(i).asType())) {
200 return false;
201 }
202 }
203 return true;
ronshapiroe05f9212017-08-08 11:22:11 -0700204 }
205
ronshapiroe05f9212017-08-08 11:22:11 -0700206 /**
207 * Returns the name of the {@code static} method that wraps {@code method}. For methods that are
208 * associated with {@code @Inject} constructors, the method will also inject all {@link
209 * InjectionSite}s.
210 */
211 private static String methodName(ExecutableElement method) {
212 switch (method.getKind()) {
213 case CONSTRUCTOR:
214 return "new" + method.getEnclosingElement().getSimpleName();
215 case METHOD:
216 return "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
217 default:
218 throw new AssertionError(method);
219 }
220 }
221 }
222
223 /**
224 * A static method that injects one member of an instance of a type. Its first parameter is an
225 * instance of the type to be injected. The remaining parameters match the dependency requests for
226 * the injection site.
227 *
228 * <p>Example:
229 *
230 * <pre><code>
231 * class Foo {
232 * {@literal @Inject} Bar bar;
233 * {@literal @Inject} void setThings(Baz baz, Qux qux) {}
234 * }
235 *
236 * public static injectBar(Foo instance, Bar bar) { … }
237 * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … }
238 * </code></pre>
239 */
240 static final class InjectionSiteMethod {
241 /**
242 * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent
243 * that's in a different package), a method in the supertype's package must be generated to give
244 * the subclass's members injector a way to inject it. Each potentially inaccessible member
245 * receives its own method, as the subclass may need to inject them in a different order from
246 * the parent class.
247 */
248 static MethodSpec create(InjectionSite injectionSite) {
249 String methodName = methodName(injectionSite);
250 switch (injectionSite.kind()) {
251 case METHOD:
252 return methodProxy(
253 MoreElements.asExecutable(injectionSite.element()),
254 methodName,
ronshapiro5f059592017-12-07 07:18:33 -0800255 ReceiverAccessibility.CAST_IF_NOT_PUBLIC,
256 CheckNotNullPolicy.IGNORE);
ronshapiroe05f9212017-08-08 11:22:11 -0700257 case FIELD:
258 return fieldProxy(MoreElements.asVariable(injectionSite.element()), methodName);
259 default:
260 throw new AssertionError(injectionSite);
261 }
262 }
263
264 /**
265 * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
266 * transformed using the {@code dependencyUsage} function.
ronshapiro55cd3922017-08-10 09:48:08 -0700267 *
268 * @param instanceType the type of the {@code instance} parameter
ronshapiroe05f9212017-08-08 11:22:11 -0700269 */
270 static CodeBlock invokeAll(
271 ImmutableSet<InjectionSite> injectionSites,
272 ClassName generatedTypeName,
273 CodeBlock instanceCodeBlock,
ronshapiro55cd3922017-08-10 09:48:08 -0700274 TypeMirror instanceType,
275 Types types,
ronshapiroe05f9212017-08-08 11:22:11 -0700276 Function<DependencyRequest, CodeBlock> dependencyUsage) {
277 return injectionSites
278 .stream()
279 .map(
ronshapiro55cd3922017-08-10 09:48:08 -0700280 injectionSite -> {
281 TypeMirror injectSiteType =
282 types.erasure(injectionSite.element().getEnclosingElement().asType());
283
284 // If instance has been declared as Object because it is not accessible from the
285 // component, but the injectionSite is in a supertype of instanceType that is
286 // publicly accessible, the InjectionSiteMethod will request the actual type and not
287 // Object as the first parameter. If so, cast to the supertype which is accessible
288 // from within generatedTypeName
289 CodeBlock maybeCastedInstance =
290 !types.isSubtype(instanceType, injectSiteType)
291 && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName())
292 ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock)
293 : instanceCodeBlock;
294 return CodeBlock.of(
295 "$L;",
296 invoke(injectionSite, generatedTypeName, maybeCastedInstance, dependencyUsage));
297 })
ronshapiroe05f9212017-08-08 11:22:11 -0700298 .collect(toConcatenatedCodeBlock());
299 }
300
301 /**
302 * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
303 * using the {@code dependencyUsage} function.
304 */
305 private static CodeBlock invoke(
306 InjectionSite injectionSite,
307 ClassName generatedTypeName,
308 CodeBlock instanceCodeBlock,
309 Function<DependencyRequest, CodeBlock> dependencyUsage) {
310 List<CodeBlock> arguments = new ArrayList<>();
311 arguments.add(instanceCodeBlock);
312 if (!injectionSite.dependencies().isEmpty()) {
313 arguments.addAll(
314 injectionSite
315 .dependencies()
316 .stream()
317 .map(dependencyUsage)
318 .collect(toList()));
319 }
320 return callInjectionMethod(
321 create(injectionSite).name,
322 arguments,
323 membersInjectorNameForType(
324 MoreElements.asType(injectionSite.element().getEnclosingElement())),
325 generatedTypeName);
326 }
327
328 /*
329 * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
330 *
331 * - @Inject void members() {} will generate a method that conflicts with the instance
332 * method `injectMembers(T)`
333 * - Adding the index could conflict with another member:
334 * @Inject void a(Object o) {}
335 * @Inject void a(String s) {}
336 * @Inject void a1(String s) {}
337 *
338 * Here, Method a(String) will add the suffix "1", which will conflict with the method
339 * generated for a1(String)
340 * - Members named "members" or "methods" could also conflict with the {@code static} injection
341 * method.
342 */
343 private static String methodName(InjectionSite injectionSite) {
344 int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
345 String indexString = index == 0 ? "" : String.valueOf(index + 1);
346 return "inject"
347 + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
348 + indexString;
349 }
350 }
351
352 /**
353 * Returns an argument list suitable for calling an injection method. Down-casts any arguments
354 * that are {@code Object} (or {@code Provider<Object>}) at the caller but not the method.
355 *
356 * @param dependencies the dependencies used by the method
357 * @param dependencyUsage function to apply on each of {@code dependencies} before casting
358 * @param requestingClass the class calling the injection method
359 */
ronshapirodc07ed52017-08-23 08:52:10 -0700360 private static ImmutableList<CodeBlock> injectionMethodArguments(
ronshapiroe05f9212017-08-08 11:22:11 -0700361 ImmutableSet<DependencyRequest> dependencies,
362 Function<DependencyRequest, CodeBlock> dependencyUsage,
363 ClassName requestingClass) {
364 return dependencies.stream()
365 .map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass))
ronshapirodc07ed52017-08-23 08:52:10 -0700366 .collect(toImmutableList());
ronshapiroe05f9212017-08-08 11:22:11 -0700367 }
368
369 private static CodeBlock injectionMethodArgument(
370 DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) {
371 TypeMirror keyType = dependency.key().type();
372 CodeBlock.Builder codeBlock = CodeBlock.builder();
373 if (!isRawTypeAccessible(keyType, generatedTypeName.packageName())
374 && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) {
ronshapiro120abc62017-12-15 09:57:09 -0800375 if (!dependency.kind().equals(RequestKind.INSTANCE)) {
ronshapiroe05f9212017-08-08 11:22:11 -0700376 TypeName usageTypeName = accessibleType(dependency);
377 codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName));
378 } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) {
379 codeBlock.add("($T)", keyType);
380 }
381 }
382 return codeBlock.add(argument).build();
383 }
384
385 /**
386 * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns
387 * {@link Object}.
388 */
389 private static TypeName accessibleType(DependencyRequest dependency) {
ronshapiro120abc62017-12-15 09:57:09 -0800390 TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type()));
ronshapiroa5023ca2018-01-02 12:55:01 -0800391 return dependency
392 .requestElement()
393 .map(element -> element.asType().getKind().isPrimitive())
394 .orElse(false)
395 ? typeName.unbox()
396 : typeName;
ronshapiroe05f9212017-08-08 11:22:11 -0700397 }
398
399 /**
400 * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
401 * Object}.
402 */
403 private static TypeName accessibleType(TypeMirror type) {
404 return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT;
405 }
406
407 private static CodeBlock callInjectionMethod(
408 String methodName,
409 List<CodeBlock> arguments,
410 ClassName enclosingClass,
411 ClassName requestingClass) {
412 CodeBlock.Builder invocation = CodeBlock.builder();
413 if (!enclosingClass.equals(requestingClass)) {
414 invocation.add("$T.", enclosingClass);
415 }
416 return invocation.add("$L($L)", methodName, makeParametersCodeBlock(arguments)).build();
417 }
418
419 private enum ReceiverAccessibility {
420 CAST_IF_NOT_PUBLIC {
421 @Override
422 TypeName parameterType(TypeMirror type) {
423 return accessibleType(type);
424 }
425
426 @Override
427 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
428 return instanceWithPotentialCast(instance, instanceType);
429 }
430 },
431 IGNORE {
432 @Override
433 TypeName parameterType(TypeMirror type) {
434 return TypeName.get(type);
435 }
436
437 @Override
438 CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
439 return instance;
440 }
441 },
442 ;
443
444 abstract TypeName parameterType(TypeMirror type);
445 abstract CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType);
446 }
447
448 private static CodeBlock instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType) {
449 return isRawTypePubliclyAccessible(instanceType)
450 ? instance
451 : CodeBlock.of("(($T) $L)", instanceType, instance);
452 }
453
ronshapiro5f059592017-12-07 07:18:33 -0800454 private enum CheckNotNullPolicy {
455 IGNORE, CHECK_FOR_NULL;
456 CodeBlock checkForNull(CodeBlock maybeNull) {
457 if (this.equals(IGNORE)) {
458 return maybeNull;
459 }
460 return checkNotNullProvidesMethod(maybeNull);
461 }
462
463 static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) {
464 return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE;
465 }
466 }
467
ronshapiroe05f9212017-08-08 11:22:11 -0700468 private static MethodSpec methodProxy(
ronshapiro5f059592017-12-07 07:18:33 -0800469 ExecutableElement method,
470 String methodName,
471 ReceiverAccessibility receiverAccessibility,
472 CheckNotNullPolicy checkNotNullPolicy) {
ronshapiroe05f9212017-08-08 11:22:11 -0700473 TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
474 MethodSpec.Builder methodBuilder = methodBuilder(methodName).addModifiers(PUBLIC, STATIC);
475
476 UniqueNameSet nameSet = new UniqueNameSet();
477 if (!method.getModifiers().contains(STATIC)) {
478 methodBuilder.addParameter(
479 receiverAccessibility.parameterType(enclosingType.asType()),
480 nameSet.getUniqueName("instance"));
481 }
482 CodeBlock arguments = copyParameters(method, methodBuilder, nameSet);
483 if (!method.getReturnType().getKind().equals(VOID)) {
484 methodBuilder.returns(TypeName.get(method.getReturnType()));
485 getNullableType(method)
486 .ifPresent(nullableType -> CodeBlocks.addAnnotation(methodBuilder, nullableType));
487 methodBuilder.addCode("return ");
488 }
ronshapiro5f059592017-12-07 07:18:33 -0800489 CodeBlock.Builder proxyInvocation = CodeBlock.builder();
ronshapiroe05f9212017-08-08 11:22:11 -0700490 if (method.getModifiers().contains(STATIC)) {
ronshapiro5f059592017-12-07 07:18:33 -0800491 proxyInvocation.add("$T", rawTypeName(TypeName.get(enclosingType.asType())));
ronshapiroe05f9212017-08-08 11:22:11 -0700492 } else {
493 copyTypeParameters(enclosingType, methodBuilder);
494 // "instance" is guaranteed b/c it was the first name into the UniqueNameSet
ronshapiro5f059592017-12-07 07:18:33 -0800495 proxyInvocation.add(
ronshapiroe05f9212017-08-08 11:22:11 -0700496 receiverAccessibility.potentiallyCast(CodeBlock.of("instance"), enclosingType.asType()));
497 }
498 copyTypeParameters(method, methodBuilder);
499 copyThrows(method, methodBuilder);
500
ronshapiro5f059592017-12-07 07:18:33 -0800501 proxyInvocation.add(".$N($L)", method.getSimpleName(), arguments);
502 methodBuilder.addCode(checkNotNullPolicy.checkForNull(proxyInvocation.build())).addCode(";");
ronshapiroe05f9212017-08-08 11:22:11 -0700503 return methodBuilder.build();
504 }
505
506 private static MethodSpec fieldProxy(VariableElement field, String methodName) {
507 TypeElement enclosingType = MoreElements.asType(field.getEnclosingElement());
508 MethodSpec.Builder methodBuilder = methodBuilder(methodName).addModifiers(PUBLIC, STATIC);
509 copyTypeParameters(enclosingType, methodBuilder);
510
511 UniqueNameSet nameSet = new UniqueNameSet();
512 String instanceName = nameSet.getUniqueName("instance");
513 methodBuilder.addParameter(accessibleType(enclosingType.asType()), instanceName);
514 return methodBuilder
515 .addCode(
516 "$L.$L = $L;",
517 instanceWithPotentialCast(CodeBlock.of(instanceName), enclosingType.asType()),
518 field.getSimpleName(),
519 copyParameter(field, methodBuilder, nameSet))
520 .build();
521 }
522
523 private static void copyThrows(ExecutableElement method, MethodSpec.Builder methodBuilder) {
524 for (TypeMirror thrownType : method.getThrownTypes()) {
525 methodBuilder.addException(TypeName.get(thrownType));
526 }
527 }
528
529 private static CodeBlock copyParameters(
530 ExecutableElement method, MethodSpec.Builder methodBuilder, UniqueNameSet nameSet) {
531 ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder();
532 for (VariableElement parameter : method.getParameters()) {
533 argumentsBuilder.add(copyParameter(parameter, methodBuilder, nameSet));
534 }
535 methodBuilder.varargs(method.isVarArgs());
536 return makeParametersCodeBlock(argumentsBuilder.build());
537 }
538
539 private static CodeBlock copyParameter(
540 VariableElement element, MethodSpec.Builder methodBuilder, UniqueNameSet nameSet) {
541 TypeMirror elementType = element.asType();
542 boolean useObject = !isRawTypePubliclyAccessible(elementType);
543 TypeName typeName = useObject ? TypeName.OBJECT : TypeName.get(elementType);
544 String name = nameSet.getUniqueName(element.getSimpleName().toString());
545 ParameterSpec parameter =
546 ParameterSpec.builder(typeName, name).build();
547 methodBuilder.addParameter(parameter);
548 return useObject
549 ? CodeBlock.of("($T) $N", elementType, parameter)
550 : CodeBlock.of("$N", parameter);
551 }
552
553 private static void copyTypeParameters(
554 Parameterizable parameterizable, MethodSpec.Builder methodBuilder) {
555 for (TypeParameterElement typeParameterElement : parameterizable.getTypeParameters()) {
556 methodBuilder.addTypeVariable(TypeVariableName.get(typeParameterElement));
557 }
558 }
559}