• 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.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