Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2014 Google, Inc.
      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 package dagger.internal.codegen;
     17 
     18 import com.google.auto.common.MoreTypes;
     19 import com.google.common.base.Function;
     20 import com.google.common.base.Joiner;
     21 import com.google.common.base.Optional;
     22 import com.google.common.base.Predicate;
     23 import com.google.common.collect.FluentIterable;
     24 import com.google.common.collect.ImmutableList;
     25 import com.google.common.collect.ImmutableMap;
     26 import com.google.common.collect.ImmutableSet;
     27 import com.google.common.collect.Iterables;
     28 import com.google.common.util.concurrent.AsyncFunction;
     29 import com.google.common.util.concurrent.Futures;
     30 import com.google.common.util.concurrent.ListenableFuture;
     31 import dagger.Provides.Type;
     32 import dagger.internal.codegen.writer.ClassName;
     33 import dagger.internal.codegen.writer.ClassWriter;
     34 import dagger.internal.codegen.writer.ConstructorWriter;
     35 import dagger.internal.codegen.writer.FieldWriter;
     36 import dagger.internal.codegen.writer.JavaWriter;
     37 import dagger.internal.codegen.writer.MethodWriter;
     38 import dagger.internal.codegen.writer.ParameterizedTypeName;
     39 import dagger.internal.codegen.writer.Snippet;
     40 import dagger.internal.codegen.writer.TypeName;
     41 import dagger.internal.codegen.writer.TypeNames;
     42 import dagger.producers.Produced;
     43 import dagger.producers.Producer;
     44 import dagger.producers.Produces;
     45 import dagger.producers.internal.AbstractProducer;
     46 import dagger.producers.internal.Producers;
     47 import dagger.producers.monitoring.ProducerMonitor;
     48 import dagger.producers.monitoring.ProducerToken;
     49 import java.util.List;
     50 import java.util.concurrent.Executor;
     51 import javax.annotation.Generated;
     52 import javax.annotation.processing.Filer;
     53 import javax.lang.model.element.Element;
     54 import javax.lang.model.type.TypeMirror;
     55 
     56 import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
     57 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
     58 import static dagger.internal.codegen.writer.Snippet.makeParametersSnippet;
     59 import static javax.lang.model.element.Modifier.FINAL;
     60 import static javax.lang.model.element.Modifier.PRIVATE;
     61 import static javax.lang.model.element.Modifier.PROTECTED;
     62 import static javax.lang.model.element.Modifier.PUBLIC;
     63 import static javax.lang.model.element.Modifier.STATIC;
     64 
     65 /**
     66  * Generates {@link Producer} implementations from {@link ProductionBinding} instances.
     67  *
     68  * @author Jesse Beder
     69  * @since 2.0
     70  */
     71 final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBinding> {
     72   private final DependencyRequestMapper dependencyRequestMapper;
     73 
     74   ProducerFactoryGenerator(Filer filer, DependencyRequestMapper dependencyRequestMapper) {
     75     super(filer);
     76     this.dependencyRequestMapper = dependencyRequestMapper;
     77   }
     78 
     79   @Override
     80   ClassName nameGeneratedType(ProductionBinding binding) {
     81     return generatedClassNameForBinding(binding);
     82   }
     83 
     84   @Override
     85   Iterable<? extends Element> getOriginatingElements(ProductionBinding binding) {
     86     return ImmutableSet.of(binding.bindingElement());
     87   }
     88 
     89   @Override
     90   Optional<? extends Element> getElementForErrorReporting(ProductionBinding binding) {
     91     return Optional.of(binding.bindingElement());
     92   }
     93 
     94   @Override
     95   ImmutableSet<JavaWriter> write(ClassName generatedTypeName, ProductionBinding binding) {
     96     TypeMirror keyType = binding.productionType().equals(Type.MAP)
     97         ? Util.getProvidedValueTypeOfMap(MoreTypes.asDeclared(binding.key().type()))
     98         : binding.key().type();
     99     TypeName providedTypeName = TypeNames.forTypeMirror(keyType);
    100     TypeName futureTypeName = ParameterizedTypeName.create(
    101         ClassName.fromClass(ListenableFuture.class), providedTypeName);
    102     JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName());
    103 
    104     ClassWriter factoryWriter = writer.addClass(generatedTypeName.simpleName());
    105     ConstructorWriter constructorWriter = factoryWriter.addConstructor();
    106     constructorWriter.addModifiers(PUBLIC);
    107 
    108     ImmutableMap<BindingKey, FrameworkField> fields =
    109         SourceFiles.generateBindingFieldsForDependencies(
    110             dependencyRequestMapper, binding.implicitDependencies());
    111 
    112     constructorWriter
    113         .body()
    114         .addSnippet(
    115             "super(%s, %s.create(%s.class));",
    116             fields.get(binding.monitorRequest().get().bindingKey()).name(),
    117             ClassName.fromClass(ProducerToken.class),
    118             factoryWriter.name());
    119 
    120     if (!binding.bindingElement().getModifiers().contains(STATIC)) {
    121       factoryWriter.addField(binding.bindingTypeElement(), "module")
    122           .addModifiers(PRIVATE, FINAL);
    123       constructorWriter.addParameter(binding.bindingTypeElement(), "module");
    124       constructorWriter.body()
    125           .addSnippet("assert module != null;")
    126           .addSnippet("this.module = module;");
    127     }
    128 
    129     factoryWriter.addField(Executor.class, "executor")
    130         .addModifiers(PRIVATE, FINAL);
    131     constructorWriter.addParameter(Executor.class, "executor");
    132     constructorWriter.body()
    133         .addSnippet("assert executor != null;")
    134         .addSnippet("this.executor = executor;");
    135 
    136     factoryWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName());
    137     factoryWriter.addModifiers(PUBLIC);
    138     factoryWriter.addModifiers(FINAL);
    139     factoryWriter.setSuperclass(
    140         ParameterizedTypeName.create(AbstractProducer.class, providedTypeName));
    141 
    142     MethodWriter computeMethodWriter = factoryWriter.addMethod(futureTypeName, "compute");
    143     computeMethodWriter.annotate(Override.class);
    144     computeMethodWriter.addModifiers(PROTECTED);
    145     computeMethodWriter.addParameter(ProducerMonitor.class, "monitor").addModifiers(FINAL);
    146 
    147     for (FrameworkField bindingField : fields.values()) {
    148       TypeName fieldType = bindingField.frameworkType();
    149       FieldWriter field = factoryWriter.addField(fieldType, bindingField.name());
    150       field.addModifiers(PRIVATE, FINAL);
    151       constructorWriter.addParameter(field.type(), field.name());
    152       constructorWriter.body()
    153           .addSnippet("assert %s != null;", field.name())
    154           .addSnippet("this.%1$s = %1$s;", field.name());
    155     }
    156 
    157     boolean returnsFuture =
    158         binding.bindingKind().equals(ContributionBinding.Kind.FUTURE_PRODUCTION);
    159     ImmutableList<DependencyRequest> asyncDependencies =
    160         FluentIterable.from(binding.implicitDependencies())
    161             .filter(
    162                 new Predicate<DependencyRequest>() {
    163                   @Override
    164                   public boolean apply(DependencyRequest dependency) {
    165                     return isAsyncDependency(dependency);
    166                   }
    167                 })
    168             .toList();
    169 
    170     for (DependencyRequest dependency : asyncDependencies) {
    171       ParameterizedTypeName futureType = ParameterizedTypeName.create(
    172           ClassName.fromClass(ListenableFuture.class),
    173           asyncDependencyType(dependency));
    174       String name = fields.get(dependency.bindingKey()).name();
    175       Snippet futureAccess = Snippet.format("%s.get()", name);
    176       computeMethodWriter
    177           .body()
    178           .addSnippet(
    179               "%s %sFuture = %s;",
    180               futureType,
    181               name,
    182               dependency.kind().equals(DependencyRequest.Kind.PRODUCED)
    183                   ? Snippet.format(
    184                       "%s.createFutureProduced(%s)",
    185                       ClassName.fromClass(Producers.class),
    186                       futureAccess)
    187                   : futureAccess);
    188     }
    189 
    190     FutureTransform futureTransform = FutureTransform.create(fields, binding, asyncDependencies);
    191     Snippet transformSnippet =
    192         Snippet.format(
    193             Joiner.on('\n')
    194                 .join(
    195                     "new %1$s<%2$s, %3$s>() {",
    196                     "  %4$s",
    197                     "  @Override public %5$s apply(%2$s %6$s) %7$s {",
    198                     "    %8$s",
    199                     "  }",
    200                     "}"),
    201             ClassName.fromClass(AsyncFunction.class),
    202             futureTransform.applyArgType(),
    203             providedTypeName,
    204             futureTransform.hasUncheckedCast()
    205                 ? "@SuppressWarnings(\"unchecked\")  // safe by specification"
    206                 : "",
    207             futureTypeName,
    208             futureTransform.applyArgName(),
    209             getThrowsClause(binding.thrownTypes()),
    210             getInvocationSnippet(!returnsFuture, binding, futureTransform.parameterSnippets()));
    211     computeMethodWriter
    212         .body()
    213         .addSnippet(
    214             "return %s.transform(%s, %s, executor);",
    215             ClassName.fromClass(Futures.class),
    216             futureTransform.futureSnippet(),
    217             transformSnippet);
    218 
    219     // TODO(gak): write a sensible toString
    220     return ImmutableSet.of(writer);
    221   }
    222 
    223   /** Represents the transformation of an input future by a producer method. */
    224   abstract static class FutureTransform {
    225     protected final ImmutableMap<BindingKey, FrameworkField> fields;
    226     protected final ProductionBinding binding;
    227 
    228     FutureTransform(ImmutableMap<BindingKey, FrameworkField> fields, ProductionBinding binding) {
    229       this.fields = fields;
    230       this.binding = binding;
    231     }
    232 
    233     /** The snippet representing the future that should be transformed. */
    234     abstract Snippet futureSnippet();
    235 
    236     /** The type of the argument to the apply method. */
    237     abstract TypeName applyArgType();
    238 
    239     /** The name of the argument to the apply method */
    240     abstract String applyArgName();
    241 
    242     /** The snippets to be passed to the produces method itself. */
    243     abstract ImmutableList<Snippet> parameterSnippets();
    244 
    245     /** Whether the transform method has an unchecked cast. */
    246     boolean hasUncheckedCast() {
    247       return false;
    248     }
    249 
    250     static FutureTransform create(
    251         ImmutableMap<BindingKey, FrameworkField> fields,
    252         ProductionBinding binding,
    253         ImmutableList<DependencyRequest> asyncDependencies) {
    254       if (asyncDependencies.isEmpty()) {
    255         return new NoArgFutureTransform(fields, binding);
    256       } else if (asyncDependencies.size() == 1) {
    257         return new SingleArgFutureTransform(
    258             fields, binding, Iterables.getOnlyElement(asyncDependencies));
    259       } else {
    260         return new MultiArgFutureTransform(fields, binding, asyncDependencies);
    261       }
    262     }
    263   }
    264 
    265   static final class NoArgFutureTransform extends FutureTransform {
    266     NoArgFutureTransform(
    267         ImmutableMap<BindingKey, FrameworkField> fields, ProductionBinding binding) {
    268       super(fields, binding);
    269     }
    270 
    271     @Override
    272     Snippet futureSnippet() {
    273       return Snippet.format(
    274           "%s.<%s>immediateFuture(null)",
    275           ClassName.fromClass(Futures.class),
    276           ClassName.fromClass(Void.class));
    277     }
    278 
    279     @Override
    280     TypeName applyArgType() {
    281       return ClassName.fromClass(Void.class);
    282     }
    283 
    284     @Override
    285     String applyArgName() {
    286       return "ignoredVoidArg";
    287     }
    288 
    289     @Override
    290     ImmutableList<Snippet> parameterSnippets() {
    291       ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
    292       for (DependencyRequest dependency : binding.dependencies()) {
    293         parameterSnippets.add(
    294             frameworkTypeUsageStatement(
    295                 Snippet.format(
    296                     "%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
    297       }
    298       return parameterSnippets.build();
    299     }
    300   }
    301 
    302   static final class SingleArgFutureTransform extends FutureTransform {
    303     private final DependencyRequest asyncDependency;
    304 
    305     SingleArgFutureTransform(
    306         ImmutableMap<BindingKey, FrameworkField> fields,
    307         ProductionBinding binding,
    308         DependencyRequest asyncDependency) {
    309       super(fields, binding);
    310       this.asyncDependency = asyncDependency;
    311     }
    312 
    313     @Override
    314     Snippet futureSnippet() {
    315       return Snippet.format("%s", fields.get(asyncDependency.bindingKey()).name() + "Future");
    316     }
    317 
    318     @Override
    319     TypeName applyArgType() {
    320       return asyncDependencyType(asyncDependency);
    321     }
    322 
    323     @Override
    324     String applyArgName() {
    325       return asyncDependency.requestElement().getSimpleName().toString();
    326     }
    327 
    328     @Override
    329     ImmutableList<Snippet> parameterSnippets() {
    330       ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder();
    331       for (DependencyRequest dependency : binding.dependencies()) {
    332         // We really want to compare instances here, because asyncDependency is an element in the
    333         // set binding.dependencies().
    334         if (dependency == asyncDependency) {
    335           parameterSnippets.add(Snippet.format("%s", applyArgName()));
    336         } else {
    337           parameterSnippets.add(
    338               frameworkTypeUsageStatement(
    339                   Snippet.format(
    340                       "%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
    341         }
    342       }
    343       return parameterSnippets.build();
    344     }
    345   }
    346 
    347   static final class MultiArgFutureTransform extends FutureTransform {
    348     private final ImmutableList<DependencyRequest> asyncDependencies;
    349 
    350     MultiArgFutureTransform(
    351         ImmutableMap<BindingKey, FrameworkField> fields,
    352         ProductionBinding binding,
    353         ImmutableList<DependencyRequest> asyncDependencies) {
    354       super(fields, binding);
    355       this.asyncDependencies = asyncDependencies;
    356     }
    357 
    358     @Override
    359     Snippet futureSnippet() {
    360       return Snippet.format(
    361           "%s.<%s>allAsList(%s)",
    362           ClassName.fromClass(Futures.class),
    363           ClassName.fromClass(Object.class),
    364           makeParametersSnippet(
    365               FluentIterable.from(asyncDependencies)
    366                   .transform(DependencyRequest.BINDING_KEY_FUNCTION)
    367                   .transform(
    368                       new Function<BindingKey, Snippet>() {
    369                         @Override
    370                         public Snippet apply(BindingKey bindingKey) {
    371                           return Snippet.format("%s", fields.get(bindingKey).name() + "Future");
    372                         }
    373                       })));
    374     }
    375 
    376     @Override
    377     TypeName applyArgType() {
    378       return ParameterizedTypeName.create(
    379           ClassName.fromClass(List.class), ClassName.fromClass(Object.class));
    380     }
    381 
    382     @Override
    383     String applyArgName() {
    384       return "args";
    385     }
    386 
    387     @Override
    388     ImmutableList<Snippet> parameterSnippets() {
    389       return getParameterSnippets(binding, fields, applyArgName());
    390     }
    391 
    392     @Override
    393     boolean hasUncheckedCast() {
    394       return true;
    395     }
    396   }
    397 
    398   private static boolean isAsyncDependency(DependencyRequest dependency) {
    399     switch (dependency.kind()) {
    400       case INSTANCE:
    401       case PRODUCED:
    402         return true;
    403       default:
    404         return false;
    405     }
    406   }
    407 
    408   private static TypeName asyncDependencyType(DependencyRequest dependency) {
    409     TypeName keyName = TypeNames.forTypeMirror(dependency.key().type());
    410     switch (dependency.kind()) {
    411       case INSTANCE:
    412         return keyName;
    413       case PRODUCED:
    414         return ParameterizedTypeName.create(ClassName.fromClass(Produced.class), keyName);
    415       default:
    416         throw new AssertionError();
    417     }
    418   }
    419 
    420   private static ImmutableList<Snippet> getParameterSnippets(
    421       ProductionBinding binding,
    422       ImmutableMap<BindingKey, FrameworkField> fields,
    423       String listArgName) {
    424     int argIndex = 0;
    425     ImmutableList.Builder<Snippet> snippets = ImmutableList.builder();
    426     for (DependencyRequest dependency : binding.dependencies()) {
    427       if (isAsyncDependency(dependency)) {
    428         snippets.add(Snippet.format(
    429             "(%s) %s.get(%s)",
    430             asyncDependencyType(dependency),
    431             listArgName,
    432             argIndex));
    433         argIndex++;
    434       } else {
    435         snippets.add(frameworkTypeUsageStatement(
    436             Snippet.format("%s", fields.get(dependency.bindingKey()).name()), dependency.kind()));
    437       }
    438     }
    439     return snippets.build();
    440   }
    441 
    442   /**
    443    * Creates a snippet for the invocation of the producer method from the module, which should be
    444    * used entirely within a method body.
    445    *
    446    * @param wrapWithFuture If true, wraps the result of the call to the producer method
    447    *        in an immediate future.
    448    * @param binding The binding to generate the invocation snippet for.
    449    * @param parameterSnippets The snippets for all the parameters to the producer method.
    450    */
    451   private Snippet getInvocationSnippet(
    452       boolean wrapWithFuture, ProductionBinding binding, ImmutableList<Snippet> parameterSnippets) {
    453      Snippet moduleSnippet = Snippet.format("%s.%s(%s)",
    454         binding.bindingElement().getModifiers().contains(STATIC)
    455             ? ClassName.fromTypeElement(binding.bindingTypeElement())
    456             : "module",
    457         binding.bindingElement().getSimpleName(),
    458         makeParametersSnippet(parameterSnippets));
    459 
    460     // NOTE(beder): We don't worry about catching exeptions from the monitor methods themselves
    461     // because we'll wrap all monitoring in non-throwing monitors before we pass them to the
    462     // factories.
    463     ImmutableList.Builder<Snippet> snippets = ImmutableList.builder();
    464     snippets.add(Snippet.format("monitor.methodStarting();"));
    465 
    466     final Snippet valueSnippet;
    467     if (binding.productionType().equals(Produces.Type.SET)) {
    468       if (binding.bindingKind().equals(ContributionBinding.Kind.FUTURE_PRODUCTION)) {
    469         valueSnippet =
    470             Snippet.format(
    471                 "%s.createFutureSingletonSet(%s)",
    472                 ClassName.fromClass(Producers.class),
    473                 moduleSnippet);
    474       } else {
    475         valueSnippet =
    476             Snippet.format("%s.of(%s)", ClassName.fromClass(ImmutableSet.class), moduleSnippet);
    477       }
    478     } else {
    479       valueSnippet = moduleSnippet;
    480     }
    481     Snippet returnSnippet =
    482         wrapWithFuture
    483             ? Snippet.format(
    484                 "%s.<%s>immediateFuture(%s)",
    485                 ClassName.fromClass(Futures.class),
    486                 TypeNames.forTypeMirror(binding.key().type()),
    487                 valueSnippet)
    488             : valueSnippet;
    489     return Snippet.format(
    490         Joiner.on('\n')
    491             .join(
    492                 "monitor.methodStarting();",
    493                 "try {",
    494                 "  return %s;",
    495                 "} finally {",
    496                 "  monitor.methodFinished();",
    497                 "}"),
    498         returnSnippet);
    499   }
    500 
    501   /**
    502    * Creates a Snippet for the throws clause.
    503    *
    504    * @param thrownTypes the list of thrown types.
    505    */
    506   private Snippet getThrowsClause(List<? extends TypeMirror> thrownTypes) {
    507     if (thrownTypes.isEmpty()) {
    508       return Snippet.format("");
    509     }
    510     return Snippet.format("throws %s ",
    511         Snippet.makeParametersSnippet(FluentIterable
    512             .from(thrownTypes)
    513             .transform(new Function<TypeMirror, Snippet>() {
    514               @Override public Snippet apply(TypeMirror thrownType) {
    515                 return Snippet.format("%s", TypeNames.forTypeMirror(thrownType));
    516               }
    517             })
    518             .toList()));
    519   }
    520 }
    521