blob: 060b07793fae6dab1c037ba1d8f7250da772df06 [file] [log] [blame]
/*
* Copyright (C) 2020 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dagger.hilt.android.processor.internal.bindvalue;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
import com.google.auto.common.MoreElements;
import com.google.auto.service.AutoService;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import dagger.hilt.processor.internal.BaseProcessor;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.ProcessorErrors;
import dagger.hilt.processor.internal.Processors;
import dagger.internal.codegen.extension.DaggerStreams;
import java.util.Collection;
import java.util.Map;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
/** Provides a test's @BindValue fields to the SINGLETON component. */
@IncrementalAnnotationProcessor(ISOLATING)
@AutoService(Processor.class)
public final class BindValueProcessor extends BaseProcessor {
private static final ImmutableSet<ClassName> SUPPORTED_ANNOTATIONS =
ImmutableSet.<ClassName>builder()
.addAll(BindValueMetadata.BIND_VALUE_ANNOTATIONS)
.addAll(BindValueMetadata.BIND_VALUE_INTO_SET_ANNOTATIONS)
.addAll(BindValueMetadata.BIND_ELEMENTS_INTO_SET_ANNOTATIONS)
.addAll(BindValueMetadata.BIND_VALUE_INTO_MAP_ANNOTATIONS)
.build();
private final Multimap<TypeElement, Element> testRootMap = ArrayListMultimap.create();
@Override
public ImmutableSet<String> getSupportedAnnotationTypes() {
return SUPPORTED_ANNOTATIONS.stream()
.map(TypeName::toString)
.collect(DaggerStreams.toImmutableSet());
}
@Override
protected void preRoundProcess(RoundEnvironment roundEnv) {
testRootMap.clear();
}
@Override
public void processEach(TypeElement annotation, Element element) throws Exception {
ClassName annotationClassName = ClassName.get(annotation);
Element enclosingElement = element.getEnclosingElement();
// Restrict BindValue to the direct test class (e.g. not allowed in a base test class) because
// otherwise generated BindValue modules from the base class will not associate with the
// correct test class. This would make the modules apply globally which would be a weird
// difference since just moving a declaration to the parent would change whether the module is
// limited to the test that declares it to global.
ProcessorErrors.checkState(
enclosingElement.getKind() == ElementKind.CLASS
&& (Processors.hasAnnotation(enclosingElement, ClassNames.HILT_ANDROID_TEST)
),
enclosingElement,
"@%s can only be used within a class annotated with "
+ "@HiltAndroidTest. Found: %s",
annotationClassName.simpleName(),
enclosingElement);
testRootMap.put(MoreElements.asType(enclosingElement), element);
}
@Override
public void postRoundProcess(RoundEnvironment roundEnvironment) throws Exception {
// Generate a module for each testing class with a @BindValue field.
for (Map.Entry<TypeElement, Collection<Element>> e : testRootMap.asMap().entrySet()) {
BindValueMetadata metadata = BindValueMetadata.create(e.getKey(), e.getValue());
new BindValueGenerator(getProcessingEnv(), metadata).generate();
}
}
static ImmutableList<ClassName> getBindValueAnnotations(Element element) {
ImmutableList.Builder<ClassName> builder = ImmutableList.builder();
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
TypeName tn = AnnotationSpec.get(annotation).type;
if (SUPPORTED_ANNOTATIONS.contains(tn)) {
builder.add((ClassName) tn); // the cast is checked by .contains()
}
}
return builder.build();
}
}