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 dagger.internal.codegen.base.ElementFormatter.elementToString; 20 import static dagger.internal.codegen.base.RequestKinds.requestType; 21 22 import com.google.errorprone.annotations.CanIgnoreReturnValue; 23 import dagger.Provides; 24 import dagger.internal.codegen.base.Formatter; 25 import dagger.internal.codegen.langmodel.DaggerTypes; 26 import dagger.producers.Produces; 27 import dagger.spi.model.DaggerAnnotation; 28 import dagger.spi.model.DependencyRequest; 29 import java.util.Optional; 30 import javax.inject.Inject; 31 import javax.lang.model.element.Element; 32 import javax.lang.model.element.ElementVisitor; 33 import javax.lang.model.element.ExecutableElement; 34 import javax.lang.model.element.TypeElement; 35 import javax.lang.model.element.VariableElement; 36 import javax.lang.model.type.TypeMirror; 37 import javax.lang.model.util.ElementKindVisitor8; 38 39 /** 40 * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing a 41 * chain of dependencies. 42 * 43 * <dl> 44 * <dt>For component provision methods 45 * <dd>{@code @Qualifier SomeType is provided at\n ComponentType.method()} 46 * <dt>For component injection methods 47 * <dd>{@code SomeType is injected at\n ComponentType.method(foo)} 48 * <dt>For parameters to {@link Provides @Provides}, {@link Produces @Produces}, or {@link 49 * Inject @Inject} methods: 50 * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.method([…, ]param[, …])} 51 * <dt>For parameters to {@link Inject @Inject} constructors: 52 * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType([…, ]param[, …])} 53 * <dt>For {@link Inject @Inject} fields: 54 * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.field} 55 * </dl> 56 */ 57 public final class DependencyRequestFormatter extends Formatter<DependencyRequest> { 58 59 private final DaggerTypes types; 60 61 @Inject DependencyRequestFormatter(DaggerTypes types)62 DependencyRequestFormatter(DaggerTypes types) { 63 this.types = types; 64 } 65 66 @Override format(DependencyRequest request)67 public String format(DependencyRequest request) { 68 return request 69 .requestElement() 70 .map(element -> element.java().accept(formatVisitor, request)) 71 .orElse(""); 72 } 73 74 /** 75 * Appends a newline and the formatted dependency request unless {@link 76 * #format(DependencyRequest)} returns the empty string. 77 */ 78 @CanIgnoreReturnValue appendFormatLine( StringBuilder builder, DependencyRequest dependencyRequest)79 public StringBuilder appendFormatLine( 80 StringBuilder builder, DependencyRequest dependencyRequest) { 81 String formatted = format(dependencyRequest); 82 if (!formatted.isEmpty()) { 83 builder.append('\n').append(formatted); 84 } 85 return builder; 86 } 87 88 private final ElementVisitor<String, DependencyRequest> formatVisitor = 89 new ElementKindVisitor8<String, DependencyRequest>() { 90 91 @Override 92 public String visitExecutableAsMethod(ExecutableElement method, DependencyRequest request) { 93 return INDENT 94 + request.key() 95 + " is " 96 + componentMethodRequestVerb(request) 97 + " at\n" 98 + DOUBLE_INDENT 99 + elementToString(method); 100 } 101 102 @Override 103 public String visitVariable(VariableElement variable, DependencyRequest request) { 104 TypeMirror requestedType = 105 requestType(request.kind(), request.key().type().java(), types); 106 return INDENT 107 + formatQualifier(request.key().qualifier()) 108 + requestedType 109 + " is injected at\n" 110 + DOUBLE_INDENT 111 + elementToString(variable); 112 } 113 114 @Override 115 public String visitType(TypeElement e, DependencyRequest request) { 116 return ""; // types by themselves provide no useful information. 117 } 118 119 @Override 120 protected String defaultAction(Element element, DependencyRequest request) { 121 throw new IllegalStateException( 122 "Invalid request " + element.getKind() + " element " + element); 123 } 124 }; 125 formatQualifier(Optional<DaggerAnnotation> maybeQualifier)126 private String formatQualifier(Optional<DaggerAnnotation> maybeQualifier) { 127 return maybeQualifier.map(qualifier -> qualifier + " ").orElse(""); 128 } 129 130 /** 131 * Returns the verb for a component method dependency request. Returns "produced", "provided", or 132 * "injected", depending on the kind of request. 133 */ componentMethodRequestVerb(DependencyRequest request)134 private String componentMethodRequestVerb(DependencyRequest request) { 135 switch (request.kind()) { 136 case FUTURE: 137 case PRODUCER: 138 case INSTANCE: 139 case LAZY: 140 case PROVIDER: 141 case PROVIDER_OF_LAZY: 142 return "requested"; 143 144 case MEMBERS_INJECTION: 145 return "injected"; 146 147 case PRODUCED: 148 break; 149 } 150 throw new AssertionError("illegal request kind for method: " + request); 151 } 152 } 153