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 androidx.room.compiler.processing.XElementKt.isMethod; 20 import static androidx.room.compiler.processing.XElementKt.isTypeElement; 21 import static androidx.room.compiler.processing.XElementKt.isVariableElement; 22 import static dagger.internal.codegen.base.ElementFormatter.elementToString; 23 import static dagger.internal.codegen.base.RequestKinds.requestType; 24 import static java.util.stream.Collectors.joining; 25 26 import androidx.room.compiler.processing.XElement; 27 import androidx.room.compiler.processing.XProcessingEnv; 28 import androidx.room.compiler.processing.XTypeElement; 29 import com.google.common.collect.ImmutableCollection; 30 import dagger.internal.codegen.base.Formatter; 31 import dagger.internal.codegen.model.BindingGraph; 32 import dagger.internal.codegen.model.BindingGraph.DependencyEdge; 33 import dagger.internal.codegen.model.BindingGraph.Node; 34 import dagger.internal.codegen.model.DaggerAnnotation; 35 import dagger.internal.codegen.model.DependencyRequest; 36 import dagger.internal.codegen.xprocessing.XTypes; 37 import java.util.Optional; 38 import javax.inject.Inject; 39 40 /** 41 * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing a 42 * chain of dependencies. 43 * 44 * <dl> 45 * <dt>For component provision methods 46 * <dd>{@code @Qualifier SomeType is provided at\n ComponentType.method()} 47 * <dt>For component injection methods 48 * <dd>{@code SomeType is injected at\n ComponentType.method(foo)} 49 * <dt>For parameters to {@code @Provides}, {@code @Produces}, or {@code @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 XProcessingEnv processingEnv; 60 61 @Inject DependencyRequestFormatter(XProcessingEnv processingEnv)62 DependencyRequestFormatter(XProcessingEnv processingEnv) { 63 this.processingEnv = processingEnv; 64 } 65 formatEdges(ImmutableCollection<DependencyEdge> edges, BindingGraph graph)66 public String formatEdges(ImmutableCollection<DependencyEdge> edges, BindingGraph graph) { 67 return edges.stream() 68 .map(edge -> formatEdge(edge, graph)) 69 .filter(line -> !line.isEmpty()) 70 .collect(joining("\n")); 71 } 72 formatEdge(DependencyEdge edge, BindingGraph graph)73 public String formatEdge(DependencyEdge edge, BindingGraph graph) { 74 Node sourceNode = graph.network().incidentNodes(edge).source(); 75 XTypeElement sourceComponent = sourceNode.componentPath().currentComponent().xprocessing(); 76 return format(Optional.of(sourceComponent), edge.dependencyRequest()); 77 } 78 79 @Override format(DependencyRequest request)80 public String format(DependencyRequest request) { 81 return format(Optional.empty(), request); 82 } 83 format(Optional<XTypeElement> optionalComponent, DependencyRequest request)84 private String format(Optional<XTypeElement> optionalComponent, DependencyRequest request) { 85 if (!request.requestElement().isPresent()) { 86 return ""; 87 } 88 XElement requestElement = request.requestElement().get().xprocessing(); 89 String componentReference = 90 optionalComponent 91 .map(component -> String.format("[%s] ", component.getQualifiedName())) 92 .orElse(""); 93 if (isMethod(requestElement)) { 94 return INDENT 95 + request.key() 96 + " is " 97 + componentMethodRequestVerb(request) 98 + " at\n" 99 + DOUBLE_INDENT 100 + componentReference 101 + elementToString(requestElement); 102 } else if (isVariableElement(requestElement)) { 103 return INDENT 104 + formatQualifier(request.key().qualifier()) 105 + XTypes.toStableString( 106 requestType(request.kind(), request.key().type().xprocessing(), processingEnv)) 107 + " is injected at\n" 108 + DOUBLE_INDENT 109 + componentReference 110 + elementToString(requestElement); 111 } else if (isTypeElement(requestElement)) { 112 return ""; // types by themselves provide no useful information. 113 } else { 114 throw new IllegalStateException("Invalid request element " + requestElement); 115 } 116 } 117 formatQualifier(Optional<DaggerAnnotation> maybeQualifier)118 private static String formatQualifier(Optional<DaggerAnnotation> maybeQualifier) { 119 return maybeQualifier.map(qualifier -> qualifier + " ").orElse(""); 120 } 121 122 /** 123 * Returns the verb for a component method dependency request. Returns "produced", "provided", or 124 * "injected", depending on the kind of request. 125 */ componentMethodRequestVerb(DependencyRequest request)126 private static String componentMethodRequestVerb(DependencyRequest request) { 127 switch (request.kind()) { 128 case FUTURE: 129 case PRODUCER: 130 case INSTANCE: 131 case LAZY: 132 case PROVIDER: 133 case PROVIDER_OF_LAZY: 134 return "requested"; 135 136 case MEMBERS_INJECTION: 137 return "injected"; 138 139 case PRODUCED: 140 break; 141 } 142 throw new AssertionError("illegal request kind for method: " + request); 143 } 144 } 145