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 
MethodSignatureFormatter(Types types)45   MethodSignatureFormatter(Types types) {
46     this.types = types;
47   }
48 
format(ExecutableElement method)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    */
format(ExecutableElement method, Optional<DeclaredType> container)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 
appendParameter(StringBuilder builder, VariableElement parameter, TypeMirror type)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 
nameOfType(TypeMirror type)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