• 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 androidx.room.compiler.processing.XElementKt.isTypeElement;
20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
21 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
22 import static java.util.stream.Collectors.joining;
23 
24 import androidx.room.compiler.processing.XAnnotation;
25 import androidx.room.compiler.processing.XAnnotationValue;
26 import androidx.room.compiler.processing.XElement;
27 import androidx.room.compiler.processing.XExecutableElement;
28 import androidx.room.compiler.processing.XTypeElement;
29 import com.google.auto.value.AutoValue;
30 import com.google.common.collect.ImmutableList;
31 import com.squareup.javapoet.ClassName;
32 import dagger.hilt.processor.internal.ClassNames;
33 import dagger.hilt.processor.internal.ProcessorErrors;
34 import dagger.hilt.processor.internal.Processors;
35 import dagger.internal.codegen.xprocessing.XAnnotations;
36 import dagger.internal.codegen.xprocessing.XElements;
37 import dagger.internal.codegen.xprocessing.XTypes;
38 import java.util.HashMap;
39 import java.util.LinkedHashSet;
40 import java.util.Map;
41 import java.util.Optional;
42 
43 /** Metadata for types annotated with {@link dagger.hilt.DefineComponent}. */
44 final class DefineComponentMetadatas {
create()45   static DefineComponentMetadatas create() {
46     return new DefineComponentMetadatas();
47   }
48 
49   private final Map<XElement, DefineComponentMetadata> metadatas = new HashMap<>();
50 
DefineComponentMetadatas()51   private DefineComponentMetadatas() {}
52 
53   /** Returns the metadata for an element annotated with {@link dagger.hilt.DefineComponent}. */
get(XElement element)54   DefineComponentMetadata get(XElement element) {
55     return get(element, new LinkedHashSet<>());
56   }
57 
get(XElement element, LinkedHashSet<XElement> childPath)58   private DefineComponentMetadata get(XElement element, LinkedHashSet<XElement> childPath) {
59     if (!metadatas.containsKey(element)) {
60       metadatas.put(element, getUncached(element, childPath));
61     }
62     return metadatas.get(element);
63   }
64 
getUncached( XElement element, LinkedHashSet<XElement> childPath)65   private DefineComponentMetadata getUncached(
66       XElement element, LinkedHashSet<XElement> childPath) {
67     ProcessorErrors.checkState(
68         childPath.add(element),
69         element,
70         "@DefineComponent cycle: %s -> %s",
71         childPath.stream().map(XElements::toStableString).collect(joining(" -> ")),
72         XElements.toStableString(element));
73 
74     ProcessorErrors.checkState(
75         element.hasAnnotation(ClassNames.DEFINE_COMPONENT),
76         element,
77         "%s, expected to be annotated with @DefineComponent. Found: %s",
78         XElements.toStableString(element),
79         element.getAllAnnotations().stream()
80             .map(XAnnotations::toStableString)
81             .collect(toImmutableList()));
82 
83     // TODO(bcorso): Allow abstract classes?
84     ProcessorErrors.checkState(
85         isTypeElement(element) && asTypeElement(element).isInterface(),
86         element,
87         "@DefineComponent is only allowed on interfaces. Found: %s",
88         XElements.toStableString(element));
89     XTypeElement component = asTypeElement(element);
90 
91     // TODO(bcorso): Allow extending interfaces?
92     ProcessorErrors.checkState(
93         component.getSuperInterfaces().isEmpty(),
94         component,
95         "@DefineComponent %s, cannot extend a super class or interface. Found: %s",
96         XElements.toStableString(component),
97         component.getSuperInterfaces().stream()
98             .map(XTypes::toStableString)
99             .collect(toImmutableList()));
100 
101     // TODO(bcorso): Allow type parameters?
102     ProcessorErrors.checkState(
103         component.getTypeParameters().isEmpty(),
104         component,
105         "@DefineComponent %s, cannot have type parameters.",
106         XTypes.toStableString(component.getType()));
107 
108     // TODO(bcorso): Allow non-static abstract methods (aka EntryPoints)?
109     ImmutableList<XExecutableElement> nonStaticMethods =
110         component.getDeclaredMethods().stream()
111             .filter(method -> !method.isStatic())
112             .collect(toImmutableList());
113 
114     ProcessorErrors.checkState(
115         nonStaticMethods.isEmpty(),
116         component,
117         "@DefineComponent %s, cannot have non-static methods. Found: %s",
118         XElements.toStableString(component),
119         nonStaticMethods.stream()
120             .map(XElements::toStableString)
121             .collect(toImmutableList()));
122 
123     // No need to check non-static fields since interfaces can't have them.
124 
125     ImmutableList<XTypeElement> scopes =
126         Processors.getScopeAnnotations(component).stream()
127             .map(XAnnotation::getTypeElement)
128             .collect(toImmutableList());
129 
130     ImmutableList<XAnnotation> aliasScopes =
131         ImmutableList.copyOf(component.getAnnotationsAnnotatedWith(ClassNames.ALIAS_OF));
132     ProcessorErrors.checkState(
133         aliasScopes.isEmpty(),
134         component,
135         "@DefineComponent %s, references invalid scope(s) annotated with @AliasOf. "
136             + "@DefineComponent scopes cannot be aliases of other scopes: %s",
137         XElements.toStableString(component),
138         aliasScopes.stream().map(XAnnotations::toStableString).collect(toImmutableList()));
139 
140     XAnnotation annotation = component.getAnnotation(ClassNames.DEFINE_COMPONENT);
141     XAnnotationValue parentValue = annotation.getAnnotationValue("parent");
142 
143     ProcessorErrors.checkState(
144         !"<error>".contentEquals(parentValue.getValue().toString()),
145         component,
146         "@DefineComponent %s, references an invalid parent type: %s",
147         XElements.toStableString(component),
148         XAnnotations.toStableString(annotation));
149 
150     XTypeElement parent = parentValue.asType().getTypeElement();
151 
152     ProcessorErrors.checkState(
153         parent.getClassName().equals(ClassNames.DEFINE_COMPONENT_NO_PARENT)
154             || parent.hasAnnotation(ClassNames.DEFINE_COMPONENT),
155         component,
156         "@DefineComponent %s, references a type not annotated with @DefineComponent: %s",
157         XElements.toStableString(component),
158         XElements.toStableString(parent));
159 
160     Optional<DefineComponentMetadata> parentComponent =
161         parent.getClassName().equals(ClassNames.DEFINE_COMPONENT_NO_PARENT)
162             ? Optional.empty()
163             : Optional.of(get(parent, childPath));
164 
165     ClassName componentClassName = component.getClassName();
166 
167     ProcessorErrors.checkState(
168         parentComponent.isPresent()
169             || componentClassName.equals(ClassNames.SINGLETON_COMPONENT),
170         component,
171         "@DefineComponent %s is missing a parent declaration.\n"
172             + "Please declare the parent, for example: @DefineComponent(parent ="
173             + " SingletonComponent.class)",
174         XElements.toStableString(component));
175 
176     ProcessorErrors.checkState(
177         componentClassName.equals(ClassNames.SINGLETON_COMPONENT)
178         || !componentClassName.simpleName().equals(ClassNames.SINGLETON_COMPONENT.simpleName()),
179         component,
180         "Cannot have a component with the same simple name as the reserved %s: %s",
181         ClassNames.SINGLETON_COMPONENT.simpleName(),
182         componentClassName.canonicalName());
183 
184     return new AutoValue_DefineComponentMetadatas_DefineComponentMetadata(
185         component, scopes, parentComponent);
186   }
187 
188   @AutoValue
189   abstract static class DefineComponentMetadata {
190 
191     /** Returns the component annotated with {@link dagger.hilt.DefineComponent}. */
component()192     abstract XTypeElement component();
193 
194     /** Returns the scopes of the component. */
scopes()195     abstract ImmutableList<XTypeElement> scopes();
196 
197     /** Returns the parent component, if one exists. */
parentMetadata()198     abstract Optional<DefineComponentMetadata> parentMetadata();
199 
isRoot()200     boolean isRoot() {
201       return !parentMetadata().isPresent();
202     }
203   }
204 }
205