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