• 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.aggregateddeps;
18 
19 import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.base.Preconditions.checkState;
22 import static com.google.common.collect.Iterables.getOnlyElement;
23 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
24 
25 import androidx.room.compiler.processing.XAnnotation;
26 import androidx.room.compiler.processing.XAnnotationValue;
27 import androidx.room.compiler.processing.XProcessingEnv;
28 import androidx.room.compiler.processing.XTypeElement;
29 import com.google.auto.value.AutoValue;
30 import com.google.common.collect.ImmutableSet;
31 import com.squareup.javapoet.ClassName;
32 import dagger.hilt.processor.internal.AggregatedElements;
33 import dagger.hilt.processor.internal.ClassNames;
34 import dagger.hilt.processor.internal.root.ir.AggregatedDepsIr;
35 import java.util.Optional;
36 import java.util.stream.Collectors;
37 
38 /**
39  * A class that represents the values stored in an {@link
40  * dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps} annotation.
41  */
42 @AutoValue
43 public abstract class AggregatedDepsMetadata {
44   private static final String AGGREGATED_DEPS_PACKAGE = "hilt_aggregated_deps";
45 
46   enum DependencyType {
47     MODULE,
48     ENTRY_POINT,
49     COMPONENT_ENTRY_POINT
50   }
51 
52   /** Returns the aggregating element */
aggregatingElement()53   public abstract XTypeElement aggregatingElement();
54 
testElement()55   public abstract Optional<XTypeElement> testElement();
56 
componentElements()57   public abstract ImmutableSet<XTypeElement> componentElements();
58 
dependencyType()59   abstract DependencyType dependencyType();
60 
dependency()61   public abstract XTypeElement dependency();
62 
replacedDependencies()63   public abstract ImmutableSet<XTypeElement> replacedDependencies();
64 
isModule()65   public boolean isModule() {
66     return dependencyType() == DependencyType.MODULE;
67   }
68 
69   /** Returns metadata for all aggregated elements in the aggregating package. */
from(XProcessingEnv env)70   public static ImmutableSet<AggregatedDepsMetadata> from(XProcessingEnv env) {
71     return from(AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, env));
72   }
73 
74   /** Returns metadata for each aggregated element. */
from( ImmutableSet<XTypeElement> aggregatedElements)75   public static ImmutableSet<AggregatedDepsMetadata> from(
76       ImmutableSet<XTypeElement> aggregatedElements) {
77     return aggregatedElements.stream()
78         .map(aggregatedElement -> create(aggregatedElement, getProcessingEnv(aggregatedElement)))
79         .collect(toImmutableSet());
80   }
81 
toIr(AggregatedDepsMetadata metadata)82   public static AggregatedDepsIr toIr(AggregatedDepsMetadata metadata) {
83     return new AggregatedDepsIr(
84         metadata.aggregatingElement().getClassName(),
85         metadata.componentElements().stream()
86             .map(XTypeElement::getClassName)
87             .map(ClassName::canonicalName)
88             .collect(Collectors.toList()),
89         metadata
90             .testElement()
91             .map(XTypeElement::getClassName)
92             .map(ClassName::canonicalName)
93             .orElse(null),
94         metadata.replacedDependencies().stream()
95             .map(XTypeElement::getClassName)
96             .map(ClassName::canonicalName)
97             .collect(Collectors.toList()),
98         metadata.dependencyType() == DependencyType.MODULE
99             ? metadata.dependency().getClassName().canonicalName()
100             : null,
101         metadata.dependencyType() == DependencyType.ENTRY_POINT
102             ? metadata.dependency().getClassName().canonicalName()
103             : null,
104         metadata.dependencyType() == DependencyType.COMPONENT_ENTRY_POINT
105             ? metadata.dependency().getClassName().canonicalName()
106             : null);
107   }
108 
create(XTypeElement element, XProcessingEnv env)109   private static AggregatedDepsMetadata create(XTypeElement element, XProcessingEnv env) {
110     checkState(
111         element.hasAnnotation(ClassNames.AGGREGATED_DEPS),
112         "Missing @AggregatedDeps annotation on %s",
113         element.getClassName().canonicalName());
114     XAnnotation annotation = element.getAnnotation(ClassNames.AGGREGATED_DEPS);
115     return new AutoValue_AggregatedDepsMetadata(
116         element,
117         getTestElement(annotation.getAnnotationValue("test"), env),
118         getComponents(annotation.getAnnotationValue("components"), env),
119         getDependencyType(
120             annotation.getAnnotationValue("modules"),
121             annotation.getAnnotationValue("entryPoints"),
122             annotation.getAnnotationValue("componentEntryPoints")),
123         getDependency(
124             annotation.getAnnotationValue("modules"),
125             annotation.getAnnotationValue("entryPoints"),
126             annotation.getAnnotationValue("componentEntryPoints"),
127             env),
128         getReplacedDependencies(annotation.getAnnotationValue("replaces"), env));
129   }
130 
getTestElement( XAnnotationValue testValue, XProcessingEnv env)131   private static Optional<XTypeElement> getTestElement(
132       XAnnotationValue testValue, XProcessingEnv env) {
133     checkNotNull(testValue);
134     String test = testValue.asString();
135     return test.isEmpty() ? Optional.empty() : Optional.of(env.findTypeElement(test));
136   }
137 
getComponents( XAnnotationValue componentsValue, XProcessingEnv env)138   private static ImmutableSet<XTypeElement> getComponents(
139       XAnnotationValue componentsValue, XProcessingEnv env) {
140     checkNotNull(componentsValue);
141     ImmutableSet<XTypeElement> componentNames =
142         componentsValue.asStringList().stream()
143             .map(
144                 // This is a temporary hack to map the old ApplicationComponent to the new
145                 // SingletonComponent. Technically, this is only needed for backwards compatibility
146                 // with libraries using the old processor since new processors should convert to the
147                 // new SingletonComponent when generating the metadata class.
148                 componentName ->
149                     componentName.contentEquals(
150                             "dagger.hilt.android.components.ApplicationComponent")
151                         ? ClassNames.SINGLETON_COMPONENT.canonicalName()
152                         : componentName)
153             .map(env::requireTypeElement)
154             .collect(toImmutableSet());
155     checkState(!componentNames.isEmpty());
156     return componentNames;
157   }
158 
getDependencyType( XAnnotationValue modulesValue, XAnnotationValue entryPointsValue, XAnnotationValue componentEntryPointsValue)159   private static DependencyType getDependencyType(
160       XAnnotationValue modulesValue,
161       XAnnotationValue entryPointsValue,
162       XAnnotationValue componentEntryPointsValue) {
163     checkNotNull(modulesValue);
164     checkNotNull(entryPointsValue);
165     checkNotNull(componentEntryPointsValue);
166 
167     ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder();
168     if (!modulesValue.asAnnotationValueList().isEmpty()) {
169       dependencyTypes.add(DependencyType.MODULE);
170     }
171     if (!entryPointsValue.asAnnotationValueList().isEmpty()) {
172       dependencyTypes.add(DependencyType.ENTRY_POINT);
173     }
174     if (!componentEntryPointsValue.asAnnotationValueList().isEmpty()) {
175       dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT);
176     }
177     return getOnlyElement(dependencyTypes.build());
178   }
179 
getDependency( XAnnotationValue modulesValue, XAnnotationValue entryPointsValue, XAnnotationValue componentEntryPointsValue, XProcessingEnv env)180   private static XTypeElement getDependency(
181       XAnnotationValue modulesValue,
182       XAnnotationValue entryPointsValue,
183       XAnnotationValue componentEntryPointsValue,
184       XProcessingEnv env) {
185     checkNotNull(modulesValue);
186     checkNotNull(entryPointsValue);
187     checkNotNull(componentEntryPointsValue);
188 
189     String dependencyName =
190         getOnlyElement(
191                 ImmutableSet.<XAnnotationValue>builder()
192                     .addAll(modulesValue.asAnnotationValueList())
193                     .addAll(entryPointsValue.asAnnotationValueList())
194                     .addAll(componentEntryPointsValue.asAnnotationValueList())
195                     .build())
196             .asString();
197     XTypeElement dependency = env.findTypeElement(dependencyName);
198     checkNotNull(dependency, "Could not get element for %s", dependencyName);
199     return dependency;
200   }
201 
getReplacedDependencies( XAnnotationValue replacedDependenciesValue, XProcessingEnv env)202   private static ImmutableSet<XTypeElement> getReplacedDependencies(
203       XAnnotationValue replacedDependenciesValue, XProcessingEnv env) {
204     // Allow null values to support libraries using a Hilt version before @TestInstallIn was added
205     return replacedDependenciesValue == null
206         ? ImmutableSet.of()
207         : replacedDependenciesValue.asStringList().stream()
208             .map(env::requireTypeElement)
209             .map(replacedDep -> getPublicDependency(replacedDep, env))
210             .collect(toImmutableSet());
211   }
212 
213   /** Returns the public Hilt wrapper module, or the module itself if its already public. */
getPublicDependency(XTypeElement dependency, XProcessingEnv env)214   private static XTypeElement getPublicDependency(XTypeElement dependency, XProcessingEnv env) {
215     return PkgPrivateMetadata.of(dependency, ClassNames.MODULE)
216         .map(metadata -> env.requireTypeElement(metadata.generatedClassName().toString()))
217         .orElse(dependency);
218   }
219 }
220