1 /*
2  * Copyright (C) 2014 The Dagger Authors.
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 
17 package dagger.internal.codegen;
18 
19 import static com.google.auto.common.MoreElements.isAnnotationPresent;
20 import static javax.lang.model.util.ElementFilter.methodsIn;
21 import static javax.lang.model.util.ElementFilter.typesIn;
22 
23 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
24 import com.google.auto.common.MoreElements;
25 import com.google.common.collect.ImmutableSet;
26 import com.google.common.collect.SetMultimap;
27 import com.google.common.collect.Sets;
28 import dagger.Binds;
29 import dagger.Module;
30 import dagger.Provides;
31 import dagger.internal.codegen.base.SourceFileGenerator;
32 import dagger.internal.codegen.binding.BindingFactory;
33 import dagger.internal.codegen.binding.ContributionBinding;
34 import dagger.internal.codegen.binding.DelegateDeclaration;
35 import dagger.internal.codegen.binding.DelegateDeclaration.Factory;
36 import dagger.internal.codegen.binding.ProductionBinding;
37 import dagger.internal.codegen.binding.ProvisionBinding;
38 import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
39 import dagger.internal.codegen.validation.ModuleValidator;
40 import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
41 import dagger.internal.codegen.validation.ValidationReport;
42 import dagger.internal.codegen.writing.InaccessibleMapKeyProxyGenerator;
43 import dagger.internal.codegen.writing.ModuleGenerator;
44 import dagger.producers.ProducerModule;
45 import dagger.producers.Produces;
46 import java.lang.annotation.Annotation;
47 import java.util.List;
48 import java.util.Set;
49 import javax.annotation.processing.Messager;
50 import javax.inject.Inject;
51 import javax.lang.model.element.Element;
52 import javax.lang.model.element.ExecutableElement;
53 import javax.lang.model.element.TypeElement;
54 
55 /**
56  * A {@link ProcessingStep} that validates module classes and generates factories for binding
57  * methods.
58  */
59 final class ModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
60   private final Messager messager;
61   private final ModuleValidator moduleValidator;
62   private final BindingFactory bindingFactory;
63   private final SourceFileGenerator<ProvisionBinding> factoryGenerator;
64   private final SourceFileGenerator<ProductionBinding> producerFactoryGenerator;
65   private final SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator;
66   private final InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator;
67   private final DelegateDeclaration.Factory delegateDeclarationFactory;
68   private final KotlinMetadataUtil metadataUtil;
69   private final Set<TypeElement> processedModuleElements = Sets.newLinkedHashSet();
70 
71   @Inject
ModuleProcessingStep( Messager messager, ModuleValidator moduleValidator, BindingFactory bindingFactory, SourceFileGenerator<ProvisionBinding> factoryGenerator, SourceFileGenerator<ProductionBinding> producerFactoryGenerator, @ModuleGenerator SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator, InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator, Factory delegateDeclarationFactory, KotlinMetadataUtil metadataUtil)72   ModuleProcessingStep(
73       Messager messager,
74       ModuleValidator moduleValidator,
75       BindingFactory bindingFactory,
76       SourceFileGenerator<ProvisionBinding> factoryGenerator,
77       SourceFileGenerator<ProductionBinding> producerFactoryGenerator,
78       @ModuleGenerator SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator,
79       InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator,
80       Factory delegateDeclarationFactory,
81       KotlinMetadataUtil metadataUtil) {
82     super(MoreElements::asType);
83     this.messager = messager;
84     this.moduleValidator = moduleValidator;
85     this.bindingFactory = bindingFactory;
86     this.factoryGenerator = factoryGenerator;
87     this.producerFactoryGenerator = producerFactoryGenerator;
88     this.moduleConstructorProxyGenerator = moduleConstructorProxyGenerator;
89     this.inaccessibleMapKeyProxyGenerator = inaccessibleMapKeyProxyGenerator;
90     this.delegateDeclarationFactory = delegateDeclarationFactory;
91     this.metadataUtil = metadataUtil;
92   }
93 
94   @Override
annotations()95   public Set<? extends Class<? extends Annotation>> annotations() {
96     return ImmutableSet.of(Module.class, ProducerModule.class);
97   }
98 
99   @Override
process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation)100   public ImmutableSet<Element> process(
101       SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
102     List<TypeElement> modules = typesIn(elementsByAnnotation.values());
103     moduleValidator.addKnownModules(modules);
104     return super.process(elementsByAnnotation);
105   }
106 
107   @Override
process( TypeElement module, ImmutableSet<Class<? extends Annotation>> annotations)108   protected void process(
109       TypeElement module, ImmutableSet<Class<? extends Annotation>> annotations) {
110     if (processedModuleElements.contains(module)) {
111       return;
112     }
113     // For backwards compatibility, we allow a companion object to be annotated with @Module even
114     // though it's no longer required. However, we skip processing the companion object itself
115     // because it will now be processed when processing the companion object's enclosing class.
116     if (metadataUtil.isCompanionObjectClass(module)) {
117       // TODO(danysantiago): Be strict about annotating companion objects with @Module,
118       //  i.e. tell user to annotate parent instead.
119       return;
120     }
121     ValidationReport<TypeElement> report = moduleValidator.validate(module);
122     report.printMessagesTo(messager);
123     if (report.isClean()) {
124       generateForMethodsIn(module);
125       if (metadataUtil.hasEnclosedCompanionObject(module)) {
126         generateForMethodsIn(metadataUtil.getEnclosedCompanionObject(module));
127       }
128     }
129     processedModuleElements.add(module);
130   }
131 
generateForMethodsIn(TypeElement module)132   private void generateForMethodsIn(TypeElement module) {
133     for (ExecutableElement method : methodsIn(module.getEnclosedElements())) {
134       if (isAnnotationPresent(method, Provides.class)) {
135         generate(factoryGenerator, bindingFactory.providesMethodBinding(method, module));
136       } else if (isAnnotationPresent(method, Produces.class)) {
137         generate(producerFactoryGenerator, bindingFactory.producesMethodBinding(method, module));
138       } else if (isAnnotationPresent(method, Binds.class)) {
139         inaccessibleMapKeyProxyGenerator.generate(bindsMethodBinding(module, method), messager);
140       }
141     }
142     moduleConstructorProxyGenerator.generate(module, messager);
143   }
144 
generate( SourceFileGenerator<B> generator, B binding)145   private <B extends ContributionBinding> void generate(
146       SourceFileGenerator<B> generator, B binding) {
147     generator.generate(binding, messager);
148     inaccessibleMapKeyProxyGenerator.generate(binding, messager);
149   }
150 
bindsMethodBinding(TypeElement module, ExecutableElement method)151   private ContributionBinding bindsMethodBinding(TypeElement module, ExecutableElement method) {
152     return bindingFactory.unresolvedDelegateBinding(
153         delegateDeclarationFactory.create(method, module));
154   }
155 }
156