• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.List;
22 import javax.lang.model.element.AnnotationMirror;
23 import javax.lang.model.element.Element;
24 import javax.lang.model.element.ElementKind;
25 import javax.lang.model.element.ExecutableElement;
26 import javax.lang.model.element.TypeElement;
27 import javax.lang.model.element.VariableElement;
28 import javax.lang.model.type.ExecutableType;
29 import javax.lang.model.type.TypeMirror;
30 import javax.lang.model.util.SimpleElementVisitor6;
31 import javax.lang.model.util.Types;
32 
33 import static com.google.common.base.Preconditions.checkState;
34 import static com.google.common.collect.Iterables.getOnlyElement;
35 import static dagger.internal.codegen.ErrorMessages.INDENT;
36 
37 /**
38  * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing
39  * a chain of dependencies.
40  *
41  * @author Christian Gruber
42  * @since 2.0
43  */
44 final class DependencyRequestFormatter extends Formatter<DependencyRequest> {
45   private final Types types;
46 
DependencyRequestFormatter(Types types)47   DependencyRequestFormatter(Types types) {
48     this.types = types;
49   }
50 
51   // TODO(cgruber): Sweep this class for TypeMirror.toString() usage and do some preventive format.
52   // TODO(cgruber): consider returning a small structure containing strings to be indented later.
format(final DependencyRequest request)53   @Override public String format(final DependencyRequest request) {
54     Element requestElement = request.requestElement();
55     Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(requestElement);
56     return requestElement.accept(new SimpleElementVisitor6<String, Optional<AnnotationMirror>>(){
57 
58       /* Handle component methods */
59       @Override public String visitExecutable(
60           ExecutableElement method, Optional<AnnotationMirror> qualifier) {
61         StringBuilder builder = new StringBuilder(INDENT);
62         if (method.getParameters().isEmpty()) {
63           // some.package.name.MyComponent.myMethod()
64           //     [component method with return type: @other.package.Qualifier some.package.name.Foo]
65           appendEnclosingTypeAndMemberName(method, builder).append("()\n")
66               .append(INDENT).append(INDENT).append("[component method with return type: ");
67           if (qualifier.isPresent()) {
68             // TODO(cgruber) use chenying's annotation mirror stringifier
69             builder.append(qualifier.get()).append(' ');
70           }
71           builder.append(method.getReturnType()).append(']');
72         } else {
73           // some.package.name.MyComponent.myMethod(some.package.name.Foo foo)
74           //     [component injection method for type: some.package.name.Foo]
75           VariableElement componentMethodParameter = getOnlyElement(method.getParameters());
76           appendEnclosingTypeAndMemberName(method, builder).append("(");
77           appendParameter(componentMethodParameter, componentMethodParameter.asType(), builder);
78           builder.append(")\n");
79           builder.append(INDENT).append(INDENT).append("[component injection method for type: ")
80               .append(componentMethodParameter.asType())
81               .append(']');
82         }
83         return builder.toString();
84       }
85 
86       /* Handle injected fields or method/constructor parameter injection. */
87       @Override public String visitVariable(
88           VariableElement variable, Optional<AnnotationMirror> qualifier) {
89         StringBuilder builder = new StringBuilder(INDENT);
90         TypeMirror resolvedVariableType =
91             MoreTypes.asMemberOf(types, request.enclosingType(), variable);
92         if (variable.getKind().equals(ElementKind.PARAMETER)) {
93           // some.package.name.MyClass.myMethod(some.package.name.Foo arg0, some.package.Bar arg1)
94           //     [parameter: @other.package.Qualifier some.package.name.Foo arg0]
95           ExecutableElement methodOrConstructor =
96               MoreElements.asExecutable(variable.getEnclosingElement());
97           ExecutableType resolvedMethodOrConstructor = MoreTypes.asExecutable(
98               types.asMemberOf(request.enclosingType(), methodOrConstructor));
99           appendEnclosingTypeAndMemberName(methodOrConstructor, builder).append('(');
100           List<? extends VariableElement> parameters = methodOrConstructor.getParameters();
101           List<? extends TypeMirror> parameterTypes =
102               resolvedMethodOrConstructor.getParameterTypes();
103           checkState(parameters.size() == parameterTypes.size());
104           for (int i = 0; i < parameters.size(); i++) {
105             appendParameter(parameters.get(i), parameterTypes.get(i), builder);
106             if (i != parameters.size() - 1) {
107               builder.append(", ");
108             }
109           }
110           builder.append(")\n").append(INDENT).append(INDENT).append("[parameter: ");
111         } else {
112           // some.package.name.MyClass.myField
113           //     [injected field of type: @other.package.Qualifier some.package.name.Foo myField]
114           appendEnclosingTypeAndMemberName(variable, builder).append("\n")
115               .append(INDENT).append(INDENT).append("[injected field of type: ");
116         }
117         if (qualifier.isPresent()) {
118           // TODO(cgruber) use chenying's annotation mirror stringifier
119           builder.append(qualifier.get()).append(' ');
120         }
121         builder.append(resolvedVariableType)
122             .append(' ')
123             .append(variable.getSimpleName())
124             .append(']');
125         return builder.toString();
126       }
127 
128       @Override
129       public String visitType(TypeElement e, Optional<AnnotationMirror> p) {
130         return ""; // types by themselves provide no useful information.
131       }
132 
133       @Override protected String defaultAction(Element element, Optional<AnnotationMirror> ignore) {
134         throw new IllegalStateException(
135             "Invalid request " + element.getKind() +  " element " + element);
136       }
137     }, qualifier);
138   }
139 
140   private StringBuilder appendParameter(VariableElement parameter, TypeMirror type,
141       StringBuilder builder) {
142     return builder.append(type).append(' ').append(parameter.getSimpleName());
143   }
144 
145   private StringBuilder appendEnclosingTypeAndMemberName(Element member, StringBuilder builder) {
146     TypeElement type = MoreElements.asType(member.getEnclosingElement());
147     return builder.append(type.getQualifiedName())
148         .append('.')
149         .append(member.getSimpleName());
150   }
151 }
152