View Javadoc

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 }