• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ProducerModuleProcessingStep( Messager messager, ModuleValidator moduleValidator, ProducesMethodValidator producesMethodValidator, ProductionBinding.Factory productionBindingFactory, ProducerFactoryGenerator factoryGenerator)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
annotations()69   public Set<Class<? extends Annotation>> annotations() {
70     return ImmutableSet.of(Produces.class, ProducerModule.class);
71   }
72 
73   @Override
process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation)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