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 */
16package dagger.internal.codegen;
17
18import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
19import com.google.auto.common.MoreElements;
20import com.google.auto.common.SuperficialValidation;
21import com.google.common.base.Function;
22import com.google.common.collect.FluentIterable;
23import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.SetMultimap;
25import com.google.common.collect.Sets;
26import dagger.producers.ProducerModule;
27import dagger.producers.Produces;
28import java.lang.annotation.Annotation;
29import java.util.List;
30import java.util.Set;
31import javax.annotation.processing.Messager;
32import javax.lang.model.element.Element;
33import javax.lang.model.element.ExecutableElement;
34import javax.lang.model.element.TypeElement;
35import javax.lang.model.util.ElementFilter;
36
37import static com.google.auto.common.MoreElements.isAnnotationPresent;
38import 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 */
47final 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