1 /* 2 * Copyright (C) 2014 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.common.base.Preconditions.checkState; 20 import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes; 21 import static dagger.internal.codegen.InjectionAnnotations.getQualifier; 22 23 import com.google.auto.common.MoreElements; 24 import com.google.auto.common.MoreTypes; 25 import dagger.internal.codegen.langmodel.DaggerTypes; 26 import java.util.Iterator; 27 import java.util.List; 28 import java.util.Optional; 29 import javax.inject.Inject; 30 import javax.lang.model.element.AnnotationMirror; 31 import javax.lang.model.element.ExecutableElement; 32 import javax.lang.model.element.TypeElement; 33 import javax.lang.model.element.VariableElement; 34 import javax.lang.model.type.DeclaredType; 35 import javax.lang.model.type.ExecutableType; 36 import javax.lang.model.type.TypeMirror; 37 38 /** 39 * Formats the signature of an {@link ExecutableElement} suitable for use in error messages. 40 */ 41 final class MethodSignatureFormatter extends Formatter<ExecutableElement> { 42 private final DaggerTypes types; 43 44 @Inject MethodSignatureFormatter(DaggerTypes types)45 MethodSignatureFormatter(DaggerTypes types) { 46 this.types = types; 47 } 48 49 /** 50 * A formatter that uses the type where the method is declared for the annotations and name of the 51 * method, but the method's resolved type as a member of {@code declaredType} for the key. 52 */ typedFormatter(DeclaredType declaredType)53 Formatter<ExecutableElement> typedFormatter(DeclaredType declaredType) { 54 return new Formatter<ExecutableElement>() { 55 @Override 56 public String format(ExecutableElement method) { 57 return MethodSignatureFormatter.this.format( 58 method, 59 MoreTypes.asExecutable(types.asMemberOf(declaredType, method)), 60 MoreElements.asType(method.getEnclosingElement())); 61 } 62 }; 63 } 64 65 @Override public String format(ExecutableElement method) { 66 return format(method, Optional.empty()); 67 } 68 69 /** 70 * Formats an ExecutableElement as if it were contained within the container, if the container is 71 * present. 72 */ 73 public String format(ExecutableElement method, Optional<DeclaredType> container) { 74 TypeElement type = MoreElements.asType(method.getEnclosingElement()); 75 ExecutableType executableType = MoreTypes.asExecutable(method.asType()); 76 if (container.isPresent()) { 77 executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method)); 78 type = MoreElements.asType(container.get().asElement()); 79 } 80 return format(method, executableType, type); 81 } 82 83 private String format( 84 ExecutableElement method, ExecutableType methodType, TypeElement declaringType) { 85 StringBuilder builder = new StringBuilder(); 86 // TODO(cgruber): AnnotationMirror formatter. 87 List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors(); 88 if (!annotations.isEmpty()) { 89 Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator(); 90 for (int i = 0; annotationIterator.hasNext(); i++) { 91 if (i > 0) { 92 builder.append(' '); 93 } 94 builder.append(formatAnnotation(annotationIterator.next())); 95 } 96 builder.append(' '); 97 } 98 if (method.getSimpleName().contentEquals("<init>")) { 99 builder.append(declaringType.getQualifiedName()); 100 } else { 101 builder 102 .append(nameOfType(methodType.getReturnType())) 103 .append(' ') 104 .append(declaringType.getQualifiedName()) 105 .append('.') 106 .append(method.getSimpleName()); 107 } 108 builder.append('('); 109 checkState(method.getParameters().size() == methodType.getParameterTypes().size()); 110 Iterator<? extends VariableElement> parameters = method.getParameters().iterator(); 111 Iterator<? extends TypeMirror> parameterTypes = methodType.getParameterTypes().iterator(); 112 for (int i = 0; parameters.hasNext(); i++) { 113 if (i > 0) { 114 builder.append(", "); 115 } 116 appendParameter(builder, parameters.next(), parameterTypes.next()); 117 } 118 builder.append(')'); 119 return builder.toString(); 120 } 121 122 private static void appendParameter(StringBuilder builder, VariableElement parameter, 123 TypeMirror type) { 124 getQualifier(parameter) 125 .ifPresent( 126 qualifier -> { 127 builder.append(formatAnnotation(qualifier)).append(' '); 128 }); 129 builder.append(nameOfType(type)); 130 } 131 132 private static String nameOfType(TypeMirror type) { 133 return stripCommonTypePrefixes(type.toString()); 134 } 135 136 private static String formatAnnotation(AnnotationMirror annotation) { 137 return stripCommonTypePrefixes(annotation.toString()); 138 } 139 } 140