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.apache.maven.plugins.dependency.tree;
20
21 import java.io.IOException;
22 import java.io.UncheckedIOException;
23 import java.io.Writer;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 import org.apache.maven.shared.dependency.graph.DependencyNode;
28 import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
29
30 /**
31 * A dependency node visitor that serializes visited nodes to a writer using the
32 * <a href="https://en.wikipedia.org/wiki/Trivial_Graph_Format">TGF format</a>.
33 *
34 * @author <a href="mailto:jerome.creignou@gmail.com">Jerome Creignou</a>
35 * @since 2.1
36 */
37 public class TGFDependencyNodeVisitor extends AbstractSerializingVisitor implements DependencyNodeVisitor {
38
39 /**
40 * Utility class to write an Edge.
41 *
42 * @author <a href="mailto:jerome.creignou@gmail.com">Jerome Creignou</a>
43 */
44 static final class EdgeAppender {
45 /**
46 * Edge start.
47 */
48 private final DependencyNode from;
49
50 /**
51 * Edge end.
52 */
53 private final DependencyNode to;
54
55 /**
56 * Edge label. (optional)
57 */
58 private final String label;
59
60 /**
61 * Build a new EdgeAppender.
62 *
63 * @param from edge start
64 * @param to edge end
65 * @param label optional label
66 */
67 EdgeAppender(DependencyNode from, DependencyNode to, String label) {
68 super();
69 this.from = from;
70 this.to = to;
71 this.label = label;
72 }
73
74 /**
75 * Build a string representing the edge.
76 */
77 @Override
78 public String toString() {
79 StringBuilder result = new StringBuilder(generateId(from));
80 result.append(' ').append(generateId(to));
81 if (label != null) {
82 result.append(' ').append(label);
83 }
84 return result.toString();
85 }
86 }
87
88 /**
89 * List of edges.
90 */
91 private final List<EdgeAppender> edges = new ArrayList<>();
92
93 /**
94 * Constructor.
95 *
96 * @param writer the writer to write to
97 */
98 public TGFDependencyNodeVisitor(Writer writer) {
99 super(writer);
100 }
101
102 /**
103 * {@inheritDoc}
104 */
105 @Override
106 public boolean endVisit(DependencyNode node) {
107 try {
108 if (node.getParent() == null || node.getParent() == node) {
109 // dump edges on last node endVisit
110 writer.write("#" + System.lineSeparator());
111 for (EdgeAppender edge : edges) {
112 writer.write(edge.toString() + System.lineSeparator());
113 }
114 writer.flush();
115 } else {
116 DependencyNode parent = node.getParent();
117 // using scope as edge label.
118 edges.add(new EdgeAppender(parent, node, node.getArtifact().getScope()));
119 }
120 return true;
121 } catch (IOException e) {
122 throw new UncheckedIOException("Failed to write TGF format output", e);
123 }
124 }
125
126 /**
127 * {@inheritDoc}
128 */
129 @Override
130 public boolean visit(DependencyNode node) {
131 try {
132 // Write node
133 writer.write(generateId(node));
134 writer.write(" ");
135 writer.write(node.toNodeString());
136 writer.write(System.lineSeparator());
137 writer.flush();
138 return true;
139 } catch (IOException e) {
140 throw new UncheckedIOException("Failed to write TGF format output", e);
141 }
142 }
143
144 /**
145 * Generate a unique id from a DependencyNode.
146 * <p>
147 * Current implementation is rather simple and uses hashcode.
148 * </p>
149 *
150 * @param node the DependencyNode to use
151 * @return the unique id
152 */
153 private static String generateId(DependencyNode node) {
154 return String.valueOf(node.hashCode());
155 }
156 }