• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.hilt.processor.internal.root;
18 
19 import static dagger.hilt.processor.internal.Processors.toClassNames;
20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
21 import static javax.lang.model.element.Modifier.ABSTRACT;
22 import static javax.lang.model.element.Modifier.PUBLIC;
23 import static javax.lang.model.element.Modifier.STATIC;
24 
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ImmutableSet;
28 import com.google.common.graph.GraphBuilder;
29 import com.google.common.graph.Graphs;
30 import com.google.common.graph.MutableGraph;
31 import com.squareup.javapoet.AnnotationSpec;
32 import com.squareup.javapoet.ClassName;
33 import com.squareup.javapoet.JavaFile;
34 import com.squareup.javapoet.MethodSpec;
35 import com.squareup.javapoet.TypeSpec;
36 import dagger.hilt.processor.internal.ClassNames;
37 import dagger.hilt.processor.internal.ComponentDescriptor;
38 import dagger.hilt.processor.internal.ComponentGenerator;
39 import dagger.hilt.processor.internal.ComponentNames;
40 import dagger.hilt.processor.internal.ComponentTree;
41 import dagger.hilt.processor.internal.Processors;
42 import java.io.IOException;
43 import java.util.Optional;
44 import javax.annotation.processing.ProcessingEnvironment;
45 import javax.lang.model.element.Modifier;
46 
47 /** Generates components and any other classes needed for a root. */
48 final class RootGenerator {
49 
generate(RootMetadata metadata, ProcessingEnvironment env)50   static void generate(RootMetadata metadata, ProcessingEnvironment env) throws IOException {
51     new RootGenerator(
52         RootMetadata.copyWithNewTree(
53             metadata,
54             filterDescriptors(metadata.componentTree())),
55         env).generateComponents();
56   }
57 
58   private final RootMetadata metadata;
59   private final ProcessingEnvironment env;
60   private final Root root;
61 
RootGenerator(RootMetadata metadata, ProcessingEnvironment env)62   private RootGenerator(RootMetadata metadata, ProcessingEnvironment env) {
63     this.metadata = metadata;
64     this.env = env;
65     this.root = metadata.root();
66   }
67 
generateComponents()68   private void generateComponents() throws IOException {
69 
70     // TODO(bcorso): Consider moving all of this logic into ComponentGenerator?
71     TypeSpec.Builder componentsWrapper =
72         TypeSpec.classBuilder(getComponentsWrapperClassName())
73             .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
74             .addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build());
75 
76     Processors.addGeneratedAnnotation(componentsWrapper, env, ClassNames.ROOT_PROCESSOR.toString());
77 
78     ImmutableMap<ComponentDescriptor, ClassName> subcomponentBuilderModules =
79         subcomponentBuilderModules(componentsWrapper);
80 
81     ComponentTree componentTree = metadata.componentTree();
82     for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) {
83       ImmutableSet<ClassName> modules =
84           ImmutableSet.<ClassName>builder()
85               .addAll(toClassNames(metadata.modules(componentDescriptor.component())))
86               .addAll(
87                   componentTree.childrenOf(componentDescriptor).stream()
88                       .map(subcomponentBuilderModules::get)
89                       .collect(toImmutableSet()))
90               .build();
91 
92       componentsWrapper.addType(
93           new ComponentGenerator(
94                   env,
95                   getComponentClassName(componentDescriptor),
96                   root.element(),
97                   Optional.empty(),
98                   modules,
99                   metadata.entryPoints(componentDescriptor.component()),
100                   metadata.scopes(componentDescriptor.component()),
101                   ImmutableList.of(),
102                   componentAnnotation(componentDescriptor),
103                   componentBuilder(componentDescriptor))
104               .generate().toBuilder().addModifiers(Modifier.STATIC).build());
105     }
106 
107     RootFileFormatter.write(
108         JavaFile.builder(root.classname().packageName(), componentsWrapper.build()).build(),
109         env.getFiler());
110   }
111 
filterDescriptors(ComponentTree componentTree)112   private static ComponentTree filterDescriptors(ComponentTree componentTree) {
113     MutableGraph<ComponentDescriptor> graph =
114         GraphBuilder.from(componentTree.graph()).build();
115 
116     componentTree.graph().nodes().forEach(graph::addNode);
117     componentTree.graph().edges().forEach(graph::putEdge);
118 
119     // Remove components that do not have builders (besides the root component) since if
120     // we didn't find any builder class, then we don't need to generate the component
121     // since it would be inaccessible.
122     componentTree.getComponentDescriptors().stream()
123         .filter(descriptor -> !descriptor.isRoot() && !descriptor.creator().isPresent())
124         .forEach(graph::removeNode);
125 
126     // The graph may still have nodes that are children of components that don't have builders,
127     // so we need to find reachable nodes from the root and create a new graph to remove those.
128     // We reuse the root from the original tree since it should not have been removed.
129     return ComponentTree.from(Graphs.reachableNodes(graph, componentTree.root()));
130   }
131 
subcomponentBuilderModules( TypeSpec.Builder componentsWrapper)132   private ImmutableMap<ComponentDescriptor, ClassName> subcomponentBuilderModules(
133       TypeSpec.Builder componentsWrapper) throws IOException {
134     ImmutableMap.Builder<ComponentDescriptor, ClassName> modules = ImmutableMap.builder();
135     for (ComponentDescriptor descriptor : metadata.componentTree().getComponentDescriptors()) {
136       // Root component builders don't have subcomponent builder modules
137       if (!descriptor.isRoot() && descriptor.creator().isPresent()) {
138         ClassName component = getComponentClassName(descriptor);
139         ClassName builder = descriptor.creator().get();
140         ClassName module = component.peerClass(component.simpleName() + "BuilderModule");
141         componentsWrapper.addType(subcomponentBuilderModule(component, builder, module));
142         modules.put(descriptor, module);
143       }
144     }
145     return modules.build();
146   }
147 
148   // Generates:
149   // @Module(subcomponents = FooSubcomponent.class)
150   // interface FooSubcomponentBuilderModule {
151   //   @Binds FooSubcomponentInterfaceBuilder bind(FooSubcomponent.Builder builder);
152   // }
subcomponentBuilderModule( ClassName componentName, ClassName builderName, ClassName moduleName)153   private TypeSpec subcomponentBuilderModule(
154       ClassName componentName, ClassName builderName, ClassName moduleName) throws IOException {
155     TypeSpec.Builder subcomponentBuilderModule =
156         TypeSpec.interfaceBuilder(moduleName)
157             .addOriginatingElement(root.element())
158             .addModifiers(ABSTRACT)
159             .addAnnotation(
160                 AnnotationSpec.builder(ClassNames.MODULE)
161                     .addMember("subcomponents", "$T.class", componentName)
162                     .build())
163             .addAnnotation(ClassNames.DISABLE_INSTALL_IN_CHECK)
164             .addMethod(
165                 MethodSpec.methodBuilder("bind")
166                     .addModifiers(ABSTRACT, PUBLIC)
167                     .addAnnotation(ClassNames.BINDS)
168                     .returns(builderName)
169                     .addParameter(componentName.nestedClass("Builder"), "builder")
170                     .build());
171 
172     Processors.addGeneratedAnnotation(
173         subcomponentBuilderModule, env, ClassNames.ROOT_PROCESSOR.toString());
174 
175     return subcomponentBuilderModule.build();
176   }
177 
componentBuilder(ComponentDescriptor descriptor)178   private Optional<TypeSpec> componentBuilder(ComponentDescriptor descriptor) {
179     return descriptor
180         .creator()
181         .map(
182             creator ->
183                 TypeSpec.interfaceBuilder("Builder")
184                     .addOriginatingElement(root.element())
185                     .addModifiers(STATIC, ABSTRACT)
186                     .addSuperinterface(creator)
187                     .addAnnotation(componentBuilderAnnotation(descriptor))
188                     .build());
189   }
190 
componentAnnotation(ComponentDescriptor componentDescriptor)191   private ClassName componentAnnotation(ComponentDescriptor componentDescriptor) {
192     if (!componentDescriptor.isRoot()
193         ) {
194       return ClassNames.SUBCOMPONENT;
195     } else {
196       return ClassNames.COMPONENT;
197     }
198   }
199 
componentBuilderAnnotation(ComponentDescriptor componentDescriptor)200   private ClassName componentBuilderAnnotation(ComponentDescriptor componentDescriptor) {
201     if (componentDescriptor.isRoot()) {
202       return ClassNames.COMPONENT_BUILDER;
203     } else {
204       return ClassNames.SUBCOMPONENT_BUILDER;
205     }
206   }
207 
getPartialRootModuleClassName()208   private ClassName getPartialRootModuleClassName() {
209     return getComponentsWrapperClassName().nestedClass("PartialRootModule");
210   }
211 
getComponentsWrapperClassName()212   private ClassName getComponentsWrapperClassName() {
213     return ComponentNames.generatedComponentsWrapper(root.classname());
214   }
215 
getComponentClassName(ComponentDescriptor componentDescriptor)216   private ClassName getComponentClassName(ComponentDescriptor componentDescriptor) {
217     return ComponentNames.generatedComponent(root.classname(), componentDescriptor.component());
218   }
219 }
220