1 package org.apache.maven.shared.dependency.graph.traversal;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.PrintWriter;
23 import java.io.Writer;
24 import java.util.List;
25
26 import org.apache.maven.shared.dependency.graph.DependencyNode;
27
28 /**
29 * A dependency node visitor that serializes visited nodes to a writer.
30 *
31 * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
32 */
33 public class SerializingDependencyNodeVisitor
34 implements DependencyNodeVisitor
35 {
36 // classes ----------------------------------------------------------------
37
38 /**
39 * Provides tokens to use when serializing the dependency graph.
40 */
41 public static class GraphTokens
42 {
43 private final String nodeIndent;
44
45 private final String lastNodeIndent;
46
47 private final String fillIndent;
48
49 private final String lastFillIndent;
50
51 public GraphTokens( String nodeIndent, String lastNodeIndent, String fillIndent, String lastFillIndent )
52 {
53 this.nodeIndent = nodeIndent;
54 this.lastNodeIndent = lastNodeIndent;
55 this.fillIndent = fillIndent;
56 this.lastFillIndent = lastFillIndent;
57 }
58
59 public String getNodeIndent( boolean last )
60 {
61 return last ? lastNodeIndent : nodeIndent;
62 }
63
64 public String getFillIndent( boolean last )
65 {
66 return last ? lastFillIndent : fillIndent;
67 }
68 }
69
70 // constants --------------------------------------------------------------
71
72 /**
73 * Whitespace tokens to use when outputing the dependency graph.
74 */
75 public static final GraphTokens WHITESPACE_TOKENS = new GraphTokens( " ", " ", " ", " " );
76
77 /**
78 * The standard ASCII tokens to use when outputing the dependency graph.
79 */
80 public static final GraphTokens STANDARD_TOKENS = new GraphTokens( "+- ", "\\- ", "| ", " " );
81
82 /**
83 * The extended ASCII tokens to use when outputing the dependency graph.
84 */
85 public static final GraphTokens EXTENDED_TOKENS =
86 new GraphTokens( "\u00c3\u00c4 ", "\u00c0\u00c4 ", "\u00b3 ", " " );
87
88 // fields -----------------------------------------------------------------
89
90 /**
91 * The writer to serialize to.
92 */
93 private final PrintWriter writer;
94
95 /**
96 * The tokens to use when serializing the dependency graph.
97 */
98 private final GraphTokens tokens;
99
100 /**
101 * The depth of the currently visited dependency node.
102 */
103 private int depth;
104
105 // constructors -----------------------------------------------------------
106
107 /**
108 * Creates a dependency node visitor that serializes visited nodes to the specified writer using whitespace tokens.
109 *
110 * @param writer
111 * the writer to serialize to
112 */
113 public SerializingDependencyNodeVisitor( Writer writer )
114 {
115 this( writer, WHITESPACE_TOKENS );
116 }
117
118 /**
119 * Creates a dependency node visitor that serializes visited nodes to the specified writer using the specified
120 * tokens.
121 *
122 * @param writer
123 * the writer to serialize to
124 * @param tokens
125 * the tokens to use when serializing the dependency graph
126 */
127 public SerializingDependencyNodeVisitor( Writer writer, GraphTokens tokens )
128 {
129 if ( writer instanceof PrintWriter )
130 {
131 this.writer = (PrintWriter) writer;
132 }
133 else
134 {
135 this.writer = new PrintWriter( writer, true );
136 }
137
138 this.tokens = tokens;
139
140 depth = 0;
141 }
142
143 // DependencyNodeVisitor methods ------------------------------------------
144
145 /**
146 * {@inheritDoc}
147 */
148 public boolean visit( DependencyNode node )
149 {
150 indent( node );
151
152 writer.println( node.toNodeString() );
153
154 depth++;
155
156 return true;
157 }
158
159 /**
160 * {@inheritDoc}
161 */
162 public boolean endVisit( DependencyNode node )
163 {
164 depth--;
165
166 return true;
167 }
168
169 // private methods --------------------------------------------------------
170
171 /**
172 * Writes the necessary tokens to indent the specified dependency node to this visitor's writer.
173 *
174 * @param node
175 * the dependency node to indent
176 */
177 private void indent( DependencyNode node )
178 {
179 for ( int i = 1; i < depth; i++ )
180 {
181 writer.write( tokens.getFillIndent( isLast( node, i ) ) );
182 }
183
184 if ( depth > 0 )
185 {
186 writer.write( tokens.getNodeIndent( isLast( node ) ) );
187 }
188 }
189
190 /**
191 * Gets whether the specified dependency node is the last of its siblings.
192 *
193 * @param node
194 * the dependency node to check
195 * @return <code>true</code> if the specified dependency node is the last of its last siblings
196 */
197 private boolean isLast( DependencyNode node )
198 {
199 // TODO: remove node argument and calculate from visitor calls only
200
201 DependencyNode parent = node.getParent();
202
203 boolean last;
204
205 if ( parent == null )
206 {
207 last = true;
208 }
209 else
210 {
211 List<DependencyNode> siblings = parent.getChildren();
212
213 last = ( siblings.indexOf( node ) == siblings.size() - 1 );
214 }
215
216 return last;
217 }
218
219 /**
220 * Gets whether the specified dependency node ancestor is the last of its siblings.
221 *
222 * @param node
223 * the dependency node whose ancestor to check
224 * @param ancestorDepth
225 * the depth of the ancestor of the specified dependency node to check
226 * @return <code>true</code> if the specified dependency node ancestor is the last of its siblings
227 */
228 private boolean isLast( DependencyNode node, int ancestorDepth )
229 {
230 // TODO: remove node argument and calculate from visitor calls only
231
232 int distance = depth - ancestorDepth;
233
234 while ( distance-- > 0 )
235 {
236 node = node.getParent();
237 }
238
239 return isLast( node );
240 }
241 }