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