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

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

The 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 androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;

import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XExecutableElement;
import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XExecutableType;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XMethodType;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import androidx.room.compiler.processing.XVariableElement;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.Formatter;
import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XTypes;
import java.util.Iterator;
import java.util.Optional;
import javax.inject.Inject;

/** Formats the signature of an {@link XExecutableElement} suitable for use in error messages. */
public final class MethodSignatureFormatter extends Formatter {
  private static final ClassName JET_BRAINS_NOT_NULL =
      ClassName.get("org.jetbrains.annotations", "NotNull");
  private static final ClassName JET_BRAINS_NULLABLE =
      ClassName.get("org.jetbrains.annotations", "Nullable");

  private final InjectionAnnotations injectionAnnotations;

  @Inject
  MethodSignatureFormatter(InjectionAnnotations injectionAnnotations) {
    this.injectionAnnotations = injectionAnnotations;
  }

  /**
   * A formatter that uses the type where the method is declared for the annotations and name of the
   * method, but the method's resolved type as a member of {@code type} for the key.
   */
  public Formatter typedFormatter(XType type) {
    checkArgument(isDeclared(type));
    return new Formatter() {
      @Override
      public String format(XMethodElement method) {
        return MethodSignatureFormatter.this.format(
            method,
            method.asMemberOf(type),
            closestEnclosingTypeElement(method),
            /* includeReturnType= */ true);
      }
    };
  }

  @Override
  public String format(XExecutableElement method) {
    return format(method, Optional.empty());
  }

  /**
   * Formats an ExecutableElement as if it were contained within the container, if the container is
   * present.
   */
  public String format(XExecutableElement method, Optional container) {
    return format(method, container, /* includeReturnType= */ true);
  }

  private String format(
      XExecutableElement method, Optional container, boolean includeReturnType) {
    return container.isPresent()
        ? format(
            method,
            method.asMemberOf(container.get()),
            container.get().getTypeElement(),
            includeReturnType)
        : format(
            method,
            method.getExecutableType(),
            closestEnclosingTypeElement(method),
            includeReturnType);
  }

  private String format(
      XExecutableElement method,
      XExecutableType methodType,
      XTypeElement container,
      boolean includeReturnType) {
    StringBuilder builder = new StringBuilder();
    ImmutableList formattedAnnotations = formatedAnnotations(method);
    if (!formattedAnnotations.isEmpty()) {
      builder.append(String.join(" ", formattedAnnotations)).append(" ");
    }
    if (getSimpleName(method).contentEquals("")) {
      builder.append(container.getQualifiedName());
    } else {
      if (includeReturnType) {
        builder.append(nameOfType(((XMethodType) methodType).getReturnType())).append(' ');
      }
      builder.append(container.getQualifiedName()).append('.').append(getSimpleName(method));
    }
    builder.append('(');
    checkState(method.getParameters().size() == methodType.getParameterTypes().size());
    Iterator parameters = method.getParameters().iterator();
    Iterator parameterTypes = methodType.getParameterTypes().iterator();
    for (int i = 0; parameters.hasNext(); i++) {
      if (i > 0) {
        builder.append(", ");
      }
      appendParameter(builder, parameters.next(), parameterTypes.next());
    }
    builder.append(')');
    return builder.toString();
  }

  public String formatWithoutReturnType(XExecutableElement method) {
    return format(method, Optional.empty(), /* includeReturnType= */ false);
  }

  private void appendParameter(
      StringBuilder builder, XVariableElement parameter, XType parameterType) {
    injectionAnnotations
        .getQualifier(parameter)
        .ifPresent(qualifier -> builder.append(formatAnnotation(qualifier)).append(' '));
    builder.append(nameOfType(parameterType));
  }

  private static String nameOfType(XType type) {
    return stripCommonTypePrefixes(XTypes.toStableString(type));
  }

  private static ImmutableList formatedAnnotations(XExecutableElement executableElement) {
    Nullability nullability = Nullability.of(executableElement);
    ImmutableList formattedAnnotations =
        Streams.concat(
                executableElement.getAllAnnotations().stream()
                    // Filter out @NotNull annotations added by KAPT to make error messages
                    // consistent
                    .filter(annotation -> !annotation.getClassName().equals(JET_BRAINS_NOT_NULL))
                    .map(MethodSignatureFormatter::formatAnnotation),
                nullability.nullableAnnotations().stream()
                    // Filter out @NotNull annotations added by KAPT to make error messages
                    // consistent
                    .filter(annotation -> !annotation.equals(JET_BRAINS_NOT_NULL))
                    .map(annotation -> String.format("@%s", annotation.canonicalName())))
            .distinct()
            .collect(toImmutableList());
    if (nullability.isKotlinTypeNullable()
        && nullability.nullableAnnotations().stream().noneMatch(JET_BRAINS_NULLABLE::equals)
        && getProcessingEnv(executableElement).findTypeElement(JET_BRAINS_NULLABLE) != null) {
      formattedAnnotations =
          ImmutableList.builder()
              .addAll(formattedAnnotations)
              .add(String.format("@%s", JET_BRAINS_NULLABLE.canonicalName()))
              .build();
    }
    return formattedAnnotations;
  }

  private static String formatAnnotation(XAnnotation annotation) {
    return stripCommonTypePrefixes(XAnnotations.toString(annotation));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy