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