• 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 com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Preconditions.checkState;
21 import static com.google.common.collect.Iterables.getOnlyElement;
22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
23 
24 import com.google.auto.value.AutoValue;
25 import com.google.common.collect.ImmutableMap;
26 import com.google.common.collect.ImmutableSet;
27 import dagger.hilt.processor.internal.AggregatedElements;
28 import dagger.hilt.processor.internal.AnnotationValues;
29 import dagger.hilt.processor.internal.ClassNames;
30 import dagger.hilt.processor.internal.Processors;
31 import java.util.Optional;
32 import javax.lang.model.element.AnnotationMirror;
33 import javax.lang.model.element.AnnotationValue;
34 import javax.lang.model.element.TypeElement;
35 import javax.lang.model.util.Elements;
36 
37 /**
38  * A class that represents the values stored in an {@link
39  * dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps} annotation.
40  */
41 @AutoValue
42 abstract class AggregatedDepsMetadata {
43   private static final String AGGREGATED_DEPS_PACKAGE = "hilt_aggregated_deps";
44 
45   enum DependencyType {
46     MODULE,
47     ENTRY_POINT,
48     COMPONENT_ENTRY_POINT
49   }
50 
testElement()51   abstract Optional<TypeElement> testElement();
52 
componentElements()53   abstract ImmutableSet<TypeElement> componentElements();
54 
dependencyType()55   abstract DependencyType dependencyType();
56 
dependency()57   abstract TypeElement dependency();
58 
replacedDependencies()59   abstract ImmutableSet<TypeElement> replacedDependencies();
60 
61   /** Returns all aggregated deps in the aggregating package. */
from(Elements elements)62   public static ImmutableSet<AggregatedDepsMetadata> from(Elements elements) {
63     return AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, elements)
64         .stream()
65         .map(aggregatedElement -> create(aggregatedElement, elements))
66         .collect(toImmutableSet());
67   }
68 
create(TypeElement element, Elements elements)69   private static AggregatedDepsMetadata create(TypeElement element, Elements elements) {
70     AnnotationMirror annotationMirror =
71         Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS);
72 
73     ImmutableMap<String, AnnotationValue> values =
74         Processors.getAnnotationValues(elements, annotationMirror);
75 
76     return new AutoValue_AggregatedDepsMetadata(
77         getTestElement(values.get("test"), elements),
78         getComponents(values.get("components"), elements),
79         getDependencyType(
80             values.get("modules"),
81             values.get("entryPoints"),
82             values.get("componentEntryPoints")),
83         getDependency(
84             values.get("modules"),
85             values.get("entryPoints"),
86             values.get("componentEntryPoints"),
87             elements),
88         getReplacedDependencies(values.get("replaces"), elements));
89   }
90 
getTestElement( AnnotationValue testValue, Elements elements)91   private static Optional<TypeElement> getTestElement(
92       AnnotationValue testValue, Elements elements) {
93     checkNotNull(testValue);
94     String test = AnnotationValues.getString(testValue);
95     return test.isEmpty() ? Optional.empty() : Optional.of(elements.getTypeElement(test));
96   }
97 
getComponents( AnnotationValue componentsValue, Elements elements)98   private static ImmutableSet<TypeElement> getComponents(
99       AnnotationValue componentsValue, Elements elements) {
100     checkNotNull(componentsValue);
101     ImmutableSet<TypeElement> componentNames =
102         AnnotationValues.getAnnotationValues(componentsValue).stream()
103             .map(AnnotationValues::getString)
104             .map(
105                 // This is a temporary hack to map the old ApplicationComponent to the new
106                 // SingletonComponent. Technically, this is only needed for backwards compatibility
107                 // with libraries using the old processor since new processors should convert to the
108                 // new SingletonComponent when generating the metadata class.
109                 componentName ->
110                     componentName.contentEquals(
111                             "dagger.hilt.android.components.ApplicationComponent")
112                         ? ClassNames.SINGLETON_COMPONENT.canonicalName()
113                         : componentName)
114             .map(elements::getTypeElement)
115             .collect(toImmutableSet());
116     checkState(!componentNames.isEmpty());
117     return componentNames;
118   }
119 
getDependencyType( AnnotationValue modulesValue, AnnotationValue entryPointsValue, AnnotationValue componentEntryPointsValue)120   private static DependencyType getDependencyType(
121       AnnotationValue modulesValue,
122       AnnotationValue entryPointsValue,
123       AnnotationValue componentEntryPointsValue) {
124     checkNotNull(modulesValue);
125     checkNotNull(entryPointsValue);
126     checkNotNull(componentEntryPointsValue);
127 
128     ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder();
129     if (!AnnotationValues.getAnnotationValues(modulesValue).isEmpty()) {
130       dependencyTypes.add(DependencyType.MODULE);
131     }
132     if (!AnnotationValues.getAnnotationValues(entryPointsValue).isEmpty()) {
133       dependencyTypes.add(DependencyType.ENTRY_POINT);
134     }
135     if (!AnnotationValues.getAnnotationValues(componentEntryPointsValue).isEmpty()) {
136       dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT);
137     }
138     return getOnlyElement(dependencyTypes.build());
139   }
140 
getDependency( AnnotationValue modulesValue, AnnotationValue entryPointsValue, AnnotationValue componentEntryPointsValue, Elements elements)141   private static TypeElement getDependency(
142       AnnotationValue modulesValue,
143       AnnotationValue entryPointsValue,
144       AnnotationValue componentEntryPointsValue,
145       Elements elements) {
146     checkNotNull(modulesValue);
147     checkNotNull(entryPointsValue);
148     checkNotNull(componentEntryPointsValue);
149 
150     return elements.getTypeElement(
151         AnnotationValues.getString(
152             getOnlyElement(
153                 ImmutableSet.<AnnotationValue>builder()
154                     .addAll(AnnotationValues.getAnnotationValues(modulesValue))
155                     .addAll(AnnotationValues.getAnnotationValues(entryPointsValue))
156                     .addAll(AnnotationValues.getAnnotationValues(componentEntryPointsValue))
157                     .build())));
158   }
159 
getReplacedDependencies( AnnotationValue replacedDependenciesValue, Elements elements)160   private static ImmutableSet<TypeElement> getReplacedDependencies(
161       AnnotationValue replacedDependenciesValue, Elements elements) {
162     // Allow null values to support libraries using a Hilt version before @TestInstallIn was added
163     return replacedDependenciesValue == null
164         ? ImmutableSet.of()
165         : AnnotationValues.getAnnotationValues(replacedDependenciesValue).stream()
166             .map(AnnotationValues::getString)
167             .map(elements::getTypeElement)
168             .map(replacedDep -> getPublicDependency(replacedDep, elements))
169             .collect(toImmutableSet());
170   }
171 
172   /** Returns the public Hilt wrapper module, or the module itself if its already public. */
getPublicDependency(TypeElement dependency, Elements elements)173   private static TypeElement getPublicDependency(TypeElement dependency, Elements elements) {
174     return PkgPrivateMetadata.of(elements, dependency, ClassNames.MODULE)
175         .map(metadata -> elements.getTypeElement(metadata.generatedClassName().toString()))
176         .orElse(dependency);
177   }
178 }
179