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.tree.DependencyNode; 26 import org.apache.maven.shared.dependency.tree.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 public TreeTokens( Sink sink ) 47 { 48 this.sink = sink; 49 } 50 51 public 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 public 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 }