1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.util.graph.visitor;
20
21 import java.util.ArrayDeque;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Deque;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Objects;
31 import java.util.function.Consumer;
32 import java.util.function.Function;
33 import java.util.stream.Collectors;
34
35 import org.eclipse.aether.artifact.Artifact;
36 import org.eclipse.aether.graph.Dependency;
37 import org.eclipse.aether.graph.DependencyNode;
38 import org.eclipse.aether.graph.DependencyVisitor;
39 import org.eclipse.aether.graph.Exclusion;
40 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
41 import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
42 import org.eclipse.aether.util.graph.transformer.ConflictResolver;
43 import org.eclipse.aether.version.VersionConstraint;
44
45 import static java.util.Objects.requireNonNull;
46
47
48
49
50
51
52
53 public class DependencyGraphDumper implements DependencyVisitor {
54
55
56
57 public static Function<DependencyNode, String> effectiveDependency() {
58 return dependencyNode -> {
59 Dependency d = dependencyNode.getDependency();
60 if (d != null) {
61 if (!d.getScope().isEmpty()) {
62 String result = d.getScope();
63 if (d.isOptional()) {
64 result += ", optional";
65 }
66 return "[" + result + "]";
67 }
68 }
69 return null;
70 };
71 }
72
73
74
75 public static Function<DependencyNode, String> premanagedVersion() {
76 return dependencyNode -> {
77 if (dependencyNode.getArtifact() != null) {
78 String premanagedVersion = DependencyManagerUtils.getPremanagedVersion(dependencyNode);
79 if (premanagedVersion != null
80 && !premanagedVersion.equals(
81 dependencyNode.getArtifact().getBaseVersion())) {
82 return "(version managed from " + premanagedVersion + ")";
83 }
84 }
85 return null;
86 };
87 }
88
89
90
91 public static Function<DependencyNode, String> premanagedScope() {
92 return dependencyNode -> {
93 Dependency d = dependencyNode.getDependency();
94 if (d != null) {
95 String premanagedScope = DependencyManagerUtils.getPremanagedScope(dependencyNode);
96 if (premanagedScope != null && !premanagedScope.equals(d.getScope())) {
97 return "(scope managed from " + premanagedScope + ")";
98 }
99 }
100 return null;
101 };
102 }
103
104
105
106 public static Function<DependencyNode, String> premanagedOptional() {
107 return dependencyNode -> {
108 Dependency d = dependencyNode.getDependency();
109 if (d != null) {
110 Boolean premanagedOptional = DependencyManagerUtils.getPremanagedOptional(dependencyNode);
111 if (premanagedOptional != null && !premanagedOptional.equals(d.getOptional())) {
112 return "(optionality managed from " + premanagedOptional + ")";
113 }
114 }
115 return null;
116 };
117 }
118
119
120
121 public static Function<DependencyNode, String> premanagedExclusions() {
122 return dependencyNode -> {
123 Dependency d = dependencyNode.getDependency();
124 if (d != null) {
125 Collection<Exclusion> premanagedExclusions =
126 DependencyManagerUtils.getPremanagedExclusions(dependencyNode);
127 if (premanagedExclusions != null && !equals(premanagedExclusions, d.getExclusions())) {
128 return "(exclusions managed from " + premanagedExclusions + ")";
129 }
130 }
131 return null;
132 };
133 }
134
135
136
137 public static Function<DependencyNode, String> premanagedProperties() {
138 return dependencyNode -> {
139 if (dependencyNode.getArtifact() != null) {
140 Map<String, String> premanagedProperties =
141 DependencyManagerUtils.getPremanagedProperties(dependencyNode);
142 if (premanagedProperties != null
143 && !equals(
144 premanagedProperties,
145 dependencyNode.getArtifact().getProperties())) {
146 return "(properties managed from " + premanagedProperties + ")";
147 }
148 }
149 return null;
150 };
151 }
152
153
154
155 public static Function<DependencyNode, String> rangeMember() {
156 return dependencyNode -> {
157 VersionConstraint constraint = dependencyNode.getVersionConstraint();
158 if (constraint != null && constraint.getRange() != null) {
159 return "(range '" + constraint.getRange() + "')";
160 }
161 return null;
162 };
163 }
164
165
166
167 public static Function<DependencyNode, String> winnerNode() {
168 return dependencyNode -> {
169 if (dependencyNode.getArtifact() != null) {
170 DependencyNode winner =
171 (DependencyNode) dependencyNode.getData().get(ConflictResolver.NODE_DATA_WINNER);
172 if (winner != null) {
173 if (ArtifactIdUtils.equalsId(dependencyNode.getArtifact(), winner.getArtifact())) {
174 return "(nearer exists)";
175 } else {
176 Artifact w = winner.getArtifact();
177 String result = "conflicts with ";
178 if (ArtifactIdUtils.toVersionlessId(dependencyNode.getArtifact())
179 .equals(ArtifactIdUtils.toVersionlessId(w))) {
180 result += w.getVersion();
181 } else {
182 result += w;
183 }
184 return "(" + result + ")";
185 }
186 }
187 }
188 return null;
189 };
190 }
191
192
193
194 public static Function<DependencyNode, String> artifactProperties(Collection<String> properties) {
195 requireNonNull(properties, "properties");
196 return dependencyNode -> {
197 if (!properties.isEmpty() && dependencyNode.getDependency() != null) {
198 String props = properties.stream()
199 .map(p -> p + "="
200 + dependencyNode.getDependency().getArtifact().getProperty(p, "n/a"))
201 .collect(Collectors.joining(","));
202 if (!props.isEmpty()) {
203 return "(" + props + ")";
204 }
205 }
206 return null;
207 };
208 }
209
210
211
212
213
214
215 private static final List<Function<DependencyNode, String>> DEFAULT_DECORATORS =
216 Collections.unmodifiableList(Arrays.asList(
217 effectiveDependency(),
218 premanagedVersion(),
219 premanagedScope(),
220 premanagedOptional(),
221 premanagedExclusions(),
222 premanagedProperties(),
223 rangeMember(),
224 winnerNode()));
225
226
227
228
229
230
231 public static List<Function<DependencyNode, String>> defaultsWith(
232 Collection<Function<DependencyNode, String>> extras) {
233 requireNonNull(extras, "extras");
234 ArrayList<Function<DependencyNode, String>> result = new ArrayList<>(DEFAULT_DECORATORS);
235 result.addAll(extras);
236 return result;
237 }
238
239 private final Consumer<String> consumer;
240
241 private final List<Function<DependencyNode, String>> decorators;
242
243 private final Deque<DependencyNode> nodes = new ArrayDeque<>();
244
245
246
247
248
249
250 public DependencyGraphDumper(Consumer<String> consumer) {
251 this(consumer, DEFAULT_DECORATORS);
252 }
253
254
255
256
257
258
259
260
261 public DependencyGraphDumper(Consumer<String> consumer, Collection<Function<DependencyNode, String>> decorators) {
262 this.consumer = requireNonNull(consumer);
263 this.decorators = new ArrayList<>(decorators);
264 }
265
266 @Override
267 public boolean visitEnter(DependencyNode node) {
268 nodes.push(node);
269 consumer.accept(formatLine(nodes));
270 return true;
271 }
272
273 @Override
274 public boolean visitLeave(DependencyNode node) {
275 if (!nodes.isEmpty()) {
276 nodes.pop();
277 }
278 return true;
279 }
280
281 protected String formatLine(Deque<DependencyNode> nodes) {
282 return formatIndentation(nodes) + formatNode(nodes);
283 }
284
285 protected String formatIndentation(Deque<DependencyNode> nodes) {
286 StringBuilder buffer = new StringBuilder(128);
287 Iterator<DependencyNode> iter = nodes.descendingIterator();
288 DependencyNode parent = iter.hasNext() ? iter.next() : null;
289 DependencyNode child = iter.hasNext() ? iter.next() : null;
290 while (parent != null && child != null) {
291 boolean lastChild = parent.getChildren().get(parent.getChildren().size() - 1) == child;
292 boolean end = child == nodes.peekFirst();
293 String indent;
294 if (end) {
295 indent = lastChild ? "\\- " : "+- ";
296 } else {
297 indent = lastChild ? " " : "| ";
298 }
299 buffer.append(indent);
300 parent = child;
301 child = iter.hasNext() ? iter.next() : null;
302 }
303 return buffer.toString();
304 }
305
306 protected String formatNode(Deque<DependencyNode> nodes) {
307 DependencyNode node = requireNonNull(nodes.peek(), "bug: should not happen");
308 StringBuilder buffer = new StringBuilder(128);
309 Artifact a = node.getArtifact();
310 buffer.append(a);
311 for (Function<DependencyNode, String> decorator : decorators) {
312 String decoration = decorator.apply(node);
313 if (decoration != null) {
314 buffer.append(" ").append(decoration);
315 }
316 }
317 return buffer.toString();
318 }
319
320 private static boolean equals(Collection<Exclusion> c1, Collection<Exclusion> c2) {
321 return c1 != null && c2 != null && c1.size() == c2.size() && c1.containsAll(c2);
322 }
323
324 private static boolean equals(Map<String, String> m1, Map<String, String> m2) {
325 return m1 != null
326 && m2 != null
327 && m1.size() == m2.size()
328 && m1.entrySet().stream().allMatch(entry -> Objects.equals(m2.get(entry.getKey()), entry.getValue()));
329 }
330 }