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