All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas Maven / Gradle / Ivy

The newest version!
/*
 * 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.hilt.processor.internal.definecomponent;

import static androidx.room.compiler.processing.XElementKt.isTypeElement;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
import static java.util.stream.Collectors.joining;

import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XAnnotationValue;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XExecutableElement;
import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.ProcessorErrors;
import dagger.hilt.processor.internal.Processors;
import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XElements;
import dagger.internal.codegen.xprocessing.XTypes;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;

/** Metadata for types annotated with {@link dagger.hilt.DefineComponent}. */
final class DefineComponentMetadatas {
  static DefineComponentMetadatas create() {
    return new DefineComponentMetadatas();
  }

  private final Map metadatas = new HashMap<>();

  private DefineComponentMetadatas() {}

  /** Returns the metadata for an element annotated with {@link dagger.hilt.DefineComponent}. */
  DefineComponentMetadata get(XElement element) {
    return get(element, new LinkedHashSet<>());
  }

  private DefineComponentMetadata get(XElement element, LinkedHashSet childPath) {
    if (!metadatas.containsKey(element)) {
      metadatas.put(element, getUncached(element, childPath));
    }
    return metadatas.get(element);
  }

  private DefineComponentMetadata getUncached(
      XElement element, LinkedHashSet childPath) {
    ProcessorErrors.checkState(
        childPath.add(element),
        element,
        "@DefineComponent cycle: %s -> %s",
        childPath.stream().map(XElements::toStableString).collect(joining(" -> ")),
        XElements.toStableString(element));

    ProcessorErrors.checkState(
        element.hasAnnotation(ClassNames.DEFINE_COMPONENT),
        element,
        "%s, expected to be annotated with @DefineComponent. Found: %s",
        XElements.toStableString(element),
        element.getAllAnnotations().stream()
            .map(XAnnotations::toStableString)
            .collect(toImmutableList()));

    // TODO(bcorso): Allow abstract classes?
    ProcessorErrors.checkState(
        isTypeElement(element) && asTypeElement(element).isInterface(),
        element,
        "@DefineComponent is only allowed on interfaces. Found: %s",
        XElements.toStableString(element));
    XTypeElement component = asTypeElement(element);

    // TODO(bcorso): Allow extending interfaces?
    ProcessorErrors.checkState(
        component.getSuperInterfaces().isEmpty(),
        component,
        "@DefineComponent %s, cannot extend a super class or interface. Found: %s",
        XElements.toStableString(component),
        component.getSuperInterfaces().stream()
            .map(XTypes::toStableString)
            .collect(toImmutableList()));

    // TODO(bcorso): Allow type parameters?
    ProcessorErrors.checkState(
        component.getTypeParameters().isEmpty(),
        component,
        "@DefineComponent %s, cannot have type parameters.",
        XTypes.toStableString(component.getType()));

    // TODO(bcorso): Allow non-static abstract methods (aka EntryPoints)?
    ImmutableList nonStaticMethods =
        component.getDeclaredMethods().stream()
            .filter(method -> !method.isStatic())
            .collect(toImmutableList());

    ProcessorErrors.checkState(
        nonStaticMethods.isEmpty(),
        component,
        "@DefineComponent %s, cannot have non-static methods. Found: %s",
        XElements.toStableString(component),
        nonStaticMethods.stream()
            .map(XElements::toStableString)
            .collect(toImmutableList()));

    // No need to check non-static fields since interfaces can't have them.

    ImmutableList scopes =
        Processors.getScopeAnnotations(component).stream()
            .map(XAnnotation::getTypeElement)
            .collect(toImmutableList());

    ImmutableList aliasScopes =
        ImmutableList.copyOf(component.getAnnotationsAnnotatedWith(ClassNames.ALIAS_OF));
    ProcessorErrors.checkState(
        aliasScopes.isEmpty(),
        component,
        "@DefineComponent %s, references invalid scope(s) annotated with @AliasOf. "
            + "@DefineComponent scopes cannot be aliases of other scopes: %s",
        XElements.toStableString(component),
        aliasScopes.stream().map(XAnnotations::toStableString).collect(toImmutableList()));

    XAnnotation annotation = component.getAnnotation(ClassNames.DEFINE_COMPONENT);
    XAnnotationValue parentValue = annotation.getAnnotationValue("parent");

    ProcessorErrors.checkState(
        !"".contentEquals(parentValue.getValue().toString()),
        component,
        "@DefineComponent %s, references an invalid parent type: %s",
        XElements.toStableString(component),
        XAnnotations.toStableString(annotation));

    XTypeElement parent = parentValue.asType().getTypeElement();

    ProcessorErrors.checkState(
        parent.getClassName().equals(ClassNames.DEFINE_COMPONENT_NO_PARENT)
            || parent.hasAnnotation(ClassNames.DEFINE_COMPONENT),
        component,
        "@DefineComponent %s, references a type not annotated with @DefineComponent: %s",
        XElements.toStableString(component),
        XElements.toStableString(parent));

    Optional parentComponent =
        parent.getClassName().equals(ClassNames.DEFINE_COMPONENT_NO_PARENT)
            ? Optional.empty()
            : Optional.of(get(parent, childPath));

    ClassName componentClassName = component.getClassName();

    ProcessorErrors.checkState(
        parentComponent.isPresent()
            || componentClassName.equals(ClassNames.SINGLETON_COMPONENT),
        component,
        "@DefineComponent %s is missing a parent declaration.\n"
            + "Please declare the parent, for example: @DefineComponent(parent ="
            + " SingletonComponent.class)",
        XElements.toStableString(component));

    ProcessorErrors.checkState(
        componentClassName.equals(ClassNames.SINGLETON_COMPONENT)
        || !componentClassName.simpleName().equals(ClassNames.SINGLETON_COMPONENT.simpleName()),
        component,
        "Cannot have a component with the same simple name as the reserved %s: %s",
        ClassNames.SINGLETON_COMPONENT.simpleName(),
        componentClassName.canonicalName());

    return new AutoValue_DefineComponentMetadatas_DefineComponentMetadata(
        component, scopes, parentComponent);
  }

  @AutoValue
  abstract static class DefineComponentMetadata {

    /** Returns the component annotated with {@link dagger.hilt.DefineComponent}. */
    abstract XTypeElement component();

    /** Returns the scopes of the component. */
    abstract ImmutableList scopes();

    /** Returns the parent component, if one exists. */
    abstract Optional parentMetadata();

    boolean isRoot() {
      return !parentMetadata().isPresent();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy