View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.util.graph.visitor;
20  
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.function.Consumer;
25  
26  import org.eclipse.aether.artifact.Artifact;
27  import org.eclipse.aether.graph.Dependency;
28  import org.eclipse.aether.graph.DependencyNode;
29  import org.eclipse.aether.graph.DependencyVisitor;
30  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
31  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
32  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
33  
34  import static java.util.Objects.requireNonNull;
35  
36  /**
37   * A dependency visitor that dumps the graph to any {@link Consumer<String>}. Meant for diagnostic and testing, as
38   * it may output the graph to standard output, error or even some logging interface.
39   *
40   * @since 1.9.8
41   */
42  public class DependencyGraphDumper implements DependencyVisitor {
43  
44      private final Consumer<String> consumer;
45  
46      private final List<ChildInfo> childInfos = new ArrayList<>();
47  
48      public DependencyGraphDumper(Consumer<String> consumer) {
49          this.consumer = requireNonNull(consumer);
50      }
51  
52      @Override
53      public boolean visitEnter(DependencyNode node) {
54          consumer.accept(formatIndentation() + formatNode(node));
55          childInfos.add(new ChildInfo(node.getChildren().size()));
56          return true;
57      }
58  
59      private String formatIndentation() {
60          StringBuilder buffer = new StringBuilder(128);
61          for (Iterator<ChildInfo> it = childInfos.iterator(); it.hasNext(); ) {
62              buffer.append(it.next().formatIndentation(!it.hasNext()));
63          }
64          return buffer.toString();
65      }
66  
67      private String formatNode(DependencyNode node) {
68          StringBuilder buffer = new StringBuilder(128);
69          Artifact a = node.getArtifact();
70          Dependency d = node.getDependency();
71          buffer.append(a);
72          if (d != null && d.getScope().length() > 0) {
73              buffer.append(" [").append(d.getScope());
74              if (d.isOptional()) {
75                  buffer.append(", optional");
76              }
77              buffer.append("]");
78          }
79          String premanaged = DependencyManagerUtils.getPremanagedVersion(node);
80          if (premanaged != null && !premanaged.equals(a.getBaseVersion())) {
81              buffer.append(" (version managed from ").append(premanaged).append(")");
82          }
83  
84          premanaged = DependencyManagerUtils.getPremanagedScope(node);
85          if (premanaged != null && !premanaged.equals(d.getScope())) {
86              buffer.append(" (scope managed from ").append(premanaged).append(")");
87          }
88          DependencyNode winner = (DependencyNode) node.getData().get(ConflictResolver.NODE_DATA_WINNER);
89          if (winner != null) {
90              if (ArtifactIdUtils.equalsId(a, winner.getArtifact())) {
91                  buffer.append(" (nearer exists)");
92              } else {
93                  Artifact w = winner.getArtifact();
94                  buffer.append(" (conflicts with ");
95                  if (ArtifactIdUtils.toVersionlessId(a).equals(ArtifactIdUtils.toVersionlessId(w))) {
96                      buffer.append(w.getVersion());
97                  } else {
98                      buffer.append(w);
99                  }
100                 buffer.append(")");
101             }
102         }
103         return buffer.toString();
104     }
105 
106     @Override
107     public boolean visitLeave(DependencyNode node) {
108         if (!childInfos.isEmpty()) {
109             childInfos.remove(childInfos.size() - 1);
110         }
111         if (!childInfos.isEmpty()) {
112             childInfos.get(childInfos.size() - 1).index++;
113         }
114         return true;
115     }
116 
117     private static class ChildInfo {
118 
119         final int count;
120 
121         int index;
122 
123         ChildInfo(int count) {
124             this.count = count;
125         }
126 
127         public String formatIndentation(boolean end) {
128             boolean last = index + 1 >= count;
129             if (end) {
130                 return last ? "\\- " : "+- ";
131             }
132             return last ? "   " : "|  ";
133         }
134     }
135 }