001 /** 002 * Copyright (C) 2009 Progress Software, Inc. 003 * http://fusesource.com 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.kuali.maven.plugins.graph; 018 019 import java.io.File; 020 import java.util.ArrayList; 021 import java.util.List; 022 023 import org.apache.maven.artifact.Artifact; 024 import org.apache.maven.artifact.factory.ArtifactFactory; 025 import org.apache.maven.artifact.metadata.ArtifactMetadataSource; 026 import org.apache.maven.artifact.repository.ArtifactRepository; 027 import org.apache.maven.artifact.resolver.ArtifactCollector; 028 import org.apache.maven.artifact.resolver.ArtifactResolver; 029 import org.apache.maven.plugin.AbstractMojo; 030 import org.apache.maven.project.MavenProject; 031 import org.apache.maven.shared.dependency.tree.DependencyNode; 032 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; 033 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; 034 import org.kuali.maven.plugins.graph.collector.DependencyNodeTokenCollector; 035 import org.kuali.maven.plugins.graph.collector.TokenCollector; 036 import org.kuali.maven.plugins.graph.dot.Dot; 037 import org.kuali.maven.plugins.graph.dot.GraphException; 038 import org.kuali.maven.plugins.graph.dot.GraphHelper; 039 import org.kuali.maven.plugins.graph.dot.StringGenerator; 040 import org.kuali.maven.plugins.graph.dot.edge.EdgeHandler; 041 import org.kuali.maven.plugins.graph.filter.ArtifactFilterWrapper; 042 import org.kuali.maven.plugins.graph.filter.DependencyNodeFilterWrapper; 043 import org.kuali.maven.plugins.graph.filter.DepthFilter; 044 import org.kuali.maven.plugins.graph.filter.Filter; 045 import org.kuali.maven.plugins.graph.filter.Filters; 046 import org.kuali.maven.plugins.graph.filter.MatchCondition; 047 import org.kuali.maven.plugins.graph.filter.NodeFilter; 048 import org.kuali.maven.plugins.graph.filter.NodeFilterChain; 049 import org.kuali.maven.plugins.graph.filter.ReverseNodeFilter; 050 import org.kuali.maven.plugins.graph.pojo.Direction; 051 import org.kuali.maven.plugins.graph.pojo.DotContext; 052 import org.kuali.maven.plugins.graph.pojo.Edge; 053 import org.kuali.maven.plugins.graph.pojo.Graph; 054 import org.kuali.maven.plugins.graph.pojo.GraphNode; 055 import org.kuali.maven.plugins.graph.pojo.MavenContext; 056 import org.kuali.maven.plugins.graph.tree.Node; 057 import org.kuali.maven.plugins.graph.tree.TreeHelper; 058 import org.kuali.maven.plugins.graph.tree.TreeMetaData; 059 060 /** 061 * 062 */ 063 public abstract class BaseMojo extends AbstractMojo { 064 Filters filters = new Filters(); 065 066 /** 067 * @required 068 * @readonly 069 * @parameter expression="${project}" 070 * @since 1.0 071 */ 072 private MavenProject project; 073 074 /** 075 * @required 076 * @readonly 077 * @parameter expression="${localRepository}" 078 * @since 1.0 079 */ 080 private ArtifactRepository localRepository; 081 082 /** 083 * @required 084 * @component 085 * @since 1.0 086 */ 087 private ArtifactResolver artifactResolver; 088 089 /** 090 * @required 091 * @readonly 092 * @component 093 * @since 1.0 094 */ 095 private ArtifactFactory artifactFactory; 096 097 /** 098 * @required 099 * @readonly 100 * @component 101 * @since 1.0 102 */ 103 private ArtifactMetadataSource artifactMetadataSource; 104 105 /** 106 * @required 107 * @readonly 108 * @component 109 */ 110 private ArtifactCollector artifactCollector; 111 112 /** 113 * @required 114 * @readonly 115 * @component 116 * @since 1.0 117 */ 118 private DependencyTreeBuilder treeBuilder; 119 120 /** 121 * The title for the graph 122 * 123 * @parameter expression="${graph.title}" default-value="Dependency Graph for ${project.name}" 124 */ 125 private String title; 126 127 /** 128 * Set to true to retain the .dot file used to draw the graph 129 * 130 * @parameter expression="${graph.keepDotFile}" default-value="false" 131 */ 132 private boolean keepDotFile; 133 134 /** 135 * Set to false to show only the dependencies for the current project 136 * 137 * @parameter expression="${graph.transitive}" default-value="true" 138 */ 139 private boolean transitive; 140 141 /** 142 * <p> 143 * Comma delimited list of artifact patterns to include. <code>Includes</code> works "bottom up" and can be 144 * overridden by <code>excludes</code>. If an artifact matches the <code>includes</code> criteria, it, and all of 145 * the dependencies in the direct path from it back to the root of the dependency tree are displayed. 146 * </p> 147 * 148 * The pattern syntax has the form: 149 * 150 * <pre> 151 * [groupId]:[artifactId]:[type]:[classifier]:[version] 152 * </pre> 153 * 154 * <p> 155 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a 156 * wildcard. 157 * </p> 158 * 159 * @parameter expression="${graph.includes}" 160 */ 161 private String includes; 162 163 /** 164 * <p> 165 * Comma delimited list of artifact patterns to exclude. <code>Excludes</code> overrides <code>includes</code> and 166 * works "top down". If a dependency matches the <code>excludes</code> criteria, it, and all dependencies below it, 167 * are removed from the display. If not provided, no artifacts are excluded. 168 * </p> 169 * 170 * The pattern syntax has the form: 171 * 172 * <pre> 173 * [groupId]:[artifactId]:[type]:[classifier]:[version] 174 * </pre> 175 * 176 * <p> 177 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a 178 * wildcard. 179 * </p> 180 * 181 * @parameter expression="${graph.excludes}" 182 */ 183 private String excludes; 184 185 /** 186 * <p> 187 * Comma delimited list of dependency qualifiers used for "hiding" artifacts. <code>Hide</code> overrides 188 * <code>show</code> and works "top down". If a dependency matches the <code>hide</code> criteria, it, and 189 * dependencies below it, are removed from the display. If not provided, no dependencies are hidden. 190 * </p> 191 * 192 * The pattern syntax has the form: 193 * 194 * <pre> 195 * [scope]:[optional|required]:[state] 196 * </pre> 197 * 198 * Scopes: <code>compile,provided,runtime,test,system,import</code><br> 199 * States: <code>normal,conflict,cyclic,duplicate</code><br> 200 * 201 * <p> 202 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a 203 * wildcard. 204 * </p> 205 * 206 * @parameter expression="${graph.hide}" 207 */ 208 private String hide; 209 210 /** 211 * <p> 212 * Comma delimited list of dependency qualifiers used for "showing" artifacts. <code>Show</code> works "bottom up" 213 * and can be overridden by <code>hide</code>. If a dependency matches the <code>show</code> criteria, it, and all 214 * of the dependencies in the direct path from it back to the root of the dependency tree are displayed. 215 * </p> 216 * 217 * The pattern syntax has the form: 218 * 219 * <pre> 220 * [scope]:[optional|required]:[state] 221 * </pre> 222 * 223 * Scopes: <code>compile,provided,runtime,test,system,import</code><br> 224 * States: <code>normal,conflict,cyclic,duplicate</code><br> 225 * 226 * <p> 227 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a 228 * wildcard. 229 * </p> 230 * 231 * @parameter expression="${graph.show}" 232 */ 233 private String show; 234 235 /** 236 * The direction for the graph layout. Top to bottom, left to right, bottom to top, and right to left. 237 * 238 * @parameter expression="${graph.direction}" default-value="TB" 239 */ 240 private Direction direction; 241 242 @Override 243 public void execute() { 244 try { 245 String content = getDotFileContent(title, direction); 246 Dot dot = new Dot(); 247 DotContext context = dot.getDotContext(getFile(), content, keepDotFile); 248 dot.execute(context); 249 } catch (Exception e) { 250 e.printStackTrace(); 251 } 252 } 253 254 protected abstract File getFile(); 255 256 protected abstract EdgeHandler getEdgeHandler(); 257 258 /** 259 * Restricts the depth of the dependency tree. To show only the dependencies of your project, set this to 1. To show 260 * the dependencies of your project and their direct dependencies, set this to 2. 261 * 262 * @parameter expression="${graph.depth}" default-value="-1" 263 */ 264 private int depth; 265 266 protected DepthFilter<MavenContext> getDepthFilter() { 267 int maxDepth = transitive ? DepthFilter.INFINITE : 1; 268 maxDepth = depth >= 0 ? depth : maxDepth; 269 return new DepthFilter<MavenContext>(maxDepth); 270 } 271 272 protected void preProcess(Node<MavenContext> node) { 273 // do nothing by default 274 } 275 276 protected void postProcess(Node<MavenContext> node, List<GraphNode> nodes, List<Edge> edges) { 277 // do nothing by default 278 } 279 280 protected String getDotFileContent(String title, Direction direction) { 281 TreeHelper helper = new TreeHelper(); 282 DependencyNode mavenTree = getMavenTree(); 283 Node<MavenContext> nodeTree = helper.getTree(mavenTree); 284 preProcess(nodeTree); 285 helper.sanitize(nodeTree); 286 TreeMetaData md = helper.getMetaData(nodeTree); 287 helper.show(md); 288 helper.include(nodeTree, getIncludeFilter()); 289 helper.exclude(nodeTree, getExcludeFilter()); 290 List<GraphNode> nodes = helper.getGraphNodes(nodeTree); 291 EdgeHandler handler = getEdgeHandler(); 292 List<Edge> edges = helper.getEdges(nodeTree, handler); 293 postProcess(nodeTree, nodes, edges); 294 helper.show(nodes, edges); 295 Graph graph = new GraphHelper().getGraph(title, direction, nodes, edges); 296 return new StringGenerator().getString(graph); 297 } 298 299 protected NodeFilter<MavenContext> getShowFilter() { 300 TokenCollector<DependencyNode> collector = new DependencyNodeTokenCollector(); 301 Filter<DependencyNode> filter = filters.getIncludePatternFilter(getShow(), collector); 302 return new DependencyNodeFilterWrapper(filter); 303 } 304 305 protected NodeFilter<MavenContext> getHideFilter() { 306 TokenCollector<DependencyNode> collector = new DependencyNodeTokenCollector(); 307 Filter<DependencyNode> filter = filters.getExcludePatternFilter(getHide(), collector); 308 return new DependencyNodeFilterWrapper(filter); 309 } 310 311 protected NodeFilter<MavenContext> getIncludeFilter() { 312 TokenCollector<Artifact> collector = new org.kuali.maven.plugins.graph.collector.ArtifactIdTokenCollector(); 313 Filter<Artifact> filter = filters.getIncludePatternFilter(getIncludes(), collector); 314 ArtifactFilterWrapper artifactFilter = new ArtifactFilterWrapper(filter); 315 List<NodeFilter<MavenContext>> filters = new ArrayList<NodeFilter<MavenContext>>(); 316 NodeFilter<MavenContext> artifactQualifierFilter = getShowFilter(); 317 filters.add(artifactQualifierFilter); 318 filters.add(artifactFilter); 319 return new NodeFilterChain<MavenContext>(filters, MatchCondition.ALL, true); 320 } 321 322 protected NodeFilter<MavenContext> getExcludeFilter() { 323 TokenCollector<Artifact> collector = new org.kuali.maven.plugins.graph.collector.ArtifactIdTokenCollector(); 324 Filter<Artifact> filter = filters.getExcludePatternFilter(getExcludes(), collector); 325 ArtifactFilterWrapper artifactFilter = new ArtifactFilterWrapper(filter); 326 ReverseNodeFilter<MavenContext> depthFilter = new ReverseNodeFilter<MavenContext>(getDepthFilter()); 327 NodeFilter<MavenContext> artifactQualifierFilter = getHideFilter(); 328 List<NodeFilter<MavenContext>> filters = new ArrayList<NodeFilter<MavenContext>>(); 329 filters.add(artifactQualifierFilter); 330 filters.add(artifactFilter); 331 filters.add(depthFilter); 332 return new NodeFilterChain<MavenContext>(filters, MatchCondition.ANY, false); 333 } 334 335 protected DependencyNode getMavenTree() { 336 try { 337 return getTreeBuilder().buildDependencyTree(project, localRepository, artifactFactory, 338 artifactMetadataSource, null, artifactCollector); 339 } catch (DependencyTreeBuilderException e) { 340 throw new GraphException(e); 341 } 342 } 343 344 public String getIncludes() { 345 return includes; 346 } 347 348 public void setIncludes(String includes) { 349 this.includes = includes; 350 } 351 352 public String getExcludes() { 353 return excludes; 354 } 355 356 public void setExcludes(String excludes) { 357 this.excludes = excludes; 358 } 359 360 public String getHide() { 361 return hide; 362 } 363 364 public void setHide(String hide) { 365 this.hide = hide; 366 } 367 368 public String getShow() { 369 return show; 370 } 371 372 public void setShow(String show) { 373 this.show = show; 374 } 375 376 public String getTitle() { 377 return title; 378 } 379 380 public void setTitle(String title) { 381 this.title = title; 382 } 383 384 public boolean isKeepDotFile() { 385 return keepDotFile; 386 } 387 388 public void setKeepDotFile(boolean keepDotFile) { 389 this.keepDotFile = keepDotFile; 390 } 391 392 public boolean isTransitive() { 393 return transitive; 394 } 395 396 public void setTransitive(boolean transitive) { 397 this.transitive = transitive; 398 } 399 400 public Direction getDirection() { 401 return direction; 402 } 403 404 public void setDirection(Direction direction) { 405 this.direction = direction; 406 } 407 408 public int getDepth() { 409 return depth; 410 } 411 412 public void setDepth(int depth) { 413 this.depth = depth; 414 } 415 416 public MavenProject getProject() { 417 return project; 418 } 419 420 public ArtifactRepository getLocalRepository() { 421 return localRepository; 422 } 423 424 public ArtifactResolver getArtifactResolver() { 425 return artifactResolver; 426 } 427 428 public ArtifactFactory getArtifactFactory() { 429 return artifactFactory; 430 } 431 432 public ArtifactMetadataSource getArtifactMetadataSource() { 433 return artifactMetadataSource; 434 } 435 436 public ArtifactCollector getArtifactCollector() { 437 return artifactCollector; 438 } 439 440 public DependencyTreeBuilder getTreeBuilder() { 441 return treeBuilder; 442 } 443 444 }