• 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     XAnnotation annotation = element.getAnnotation(ClassNames.AGGREGATED_DEPS);
111 
112     return new AutoValue_AggregatedDepsMetadata(
113         element,
114         getTestElement(annotation.getAnnotationValue("test"), env),
115         getComponents(annotation.getAnnotationValue("components"), env),
116         getDependencyType(
117             annotation.getAnnotationValue("modules"),
118             annotation.getAnnotationValue("entryPoints"),
119             annotation.getAnnotationValue("componentEntryPoints")),
120         getDependency(
121             annotation.getAnnotationValue("modules"),
122             annotation.getAnnotationValue("entryPoints"),
123             annotation.getAnnotationValue("componentEntryPoints"),
124             env),
125         getReplacedDependencies(annotation.getAnnotationValue("replaces"), env));
126   }
127 
getTestElement( XAnnotationValue testValue, XProcessingEnv env)128   private static Optional<XTypeElement> getTestElement(
129       XAnnotationValue testValue, XProcessingEnv env) {
130     checkNotNull(testValue);
131     String test = testValue.asString();
132     return test.isEmpty() ? Optional.empty() : Optional.of(env.findTypeElement(test));
133   }
134 
getComponents( XAnnotationValue componentsValue, XProcessingEnv env)135   private static ImmutableSet<XTypeElement> getComponents(
136       XAnnotationValue componentsValue, XProcessingEnv env) {
137     checkNotNull(componentsValue);
138     ImmutableSet<XTypeElement> componentNames =
139         componentsValue.asStringList().stream()
140             .map(
141                 // This is a temporary hack to map the old ApplicationComponent to the new
142                 // SingletonComponent. Technically, this is only needed for backwards compatibility
143                 // with libraries using the old processor since new processors should convert to the
144                 // new SingletonComponent when generating the metadata class.
145                 componentName ->
146                     componentName.contentEquals(
147                             "dagger.hilt.android.components.ApplicationComponent")
148                         ? ClassNames.SINGLETON_COMPONENT.canonicalName()
149                         : componentName)
150             .map(env::requireTypeElement)
151             .collect(toImmutableSet());
152     checkState(!componentNames.isEmpty());
153     return componentNames;
154   }
155 
getDependencyType( XAnnotationValue modulesValue, XAnnotationValue entryPointsValue, XAnnotationValue componentEntryPointsValue)156   private static DependencyType getDependencyType(
157       XAnnotationValue modulesValue,
158       XAnnotationValue entryPointsValue,
159       XAnnotationValue componentEntryPointsValue) {
160     checkNotNull(modulesValue);
161     checkNotNull(entryPointsValue);
162     checkNotNull(componentEntryPointsValue);
163 
164     ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder();
165     if (!modulesValue.asAnnotationValueList().isEmpty()) {
166       dependencyTypes.add(DependencyType.MODULE);
167     }
168     if (!entryPointsValue.asAnnotationValueList().isEmpty()) {
169       dependencyTypes.add(DependencyType.ENTRY_POINT);
170     }
171     if (!componentEntryPointsValue.asAnnotationValueList().isEmpty()) {
172       dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT);
173     }
174     return getOnlyElement(dependencyTypes.build());
175   }
176 
getDependency( XAnnotationValue modulesValue, XAnnotationValue entryPointsValue, XAnnotationValue componentEntryPointsValue, XProcessingEnv env)177   private static XTypeElement getDependency(
178       XAnnotationValue modulesValue,
179       XAnnotationValue entryPointsValue,
180       XAnnotationValue componentEntryPointsValue,
181       XProcessingEnv env) {
182     checkNotNull(modulesValue);
183     checkNotNull(entryPointsValue);
184     checkNotNull(componentEntryPointsValue);
185 
186     String dependencyName =
187         getOnlyElement(
188                 ImmutableSet.<XAnnotationValue>builder()
189                     .addAll(modulesValue.asAnnotationValueList())
190                     .addAll(entryPointsValue.asAnnotationValueList())
191                     .addAll(componentEntryPointsValue.asAnnotationValueList())
192                     .build())
193             .asString();
194     XTypeElement dependency = env.findTypeElement(dependencyName);
195     checkNotNull(dependency, "Could not get element for %s", dependencyName);
196     return dependency;
197   }
198 
getReplacedDependencies( XAnnotationValue replacedDependenciesValue, XProcessingEnv env)199   private static ImmutableSet<XTypeElement> getReplacedDependencies(
200       XAnnotationValue replacedDependenciesValue, XProcessingEnv env) {
201     // Allow null values to support libraries using a Hilt version before @TestInstallIn was added
202     return replacedDependenciesValue == null
203         ? ImmutableSet.of()
204         : replacedDependenciesValue.asStringList().stream()
205             .map(env::requireTypeElement)
206             .map(replacedDep -> getPublicDependency(replacedDep, env))
207             .collect(toImmutableSet());
208   }
209 
210   /** Returns the public Hilt wrapper module, or the module itself if its already public. */
getPublicDependency(XTypeElement dependency, XProcessingEnv env)211   private static XTypeElement getPublicDependency(XTypeElement dependency, XProcessingEnv env) {
212     return PkgPrivateMetadata.of(dependency, ClassNames.MODULE)
213         .map(metadata -> env.requireTypeElement(metadata.generatedClassName().toString()))
214         .orElse(dependency);
215   }
216 }
217