/* * Copyright (C) 2019 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.internal.codegen.base; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import static dagger.internal.codegen.xprocessing.XAnnotations.getClassName; import static dagger.internal.codegen.xprocessing.XElements.getAnyAnnotation; import androidx.room.compiler.processing.XAnnotation; import androidx.room.compiler.processing.XElement; import androidx.room.compiler.processing.XType; import androidx.room.compiler.processing.XTypeElement; import com.google.auto.value.AutoValue; import com.google.auto.value.extension.memoized.Memoized; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.ClassName; import dagger.internal.codegen.javapoet.TypeNames; import java.util.Collection; import java.util.Optional; /** * A {@code @Component}, {@code @Subcomponent}, {@code @ProductionComponent}, or * {@code @ProductionSubcomponent} annotation, or a {@code @Module} or {@code @ProducerModule} * annotation that is being treated as a component annotation when validating full binding graphs * for modules. */ @AutoValue public abstract class ComponentAnnotation { /** The root component annotation types. */ private static final ImmutableSet ROOT_COMPONENT_ANNOTATIONS = ImmutableSet.of(TypeNames.COMPONENT, TypeNames.PRODUCTION_COMPONENT); /** The subcomponent annotation types. */ private static final ImmutableSet SUBCOMPONENT_ANNOTATIONS = ImmutableSet.of(TypeNames.SUBCOMPONENT, TypeNames.PRODUCTION_SUBCOMPONENT); /** All component annotation types. */ private static final ImmutableSet ALL_COMPONENT_ANNOTATIONS = ImmutableSet.builder() .addAll(ROOT_COMPONENT_ANNOTATIONS) .addAll(SUBCOMPONENT_ANNOTATIONS) .build(); /** All component and creator annotation types. */ private static final ImmutableSet ALL_COMPONENT_AND_CREATOR_ANNOTATIONS = ImmutableSet.builder() .addAll(ALL_COMPONENT_ANNOTATIONS) .addAll(ComponentCreatorAnnotation.allCreatorAnnotations()) .build(); /** All production annotation types. */ private static final ImmutableSet PRODUCTION_ANNOTATIONS = ImmutableSet.of( TypeNames.PRODUCTION_COMPONENT, TypeNames.PRODUCTION_SUBCOMPONENT, TypeNames.PRODUCER_MODULE); private XAnnotation annotation; /** The annotation itself. */ public final XAnnotation annotation() { return annotation; } /** Returns the {@link ClassName} name of the annotation. */ public abstract ClassName className(); /** The simple name of the annotation type. */ public final String simpleName() { return className().simpleName(); } /** * Returns {@code true} if the annotation is a {@code @Subcomponent} or * {@code @ProductionSubcomponent}. */ public final boolean isSubcomponent() { return SUBCOMPONENT_ANNOTATIONS.contains(className()); } /** * Returns {@code true} if the annotation is a {@code @ProductionComponent}, * {@code @ProductionSubcomponent}, or {@code @ProducerModule}. */ public final boolean isProduction() { return PRODUCTION_ANNOTATIONS.contains(className()); } /** * Returns {@code true} if the annotation is a real component annotation and not a module * annotation. */ public final boolean isRealComponent() { return ALL_COMPONENT_ANNOTATIONS.contains(className()); } /** The types listed as {@code dependencies}. */ @Memoized public ImmutableList dependencyTypes() { return isRootComponent() ? ImmutableList.copyOf(annotation.getAsTypeList("dependencies")) : ImmutableList.of(); } /** * The types listed as {@code dependencies}. * * @throws IllegalArgumentException if any of {@link #dependencyTypes()} are error types */ @Memoized public ImmutableSet dependencies() { return dependencyTypes().stream().map(XType::getTypeElement).collect(toImmutableSet()); } /** * The types listed as {@code modules}. * * @throws IllegalArgumentException if any module is an error type. */ @Memoized public ImmutableSet modules() { return annotation.getAsTypeList(isRealComponent() ? "modules" : "includes").stream() .map(XType::getTypeElement) .collect(toImmutableSet()); } private final boolean isRootComponent() { return ROOT_COMPONENT_ANNOTATIONS.contains(className()); } /** * Returns an object representing a root component annotation, not a subcomponent annotation, if * one is present on {@code typeElement}. */ public static Optional rootComponentAnnotation( XTypeElement typeElement, DaggerSuperficialValidation superficialValidation) { return anyComponentAnnotation(typeElement, ROOT_COMPONENT_ANNOTATIONS, superficialValidation); } /** * Returns an object representing a subcomponent annotation, if one is present on {@code * typeElement}. */ public static Optional subcomponentAnnotation( XTypeElement typeElement, DaggerSuperficialValidation superficialValidation) { return anyComponentAnnotation(typeElement, SUBCOMPONENT_ANNOTATIONS, superficialValidation); } /** * Returns an object representing a root component or subcomponent annotation, if one is present * on {@code typeElement}. */ public static Optional anyComponentAnnotation( XElement element, DaggerSuperficialValidation superficialValidation) { return anyComponentAnnotation(element, ALL_COMPONENT_ANNOTATIONS, superficialValidation); } private static Optional anyComponentAnnotation( XElement element, Collection annotations, DaggerSuperficialValidation superficialValidation) { return getAnyAnnotation(element, annotations) .map( annotation -> { superficialValidation.validateAnnotationOf(element, annotation); return create(annotation); }); } /** Returns {@code true} if the argument is a component annotation. */ public static boolean isComponentAnnotation(XAnnotation annotation) { return ALL_COMPONENT_ANNOTATIONS.contains(getClassName(annotation)); } /** Creates a fictional component annotation representing a module. */ public static ComponentAnnotation fromModuleAnnotation(ModuleAnnotation moduleAnnotation) { return create(moduleAnnotation.annotation()); } private static ComponentAnnotation create(XAnnotation annotation) { ComponentAnnotation componentAnnotation = new AutoValue_ComponentAnnotation(getClassName(annotation)); componentAnnotation.annotation = annotation; return componentAnnotation; } /** The root component annotation types. */ public static ImmutableSet rootComponentAnnotations() { return ROOT_COMPONENT_ANNOTATIONS; } /** The subcomponent annotation types. */ public static ImmutableSet subcomponentAnnotations() { return SUBCOMPONENT_ANNOTATIONS; } /** All component annotation types. */ public static ImmutableSet allComponentAnnotations() { return ALL_COMPONENT_ANNOTATIONS; } /** All component and creator annotation types. */ public static ImmutableSet allComponentAndCreatorAnnotations() { return ALL_COMPONENT_AND_CREATOR_ANNOTATIONS; } }