• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.android.processor.internal.customtestapplication;
18 
19 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
20 
21 import androidx.room.compiler.processing.XElement;
22 import androidx.room.compiler.processing.XElementKt;
23 import androidx.room.compiler.processing.XExecutableElement;
24 import androidx.room.compiler.processing.XFieldElement;
25 import androidx.room.compiler.processing.XTypeElement;
26 import com.google.auto.value.AutoValue;
27 import com.google.common.base.Preconditions;
28 import com.google.common.collect.ImmutableList;
29 import com.squareup.javapoet.ClassName;
30 import dagger.hilt.processor.internal.ClassNames;
31 import dagger.hilt.processor.internal.ProcessorErrors;
32 import dagger.hilt.processor.internal.Processors;
33 import dagger.internal.codegen.xprocessing.XElements;
34 
35 /** Stores the metadata for a custom base test application. */
36 @AutoValue
37 abstract class CustomTestApplicationMetadata {
38   /** Returns the annotated element. */
element()39   abstract XTypeElement element();
40 
41   /** Returns the name of the base application. */
baseAppName()42   abstract ClassName baseAppName();
43 
44   /** Returns the name of the generated application */
appName()45   ClassName appName() {
46     return Processors.append(
47         Processors.getEnclosedClassName(element().getClassName()), "_Application");
48   }
49 
of(XElement element)50   static CustomTestApplicationMetadata of(XElement element) {
51     Preconditions.checkState(
52         element.hasAnnotation(ClassNames.CUSTOM_TEST_APPLICATION),
53         "The given element, %s, is not annotated with @%s.",
54         XElements.toStableString(element),
55         ClassNames.CUSTOM_TEST_APPLICATION.simpleName());
56 
57     ProcessorErrors.checkState(
58         XElementKt.isTypeElement(element),
59         element,
60         "@%s should only be used on classes or interfaces but found: %s",
61         ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
62         XElements.toStableString(element));
63 
64     XTypeElement baseAppElement = getBaseElement(element);
65 
66     return new AutoValue_CustomTestApplicationMetadata(
67         XElements.asTypeElement(element), baseAppElement.getClassName());
68   }
69 
getBaseElement(XElement element)70   private static XTypeElement getBaseElement(XElement element) {
71     XTypeElement baseElement =
72         element.getAnnotation(ClassNames.CUSTOM_TEST_APPLICATION)
73             .getAsType("value")
74             .getTypeElement();
75 
76     XTypeElement baseSuperclassElement = baseElement;
77     while (baseSuperclassElement.getSuperClass() != null) {
78       ProcessorErrors.checkState(
79           !baseSuperclassElement.hasAnnotation(ClassNames.HILT_ANDROID_APP),
80           element,
81           "@%s value cannot be annotated with @%s. Found: %s",
82           ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
83           ClassNames.HILT_ANDROID_APP.simpleName(),
84           baseSuperclassElement.getClassName());
85 
86       ImmutableList<XFieldElement> injectFields =
87           baseSuperclassElement.getDeclaredFields().stream()
88               .filter(field -> field.hasAnnotation(ClassNames.INJECT))
89               .collect(toImmutableList());
90       ProcessorErrors.checkState(
91           injectFields.isEmpty(),
92           element,
93           "@%s does not support application classes (or super classes) with @Inject fields. Found "
94               + "%s with @Inject fields %s.",
95           ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
96           baseSuperclassElement.getClassName(),
97           injectFields.stream().map(XElements::toStableString).collect(toImmutableList()));
98 
99       ImmutableList<XExecutableElement> injectMethods =
100           baseSuperclassElement.getDeclaredMethods().stream()
101               .filter(method -> method.hasAnnotation(ClassNames.INJECT))
102               .collect(toImmutableList());
103       ProcessorErrors.checkState(
104           injectMethods.isEmpty(),
105           element,
106           "@%s does not support application classes (or super classes) with @Inject methods. Found "
107               + "%s with @Inject methods %s.",
108           ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
109           baseSuperclassElement.getClassName(),
110           injectMethods.stream().map(XElements::toStableString).collect(toImmutableList()));
111 
112       ImmutableList<XExecutableElement> injectConstructors =
113           baseSuperclassElement.getConstructors().stream()
114               .filter(method -> method.hasAnnotation(ClassNames.INJECT))
115               .collect(toImmutableList());
116       ProcessorErrors.checkState(
117           injectConstructors.isEmpty(),
118           element,
119           "@%s does not support application classes (or super classes) with @Inject constructors. "
120               + "Found %s with @Inject constructors %s.",
121           ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
122           baseSuperclassElement.getClassName().canonicalName(),
123           injectConstructors.stream().map(XElements::toStableString).collect(toImmutableList()));
124 
125       baseSuperclassElement = baseSuperclassElement.getSuperClass().getTypeElement();
126     }
127 
128     // We check this last because if the base type is a @HiltAndroidApp we'd accidentally fail
129     // with this message instead of the one above when the superclass hasn't yet been generated.
130     ProcessorErrors.checkState(
131         Processors.isAssignableFrom(baseElement, ClassNames.APPLICATION),
132         element,
133         "@%s value should be an instance of %s. Found: %s",
134         ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
135         ClassNames.APPLICATION,
136         baseElement.getClassName());
137 
138     return baseElement;
139   }
140 }
141