• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.processingstep;
18 
19 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
21 
22 import androidx.room.compiler.processing.XElement;
23 import androidx.room.compiler.processing.XMessager;
24 import androidx.room.compiler.processing.XMethodElement;
25 import androidx.room.compiler.processing.XProcessingEnv;
26 import androidx.room.compiler.processing.XTypeElement;
27 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
28 import com.google.common.collect.ImmutableSet;
29 import com.google.common.collect.Sets;
30 import com.squareup.javapoet.ClassName;
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.ProductionBinding;
36 import dagger.internal.codegen.binding.ProvisionBinding;
37 import dagger.internal.codegen.javapoet.TypeNames;
38 import dagger.internal.codegen.validation.ModuleValidator;
39 import dagger.internal.codegen.validation.ValidationReport;
40 import dagger.internal.codegen.writing.InaccessibleMapKeyProxyGenerator;
41 import dagger.internal.codegen.writing.ModuleGenerator;
42 import java.util.Map;
43 import java.util.Set;
44 import javax.inject.Inject;
45 
46 /**
47  * A {@link ProcessingStep} that validates module classes and generates factories for binding
48  * methods.
49  */
50 final class ModuleProcessingStep extends TypeCheckingProcessingStep<XTypeElement> {
51   private final XMessager messager;
52   private final ModuleValidator moduleValidator;
53   private final BindingFactory bindingFactory;
54   private final SourceFileGenerator<ProvisionBinding> factoryGenerator;
55   private final SourceFileGenerator<ProductionBinding> producerFactoryGenerator;
56   private final SourceFileGenerator<XTypeElement> moduleConstructorProxyGenerator;
57   private final InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator;
58   private final DelegateDeclaration.Factory delegateDeclarationFactory;
59   private final Set<XTypeElement> processedModuleElements = Sets.newLinkedHashSet();
60 
61   @Inject
ModuleProcessingStep( XMessager messager, ModuleValidator moduleValidator, BindingFactory bindingFactory, SourceFileGenerator<ProvisionBinding> factoryGenerator, SourceFileGenerator<ProductionBinding> producerFactoryGenerator, @ModuleGenerator SourceFileGenerator<XTypeElement> moduleConstructorProxyGenerator, InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator, DelegateDeclaration.Factory delegateDeclarationFactory)62   ModuleProcessingStep(
63       XMessager messager,
64       ModuleValidator moduleValidator,
65       BindingFactory bindingFactory,
66       SourceFileGenerator<ProvisionBinding> factoryGenerator,
67       SourceFileGenerator<ProductionBinding> producerFactoryGenerator,
68       @ModuleGenerator SourceFileGenerator<XTypeElement> moduleConstructorProxyGenerator,
69       InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator,
70       DelegateDeclaration.Factory delegateDeclarationFactory) {
71     this.messager = messager;
72     this.moduleValidator = moduleValidator;
73     this.bindingFactory = bindingFactory;
74     this.factoryGenerator = factoryGenerator;
75     this.producerFactoryGenerator = producerFactoryGenerator;
76     this.moduleConstructorProxyGenerator = moduleConstructorProxyGenerator;
77     this.inaccessibleMapKeyProxyGenerator = inaccessibleMapKeyProxyGenerator;
78     this.delegateDeclarationFactory = delegateDeclarationFactory;
79   }
80 
81   @Override
annotationClassNames()82   public ImmutableSet<ClassName> annotationClassNames() {
83     return ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE);
84   }
85 
86   @Override
process( XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation)87   public ImmutableSet<XElement> process(
88       XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
89     moduleValidator.addKnownModules(
90         elementsByAnnotation.values().stream()
91             .flatMap(Set::stream)
92             // This cast is safe because @Module has @Target(ElementType.TYPE)
93             .map(XTypeElement.class::cast)
94             .collect(toImmutableSet()));
95     return super.process(env, elementsByAnnotation);
96   }
97 
98   @Override
process(XTypeElement module, ImmutableSet<ClassName> annotations)99   protected void process(XTypeElement module, ImmutableSet<ClassName> annotations) {
100     if (processedModuleElements.contains(module)) {
101       return;
102     }
103     // For backwards compatibility, we allow a companion object to be annotated with @Module even
104     // though it's no longer required. However, we skip processing the companion object itself
105     // because it will now be processed when processing the companion object's enclosing class.
106     if (module.isCompanionObject()) {
107       // TODO(danysantiago): Be strict about annotating companion objects with @Module,
108       //  i.e. tell user to annotate parent instead.
109       return;
110     }
111     ValidationReport report = moduleValidator.validate(module);
112     report.printMessagesTo(messager);
113     if (report.isClean()) {
114       generateForMethodsIn(module);
115       module.getEnclosedTypeElements().stream()
116           .filter(XTypeElement::isCompanionObject)
117           .collect(toOptional())
118           .ifPresent(this::generateForMethodsIn);
119     }
120     processedModuleElements.add(module);
121   }
122 
generateForMethodsIn(XTypeElement module)123   private void generateForMethodsIn(XTypeElement module) {
124     for (XMethodElement method : module.getDeclaredMethods()) {
125       if (method.hasAnnotation(TypeNames.PROVIDES)) {
126         generate(factoryGenerator, bindingFactory.providesMethodBinding(method, module));
127       } else if (method.hasAnnotation(TypeNames.PRODUCES)) {
128         generate(producerFactoryGenerator, bindingFactory.producesMethodBinding(method, module));
129       } else if (method.hasAnnotation(TypeNames.BINDS)) {
130         inaccessibleMapKeyProxyGenerator.generate(bindsMethodBinding(module, method), messager);
131       }
132     }
133     // We should never need to generate a constructor proxy for a companion object since we never
134     // need to call a companion object's constructor.
135     if (!module.isCompanionObject()) {
136       moduleConstructorProxyGenerator.generate(module, messager);
137     }
138   }
139 
generate( SourceFileGenerator<B> generator, B binding)140   private <B extends ContributionBinding> void generate(
141       SourceFileGenerator<B> generator, B binding) {
142     generator.generate(binding, messager);
143     inaccessibleMapKeyProxyGenerator.generate(binding, messager);
144   }
145 
bindsMethodBinding(XTypeElement module, XMethodElement method)146   private ContributionBinding bindsMethodBinding(XTypeElement module, XMethodElement method) {
147     return bindingFactory.unresolvedDelegateBinding(
148         delegateDeclarationFactory.create(method, module));
149   }
150 }
151