• 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 javax.lang.model.element.Modifier.STATIC;
20 
21 import com.google.auto.common.MoreElements;
22 import com.google.auto.common.MoreTypes;
23 import com.google.auto.value.AutoValue;
24 import com.squareup.javapoet.ClassName;
25 import com.squareup.javapoet.TypeName;
26 import dagger.hilt.processor.internal.ClassNames;
27 import dagger.hilt.processor.internal.ProcessorErrors;
28 import dagger.hilt.processor.internal.Processors;
29 import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.stream.Collectors;
34 import javax.lang.model.element.Element;
35 import javax.lang.model.element.ElementKind;
36 import javax.lang.model.element.ExecutableElement;
37 import javax.lang.model.element.TypeElement;
38 import javax.lang.model.element.VariableElement;
39 import javax.lang.model.type.TypeKind;
40 import javax.lang.model.type.TypeMirror;
41 import javax.lang.model.util.ElementFilter;
42 
43 /** Metadata for types annotated with {@link dagger.hilt.DefineComponent.Builder}. */
44 final class DefineComponentBuilderMetadatas {
create(DefineComponentMetadatas componentMetadatas)45   static DefineComponentBuilderMetadatas create(DefineComponentMetadatas componentMetadatas) {
46     return new DefineComponentBuilderMetadatas(componentMetadatas);
47   }
48 
49   private final Map<Element, DefineComponentBuilderMetadata> builderMetadatas = new HashMap<>();
50   private final DefineComponentMetadatas componentMetadatas;
51 
DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas)52   private DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas) {
53     this.componentMetadatas = componentMetadatas;
54   }
55 
get(Element element)56   DefineComponentBuilderMetadata get(Element element) {
57     if (!builderMetadatas.containsKey(element)) {
58       builderMetadatas.put(element, getUncached(element));
59     }
60     return builderMetadatas.get(element);
61   }
62 
getUncached(Element element)63   private DefineComponentBuilderMetadata getUncached(Element element) {
64     ProcessorErrors.checkState(
65         Processors.hasAnnotation(element, ClassNames.DEFINE_COMPONENT_BUILDER),
66         element,
67         "%s, expected to be annotated with @DefineComponent.Builder. Found: %s",
68         element,
69         element.getAnnotationMirrors());
70 
71     // TODO(bcorso): Allow abstract classes?
72     ProcessorErrors.checkState(
73         element.getKind().equals(ElementKind.INTERFACE),
74         element,
75         "@DefineComponent.Builder is only allowed on interfaces. Found: %s",
76         element);
77     TypeElement builder = MoreElements.asType(element);
78 
79     // TODO(bcorso): Allow extending interfaces?
80     ProcessorErrors.checkState(
81         builder.getInterfaces().isEmpty(),
82         builder,
83         "@DefineComponent.Builder %s, cannot extend a super class or interface. Found: %s",
84         builder,
85         builder.getInterfaces());
86 
87     // TODO(bcorso): Allow type parameters?
88     ProcessorErrors.checkState(
89         builder.getTypeParameters().isEmpty(),
90         builder,
91         "@DefineComponent.Builder %s, cannot have type parameters.",
92         builder.asType());
93 
94     List<VariableElement> nonStaticFields =
95         ElementFilter.fieldsIn(builder.getEnclosedElements()).stream()
96             .filter(method -> !method.getModifiers().contains(STATIC))
97             .collect(Collectors.toList());
98     ProcessorErrors.checkState(
99         nonStaticFields.isEmpty(),
100         builder,
101         "@DefineComponent.Builder %s, cannot have non-static fields. Found: %s",
102         builder,
103         nonStaticFields);
104 
105     List<ExecutableElement> buildMethods =
106         ElementFilter.methodsIn(builder.getEnclosedElements()).stream()
107             .filter(method -> !method.getModifiers().contains(STATIC))
108             .filter(method -> method.getParameters().isEmpty())
109             .collect(Collectors.toList());
110 
111     ProcessorErrors.checkState(
112         buildMethods.size() == 1,
113         builder,
114         "@DefineComponent.Builder %s, must have exactly 1 build method that takes no parameters. "
115             + "Found: %s",
116         builder,
117         buildMethods);
118 
119     ExecutableElement buildMethod = buildMethods.get(0);
120     TypeMirror component = buildMethod.getReturnType();
121     ProcessorErrors.checkState(
122         buildMethod.getReturnType().getKind().equals(TypeKind.DECLARED)
123             && Processors.hasAnnotation(
124                 MoreTypes.asTypeElement(component), ClassNames.DEFINE_COMPONENT),
125         builder,
126         "@DefineComponent.Builder method, %s#%s, must return a @DefineComponent type. Found: %s",
127         builder,
128         buildMethod,
129         component);
130 
131     List<ExecutableElement> nonStaticNonBuilderMethods =
132         ElementFilter.methodsIn(builder.getEnclosedElements()).stream()
133             .filter(method -> !method.getModifiers().contains(STATIC))
134             .filter(method -> !method.equals(buildMethod))
135             .filter(method -> !TypeName.get(method.getReturnType()).equals(ClassName.get(builder)))
136             .collect(Collectors.toList());
137 
138     ProcessorErrors.checkState(
139         nonStaticNonBuilderMethods.isEmpty(),
140         nonStaticNonBuilderMethods,
141         "@DefineComponent.Builder %s, all non-static methods must return %s or %s. Found: %s",
142         builder,
143         builder,
144         component,
145         nonStaticNonBuilderMethods);
146 
147     return new AutoValue_DefineComponentBuilderMetadatas_DefineComponentBuilderMetadata(
148         builder,
149         buildMethod,
150         componentMetadatas.get(MoreTypes.asTypeElement(component)));
151   }
152 
153   @AutoValue
154   abstract static class DefineComponentBuilderMetadata {
builder()155     abstract TypeElement builder();
156 
buildMethod()157     abstract ExecutableElement buildMethod();
158 
componentMetadata()159     abstract DefineComponentMetadata componentMetadata();
160   }
161 }
162