1 /* 2 * Copyright (C) 2014 Google, Inc. 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 package dagger.internal.codegen; 17 18 import com.google.auto.common.MoreElements; 19 import com.google.auto.common.MoreTypes; 20 import com.google.common.base.Optional; 21 import java.util.Iterator; 22 import java.util.List; 23 import javax.lang.model.element.AnnotationMirror; 24 import javax.lang.model.element.ExecutableElement; 25 import javax.lang.model.element.TypeElement; 26 import javax.lang.model.element.VariableElement; 27 import javax.lang.model.type.DeclaredType; 28 import javax.lang.model.type.ExecutableType; 29 import javax.lang.model.type.TypeKind; 30 import javax.lang.model.type.TypeMirror; 31 import javax.lang.model.util.Types; 32 33 import static com.google.common.base.Preconditions.checkState; 34 import static dagger.internal.codegen.ErrorMessages.stripCommonTypePrefixes; 35 36 /** 37 * Formats the signature of an {@link ExecutableElement} suitable for use in error messages. 38 * 39 * @author Christian Gruber 40 * @since 2.0 41 */ 42 final class MethodSignatureFormatter extends Formatter<ExecutableElement> { 43 private final Types types; 44 45 MethodSignatureFormatter(Types types) { 46 this.types = types; 47 } 48 49 @Override public String format(ExecutableElement method) { 50 return format(method, Optional.<DeclaredType>absent()); 51 } 52 53 /** 54 * Formats an ExecutableElement as if it were contained within the container, if the container is 55 * present. 56 */ 57 public String format(ExecutableElement method, Optional<DeclaredType> container) { 58 StringBuilder builder = new StringBuilder(); 59 TypeElement type = MoreElements.asType(method.getEnclosingElement()); 60 ExecutableType executableType = MoreTypes.asExecutable(method.asType()); 61 if (container.isPresent()) { 62 executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method)); 63 type = MoreElements.asType(container.get().asElement()); 64 } 65 66 // TODO(cgruber): AnnotationMirror formatter. 67 List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors(); 68 if (!annotations.isEmpty()) { 69 Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator(); 70 for (int i = 0; annotationIterator.hasNext(); i++) { 71 if (i > 0) { 72 builder.append(' '); 73 } 74 builder.append(ErrorMessages.format(annotationIterator.next())); 75 } 76 builder.append(' '); 77 } 78 builder.append(nameOfType(executableType.getReturnType())); 79 builder.append(' '); 80 builder.append(type.getQualifiedName()); 81 builder.append('.'); 82 builder.append(method.getSimpleName()); 83 builder.append('('); 84 checkState(method.getParameters().size() == executableType.getParameterTypes().size()); 85 Iterator<? extends VariableElement> parameters = method.getParameters().iterator(); 86 Iterator<? extends TypeMirror> parameterTypes = executableType.getParameterTypes().iterator(); 87 for (int i = 0; parameters.hasNext(); i++) { 88 if (i > 0) { 89 builder.append(", "); 90 } 91 appendParameter(builder, parameters.next(), parameterTypes.next()); 92 } 93 builder.append(')'); 94 return builder.toString(); 95 } 96 97 private static void appendParameter(StringBuilder builder, VariableElement parameter, 98 TypeMirror type) { 99 Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(parameter); 100 if (qualifier.isPresent()) { 101 builder.append(ErrorMessages.format(qualifier.get())).append(' '); 102 } 103 builder.append(nameOfType(type)); 104 } 105 106 private static String nameOfType(TypeMirror type) { 107 if (type.getKind().isPrimitive()) { 108 return MoreTypes.asPrimitiveType(type).toString(); 109 } else if (type.getKind() == TypeKind.VOID) { 110 return "void"; 111 } else { 112 return stripCommonTypePrefixes(MoreTypes.asDeclared(type).toString()); 113 } 114 } 115 } 116