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 net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING; 20 21 import com.google.auto.common.MoreElements; 22 import com.google.auto.service.AutoService; 23 import com.google.common.collect.ArrayListMultimap; 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableSet; 26 import com.google.common.collect.Multimap; 27 import com.squareup.javapoet.AnnotationSpec; 28 import com.squareup.javapoet.ClassName; 29 import com.squareup.javapoet.TypeName; 30 import dagger.hilt.processor.internal.BaseProcessor; 31 import dagger.hilt.processor.internal.ClassNames; 32 import dagger.hilt.processor.internal.ProcessorErrors; 33 import dagger.hilt.processor.internal.Processors; 34 import dagger.internal.codegen.extension.DaggerStreams; 35 import java.util.Collection; 36 import java.util.Map; 37 import javax.annotation.processing.Processor; 38 import javax.annotation.processing.RoundEnvironment; 39 import javax.lang.model.element.AnnotationMirror; 40 import javax.lang.model.element.Element; 41 import javax.lang.model.element.ElementKind; 42 import javax.lang.model.element.TypeElement; 43 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; 44 45 /** Provides a test's @BindValue fields to the SINGLETON component. */ 46 @IncrementalAnnotationProcessor(ISOLATING) 47 @AutoService(Processor.class) 48 public final class BindValueProcessor extends BaseProcessor { 49 50 private static final ImmutableSet<ClassName> SUPPORTED_ANNOTATIONS = 51 ImmutableSet.<ClassName>builder() 52 .addAll(BindValueMetadata.BIND_VALUE_ANNOTATIONS) 53 .addAll(BindValueMetadata.BIND_VALUE_INTO_SET_ANNOTATIONS) 54 .addAll(BindValueMetadata.BIND_ELEMENTS_INTO_SET_ANNOTATIONS) 55 .addAll(BindValueMetadata.BIND_VALUE_INTO_MAP_ANNOTATIONS) 56 .build(); 57 58 private final Multimap<TypeElement, Element> testRootMap = ArrayListMultimap.create(); 59 60 @Override getSupportedAnnotationTypes()61 public ImmutableSet<String> getSupportedAnnotationTypes() { 62 return SUPPORTED_ANNOTATIONS.stream() 63 .map(TypeName::toString) 64 .collect(DaggerStreams.toImmutableSet()); 65 } 66 67 @Override preRoundProcess(RoundEnvironment roundEnv)68 protected void preRoundProcess(RoundEnvironment roundEnv) { 69 testRootMap.clear(); 70 } 71 72 @Override processEach(TypeElement annotation, Element element)73 public void processEach(TypeElement annotation, Element element) throws Exception { 74 ClassName annotationClassName = ClassName.get(annotation); 75 Element enclosingElement = element.getEnclosingElement(); 76 // Restrict BindValue to the direct test class (e.g. not allowed in a base test class) because 77 // otherwise generated BindValue modules from the base class will not associate with the 78 // correct test class. This would make the modules apply globally which would be a weird 79 // difference since just moving a declaration to the parent would change whether the module is 80 // limited to the test that declares it to global. 81 ProcessorErrors.checkState( 82 enclosingElement.getKind() == ElementKind.CLASS 83 && (Processors.hasAnnotation(enclosingElement, ClassNames.HILT_ANDROID_TEST) 84 ), 85 enclosingElement, 86 "@%s can only be used within a class annotated with " 87 + "@HiltAndroidTest. Found: %s", 88 annotationClassName.simpleName(), 89 enclosingElement); 90 91 testRootMap.put(MoreElements.asType(enclosingElement), element); 92 } 93 94 @Override postRoundProcess(RoundEnvironment roundEnvironment)95 public void postRoundProcess(RoundEnvironment roundEnvironment) throws Exception { 96 // Generate a module for each testing class with a @BindValue field. 97 for (Map.Entry<TypeElement, Collection<Element>> e : testRootMap.asMap().entrySet()) { 98 BindValueMetadata metadata = BindValueMetadata.create(e.getKey(), e.getValue()); 99 new BindValueGenerator(getProcessingEnv(), metadata).generate(); 100 } 101 } 102 getBindValueAnnotations(Element element)103 static ImmutableList<ClassName> getBindValueAnnotations(Element element) { 104 ImmutableList.Builder<ClassName> builder = ImmutableList.builder(); 105 for (AnnotationMirror annotation : element.getAnnotationMirrors()) { 106 TypeName tn = AnnotationSpec.get(annotation).type; 107 if (SUPPORTED_ANNOTATIONS.contains(tn)) { 108 builder.add((ClassName) tn); // the cast is checked by .contains() 109 } 110 } 111 return builder.build(); 112 } 113 } 114