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

dagger.internal.codegen.binding.ComponentDescriptorFactory Maven / Gradle / Ivy

There is a newer version: 2.54
Show newest version
/*
 * Copyright (C) 2014 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.binding;

import static com.google.auto.common.MoreElements.asType;
import static com.google.auto.common.MoreTypes.asTypeElement;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
import static dagger.internal.codegen.base.Scopes.productionScope;
import static dagger.internal.codegen.base.Scopes.scopesOf;
import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.creatorAnnotationsFor;
import static dagger.internal.codegen.binding.ComponentDescriptor.isComponentContributionMethod;
import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes;
import static dagger.internal.codegen.binding.ConfigurationAnnotations.isSubcomponentCreator;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static javax.lang.model.type.TypeKind.DECLARED;
import static javax.lang.model.type.TypeKind.VOID;
import static javax.lang.model.util.ElementFilter.methodsIn;

import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import dagger.internal.codegen.base.ComponentAnnotation;
import dagger.internal.codegen.base.ModuleAnnotation;
import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.langmodel.DaggerElements;
import dagger.internal.codegen.langmodel.DaggerTypes;
import dagger.spi.model.Scope;
import java.util.Optional;
import java.util.function.Function;
import javax.inject.Inject;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeMirror;

/** A factory for {@link ComponentDescriptor}s. */
public final class ComponentDescriptorFactory {
  private final DaggerElements elements;
  private final DaggerTypes types;
  private final DependencyRequestFactory dependencyRequestFactory;
  private final ModuleDescriptor.Factory moduleDescriptorFactory;
  private final InjectionAnnotations injectionAnnotations;

  @Inject
  ComponentDescriptorFactory(
      DaggerElements elements,
      DaggerTypes types,
      DependencyRequestFactory dependencyRequestFactory,
      ModuleDescriptor.Factory moduleDescriptorFactory,
      InjectionAnnotations injectionAnnotations) {
    this.elements = elements;
    this.types = types;
    this.dependencyRequestFactory = dependencyRequestFactory;
    this.moduleDescriptorFactory = moduleDescriptorFactory;
    this.injectionAnnotations = injectionAnnotations;
  }

  /** Returns a descriptor for a root component type. */
  public ComponentDescriptor rootComponentDescriptor(TypeElement typeElement) {
    return create(
        typeElement,
        checkAnnotation(
            typeElement,
            ComponentAnnotation::rootComponentAnnotation,
            "must have a component annotation"));
  }

  /** Returns a descriptor for a subcomponent type. */
  public ComponentDescriptor subcomponentDescriptor(TypeElement typeElement) {
    return create(
        typeElement,
        checkAnnotation(
            typeElement,
            ComponentAnnotation::subcomponentAnnotation,
            "must have a subcomponent annotation"));
  }

  /**
   * Returns a descriptor for a fictional component based on a module type in order to validate its
   * bindings.
   */
  public ComponentDescriptor moduleComponentDescriptor(TypeElement typeElement) {
    return create(
        typeElement,
        ComponentAnnotation.fromModuleAnnotation(
            checkAnnotation(
                typeElement, ModuleAnnotation::moduleAnnotation, "must have a module annotation")));
  }

  private static  A checkAnnotation(
      TypeElement typeElement,
      Function> annotationFunction,
      String message) {
    return annotationFunction
        .apply(typeElement)
        .orElseThrow(() -> new IllegalArgumentException(typeElement + " " + message));
  }

