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.binding; 18 19 import static com.google.common.base.Preconditions.checkState; 20 import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes; 21 22 import com.google.auto.common.MoreElements; 23 import com.google.auto.common.MoreTypes; 24 import dagger.internal.codegen.base.Formatter; 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 /** Formats the signature of an {@link ExecutableElement} suitable for use in error messages. */ 39 public final class MethodSignatureFormatter extends Formatter<ExecutableElement> { 40 private final DaggerTypes types; 41 private final InjectionAnnotations injectionAnnotations; 42 43 @Inject MethodSignatureFormatter(DaggerTypes types, InjectionAnnotations injectionAnnotations)44 public MethodSignatureFormatter(DaggerTypes types, InjectionAnnotations injectionAnnotations) { 45 this.types = types; 46 this.injectionAnnotations = injectionAnnotations; 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 public 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 66 public String format(ExecutableElement method) { 67 return format(method, Optional.empty()); 68 } 69 70 /** 71 * Formats an ExecutableElement as if it were contained within the container, if the container is 72 * present. 73 */ 74 public String format(ExecutableElement method, Optional<DeclaredType> container) { 75 TypeElement type = MoreElements.asType(method.getEnclosingElement()); 76 ExecutableType executableType = MoreTypes.asExecutable(method.asType()); 77 if (container.isPresent()) { 78 executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method)); 79 type = MoreElements.asType(container.get().asElement()); 80 } 81 return format(method, executableType, type); 82 } 83 84 private String format( 85 ExecutableElement method, ExecutableType methodType, TypeElement declaringType) { 86 StringBuilder builder = new StringBuilder(); 87 // TODO(user): AnnotationMirror formatter. 88 List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors(); 89 if (!annotations.isEmpty()) { 90 Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator(); 91 for (int i = 0; annotationIterator.hasNext(); i++) { 92 if (i > 0) { 93 builder.append(' '); 94 } 95 builder.append(formatAnnotation(annotationIterator.next())); 96 } 97 builder.append(' '); 98 } 99 if (method.getSimpleName().contentEquals("<init>")) { 100 builder.append(declaringType.getQualifiedName()); 101 } else { 102 builder 103 .append(nameOfType(methodType.getReturnType())) 104 .append(' ') 105 .append(declaringType.getQualifiedName()) 106 .append('.') 107 .append(method.getSimpleName()); 108 } 109 builder.append('('); 110 checkState(method.getParameters().size() == methodType.getParameterTypes().size()); 111 Iterator<? extends VariableElement> parameters = method.getParameters().iterator(); 112 Iterator<? extends TypeMirror> parameterTypes = methodType.getParameterTypes().iterator(); 113 for (int i = 0; parameters.hasNext(); i++) { 114 if (i > 0) { 115 builder.append(", "); 116 } 117 appendParameter(builder, parameters.next(), parameterTypes.next()); 118 } 119 builder.append(')'); 120 return builder.toString(); 121 } 122 123 private void appendParameter(StringBuilder builder, VariableElement parameter, TypeMirror type) { 124 injectionAnnotations 125 .getQualifier(parameter) 126 .ifPresent( 127 qualifier -> { 128 builder.append(formatAnnotation(qualifier)).append(' '); 129 }); 130 builder.append(nameOfType(type)); 131 } 132 133 private static String nameOfType(TypeMirror type) { 134 return stripCommonTypePrefixes(type.toString()); 135 } 136 137 private static String formatAnnotation(AnnotationMirror annotation) { 138 return stripCommonTypePrefixes(annotation.toString()); 139 } 140 } 141