/* * 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.common.base.Preconditions.checkState; import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import dagger.internal.codegen.base.Formatter; import dagger.internal.codegen.langmodel.DaggerTypes; import java.util.Iterator; import java.util.List; import java.util.Optional; import javax.inject.Inject; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; /** Formats the signature of an {@link ExecutableElement} suitable for use in error messages. */ public final class MethodSignatureFormatter extends Formatter { private final DaggerTypes types; private final InjectionAnnotations injectionAnnotations; @Inject public MethodSignatureFormatter(DaggerTypes types, InjectionAnnotations injectionAnnotations) { this.types = types; 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 declaredType} for the key. */ public Formatter typedFormatter(DeclaredType declaredType) { return new Formatter() { @Override public String format(ExecutableElement method) { return MethodSignatureFormatter.this.format( method, MoreTypes.asExecutable(types.asMemberOf(declaredType, method)), MoreElements.asType(method.getEnclosingElement())); } }; } @Override public String format(ExecutableElement 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(ExecutableElement method, Optional container) { TypeElement type = MoreElements.asType(method.getEnclosingElement()); ExecutableType executableType = MoreTypes.asExecutable(method.asType()); if (container.isPresent()) { executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method)); type = MoreElements.asType(container.get().asElement()); } return format(method, executableType, type); } private String format( ExecutableElement method, ExecutableType methodType, TypeElement declaringType) { StringBuilder builder = new StringBuilder(); // TODO(user): AnnotationMirror formatter. List annotations = method.getAnnotationMirrors(); if (!annotations.isEmpty()) { Iterator annotationIterator = annotations.iterator(); for (int i = 0; annotationIterator.hasNext(); i++) { if (i > 0) { builder.append(' '); } builder.append(formatAnnotation(annotationIterator.next())); } builder.append(' '); } if (method.getSimpleName().contentEquals("")) { builder.append(declaringType.getQualifiedName()); } else { builder .append(nameOfType(methodType.getReturnType())) .append(' ') .append(declaringType.getQualifiedName()) .append('.') .append(method.getSimpleName()); } 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(); } private void appendParameter(StringBuilder builder, VariableElement parameter, TypeMirror type) { injectionAnnotations .getQualifier(parameter) .ifPresent( qualifier -> { builder.append(formatAnnotation(qualifier)).append(' '); }); builder.append(nameOfType(type)); } private static String nameOfType(TypeMirror type) { return stripCommonTypePrefixes(type.toString()); } private static String formatAnnotation(AnnotationMirror annotation) { return stripCommonTypePrefixes(annotation.toString()); } }