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