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