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