blob: fd1e0b23ff115d64d0287980afca2d84935479dc [file] [log] [blame]
/*
* 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 com.google.common.base.Preconditions.checkState;
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<ClassName> ROOT_COMPONENT_ANNOTATIONS =
ImmutableSet.of(TypeNames.COMPONENT, TypeNames.PRODUCTION_COMPONENT);
/** The subcomponent annotation types. */
private static final ImmutableSet<ClassName> SUBCOMPONENT_ANNOTATIONS =
ImmutableSet.of(TypeNames.SUBCOMPONENT, TypeNames.PRODUCTION_SUBCOMPONENT);
// TODO(erichang): Move ComponentCreatorAnnotation into /base and use that here?
/** The component/subcomponent creator annotation types. */
private static final ImmutableSet<ClassName> CREATOR_ANNOTATIONS =
ImmutableSet.of(
TypeNames.COMPONENT_BUILDER,
TypeNames.COMPONENT_FACTORY,
TypeNames.PRODUCTION_COMPONENT_BUILDER,
TypeNames.PRODUCTION_COMPONENT_FACTORY,
TypeNames.SUBCOMPONENT_BUILDER,
TypeNames.SUBCOMPONENT_FACTORY,
TypeNames.PRODUCTION_SUBCOMPONENT_BUILDER,
TypeNames.PRODUCTION_SUBCOMPONENT_FACTORY);
/** All component annotation types. */
private static final ImmutableSet<ClassName> ALL_COMPONENT_ANNOTATIONS =
ImmutableSet.<ClassName>builder()
.addAll(ROOT_COMPONENT_ANNOTATIONS)
.addAll(SUBCOMPONENT_ANNOTATIONS)
.build();
/** All component and creator annotation types. */
private static final ImmutableSet<ClassName> ALL_COMPONENT_AND_CREATOR_ANNOTATIONS =
ImmutableSet.<ClassName>builder()
.addAll(ALL_COMPONENT_ANNOTATIONS)
.addAll(CREATOR_ANNOTATIONS)
.build();
/** All production annotation types. */
private static final ImmutableSet<ClassName> 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<XType> 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<XTypeElement> 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<XTypeElement> 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<ComponentAnnotation> rootComponentAnnotation(XTypeElement typeElement) {
return anyComponentAnnotation(typeElement, ROOT_COMPONENT_ANNOTATIONS);
}
/**
* Returns an object representing a subcomponent annotation, if one is present on {@code
* typeElement}.
*/
public static Optional<ComponentAnnotation> subcomponentAnnotation(XTypeElement typeElement) {
return anyComponentAnnotation(typeElement, SUBCOMPONENT_ANNOTATIONS);
}
/**
* Returns an object representing a root component or subcomponent annotation, if one is present
* on {@code typeElement}.
*/
public static Optional<ComponentAnnotation> anyComponentAnnotation(XElement element) {
return anyComponentAnnotation(element, ALL_COMPONENT_ANNOTATIONS);
}
private static Optional<ComponentAnnotation> anyComponentAnnotation(
XElement element, Collection<ClassName> annotations) {
return getAnyAnnotation(element, annotations).map(ComponentAnnotation::componentAnnotation);
}
/** Returns {@code true} if the argument is a component annotation. */
public static boolean isComponentAnnotation(XAnnotation annotation) {
return ALL_COMPONENT_ANNOTATIONS.contains(getClassName(annotation));
}
/** Creates an object representing a component or subcomponent annotation. */
public static ComponentAnnotation componentAnnotation(XAnnotation annotation) {
checkState(
isComponentAnnotation(annotation),
annotation
+ " must be a Component, Subcomponent, ProductionComponent, "
+ "or ProductionSubcomponent annotation");
return create(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<ClassName> rootComponentAnnotations() {
return ROOT_COMPONENT_ANNOTATIONS;
}
/** The subcomponent annotation types. */
public static ImmutableSet<ClassName> subcomponentAnnotations() {
return SUBCOMPONENT_ANNOTATIONS;
}
/** All component annotation types. */
public static ImmutableSet<ClassName> allComponentAnnotations() {
return ALL_COMPONENT_ANNOTATIONS;
}
/** All component and creator annotation types. */
public static ImmutableSet<ClassName> allComponentAndCreatorAnnotations() {
return ALL_COMPONENT_AND_CREATOR_ANNOTATIONS;
}
}