1 package org.apache.maven.report.projectinfo.dependencies;
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.util.List;
23
24 import org.apache.maven.doxia.sink.Sink;
25 import org.apache.maven.shared.dependency.tree.DependencyNode;
26 import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
27
28 /**
29 * A dependency node visitor that serializes visited nodes to a sink writer. It's used to serialize tree in project
30 * information report page.
31 *
32 * @author <a href="mailto:wangyf2010@gmail.com">Simon Wang</a>
33 */
34 public class SinkSerializingDependencyNodeVisitor
35 implements DependencyNodeVisitor
36 {
37 // classes ----------------------------------------------------------------
38
39 /**
40 * Provides tokens to use when serializing the dependency tree.
41 */
42 private class TreeTokens
43 {
44 private final Sink sink;
45
46 TreeTokens( Sink sink )
47 {
48 this.sink = sink;
49 }
50
51 void addNodeIndent( boolean last )
52 {
53 if ( last )
54 {
55 sink.text( "\\-" );
56 sink.nonBreakingSpace();
57 }
58 else
59 {
60 sink.text( "+-" );
61 sink.nonBreakingSpace();
62 }
63 }
64
65 void fillIndent( boolean last )
66 {
67 if ( last )
68 {
69 sink.nonBreakingSpace();
70 sink.nonBreakingSpace();
71 sink.nonBreakingSpace();
72 }
73 else
74 {
75 sink.text( "|" );
76 sink.nonBreakingSpace();
77 sink.nonBreakingSpace();
78 }
79 }
80 }
81
82 // fields -----------------------------------------------------------------
83
84 /**
85 * The writer to serialize to.
86 */
87 private final Sink sink;
88
89 /**
90 * The tokens to use when serializing the dependency tree.
91 */
92 private final TreeTokens tokens;
93
94 /**
95 * The depth of the currently visited dependency node.
96 */
97 private int depth;
98
99 // constructors -----------------------------------------------------------
100
101 /**
102 * Creates a dependency node visitor that serializes visited nodes to the specified writer using the specified
103 * tokens.
104 *
105 * @param sink the writer to serialize to
106 */
107 public SinkSerializingDependencyNodeVisitor( Sink sink )
108 {
109 this.sink = sink;
110 this.tokens = new TreeTokens( sink );
111 depth = 0;
112 }
113
114 // DependencyNodeVisitor methods ------------------------------------------
115
116 /**
117 * {@inheritDoc}
118 */
119 public boolean visit( DependencyNode node )
120 {
121 indent( node );
122
123 sink.text( node.toNodeString() );
124 sink.lineBreak();
125
126 depth++;
127
128 return true;
129 }
130
131 /**
132 * {@inheritDoc}
133 */
134 public boolean endVisit( DependencyNode node )
135 {
136 depth--;
137
138 return true;
139 }
140
141 // private methods --------------------------------------------------------
142
143 /**
144 * Writes the necessary tokens to indent the specified dependency node to this visitor's writer.
145 *
146 * @param node the dependency node to indent
147 */
148 private void indent( DependencyNode node )
149 {
150 for ( int i = 1; i < depth; i++ )
151 {
152 tokens.fillIndent( isLast( node, i ) );
153 }
154
155 if ( depth > 0 )
156 {
157 tokens.addNodeIndent( isLast( node ) );
158 }
159 }
160
161 /**
162 * Gets whether the specified dependency node is the last of its siblings.
163 *
164 * @param node the dependency node to check
165 * @return <code>true</code> if the specified dependency node is the last of its last siblings
166 */
167 private boolean isLast( DependencyNode node )
168 {
169 // TODO: remove node argument and calculate from visitor calls only
170
171 DependencyNode parent = node.getParent();
172
173 boolean last;
174
175 if ( parent == null )
176 {
177 last = true;
178 }
179 else
180 {
181 List<DependencyNode> siblings = parent.getChildren();
182
183 last = ( siblings.indexOf( node ) == siblings.size() - 1 );
184 }
185
186 return last;
187 }
188
189 /**
190 * Gets whether the specified dependency node ancestor is the last of its siblings.
191 *
192 * @param node the dependency node whose ancestor to check
193 * @param ancestorDepth the depth of the ancestor of the specified dependency node to check
194 * @return <code>true</code> if the specified dependency node ancestor is the last of its siblings
195 */
196 private boolean isLast( DependencyNode node, int ancestorDepth )
197 {
198 // TODO: remove node argument and calculate from visitor calls only
199
200 int distance = depth - ancestorDepth;
201
202 while ( distance-- > 0 )
203 {
204 node = node.getParent();
205 }
206
207 return isLast( node );
208 }
209 }