View Javadoc
1   package org.apache.maven.shared.dependency.tree.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.tree.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   * @version $Id: SerializingDependencyNodeVisitor.java 1595642 2014-05-18 17:32:08Z jvanzyl $
33   * @since 1.1
34   */
35  public class SerializingDependencyNodeVisitor
36      implements DependencyNodeVisitor
37  {
38      // classes ----------------------------------------------------------------
39  
40      /**
41       * Provides tokens to use when serializing the dependency tree.
42       */
43      public static class TreeTokens
44      {
45          private final String nodeIndent;
46  
47          private final String lastNodeIndent;
48  
49          private final String fillIndent;
50  
51          private final String lastFillIndent;
52  
53          public TreeTokens( String nodeIndent, String lastNodeIndent, String fillIndent, String lastFillIndent )
54          {
55              this.nodeIndent = nodeIndent;
56              this.lastNodeIndent = lastNodeIndent;
57              this.fillIndent = fillIndent;
58              this.lastFillIndent = lastFillIndent;
59          }
60  
61          public String getNodeIndent( boolean last )
62          {
63              return last ? lastNodeIndent : nodeIndent;
64          }
65  
66          public String getFillIndent( boolean last )
67          {
68              return last ? lastFillIndent : fillIndent;
69          }
70      }
71  
72      // constants --------------------------------------------------------------
73  
74      /**
75       * Whitespace tokens to use when outputing the dependency tree.
76       */
77      public static final TreeTokens WHITESPACE_TOKENS = new TreeTokens( "   ", "   ", "   ", "   " );
78  
79      /**
80       * The standard ASCII tokens to use when outputing the dependency tree.
81       */
82      public static final TreeTokens STANDARD_TOKENS = new TreeTokens( "+- ", "\\- ", "|  ", "   " );
83  
84      /**
85       * The extended ASCII tokens to use when outputing the dependency tree.
86       */
87      public static final TreeTokens EXTENDED_TOKENS = new TreeTokens( "\u00c3\u00c4 ", "\u00c0\u00c4 ", "\u00b3  ",
88                                                                       "   " );
89  
90      // fields -----------------------------------------------------------------
91  
92      /**
93       * The writer to serialize to.
94       */
95      private final PrintWriter writer;
96  
97      /**
98       * The tokens to use when serializing the dependency tree.
99       */
100     private final TreeTokens tokens;
101 
102     /**
103      * The depth of the currently visited dependency node.
104      */
105     private int depth;
106 
107     // constructors -----------------------------------------------------------
108 
109     /**
110      * Creates a dependency node visitor that serializes visited nodes to the specified writer using whitespace tokens.
111      * 
112      * @param writer the writer to serialize to
113      */
114     public SerializingDependencyNodeVisitor( Writer writer )
115     {
116         this( writer, WHITESPACE_TOKENS );
117     }
118 
119     /**
120      * Creates a dependency node visitor that serializes visited nodes to the specified writer using the specified
121      * tokens.
122      * 
123      * @param writer the writer to serialize to
124      * @param tokens the tokens to use when serializing the dependency tree
125      */
126     public SerializingDependencyNodeVisitor( Writer writer, TreeTokens tokens )
127     {
128         if ( writer instanceof PrintWriter )
129         {
130             this.writer = (PrintWriter) writer;
131         }
132         else
133         {
134             this.writer = new PrintWriter( writer, true );
135         }
136 
137         this.tokens = tokens;
138 
139         depth = 0;
140     }
141 
142     // DependencyNodeVisitor methods ------------------------------------------
143 
144     /**
145      * {@inheritDoc}
146      */
147     public boolean visit( DependencyNode node )
148     {
149         indent( node );
150 
151         writer.println( node.toNodeString() );
152 
153         depth++;
154 
155         return true;
156     }
157 
158     /**
159      * {@inheritDoc}
160      */
161     public boolean endVisit( DependencyNode node )
162     {
163         depth--;
164 
165         return true;
166     }
167 
168     // private methods --------------------------------------------------------
169 
170     /**
171      * Writes the necessary tokens to indent the specified dependency node to this visitor's writer.
172      * 
173      * @param node the dependency node to indent
174      */
175     private void indent( DependencyNode node )
176     {
177         for ( int i = 1; i < depth; i++ )
178         {
179             writer.write( tokens.getFillIndent( isLast( node, i ) ) );
180         }
181 
182         if ( depth > 0 )
183         {
184             writer.write( tokens.getNodeIndent( isLast( node ) ) );
185         }
186     }
187 
188     /**
189      * Gets whether the specified dependency node is the last of its siblings.
190      * 
191      * @param node the dependency node to check
192      * @return <code>true</code> if the specified dependency node is the last of its last siblings
193      */
194     private boolean isLast( DependencyNode node )
195     {
196         // TODO: remove node argument and calculate from visitor calls only
197 
198         DependencyNode parent = node.getParent();
199 
200         boolean last;
201 
202         if ( parent == null )
203         {
204             last = true;
205         }
206         else
207         {
208             List<DependencyNode> siblings = parent.getChildren();
209 
210             last = ( siblings.indexOf( node ) == siblings.size() - 1 );
211         }
212 
213         return last;
214     }
215 
216     /**
217      * Gets whether the specified dependency node ancestor is the last of its siblings.
218      * 
219      * @param node the dependency node whose ancestor to check
220      * @param ancestorDepth the depth of the ancestor of the specified dependency node to check
221      * @return <code>true</code> if the specified dependency node ancestor is the last of its siblings
222      */
223     private boolean isLast( DependencyNode node, int ancestorDepth )
224     {
225         // TODO: remove node argument and calculate from visitor calls only
226 
227         int distance = depth - ancestorDepth;
228 
229         while ( distance-- > 0 )
230         {
231             node = node.getParent();
232         }
233 
234         return isLast( node );
235     }
236 }