• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 javax.lang.model.element.Modifier.PUBLIC;
20 
21 import androidx.room.compiler.processing.XFiler.Mode;
22 import androidx.room.compiler.processing.XProcessingEnv;
23 import androidx.room.compiler.processing.XTypeElement;
24 import com.google.common.collect.ImmutableSet;
25 import com.squareup.javapoet.AnnotationSpec;
26 import com.squareup.javapoet.ClassName;
27 import com.squareup.javapoet.JavaFile;
28 import com.squareup.javapoet.TypeSpec;
29 import dagger.hilt.processor.internal.AggregatedElements;
30 import dagger.hilt.processor.internal.ClassNames;
31 import dagger.hilt.processor.internal.Processors;
32 import java.io.IOException;
33 import java.util.HashSet;
34 import java.util.Optional;
35 import java.util.Set;
36 
37 /** Generates an {@link dagger.hilt.internal.componenttreedeps.ComponentTreeDeps}. */
38 final class ComponentTreeDepsGenerator {
39   // Keeps track of already generated proxies. For correctness, this same instance of
40   // ComponentTreeDepsGenerator must be used for a given round.
41   private final Set<ClassName> generatedProxies = new HashSet<>();
42   private final XProcessingEnv env;
43   private final Mode mode;
44 
ComponentTreeDepsGenerator(XProcessingEnv env, Mode mode)45   ComponentTreeDepsGenerator(XProcessingEnv env, Mode mode) {
46     this.env = env;
47     this.mode = mode;
48   }
49 
generate(ComponentTreeDepsMetadata metadata)50   void generate(ComponentTreeDepsMetadata metadata) throws IOException {
51     ClassName name = metadata.name();
52     TypeSpec.Builder builder =
53         TypeSpec.classBuilder(name)
54             // No originating element since this is generated by the aggregating processor.
55             .addAnnotation(componentTreeDepsAnnotation(metadata));
56 
57     Processors.addGeneratedAnnotation(builder, env, ClassNames.ROOT_PROCESSOR.toString());
58 
59     env.getFiler().write(JavaFile.builder(name.packageName(), builder.build()).build(), mode);
60   }
61 
componentTreeDepsAnnotation(ComponentTreeDepsMetadata metadata)62   AnnotationSpec componentTreeDepsAnnotation(ComponentTreeDepsMetadata metadata)
63       throws IOException {
64     AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassNames.COMPONENT_TREE_DEPS);
65     addDeps(builder, metadata.aggregatedRootDeps(), "rootDeps");
66     addDeps(builder, metadata.defineComponentDeps(), "defineComponentDeps");
67     addDeps(builder, metadata.aliasOfDeps(), "aliasOfDeps");
68     addDeps(builder, metadata.aggregatedDeps(), "aggregatedDeps");
69     addDeps(builder, metadata.aggregatedUninstallModulesDeps(), "uninstallModulesDeps");
70     addDeps(builder, metadata.aggregatedEarlyEntryPointDeps(), "earlyEntryPointDeps");
71     return builder.build();
72   }
73 
addDeps(AnnotationSpec.Builder builder, ImmutableSet<XTypeElement> deps, String name)74   private void addDeps(AnnotationSpec.Builder builder, ImmutableSet<XTypeElement> deps, String name)
75       throws IOException {
76     for (XTypeElement dep : deps) {
77       builder.addMember(name, "$T.class", maybeWrapInPublicProxy(dep));
78     }
79   }
80 
81   /**
82    * This method will return the public proxy for {@code dep} if it is not public, otherwise it will
83    * return {@code dep} itself. It will also generate the proxy if it doesn't already exist.
84    *
85    * <p>Note: These proxies are only used for serialization. The proxy will be unwrapped when
86    * converting to {@link ComponentTreeDepsMetadata}.
87    *
88    * <p>Note: The public proxy is needed because Hilt versions < 2.35 generated package-private
89    * aggregating elements, which can't be referenced directly in the {@code @ComponentTreeDeps}.
90    */
maybeWrapInPublicProxy(XTypeElement dep)91   private ClassName maybeWrapInPublicProxy(XTypeElement dep) {
92     Optional<ClassName> proxyName = AggregatedElements.aggregatedElementProxyName(dep);
93     if (proxyName.isPresent()) {
94       // Check the set of already generated proxies to ensure we don't regenerate the proxy in
95       // this round. Also check that the element doesn't already exist to ensure we don't regenerate
96       // a proxy generated in a previous round.
97       if (generatedProxies.add(proxyName.get())
98           && env.findTypeElement(proxyName.get().canonicalName()) == null) {
99         generateProxy(dep, proxyName.get());
100       }
101       return proxyName.get();
102     }
103     return dep.getClassName();
104   }
105 
generateProxy(XTypeElement dep, ClassName proxyName)106   private void generateProxy(XTypeElement dep, ClassName proxyName) {
107     TypeSpec.Builder builder =
108         TypeSpec.classBuilder(proxyName)
109             .addModifiers(PUBLIC)
110             // No originating element since this is generated by the aggregating processor.
111             .addAnnotation(
112                 AnnotationSpec.builder(ClassNames.AGGREGATED_ELEMENT_PROXY)
113                     .addMember("value", "$T.class", dep.getClassName())
114                     .build());
115 
116     Processors.addGeneratedAnnotation(builder, env, ClassNames.ROOT_PROCESSOR.toString());
117     env.getFiler().write(JavaFile.builder(proxyName.packageName(), builder.build()).build(), mode);
118   }
119 }
120