• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.definecomponent;
18 
19 import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue;
20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
22 
23 import com.google.auto.common.MoreElements;
24 import com.google.auto.value.AutoValue;
25 import com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.collect.ListMultimap;
28 import com.squareup.javapoet.ClassName;
29 import dagger.hilt.processor.internal.AnnotationValues;
30 import dagger.hilt.processor.internal.ClassNames;
31 import dagger.hilt.processor.internal.ComponentDescriptor;
32 import dagger.hilt.processor.internal.ComponentTree;
33 import dagger.hilt.processor.internal.ProcessorErrors;
34 import dagger.hilt.processor.internal.Processors;
35 import dagger.hilt.processor.internal.definecomponent.DefineComponentBuilderMetadatas.DefineComponentBuilderMetadata;
36 import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata;
37 import java.util.HashMap;
38 import java.util.LinkedHashMap;
39 import java.util.Map;
40 import java.util.Optional;
41 import javax.lang.model.element.AnnotationMirror;
42 import javax.lang.model.element.AnnotationValue;
43 import javax.lang.model.element.Element;
44 import javax.lang.model.element.PackageElement;
45 import javax.lang.model.element.TypeElement;
46 import javax.lang.model.util.Elements;
47 
48 /**
49  * A utility class for getting {@link DefineComponentMetadata} and {@link
50  * DefineComponentBuilderMetadata}.
51  */
52 public final class DefineComponents {
53   static final String AGGREGATING_PACKAGE =
54       DefineComponents.class.getPackage().getName() + ".codegen";
55 
create()56   public static DefineComponents create() {
57     return new DefineComponents();
58   }
59 
60   private final Map<Element, ComponentDescriptor> componentDescriptors = new HashMap<>();
61   private final DefineComponentMetadatas componentMetadatas = DefineComponentMetadatas.create();
62   private final DefineComponentBuilderMetadatas componentBuilderMetadatas =
63       DefineComponentBuilderMetadatas.create(componentMetadatas);
64 
DefineComponents()65   private DefineComponents() {}
66 
67   /** Returns the {@link ComponentDescriptor} for the given component element. */
68   // TODO(b/144940889): This descriptor doesn't contain the "creator" or the "installInName".
componentDescriptor(Element element)69   public ComponentDescriptor componentDescriptor(Element element) {
70     if (!componentDescriptors.containsKey(element)) {
71       componentDescriptors.put(element, uncachedComponentDescriptor(element));
72     }
73     return componentDescriptors.get(element);
74   }
75 
uncachedComponentDescriptor(Element element)76   private ComponentDescriptor uncachedComponentDescriptor(Element element) {
77     DefineComponentMetadata metadata = componentMetadatas.get(element);
78     ComponentDescriptor.Builder builder =
79         ComponentDescriptor.builder()
80             .component(ClassName.get(metadata.component()))
81             .scopes(metadata.scopes().stream().map(ClassName::get).collect(toImmutableSet()));
82 
83 
84     metadata.parentMetadata()
85         .map(DefineComponentMetadata::component)
86         .map(this::componentDescriptor)
87         .ifPresent(builder::parent);
88 
89     return builder.build();
90   }
91 
92   /** Returns the {@link ComponentTree} from the aggregated {@link ComponentDescriptor}s. */
getComponentTree(Elements elements)93   public ComponentTree getComponentTree(Elements elements) {
94     AggregatedMetadata aggregatedMetadata =
95         AggregatedMetadata.from(elements, componentMetadatas, componentBuilderMetadatas);
96     ListMultimap<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMultimap =
97         ArrayListMultimap.create();
98     aggregatedMetadata.builders()
99         .forEach(builder -> builderMultimap.put(builder.componentMetadata(), builder));
100 
101     // Check that there are not multiple builders per component
102     for (DefineComponentMetadata componentMetadata : builderMultimap.keySet()) {
103       TypeElement component = componentMetadata.component();
104       ProcessorErrors.checkState(
105           builderMultimap.get(componentMetadata).size() <= 1,
106           component,
107           "Multiple @%s declarations are not allowed for @%s type, %s. Found: %s",
108           ClassNames.DEFINE_COMPONENT_BUILDER,
109           ClassNames.DEFINE_COMPONENT,
110           component,
111           builderMultimap.get(componentMetadata).stream()
112               .map(DefineComponentBuilderMetadata::builder)
113               .map(TypeElement::toString)
114               .sorted()
115               .collect(toImmutableList()));
116     }
117 
118     // Now that we know there is at most 1 builder per component, convert the Multimap to Map.
119     Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap = new LinkedHashMap<>();
120     builderMultimap.entries().forEach(e -> builderMap.put(e.getKey(), e.getValue()));
121 
122 
123     return ComponentTree.from(aggregatedMetadata.components().stream()
124         .map(componentMetadata -> toComponentDescriptor(componentMetadata, builderMap))
125         .collect(toImmutableSet()));
126   }
127 
toComponentDescriptor( DefineComponentMetadata componentMetadata, Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap)128   private static ComponentDescriptor toComponentDescriptor(
129       DefineComponentMetadata componentMetadata,
130       Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap) {
131     ComponentDescriptor.Builder builder =
132         ComponentDescriptor.builder()
133             .component(ClassName.get(componentMetadata.component()))
134             .scopes(
135                 componentMetadata.scopes().stream().map(ClassName::get).collect(toImmutableSet()));
136 
137 
138     if (builderMap.containsKey(componentMetadata)) {
139       builder.creator(ClassName.get(builderMap.get(componentMetadata).builder()));
140     }
141 
142     componentMetadata
143         .parentMetadata()
144         .map(parent -> toComponentDescriptor(parent, builderMap))
145         .ifPresent(builder::parent);
146 
147     return builder.build();
148   }
149 
150   @AutoValue
151   abstract static class AggregatedMetadata {
152     /** Returns the aggregated metadata for {@link DefineComponentClasses#component()}. */
components()153     abstract ImmutableList<DefineComponentMetadata> components();
154 
155     /** Returns the aggregated metadata for {@link DefineComponentClasses#builder()}. */
builders()156     abstract ImmutableList<DefineComponentBuilderMetadata> builders();
157 
from( Elements elements, DefineComponentMetadatas componentMetadatas, DefineComponentBuilderMetadatas componentBuilderMetadatas)158     static AggregatedMetadata from(
159         Elements elements,
160         DefineComponentMetadatas componentMetadatas,
161         DefineComponentBuilderMetadatas componentBuilderMetadatas) {
162       PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE);
163 
164       if (packageElement == null) {
165         return new AutoValue_DefineComponents_AggregatedMetadata(
166             ImmutableList.of(), ImmutableList.of());
167       }
168 
169       ImmutableList.Builder<DefineComponentMetadata> components = ImmutableList.builder();
170       ImmutableList.Builder<DefineComponentBuilderMetadata> builders = ImmutableList.builder();
171       for (Element element : packageElement.getEnclosedElements()) {
172         ProcessorErrors.checkState(
173             MoreElements.isType(element),
174             element,
175             "Only types may be in package %s. Did you add custom code in the package?",
176             packageElement);
177 
178         TypeElement typeElement = MoreElements.asType(element);
179         ProcessorErrors.checkState(
180             Processors.hasAnnotation(typeElement, ClassNames.DEFINE_COMPONENT_CLASSES),
181             typeElement,
182             "Class, %s, must be annotated with @%s. Found: %s.",
183             typeElement,
184             ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(),
185             typeElement.getAnnotationMirrors());
186 
187         Optional<TypeElement> component = defineComponentClass(elements, typeElement, "component");
188         Optional<TypeElement> builder = defineComponentClass(elements, typeElement, "builder");
189         ProcessorErrors.checkState(
190             component.isPresent() || builder.isPresent(),
191             typeElement,
192             "@DefineComponentClasses missing both `component` and `builder` members.");
193 
194         component.map(componentMetadatas::get).ifPresent(components::add);
195         builder.map(componentBuilderMetadatas::get).ifPresent(builders::add);
196       }
197 
198       return new AutoValue_DefineComponents_AggregatedMetadata(
199           components.build(), builders.build());
200     }
201 
defineComponentClass( Elements elements, Element element, String annotationMember)202     private static Optional<TypeElement> defineComponentClass(
203         Elements elements, Element element, String annotationMember) {
204       AnnotationMirror mirror =
205           Processors.getAnnotationMirror(element, ClassNames.DEFINE_COMPONENT_CLASSES);
206       AnnotationValue value = getAnnotationElementAndValue(mirror, annotationMember).getValue();
207       String className = AnnotationValues.getString(value);
208 
209       if (className.isEmpty()) { // The default value.
210         return Optional.empty();
211       }
212 
213       TypeElement type = elements.getTypeElement(className);
214       ProcessorErrors.checkState(
215           type != null,
216           element,
217           "%s.%s(), has invalid value: `%s`.",
218           ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(),
219           annotationMember,
220           className);
221 
222       return Optional.of(type);
223     }
224   }
225 }
226