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.bindvalue; 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 23 import androidx.room.compiler.processing.XAnnotation; 24 import androidx.room.compiler.processing.XElement; 25 import androidx.room.compiler.processing.XProcessingEnv; 26 import androidx.room.compiler.processing.XRoundEnv; 27 import androidx.room.compiler.processing.XTypeElement; 28 import com.google.common.collect.ArrayListMultimap; 29 import com.google.common.collect.ImmutableList; 30 import com.google.common.collect.ImmutableSet; 31 import com.google.common.collect.ListMultimap; 32 import com.squareup.javapoet.ClassName; 33 import dagger.hilt.processor.internal.BaseProcessingStep; 34 import dagger.hilt.processor.internal.ClassNames; 35 import dagger.hilt.processor.internal.ProcessorErrors; 36 import dagger.internal.codegen.xprocessing.XElements; 37 import java.util.Collection; 38 import java.util.Map; 39 40 /** Provides a test's @BindValue fields to the SINGLETON component. */ 41 public final class BindValueProcessingStep extends BaseProcessingStep { 42 43 private static final ImmutableSet<ClassName> SUPPORTED_ANNOTATIONS = 44 ImmutableSet.<ClassName>builder() 45 .addAll(BindValueMetadata.BIND_VALUE_ANNOTATIONS) 46 .addAll(BindValueMetadata.BIND_VALUE_INTO_SET_ANNOTATIONS) 47 .addAll(BindValueMetadata.BIND_ELEMENTS_INTO_SET_ANNOTATIONS) 48 .addAll(BindValueMetadata.BIND_VALUE_INTO_MAP_ANNOTATIONS) 49 .build(); 50 51 private final ListMultimap<XTypeElement, XElement> testRootMap = ArrayListMultimap.create(); 52 BindValueProcessingStep(XProcessingEnv env)53 public BindValueProcessingStep(XProcessingEnv env) { 54 super(env); 55 } 56 57 @Override annotationClassNames()58 protected ImmutableSet<ClassName> annotationClassNames() { 59 return SUPPORTED_ANNOTATIONS; 60 } 61 62 @Override preProcess(XProcessingEnv env, XRoundEnv round)63 protected void preProcess(XProcessingEnv env, XRoundEnv round) { 64 testRootMap.clear(); 65 } 66 67 @Override processEach(ClassName annotation, XElement element)68 public void processEach(ClassName annotation, XElement element) { 69 XElement enclosingElement = element.getEnclosingElement(); 70 // Restrict BindValue to the direct test class (e.g. not allowed in a base test class) because 71 // otherwise generated BindValue modules from the base class will not associate with the 72 // correct test class. This would make the modules apply globally which would be a weird 73 // difference since just moving a declaration to the parent would change whether the module is 74 // limited to the test that declares it to global. 75 ProcessorErrors.checkState( 76 isTypeElement(enclosingElement) 77 && asTypeElement(enclosingElement).isClass() 78 && (enclosingElement.hasAnnotation(ClassNames.HILT_ANDROID_TEST) 79 ), 80 enclosingElement, 81 "@%s can only be used within a class annotated with " 82 + "@HiltAndroidTest. Found: %s", 83 annotation.simpleName(), 84 XElements.toStableString(enclosingElement)); 85 testRootMap.put(asTypeElement(enclosingElement), element); 86 } 87 88 @Override postProcess(XProcessingEnv env, XRoundEnv round)89 protected void postProcess(XProcessingEnv env, XRoundEnv round) throws Exception { 90 // Generate a module for each testing class with a @BindValue field. 91 for (Map.Entry<XTypeElement, Collection<XElement>> e : testRootMap.asMap().entrySet()) { 92 BindValueMetadata metadata = BindValueMetadata.create(e.getKey(), e.getValue()); 93 new BindValueGenerator(processingEnv(), metadata).generate(); 94 } 95 } 96 getBindValueAnnotations(XElement element)97 static ImmutableList<ClassName> getBindValueAnnotations(XElement element) { 98 return element.getAllAnnotations().stream() 99 .map(XAnnotation::getClassName) 100 .filter(SUPPORTED_ANNOTATIONS::contains) 101 .collect(toImmutableList()); 102 } 103 } 104