dagger.internal.codegen.binding.MethodSignatureFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dagger-compiler Show documentation
Show all versions of dagger-compiler Show documentation
A fast dependency injector for Android and Java.
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));
}
}