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 = new GraphTokens( "\u251C\u2500 ", "\u2514\u2500 ", "\u2502  ",
86                                                                         "   " );
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 the writer to serialize to
111      */
112     public SerializingDependencyNodeVisitor( Writer writer )
113     {
114         this( writer, WHITESPACE_TOKENS );
115     }
116 
117     /**
118      * Creates a dependency node visitor that serializes visited nodes to the specified writer using the specified
119      * tokens.
120      * 
121      * @param writer the writer to serialize to
122      * @param tokens the tokens to use when serializing the dependency graph
123      */
124     public SerializingDependencyNodeVisitor( Writer writer, GraphTokens tokens )
125     {
126         if ( writer instanceof PrintWriter )
127         {
128             this.writer = (PrintWriter) writer;
129         }
130         else
131         {
132             this.writer = new PrintWriter( writer, true );
133         }
134 
135         this.tokens = tokens;
136 
137         depth = 0;
138     }
139 
140     // DependencyNodeVisitor methods ------------------------------------------
141 
142     /**
143      * {@inheritDoc}
144      */
145     @Override
146     public boolean visit( DependencyNode node )
147     {
148         indent( node );
149 
150         writer.println( node.toNodeString() );
151 
152         depth++;
153 
154         return true;
155     }
156 
157     /**
158      * {@inheritDoc}
159      */
160     @Override
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 }