• 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 dagger.internal.codegen.xprocessing.XTypes.isDeclared;
23 
24 import androidx.room.compiler.processing.XElement;
25 import androidx.room.compiler.processing.XFieldElement;
26 import androidx.room.compiler.processing.XMethodElement;
27 import androidx.room.compiler.processing.XType;
28 import androidx.room.compiler.processing.XTypeElement;
29 import com.google.auto.value.AutoValue;
30 import com.google.common.collect.ImmutableList;
31 import dagger.hilt.processor.internal.ClassNames;
32 import dagger.hilt.processor.internal.ProcessorErrors;
33 import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata;
34 import dagger.internal.codegen.xprocessing.XAnnotations;
35 import dagger.internal.codegen.xprocessing.XElements;
36 import dagger.internal.codegen.xprocessing.XTypes;
37 import java.util.HashMap;
38 import java.util.Map;
39 
40 /** Metadata for types annotated with {@link dagger.hilt.DefineComponent.Builder}. */
41 final class DefineComponentBuilderMetadatas {
create(DefineComponentMetadatas componentMetadatas)42   static DefineComponentBuilderMetadatas create(DefineComponentMetadatas componentMetadatas) {
43     return new DefineComponentBuilderMetadatas(componentMetadatas);
44   }
45 
46   private final Map<XElement, DefineComponentBuilderMetadata> builderMetadatas = new HashMap<>();
47   private final DefineComponentMetadatas componentMetadatas;
48 
DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas)49   private DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas) {
50     this.componentMetadatas = componentMetadatas;
51   }
52 
get(XElement element)53   DefineComponentBuilderMetadata get(XElement element) {
54     if (!builderMetadatas.containsKey(element)) {
55       builderMetadatas.put(element, getUncached(element));
56     }
57     return builderMetadatas.get(element);
58   }
59 
getUncached(XElement element)60   private DefineComponentBuilderMetadata getUncached(XElement element) {
61     ProcessorErrors.checkState(
62         element.hasAnnotation(ClassNames.DEFINE_COMPONENT_BUILDER),
63         element,
64         "%s, expected to be annotated with @DefineComponent.Builder. Found: %s",
65         XElements.toStableString(element),
66         element.getAllAnnotations().stream()
67             .map(XAnnotations::toStableString)
68             .collect(toImmutableList()));
69 
70     // TODO(bcorso): Allow abstract classes?
71     ProcessorErrors.checkState(
72         isTypeElement(element) && asTypeElement(element).isInterface(),
73         element,
74         "@DefineComponent.Builder is only allowed on interfaces. Found: %s",
75         XElements.toStableString(element));
76     XTypeElement builder = asTypeElement(element);
77 
78     // TODO(bcorso): Allow extending interfaces?
79     ProcessorErrors.checkState(
80         builder.getSuperInterfaces().isEmpty(),
81         builder,
82         "@DefineComponent.Builder %s, cannot extend a super class or interface. Found: %s",
83         XElements.toStableString(builder),
84         builder.getSuperInterfaces().stream()
85             .map(XTypes::toStableString)
86             .collect(toImmutableList()));
87 
88     // TODO(bcorso): Allow type parameters?
89     ProcessorErrors.checkState(
90         builder.getTypeParameters().isEmpty(),
91         builder,
92         "@DefineComponent.Builder %s, cannot have type parameters.",
93         XTypes.toStableString(builder.getType()));
94 
95     ImmutableList<XFieldElement> nonStaticFields =
96         builder.getDeclaredFields().stream()
97             .filter(field -> !field.isStatic())
98             .collect(toImmutableList());
99 
100     ProcessorErrors.checkState(
101         nonStaticFields.isEmpty(),
102         builder,
103         "@DefineComponent.Builder %s, cannot have non-static fields. Found: %s",
104         XElements.toStableString(builder),
105         nonStaticFields.stream()
106             .map(XElements::toStableString)
107             .collect(toImmutableList()));
108 
109     ImmutableList<XMethodElement> buildMethods =
110         builder.getDeclaredMethods().stream()
111             .filter(method -> !method.isStatic())
112             .filter(method -> method.getParameters().isEmpty())
113             .collect(toImmutableList());
114 
115     ProcessorErrors.checkState(
116         buildMethods.size() == 1,
117         builder,
118         "@DefineComponent.Builder %s, must have exactly 1 build method that takes no parameters. "
119             + "Found: %s",
120         XElements.toStableString(builder),
121         buildMethods.stream()
122             .map(XElements::toStableString)
123             .collect(toImmutableList()));
124 
125     XMethodElement buildMethod = buildMethods.get(0);
126     XType componentType = buildMethod.getReturnType();
127     ProcessorErrors.checkState(
128         isDeclared(componentType)
129             && componentType.getTypeElement().hasAnnotation(ClassNames.DEFINE_COMPONENT),
130         builder,
131         "@DefineComponent.Builder method, %s#%s, must return a @DefineComponent type. Found: %s",
132         XElements.toStableString(builder),
133         XElements.toStableString(buildMethod),
134         XTypes.toStableString(componentType));
135 
136     ImmutableList<XMethodElement> nonStaticNonBuilderMethods =
137         builder.getDeclaredMethods().stream()
138             .filter(method -> !method.isStatic())
139             .filter(method -> !method.equals(buildMethod))
140             .filter(method -> !method.getReturnType().getTypeName().equals(builder.getClassName()))
141             .collect(toImmutableList());
142 
143     ProcessorErrors.checkStateX(
144         nonStaticNonBuilderMethods.isEmpty(),
145         nonStaticNonBuilderMethods,
146         "@DefineComponent.Builder %s, all non-static methods must return %s or %s. Found: %s",
147         XElements.toStableString(builder),
148         XElements.toStableString(builder),
149         XTypes.toStableString(componentType),
150         nonStaticNonBuilderMethods.stream()
151             .map(XElements::toStableString)
152             .collect(toImmutableList()));
153 
154     return new AutoValue_DefineComponentBuilderMetadatas_DefineComponentBuilderMetadata(
155         builder,
156         buildMethod,
157         componentMetadatas.get(componentType.getTypeElement()));
158   }
159 
160   @AutoValue
161   abstract static class DefineComponentBuilderMetadata {
builder()162     abstract XTypeElement builder();
163 
buildMethod()164     abstract XMethodElement buildMethod();
165 
componentMetadata()166     abstract DefineComponentMetadata componentMetadata();
167   }
168 }
169