  private ComponentDescriptor create(
      TypeElement typeElement, ComponentAnnotation componentAnnotation) {
    ImmutableSet componentDependencies =
        componentAnnotation.dependencyTypes().stream()
            .map(ComponentRequirement::forDependency)
            .collect(toImmutableSet());

    ImmutableMap.Builder dependenciesByDependencyMethod =
        ImmutableMap.builder();

    for (ComponentRequirement componentDependency : componentDependencies) {
      for (ExecutableElement dependencyMethod :
          methodsIn(elements.getAllMembers(componentDependency.typeElement()))) {
        if (isComponentContributionMethod(elements, dependencyMethod)) {
          dependenciesByDependencyMethod.put(dependencyMethod, componentDependency);
        }
      }
    }

    // Start with the component's modules. For fictional components built from a module, start with
    // that module.
    ImmutableSet modules =
        componentAnnotation.isRealComponent()
            ? componentAnnotation.modules()
            : ImmutableSet.of(typeElement);

    ImmutableSet transitiveModules =
        moduleDescriptorFactory.transitiveModules(modules);

    ImmutableSet.Builder subcomponentsFromModules = ImmutableSet.builder();
    for (ModuleDescriptor module : transitiveModules) {
      for (SubcomponentDeclaration subcomponentDeclaration : module.subcomponentDeclarations()) {
        TypeElement subcomponent = subcomponentDeclaration.subcomponentType();
        subcomponentsFromModules.add(subcomponentDescriptor(subcomponent));
      }
    }

    ImmutableSet.Builder componentMethodsBuilder =
        ImmutableSet.builder();
    ImmutableBiMap.Builder
        subcomponentsByFactoryMethod = ImmutableBiMap.builder();
    ImmutableBiMap.Builder
        subcomponentsByBuilderMethod = ImmutableBiMap.builder();
    if (componentAnnotation.isRealComponent()) {
      ImmutableSet unimplementedMethods =
          elements.getUnimplementedMethods(typeElement);
      for (ExecutableElement componentMethod : unimplementedMethods) {
        ComponentMethodDescriptor componentMethodDescriptor =
            getDescriptorForComponentMethod(typeElement, componentAnnotation, componentMethod);
        componentMethodsBuilder.add(componentMethodDescriptor);
        componentMethodDescriptor
            .subcomponent()
            .ifPresent(
                subcomponent -> {
                  // If the dependency request is present, that means the method returns the
                  // subcomponent factory.
                  if (componentMethodDescriptor.dependencyRequest().isPresent()) {
                    subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
                  } else {
                    subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
                  }
                });
      }
    }

    // Validation should have ensured that this set will have at most one element.
    ImmutableSet enclosedCreators =
        creatorAnnotationsFor(componentAnnotation).stream()
            .flatMap(
                creatorAnnotation ->
                    enclosedAnnotatedTypes(typeElement, creatorAnnotation).stream())
            .collect(toImmutableSet());
    Optional creatorDescriptor =
        enclosedCreators.isEmpty()
            ? Optional.empty()
            : Optional.of(
                ComponentCreatorDescriptor.create(
                    getOnlyElement(enclosedCreators), elements, types, dependencyRequestFactory));

    ImmutableSet scopes = scopesOf(typeElement);
    if (componentAnnotation.isProduction()) {
      scopes = ImmutableSet.builder().addAll(scopes).add(productionScope(elements)).build();
    }

    return new AutoValue_ComponentDescriptor(
        componentAnnotation,
        typeElement,
        componentDependencies,
        transitiveModules,
        dependenciesByDependencyMethod.build(),
        scopes,
        subcomponentsFromModules.build(),
        subcomponentsByFactoryMethod.build(),
        subcomponentsByBuilderMethod.build(),
        componentMethodsBuilder.build(),
        creatorDescriptor);
  }

  private ComponentMethodDescriptor getDescriptorForComponentMethod(
      TypeElement componentElement,
      ComponentAnnotation componentAnnotation,
      ExecutableElement componentMethod) {
    ComponentMethodDescriptor.Builder descriptor =
        ComponentMethodDescriptor.builder(componentMethod);

    ExecutableType resolvedComponentMethod =
        MoreTypes.asExecutable(
            types.asMemberOf(MoreTypes.asDeclared(componentElement.asType()), componentMethod));
    TypeMirror returnType = resolvedComponentMethod.getReturnType();
    if (returnType.getKind().equals(DECLARED)
        && !injectionAnnotations.getQualifier(componentMethod).isPresent()) {
      TypeElement returnTypeElement = asTypeElement(returnType);
      if (subcomponentAnnotation(returnTypeElement).isPresent()) {
        // It's a subcomponent factory method. There is no dependency request, and there could be
        // any number of parameters. Just return the descriptor.
        return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
      }
      if (isSubcomponentCreator(returnTypeElement)) {
        descriptor.subcomponent(
            subcomponentDescriptor(asType(returnTypeElement.getEnclosingElement())));
      }
    }

    switch (componentMethod.getParameters().size()) {
      case 0:
        checkArgument(
            !returnType.getKind().equals(VOID),
            "component method cannot be void: %s",
            componentMethod);
        descriptor.dependencyRequest(
            componentAnnotation.isProduction()
                ? dependencyRequestFactory.forComponentProductionMethod(
                    componentMethod, resolvedComponentMethod)
                : dependencyRequestFactory.forComponentProvisionMethod(
                    componentMethod, resolvedComponentMethod));
        break;

      case 1:
        checkArgument(
            returnType.getKind().equals(VOID)
                || MoreTypes.equivalence()
                    .equivalent(returnType, resolvedComponentMethod.getParameterTypes().get(0)),
            "members injection method must return void or parameter type: %s",
            componentMethod);
        descriptor.dependencyRequest(
            dependencyRequestFactory.forComponentMembersInjectionMethod(
                componentMethod, resolvedComponentMethod));
        break;

      default:
        throw new IllegalArgumentException(
            "component method has too many parameters: " + componentMethod);
    }

    return descriptor.build();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy