View Javadoc
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.graph.DependencyNode;
26  import org.apache.maven.shared.dependency.graph.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 }