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.BasicAnnotationProcessor.ProcessingStep;
     19 import com.google.auto.common.MoreElements;
     20 import com.google.auto.common.SuperficialValidation;
     21 import com.google.common.base.Function;
     22 import com.google.common.collect.FluentIterable;
     23 import com.google.common.collect.ImmutableSet;
     24 import com.google.common.collect.SetMultimap;
     25 import com.google.common.collect.Sets;
     26 import dagger.producers.ProducerModule;
     27 import dagger.producers.Produces;
     28 import java.lang.annotation.Annotation;
     29 import java.util.List;
     30 import java.util.Set;
     31 import javax.annotation.processing.Messager;
     32 import javax.lang.model.element.Element;
     33 import javax.lang.model.element.ExecutableElement;
     34 import javax.lang.model.element.TypeElement;
     35 import javax.lang.model.util.ElementFilter;
     36 
     37 import static com.google.auto.common.MoreElements.isAnnotationPresent;
     38 import static javax.lang.model.element.ElementKind.METHOD;
     39 
     40 /**
     41  * An annotation processor for generating Dagger implementation code based on the
     42  * {@link ProducerModule} (and {@link Produces}) annotation.
     43  *
     44  * @author Jesse Beder
     45  * @since 2.0
     46  */
     47 final class ProducerModuleProcessingStep implements ProcessingStep {
     48   private final Messager messager;
     49   private final ModuleValidator moduleValidator;
     50   private final ProducesMethodValidator producesMethodValidator;
     51   private final ProductionBinding.Factory productionBindingFactory;
     52   private final ProducerFactoryGenerator factoryGenerator;
     53   private final Set<Element> processedModuleElements = Sets.newLinkedHashSet();
     54 
     55   ProducerModuleProcessingStep(
     56       Messager messager,
     57       ModuleValidator moduleValidator,
     58       ProducesMethodValidator producesMethodValidator,
     59       ProductionBinding.Factory productionBindingFactory,
     60       ProducerFactoryGenerator factoryGenerator) {
     61     this.messager = messager;
     62     this.moduleValidator = moduleValidator;
     63     this.producesMethodValidator = producesMethodValidator;
     64     this.productionBindingFactory = productionBindingFactory;
     65     this.factoryGenerator = factoryGenerator;
     66   }
     67 
     68   @Override
     69   public Set<Class<? extends Annotation>> annotations() {
     70     return ImmutableSet.of(Produces.class, ProducerModule.class);
     71   }
     72 
     73   @Override
     74   public Set<Element> process(
     75       SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
     76     // first, check and collect all produces methods
     77     ImmutableSet.Builder<ExecutableElement> validProducesMethodsBuilder = ImmutableSet.builder();
     78     for (Element producesElement : elementsByAnnotation.get(Produces.class)) {
     79       if (producesElement.getKind().equals(METHOD)) {
     80         ExecutableElement producesMethodElement = (ExecutableElement) producesElement;
     81         ValidationReport<ExecutableElement> methodReport =
     82             producesMethodValidator.validate(producesMethodElement);
     83         methodReport.printMessagesTo(messager);
     84         if (methodReport.isClean()) {
     85           validProducesMethodsBuilder.add(producesMethodElement);
     86         }
     87       }
     88     }
     89     ImmutableSet<ExecutableElement> validProducesMethods = validProducesMethodsBuilder.build();
     90 
     91     // process each module
     92     for (Element moduleElement :
     93         Sets.difference(elementsByAnnotation.get(ProducerModule.class),
     94             processedModuleElements)) {
     95       if (SuperficialValidation.validateElement(moduleElement)) {
     96         ValidationReport<TypeElement> report =
     97             moduleValidator.validate(MoreElements.asType(moduleElement));
     98         report.printMessagesTo(messager);
     99 
    100         if (report.isClean()) {
    101           ImmutableSet.Builder<ExecutableElement> moduleProducesMethodsBuilder =
    102               ImmutableSet.builder();
    103           List<ExecutableElement> moduleMethods =
    104               ElementFilter.methodsIn(moduleElement.getEnclosedElements());
    105           for (ExecutableElement methodElement : moduleMethods) {
    106             if (isAnnotationPresent(methodElement, Produces.class)) {
    107               moduleProducesMethodsBuilder.add(methodElement);
    108             }
    109           }
    110           ImmutableSet<ExecutableElement> moduleProducesMethods =
    111               moduleProducesMethodsBuilder.build();
    112 
    113           if (Sets.difference(moduleProducesMethods, validProducesMethods).isEmpty()) {
    114             // all of the produces methods in this module are valid!
    115             // time to generate some factories!
    116             ImmutableSet<ProductionBinding> bindings = FluentIterable.from(moduleProducesMethods)
    117                 .transform(new Function<ExecutableElement, ProductionBinding>() {
    118                   @Override
    119                   public ProductionBinding apply(ExecutableElement producesMethod) {
    120                     return productionBindingFactory.forProducesMethod(producesMethod,
    121                         producesMethod.getEnclosingElement().asType());
    122                   }
    123                 })
    124                 .toSet();
    125 
    126             try {
    127               for (ProductionBinding binding : bindings) {
    128                 factoryGenerator.generate(binding);
    129               }
    130             } catch (SourceFileGenerationException e) {
    131               e.printMessageTo(messager);
    132             }
    133           }
    134         }
    135 
    136         processedModuleElements.add(moduleElement);
    137       }
    138     }
    139     return ImmutableSet.of();
    140   }
    141 }
    142