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 }