View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.report.projectinfo.dependencies;
20  
21  import java.util.List;
22  
23  import org.apache.maven.doxia.sink.Sink;
24  import org.apache.maven.shared.dependency.graph.DependencyNode;
25  import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
26  
27  /**
28   * A dependency node visitor that serializes visited nodes to a sink writer. It's used to serialize tree in project
29   * information report page.
30   *
31   * @author <a href="mailto:wangyf2010@gmail.com">Simon Wang</a>
32   */
33  public class SinkSerializingDependencyNodeVisitor implements DependencyNodeVisitor {
34      // classes ----------------------------------------------------------------
35  
36      /**
37       * Provides tokens to use when serializing the dependency tree.
38       */
39      private class TreeTokens {
40          private final Sink sink;
41  
42          TreeTokens(Sink sink) {
43              this.sink = sink;
44          }
45  
46          void addNodeIndent(boolean last) {
47              if (last) {
48                  sink.text("\\-");
49                  sink.nonBreakingSpace();
50              } else {
51                  sink.text("+-");
52                  sink.nonBreakingSpace();
53              }
54          }
55  
56          void fillIndent(boolean last) {
57              if (last) {
58                  sink.nonBreakingSpace();
59                  sink.nonBreakingSpace();
60                  sink.nonBreakingSpace();
61              } else {
62                  sink.text("|");
63                  sink.nonBreakingSpace();
64                  sink.nonBreakingSpace();
65              }
66          }
67      }
68  
69      // fields -----------------------------------------------------------------
70  
71      /**
72       * The writer to serialize to.
73       */
74      private final Sink sink;
75  
76      /**
77       * The tokens to use when serializing the dependency tree.
78       */
79      private final TreeTokens tokens;
80  
81      /**
82       * The depth of the currently visited dependency node.
83       */
84      private int depth;
85  
86      // constructors -----------------------------------------------------------
87  
88      /**
89       * Creates a dependency node visitor that serializes visited nodes to the specified writer using the specified
90       * tokens.
91       *
92       * @param sink the writer to serialize to
93       */
94      public SinkSerializingDependencyNodeVisitor(Sink sink) {
95          this.sink = sink;
96          this.tokens = new TreeTokens(sink);
97          depth = 0;
98      }
99  
100     // DependencyNodeVisitor methods ------------------------------------------
101 
102     /**
103      * {@inheritDoc}
104      */
105     public boolean visit(DependencyNode node) {
106         indent(node);
107 
108         sink.text(node.toNodeString());
109         sink.lineBreak();
110 
111         depth++;
112 
113         return true;
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     public boolean endVisit(DependencyNode node) {
120         depth--;
121 
122         return true;
123     }
124 
125     // private methods --------------------------------------------------------
126 
127     /**
128      * Writes the necessary tokens to indent the specified dependency node to this visitor's writer.
129      *
130      * @param node the dependency node to indent
131      */
132     private void indent(DependencyNode node) {
133         for (int i = 1; i < depth; i++) {
134             tokens.fillIndent(isLast(node, i));
135         }
136 
137         if (depth > 0) {
138             tokens.addNodeIndent(isLast(node));
139         }
140     }
141 
142     /**
143      * Gets whether the specified dependency node is the last of its siblings.
144      *
145      * @param node the dependency node to check
146      * @return <code>true</code> if the specified dependency node is the last of its last siblings
147      */
148     private boolean isLast(DependencyNode node) {
149         // TODO: remove node argument and calculate from visitor calls only
150 
151         DependencyNode parent = node.getParent();
152 
153         boolean last;
154 
155         if (parent == null) {
156             last = true;
157         } else {
158             List<DependencyNode> siblings = parent.getChildren();
159 
160             last = (siblings.indexOf(node) == siblings.size() - 1);
161         }
162 
163         return last;
164     }
165 
166     /**
167      * Gets whether the specified dependency node ancestor is the last of its siblings.
168      *
169      * @param node the dependency node whose ancestor to check
170      * @param ancestorDepth the depth of the ancestor of the specified dependency node to check
171      * @return <code>true</code> if the specified dependency node ancestor is the last of its siblings
172      */
173     private boolean isLast(DependencyNode node, int ancestorDepth) {
174         // TODO: remove node argument and calculate from visitor calls only
175 
176         int distance = depth - ancestorDepth;
177 
178         while (distance-- > 0) {
179             node = node.getParent();
180         }
181 
182         return isLast(node);
183     }
184 }