blob: c9b3580c1c9e5653235c9256d27eb016c1331068 [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.auto.common.AnnotationMirrors.getAnnotationValue;
import static com.google.auto.common.MoreElements.asType;
import static com.google.auto.common.MoreTypes.asTypeElements;
import static com.google.auto.common.MoreTypes.isTypeOf;
import static dagger.internal.codegen.base.MoreAnnotationValues.asAnnotationValues;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER_MODULE;
import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
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.Component;
import dagger.Subcomponent;
import dagger.producers.ProductionComponent;
import dagger.producers.ProductionSubcomponent;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
/**
* 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.
*/
public abstract class ComponentAnnotation {
/** The root component annotation types. */
private static final ImmutableSet<Class<? extends Annotation>> ROOT_COMPONENT_ANNOTATIONS =
ImmutableSet.of(Component.class, ProductionComponent.class);
/** The subcomponent annotation types. */
private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_ANNOTATIONS =
ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
/** All component annotation types. */
private static final ImmutableSet<Class<? extends Annotation>> ALL_COMPONENT_ANNOTATIONS =
ImmutableSet.<Class<? extends Annotation>>builder()
.addAll(ROOT_COMPONENT_ANNOTATIONS)
.addAll(SUBCOMPONENT_ANNOTATIONS)
.build();
/** The annotation itself. */
public abstract AnnotationMirror annotation();
/** The simple name of the annotation type. */
public String simpleName() {
return MoreAnnotationMirrors.simpleName(annotation()).toString();
}
/**
* Returns {@code true} if the annotation is a {@code @Subcomponent} or
* {@code @ProductionSubcomponent}.
*/
public abstract boolean isSubcomponent();
/**
* Returns {@code true} if the annotation is a {@code @ProductionComponent},
* {@code @ProductionSubcomponent}, or {@code @ProducerModule}.
*/
public abstract boolean isProduction();
/**
* Returns {@code true} if the annotation is a real component annotation and not a module
* annotation.
*/
public abstract boolean isRealComponent();
/** The values listed as {@code dependencies}. */
public abstract ImmutableList<AnnotationValue> dependencyValues();
/** The types listed as {@code dependencies}. */
public ImmutableList<TypeMirror> dependencyTypes() {
return dependencyValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
}
/**
* The types listed as {@code dependencies}.
*
* @throws IllegalArgumentException if any of {@link #dependencyTypes()} are error types
*/
public ImmutableList<TypeElement> dependencies() {
return asTypeElements(dependencyTypes()).asList();
}
/** The values listed as {@code modules}. */
public abstract ImmutableList<AnnotationValue> moduleValues();
/** The types listed as {@code modules}. */
public ImmutableList<TypeMirror> moduleTypes() {
return moduleValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
}
/**
* The types listed as {@code modules}.
*
* @throws IllegalArgumentException if any of {@link #moduleTypes()} are error types
*/
public ImmutableSet<TypeElement> modules() {
return asTypeElements(moduleTypes());
}
protected final ImmutableList<AnnotationValue> getAnnotationValues(String parameterName) {
return asAnnotationValues(getAnnotationValue(annotation(), parameterName));
}
/**
* Returns an object representing a root component annotation, not a subcomponent annotation, if
* one is present on {@code typeElement}.
*/
public static Optional<ComponentAnnotation> rootComponentAnnotation(TypeElement 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(TypeElement 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(TypeElement typeElement) {
return anyComponentAnnotation(typeElement, ALL_COMPONENT_ANNOTATIONS);
}
private static Optional<ComponentAnnotation> anyComponentAnnotation(
TypeElement typeElement, Collection<Class<? extends Annotation>> annotations) {
return getAnyAnnotation(typeElement, annotations).map(ComponentAnnotation::componentAnnotation);
}
/** Returns {@code true} if the argument is a component annotation. */
public static boolean isComponentAnnotation(AnnotationMirror annotation) {
return ALL_COMPONENT_ANNOTATIONS.stream()
.anyMatch(annotationClass -> isTypeOf(annotationClass, annotation.getAnnotationType()));
}
/** Creates an object representing a component or subcomponent annotation. */
public static ComponentAnnotation componentAnnotation(AnnotationMirror annotation) {
RealComponentAnnotation.Builder annotationBuilder =
RealComponentAnnotation.builder().annotation(annotation);
if (isTypeOf(Component.class, annotation.getAnnotationType())) {
return annotationBuilder.isProduction(false).isSubcomponent(false).build();
}
if (isTypeOf(Subcomponent.class, annotation.getAnnotationType())) {
return annotationBuilder.isProduction(false).isSubcomponent(true).build();
}
if (isTypeOf(ProductionComponent.class, annotation.getAnnotationType())) {
return annotationBuilder.isProduction(true).isSubcomponent(false).build();
}
if (isTypeOf(ProductionSubcomponent.class, annotation.getAnnotationType())) {
return annotationBuilder.isProduction(true).isSubcomponent(true).build();
}
throw new IllegalArgumentException(
annotation
+ " must be a Component, Subcomponent, ProductionComponent, "
+ "or ProductionSubcomponent annotation");
}
/** Creates a fictional component annotation representing a module. */
public static ComponentAnnotation fromModuleAnnotation(ModuleAnnotation moduleAnnotation) {
return new AutoValue_ComponentAnnotation_FictionalComponentAnnotation(moduleAnnotation);
}
/** The root component annotation types. */
public static ImmutableSet<Class<? extends Annotation>> rootComponentAnnotations() {
return ROOT_COMPONENT_ANNOTATIONS;
}
/** The subcomponent annotation types. */
public static ImmutableSet<Class<? extends Annotation>> subcomponentAnnotations() {
return SUBCOMPONENT_ANNOTATIONS;
}
/** All component annotation types. */
public static ImmutableSet<Class<? extends Annotation>> allComponentAnnotations() {
return ALL_COMPONENT_ANNOTATIONS;
}
/**
* An actual component annotation.
*
* @see FictionalComponentAnnotation
*/
@AutoValue
abstract static class RealComponentAnnotation extends ComponentAnnotation {
@Override
@Memoized
public ImmutableList<AnnotationValue> dependencyValues() {
return isSubcomponent() ? ImmutableList.of() : getAnnotationValues("dependencies");
}
@Override
@Memoized
public ImmutableList<TypeMirror> dependencyTypes() {
return super.dependencyTypes();
}
@Override
@Memoized
public ImmutableList<TypeElement> dependencies() {
return super.dependencies();
}
@Override
public boolean isRealComponent() {
return true;
}
@Override
@Memoized
public ImmutableList<AnnotationValue> moduleValues() {
return getAnnotationValues("modules");
}
@Override
@Memoized
public ImmutableList<TypeMirror> moduleTypes() {
return super.moduleTypes();
}
@Override
@Memoized
public ImmutableSet<TypeElement> modules() {
return super.modules();
}
static Builder builder() {
return new AutoValue_ComponentAnnotation_RealComponentAnnotation.Builder();
}
@AutoValue.Builder
interface Builder {
Builder annotation(AnnotationMirror annotation);
Builder isSubcomponent(boolean isSubcomponent);
Builder isProduction(boolean isProduction);
RealComponentAnnotation build();
}
}
/**
* A fictional component annotation used to represent modules or other collections of bindings as
* a component.
*/
@AutoValue
abstract static class FictionalComponentAnnotation extends ComponentAnnotation {
@Override
public AnnotationMirror annotation() {
return moduleAnnotation().annotation();
}
@Override
public boolean isSubcomponent() {
return false;
}
@Override
public boolean isProduction() {
return ClassName.get(asType(moduleAnnotation().annotation().getAnnotationType().asElement()))
.equals(PRODUCER_MODULE);
}
@Override
public boolean isRealComponent() {
return false;
}
@Override
public ImmutableList<AnnotationValue> dependencyValues() {
return ImmutableList.of();
}
@Override
public ImmutableList<AnnotationValue> moduleValues() {
return moduleAnnotation().includesAsAnnotationValues();
}
@Override
@Memoized
public ImmutableList<TypeMirror> moduleTypes() {
return super.moduleTypes();
}
@Override
@Memoized
public ImmutableSet<TypeElement> modules() {
return super.modules();
}
public abstract ModuleAnnotation moduleAnnotation();
}
}