• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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