• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
20 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
21 import static com.google.common.base.Verify.verify;
22 import static com.google.common.collect.Iterables.getOnlyElement;
23 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
24 import static com.squareup.javapoet.MethodSpec.methodBuilder;
25 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
26 import static com.squareup.javapoet.TypeSpec.classBuilder;
27 import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.ABSENT_OPTIONAL_FIELD;
28 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.ABSENT_OPTIONAL_METHOD;
29 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.PRESENT_FACTORY;
30 import static dagger.internal.codegen.RequestKinds.requestTypeName;
31 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
32 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
33 import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
34 import static dagger.internal.codegen.javapoet.TypeNames.abstractProducerOf;
35 import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
36 import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
37 import static javax.lang.model.element.Modifier.FINAL;
38 import static javax.lang.model.element.Modifier.PRIVATE;
39 import static javax.lang.model.element.Modifier.PUBLIC;
40 import static javax.lang.model.element.Modifier.STATIC;
41 
42 import com.google.auto.value.AutoValue;
43 import com.google.common.base.Function;
44 import com.google.common.util.concurrent.Futures;
45 import com.google.common.util.concurrent.ListenableFuture;
46 import com.google.common.util.concurrent.MoreExecutors;
47 import com.squareup.javapoet.ClassName;
48 import com.squareup.javapoet.CodeBlock;
49 import com.squareup.javapoet.FieldSpec;
50 import com.squareup.javapoet.MethodSpec;
51 import com.squareup.javapoet.ParameterSpec;
52 import com.squareup.javapoet.ParameterizedTypeName;
53 import com.squareup.javapoet.TypeName;
54 import com.squareup.javapoet.TypeSpec;
55 import com.squareup.javapoet.TypeVariableName;
56 import dagger.internal.InstanceFactory;
57 import dagger.internal.Preconditions;
58 import dagger.internal.codegen.OptionalType.OptionalKind;
59 import dagger.internal.codegen.javapoet.AnnotationSpecs;
60 import dagger.model.RequestKind;
61 import dagger.producers.Producer;
62 import dagger.producers.internal.Producers;
63 import java.util.Comparator;
64 import java.util.Map;
65 import java.util.Optional;
66 import java.util.TreeMap;
67 import java.util.concurrent.Executor;
68 import javax.inject.Inject;
69 import javax.inject.Provider;
70 
71 /** The nested class and static methods required by the component to implement optional bindings. */
72 // TODO(dpb): Name members simply if a component uses only one of Guava or JDK Optional.
73 @PerGeneratedFile
74 final class OptionalFactories {
75   private final ComponentImplementation componentImplementation;
76 
OptionalFactories(@opLevel ComponentImplementation componentImplementation)77   @Inject OptionalFactories(@TopLevel ComponentImplementation componentImplementation) {
78     this.componentImplementation = componentImplementation;
79   }
80 
81   /**
82    * The factory classes that implement {@code Provider<Optional<T>>} or {@code
83    * Producer<Optional<T>>} for present optional bindings for a given kind of dependency request
84    * within the component.
85    *
86    * <p>The key is the {@code Provider<Optional<T>>} type.
87    */
88   private final Map<PresentFactorySpec, TypeSpec> presentFactoryClasses =
89       new TreeMap<>(
90           Comparator.comparing(PresentFactorySpec::valueKind)
91               .thenComparing(PresentFactorySpec::frameworkType)
92               .thenComparing(PresentFactorySpec::optionalKind));
93 
94   /**
95    * The static methods that return a {@code Provider<Optional<T>>} that always returns an absent
96    * value.
97    */
98   private final Map<OptionalKind, MethodSpec> absentOptionalProviderMethods = new TreeMap<>();
99 
100   /**
101    * The static fields for {@code Provider<Optional<T>>} objects that always return an absent value.
102    */
103   private final Map<OptionalKind, FieldSpec> absentOptionalProviderFields = new TreeMap<>();
104 
105   /**
106    * Returns an expression that calls a static method that returns a {@code Provider<Optional<T>>}
107    * for absent optional bindings.
108    */
absentOptionalProvider(ContributionBinding binding)109   CodeBlock absentOptionalProvider(ContributionBinding binding) {
110     verify(
111         binding.bindingType().equals(BindingType.PROVISION),
112         "Absent optional bindings should be provisions: %s",
113         binding);
114     OptionalKind optionalKind = OptionalType.from(binding.key()).kind();
115     return CodeBlock.of(
116         "$N()",
117         absentOptionalProviderMethods.computeIfAbsent(
118             optionalKind,
119             kind -> {
120               MethodSpec method = absentOptionalProviderMethod(kind);
121               componentImplementation.addMethod(ABSENT_OPTIONAL_METHOD, method);
122               return method;
123             }));
124   }
125 
126   /**
127    * Creates a method specification for a {@code Provider<Optional<T>>} that always returns an
128    * absent value.
129    */
absentOptionalProviderMethod(OptionalKind optionalKind)130   private MethodSpec absentOptionalProviderMethod(OptionalKind optionalKind) {
131     TypeVariableName typeVariable = TypeVariableName.get("T");
132     return methodBuilder(
133             String.format(
134                 "absent%sProvider", UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind.name())))
135         .addModifiers(PRIVATE, STATIC)
136         .addTypeVariable(typeVariable)
137         .returns(providerOf(optionalKind.of(typeVariable)))
138         .addJavadoc(
139             "Returns a {@link $T} that returns {@code $L}.",
140             Provider.class,
141             optionalKind.absentValueExpression())
142         .addCode("$L // safe covariant cast\n", AnnotationSpecs.suppressWarnings(UNCHECKED))
143         .addCode(
144             "$1T provider = ($1T) $2N;",
145             providerOf(optionalKind.of(typeVariable)),
146             absentOptionalProviderFields.computeIfAbsent(
147                 optionalKind,
148                 kind -> {
149                   FieldSpec field = absentOptionalProviderField(kind);
150                   componentImplementation.addField(ABSENT_OPTIONAL_FIELD, field);
151                   return field;
152                 }))
153         .addCode("return provider;")
154         .build();
155   }
156 
157   /**
158    * Creates a field specification for a {@code Provider<Optional<T>>} that always returns an absent
159    * value.
160    */
161   private FieldSpec absentOptionalProviderField(OptionalKind optionalKind) {
162     return FieldSpec.builder(
163             PROVIDER,
164             String.format("ABSENT_%s_PROVIDER", optionalKind.name()),
165             PRIVATE,
166             STATIC,
167             FINAL)
168         .addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES))
169         .initializer("$T.create($L)", InstanceFactory.class, optionalKind.absentValueExpression())
170         .addJavadoc(
171             "A {@link $T} that returns {@code $L}.",
172             Provider.class,
173             optionalKind.absentValueExpression())
174         .build();
175   }
176 
177   /** Information about the type of a factory for present bindings. */
178   @AutoValue
179   abstract static class PresentFactorySpec {
180     /** Whether the factory is a {@link Provider} or a {@link Producer}. */
181     abstract FrameworkType frameworkType();
182 
183     /** What kind of {@code Optional} is returned. */
184     abstract OptionalKind optionalKind();
185 
186     /** The kind of request satisfied by the value of the {@code Optional}. */
187     abstract RequestKind valueKind();
188 
189     /** The type variable for the factory class. */
190     TypeVariableName typeVariable() {
191       return TypeVariableName.get("T");
192     }
193 
194     /** The type contained by the {@code Optional}. */
195     TypeName valueType() {
196       return requestTypeName(valueKind(), typeVariable());
197     }
198 
199     /** The type provided or produced by the factory. */
200     ParameterizedTypeName optionalType() {
201       return optionalKind().of(valueType());
202     }
203 
204     /** The type of the factory. */
205     ParameterizedTypeName factoryType() {
206       return frameworkType().frameworkClassOf(optionalType());
207     }
208 
209     /** The type of the delegate provider or producer. */
210     ParameterizedTypeName delegateType() {
211       return frameworkType().frameworkClassOf(typeVariable());
212     }
213 
214     /** Returns the superclass the generated factory should have, if any. */
215     Optional<ParameterizedTypeName> superclass() {
216       switch (frameworkType()) {
217         case PRODUCER_NODE:
218           // TODO(cgdecker): This probably isn't a big issue for now, but it's possible this
219           // shouldn't be an AbstractProducer:
220           // - As AbstractProducer, it'll only call the delegate's get() method once and then cache
221           //   that result (essentially) rather than calling the delegate's get() method each time
222           //   its get() method is called (which was what it did before the cancellation change).
223           // - It's not 100% clear to me whether the view-creation methods should return a view of
224           //   the same view created by the delegate or if they should just return their own views.
225           return Optional.of(abstractProducerOf(optionalType()));
226         default:
227           return Optional.empty();
228       }
229     }
230 
231     /** Returns the superinterface the generated factory should have, if any. */
232     Optional<ParameterizedTypeName> superinterface() {
233       switch (frameworkType()) {
234         case PROVIDER:
235           return Optional.of(factoryType());
236         default:
237           return Optional.empty();
238       }
239     }
240 
241     /** Returns the name of the factory method to generate. */
242     String factoryMethodName() {
243       switch (frameworkType()) {
244         case PROVIDER:
245           return "get";
246         case PRODUCER_NODE:
247           return "compute";
248       }
249       throw new AssertionError(frameworkType());
250     }
251 
252     /** The name of the factory class. */
253     String factoryClassName() {
254       return new StringBuilder("Present")
255           .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind().name()))
256           .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, valueKind().toString()))
257           .append(frameworkType().frameworkClass().getSimpleName())
258           .toString();
259     }
260 
261     private static PresentFactorySpec of(ContributionBinding binding) {
262       return new AutoValue_OptionalFactories_PresentFactorySpec(
263           FrameworkType.forBindingType(binding.bindingType()),
264           OptionalType.from(binding.key()).kind(),
265           getOnlyElement(binding.dependencies()).kind());
266     }
267   }
268 
269   /**
270    * Returns an expression for an instance of a nested class that implements {@code
271    * Provider<Optional<T>>} or {@code Producer<Optional<T>>} for a present optional binding, where
272    * {@code T} represents dependency requests of that kind.
273    *
274    * <ul>
275    *   <li>If {@code optionalRequestKind} is {@link RequestKind#INSTANCE}, the class implements
276    *       {@code ProviderOrProducer<Optional<T>>}.
277    *   <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER}, the class implements
278    *       {@code Provider<Optional<Provider<T>>>}.
279    *   <li>If {@code optionalRequestKind} is {@link RequestKind#LAZY}, the class implements {@code
280    *       Provider<Optional<Lazy<T>>>}.
281    *   <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER_OF_LAZY}, the class
282    *       implements {@code Provider<Optional<Provider<Lazy<T>>>>}.
283    *   <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCER}, the class implements
284    *       {@code Producer<Optional<Producer<T>>>}.
285    *   <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCED}, the class implements
286    *       {@code Producer<Optional<Produced<T>>>}.
287    * </ul>
288    *
289    * @param delegateFactory an expression for a {@link Provider} or {@link Producer} of the
290    *     underlying type
291    */
292   CodeBlock presentOptionalFactory(ContributionBinding binding, CodeBlock delegateFactory) {
293     return CodeBlock.of(
294         "$N.of($L)",
295         presentFactoryClasses.computeIfAbsent(
296             PresentFactorySpec.of(binding),
297             spec -> {
298               TypeSpec type = presentOptionalFactoryClass(spec);
299               componentImplementation.addType(PRESENT_FACTORY, type);
300               return type;
301             }),
302         delegateFactory);
303   }
304 
305   private TypeSpec presentOptionalFactoryClass(PresentFactorySpec spec) {
306     FieldSpec delegateField =
307         FieldSpec.builder(spec.delegateType(), "delegate", PRIVATE, FINAL).build();
308     ParameterSpec delegateParameter = ParameterSpec.builder(delegateField.type, "delegate").build();
309     TypeSpec.Builder factoryClassBuilder =
310         classBuilder(spec.factoryClassName())
311             .addTypeVariable(spec.typeVariable())
312             .addModifiers(PRIVATE, STATIC, FINAL)
313             .addJavadoc(
314                 "A {@code $T} that uses a delegate {@code $T}.",
315                 spec.factoryType(),
316                 delegateField.type);
317 
318     spec.superclass().ifPresent(factoryClassBuilder::superclass);
319     spec.superinterface().ifPresent(factoryClassBuilder::addSuperinterface);
320 
321     return factoryClassBuilder
322         .addField(delegateField)
323         .addMethod(
324             constructorBuilder()
325                 .addModifiers(PRIVATE)
326                 .addParameter(delegateParameter)
327                 .addCode(
328                     "this.$N = $T.checkNotNull($N);",
329                     delegateField,
330                     Preconditions.class,
331                     delegateParameter)
332                 .build())
333         .addMethod(presentOptionalFactoryGetMethod(spec, delegateField))
334         .addMethod(
335             methodBuilder("of")
336                 .addModifiers(PRIVATE, STATIC)
337                 .addTypeVariable(spec.typeVariable())
338                 .returns(spec.factoryType())
339                 .addParameter(delegateParameter)
340                 .addCode(
341                     "return new $L<$T>($N);",
342                     spec.factoryClassName(),
343                     spec.typeVariable(),
344                     delegateParameter)
345                 .build())
346         .build();
347   }
348 
349   private MethodSpec presentOptionalFactoryGetMethod(
350       PresentFactorySpec spec, FieldSpec delegateField) {
351     MethodSpec.Builder getMethodBuilder =
352         methodBuilder(spec.factoryMethodName()).addAnnotation(Override.class).addModifiers(PUBLIC);
353 
354     switch (spec.frameworkType()) {
355       case PROVIDER:
356         return getMethodBuilder
357             .returns(spec.optionalType())
358             .addCode(
359                 "return $L;",
360                 spec.optionalKind()
361                     .presentExpression(
362                         FrameworkType.PROVIDER.to(
363                             spec.valueKind(), CodeBlock.of("$N", delegateField))))
364             .build();
365 
366       case PRODUCER_NODE:
367         getMethodBuilder.returns(listenableFutureOf(spec.optionalType()));
368 
369         switch (spec.valueKind()) {
370           case FUTURE: // return a ListenableFuture<Optional<ListenableFuture<T>>>
371           case PRODUCER: // return a ListenableFuture<Optional<Producer<T>>>
372             return getMethodBuilder
373                 .addCode(
374                     "return $T.immediateFuture($L);",
375                     Futures.class,
376                     spec.optionalKind()
377                         .presentExpression(
378                             FrameworkType.PRODUCER_NODE.to(
379                                 spec.valueKind(), CodeBlock.of("$N", delegateField))))
380                 .build();
381 
382           case INSTANCE: // return a ListenableFuture<Optional<T>>
383             return getMethodBuilder
384                 .addCode(
385                     "return $L;",
386                     transformFutureToOptional(
387                         spec.optionalKind(),
388                         spec.typeVariable(),
389                         CodeBlock.of("$N.get()", delegateField)))
390                 .build();
391 
392           case PRODUCED: // return a ListenableFuture<Optional<Produced<T>>>
393             return getMethodBuilder
394                 .addCode(
395                     "return $L;",
396                     transformFutureToOptional(
397                         spec.optionalKind(),
398                         spec.valueType(),
399                         CodeBlock.of(
400                             "$T.createFutureProduced($N.get())", Producers.class, delegateField)))
401                 .build();
402 
403           default:
404             throw new UnsupportedOperationException(
405                 spec.factoryType() + " objects are not supported");
406         }
407     }
408     throw new AssertionError(spec.frameworkType());
409   }
410 
411   /**
412    * An expression that uses {@link Futures#transform(ListenableFuture, Function, Executor)} to
413    * transform a {@code ListenableFuture<inputType>} into a {@code
414    * ListenableFuture<Optional<inputType>>}.
415    *
416    * @param inputFuture an expression of type {@code ListenableFuture<inputType>}
417    */
418   private static CodeBlock transformFutureToOptional(
419       OptionalKind optionalKind, TypeName inputType, CodeBlock inputFuture) {
420     return CodeBlock.of(
421         "$T.transform($L, $L, $T.directExecutor())",
422         Futures.class,
423         inputFuture,
424         anonymousClassBuilder("")
425             .addSuperinterface(
426                 ParameterizedTypeName.get(
427                     ClassName.get(Function.class), inputType, optionalKind.of(inputType)))
428             .addMethod(
429                 methodBuilder("apply")
430                     .addAnnotation(Override.class)
431                     .addModifiers(PUBLIC)
432                     .returns(optionalKind.of(inputType))
433                     .addParameter(inputType, "input")
434                     .addCode("return $L;", optionalKind.presentExpression(CodeBlock.of("input")))
435                     .build())
436             .build(),
437         MoreExecutors.class);
438   }
439 }
440