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.MoreTypes;
20 import com.google.common.base.Optional;
21 import com.google.common.collect.ImmutableSet;
22 import com.google.common.collect.SetMultimap;
23 
24 import java.lang.annotation.Annotation;
25 import java.util.Set;
26 
27 import javax.annotation.processing.Messager;
28 import javax.inject.Inject;
29 import javax.lang.model.element.Element;
30 import javax.lang.model.element.ExecutableElement;
31 import javax.lang.model.element.TypeElement;
32 import javax.lang.model.element.VariableElement;
33 import javax.lang.model.type.DeclaredType;
34 import javax.lang.model.type.TypeMirror;
35 import javax.lang.model.util.ElementKindVisitor6;
36 
37 /**
38  * An annotation processor for generating Dagger implementation code based on the {@link Inject}
39  * annotation.
40  *
41  * @author Gregory Kick
42  * @since 2.0
43  */
44 final class InjectProcessingStep implements BasicAnnotationProcessor.ProcessingStep {
45   private final Messager messager;
46   private final InjectConstructorValidator constructorValidator;
47   private final InjectFieldValidator fieldValidator;
48   private final InjectMethodValidator methodValidator;
49   private final ProvisionBinding.Factory provisionBindingFactory;
50   private final MembersInjectionBinding.Factory membersInjectionBindingFactory;
51   private final InjectBindingRegistry injectBindingRegistry;
52 
InjectProcessingStep( Messager messager, InjectConstructorValidator constructorValidator, InjectFieldValidator fieldValidator, InjectMethodValidator methodValidator, ProvisionBinding.Factory provisionBindingFactory, MembersInjectionBinding.Factory membersInjectionBindingFactory, InjectBindingRegistry factoryRegistrar)53   InjectProcessingStep(
54       Messager messager,
55       InjectConstructorValidator constructorValidator,
56       InjectFieldValidator fieldValidator,
57       InjectMethodValidator methodValidator,
58       ProvisionBinding.Factory provisionBindingFactory,
59       MembersInjectionBinding.Factory membersInjectionBindingFactory,
60       InjectBindingRegistry factoryRegistrar) {
61     this.messager = messager;
62     this.constructorValidator = constructorValidator;
63     this.fieldValidator = fieldValidator;
64     this.methodValidator = methodValidator;
65     this.provisionBindingFactory = provisionBindingFactory;
66     this.membersInjectionBindingFactory = membersInjectionBindingFactory;
67     this.injectBindingRegistry = factoryRegistrar;
68   }
69 
70   @Override
annotations()71   public Set<Class<? extends Annotation>> annotations() {
72     return ImmutableSet.<Class<? extends Annotation>>of(Inject.class);
73   }
74 
75   @Override
process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation)76   public Set<Element> process(
77       SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
78     ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
79     // TODO(gak): add some error handling for bad source files
80     final ImmutableSet.Builder<ProvisionBinding> provisions = ImmutableSet.builder();
81     // TODO(gak): instead, we should collect reports by type and check later
82     final ImmutableSet.Builder<DeclaredType> membersInjectedTypes = ImmutableSet.builder();
83 
84     for (Element injectElement : elementsByAnnotation.get(Inject.class)) {
85       try {
86         injectElement.accept(
87             new ElementKindVisitor6<Void, Void>() {
88               @Override
89               public Void visitExecutableAsConstructor(
90                   ExecutableElement constructorElement, Void v) {
91                 ValidationReport<TypeElement> report =
92                     constructorValidator.validate(constructorElement);
93 
94                 report.printMessagesTo(messager);
95 
96                 if (report.isClean()) {
97                   provisions.add(
98                       provisionBindingFactory.forInjectConstructor(
99                           constructorElement, Optional.<TypeMirror>absent()));
100                   DeclaredType type =
101                       MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
102                   if (membersInjectionBindingFactory.hasInjectedMembers(type)) {
103                     membersInjectedTypes.add(type);
104                   }
105                 }
106 
107                 return null;
108               }
109 
110               @Override
111               public Void visitVariableAsField(VariableElement fieldElement, Void p) {
112                 ValidationReport<VariableElement> report = fieldValidator.validate(fieldElement);
113 
114                 report.printMessagesTo(messager);
115 
116                 if (report.isClean()) {
117                   membersInjectedTypes.add(
118                       MoreTypes.asDeclared(fieldElement.getEnclosingElement().asType()));
119                 }
120 
121                 return null;
122               }
123 
124               @Override
125               public Void visitExecutableAsMethod(ExecutableElement methodElement, Void p) {
126                 ValidationReport<ExecutableElement> report =
127                     methodValidator.validate(methodElement);
128 
129                 report.printMessagesTo(messager);
130 
131                 if (report.isClean()) {
132                   membersInjectedTypes.add(
133                       MoreTypes.asDeclared(methodElement.getEnclosingElement().asType()));
134                 }
135 
136                 return null;
137               }
138             },
139             null);
140       } catch (TypeNotPresentException e) {
141         rejectedElements.add(injectElement);
142       }
143     }
144 
145     for (DeclaredType injectedType : membersInjectedTypes.build()) {
146       injectBindingRegistry.registerBinding(membersInjectionBindingFactory.forInjectedType(
147           injectedType, Optional.<TypeMirror>absent()));
148     }
149 
150     for (ProvisionBinding binding : provisions.build()) {
151       injectBindingRegistry.registerBinding(binding);
152     }
153     return rejectedElements.build();
154   }
155 }
156