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.internal.impl;
20  
21  import java.io.PrintWriter;
22  import java.util.ArrayList;
23  import java.util.IdentityHashMap;
24  import java.util.Iterator;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.eclipse.aether.graph.Dependency;
30  import org.eclipse.aether.graph.DependencyNode;
31  
32  /**
33   * A helper to visualize dependency graphs.
34   */
35  public class DependencyGraphDumper {
36  
37      public static void dump(PrintWriter writer, DependencyNode node) {
38          Context context = new Context();
39          dump(context, node, 0, true);
40  
41          LinkedList<Indent> indents = new LinkedList<>();
42          for (Line line : context.lines) {
43              if (line.depth > indents.size()) {
44                  if (!indents.isEmpty()) {
45                      if (indents.getLast() == Indent.CHILD) {
46                          indents.removeLast();
47                          indents.addLast(Indent.CHILDREN);
48                      } else if (indents.getLast() == Indent.LAST_CHILD) {
49                          indents.removeLast();
50                          indents.addLast(Indent.NO_CHILDREN);
51                      }
52                  }
53                  indents.addLast(line.last ? Indent.LAST_CHILD : Indent.CHILD);
54              } else if (line.depth < indents.size()) {
55                  while (line.depth <= indents.size()) {
56                      indents.removeLast();
57                  }
58                  indents.addLast(line.last ? Indent.LAST_CHILD : Indent.CHILD);
59              } else if (line.last && !indents.isEmpty()) {
60                  indents.removeLast();
61                  indents.addLast(Indent.LAST_CHILD);
62              }
63  
64              for (Indent indent : indents) {
65                  writer.print(indent);
66              }
67  
68              line.print(writer);
69          }
70  
71          writer.flush();
72      }
73  
74      private static void dump(Context context, DependencyNode node, int depth, boolean last) {
75          Line line = context.nodes.get(node);
76          if (line != null) {
77              if (line.id <= 0) {
78                  line.id = ++context.ids;
79              }
80              context.lines.add(new Line(null, line.id, depth, last));
81              return;
82          }
83  
84          Dependency dependency = node.getDependency();
85  
86          if (dependency == null) {
87              line = new Line(null, 0, depth, last);
88          } else {
89              line = new Line(dependency, 0, depth, last);
90          }
91  
92          context.lines.add(line);
93  
94          context.nodes.put(node, line);
95  
96          depth++;
97  
98          for (Iterator<DependencyNode> it = node.getChildren().iterator(); it.hasNext(); ) {
99              DependencyNode child = it.next();
100             dump(context, child, depth, !it.hasNext());
101         }
102     }
103 
104     enum Indent {
105         NO_CHILDREN("   "),
106 
107         CHILDREN("|  "),
108 
109         CHILD("+- "),
110 
111         LAST_CHILD("\\- ");
112 
113         private final String chars;
114 
115         Indent(String chars) {
116             this.chars = chars;
117         }
118 
119         @Override
120         public String toString() {
121             return chars;
122         }
123     }
124 
125     static class Context {
126 
127         int ids;
128 
129         List<Line> lines;
130 
131         Map<DependencyNode, Line> nodes;
132 
133         Context() {
134             this.lines = new ArrayList<>();
135             this.nodes = new IdentityHashMap<>(1024);
136         }
137     }
138 
139     static class Line {
140 
141         Dependency dependency;
142 
143         int id;
144 
145         int depth;
146 
147         boolean last;
148 
149         Line(Dependency dependency, int id, int depth, boolean last) {
150             this.dependency = dependency;
151             this.id = id;
152             this.depth = depth;
153             this.last = last;
154         }
155 
156         void print(PrintWriter writer) {
157             if (dependency == null) {
158                 if (id > 0) {
159                     writer.print("^");
160                     writer.print(id);
161                 } else {
162                     writer.print("(null)");
163                 }
164             } else {
165                 if (id > 0) {
166                     writer.print("(");
167                     writer.print(id);
168                     writer.print(")");
169                 }
170                 writer.print(dependency.getArtifact());
171                 if (dependency.getScope().length() > 0) {
172                     writer.print(":");
173                     writer.print(dependency.getScope());
174                 }
175             }
176             writer.println();
177         }
178     }
179 }