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