• 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;
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 
ModuleProcessingStep( Messager messager, ModuleValidator moduleValidator, ProvidesMethodValidator providesMethodValidator, ProvisionBinding.Factory provisionBindingFactory, FactoryGenerator factoryGenerator)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
annotations()68   public Set<Class<? extends Annotation>> annotations() {
69     return ImmutableSet.of(Module.class, Provides.class);
70   }
71 
72   @Override
process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation)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