1.1 --- a/src/main/java/ru/indvdum/maven/plugin/wd/TreeMojo.java Wed May 11 01:42:42 2011 +0400
1.2 +++ b/src/main/java/ru/indvdum/maven/plugin/wd/TreeMojo.java Fri May 13 17:32:05 2011 +0400
1.3 @@ -1,10 +1,492 @@
1.4 package ru.indvdum.maven.plugin.wd;
1.5
1.6 +import java.io.File;
1.7 +import java.io.IOException;
1.8 +import java.io.StringWriter;
1.9 +import java.io.Writer;
1.10 +import java.util.ArrayList;
1.11 +import java.util.Arrays;
1.12 +import java.util.Iterator;
1.13 +import java.util.List;
1.14 +
1.15 +import org.apache.maven.artifact.factory.ArtifactFactory;
1.16 +import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
1.17 +import org.apache.maven.artifact.repository.ArtifactRepository;
1.18 +import org.apache.maven.artifact.resolver.ArtifactCollector;
1.19 +import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
1.20 +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
1.21 +import org.apache.maven.artifact.versioning.ArtifactVersion;
1.22 +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
1.23 +import org.apache.maven.artifact.versioning.Restriction;
1.24 +import org.apache.maven.artifact.versioning.VersionRange;
1.25 +import org.apache.maven.execution.RuntimeInformation;
1.26 +import org.apache.maven.plugin.AbstractMojo;
1.27 +import org.apache.maven.plugin.MojoExecutionException;
1.28 +import org.apache.maven.plugin.MojoFailureException;
1.29 +import org.apache.maven.plugin.dependency.treeSerializers.DOTDependencyNodeVisitor;
1.30 +import org.apache.maven.plugin.dependency.treeSerializers.GraphmlDependencyNodeVisitor;
1.31 +import org.apache.maven.plugin.dependency.treeSerializers.TGFDependencyNodeVisitor;
1.32 +import org.apache.maven.plugin.dependency.utils.DependencyUtil;
1.33 +import org.apache.maven.project.MavenProject;
1.34 +import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
1.35 +import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
1.36 +import org.apache.maven.shared.dependency.tree.DependencyNode;
1.37 +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
1.38 +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
1.39 +import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
1.40 +import org.apache.maven.shared.dependency.tree.filter.AndDependencyNodeFilter;
1.41 +import org.apache.maven.shared.dependency.tree.filter.ArtifactDependencyNodeFilter;
1.42 +import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
1.43 +import org.apache.maven.shared.dependency.tree.filter.StateDependencyNodeFilter;
1.44 +import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
1.45 +import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
1.46 +import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
1.47 +import org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor;
1.48 +import org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor;
1.49 +import org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.TreeTokens;
1.50 +
1.51 /**
1.52 * Displays the dependency tree with node id's for this project.
1.53 + * Based on {@link org.apache.maven.plugin.dependency.TreeMojo}.
1.54 + *
1.55 + * @author indvdum
1.56 + * 13.05.2011 17:26:50
1.57 *
1.58 * @goal tree
1.59 */
1.60 -public class TreeMojo extends org.apache.maven.plugin.dependency.TreeMojo {
1.61 +public class TreeMojo extends AbstractMojo {
1.62 + // fields -----------------------------------------------------------------
1.63
1.64 + /**
1.65 + * The Maven project.
1.66 + *
1.67 + * @parameter expression="${project}"
1.68 + * @required
1.69 + * @readonly
1.70 + */
1.71 + private MavenProject project;
1.72 +
1.73 + /**
1.74 + * The artifact repository to use.
1.75 + *
1.76 + * @parameter expression="${localRepository}"
1.77 + * @required
1.78 + * @readonly
1.79 + */
1.80 + private ArtifactRepository localRepository;
1.81 +
1.82 + /**
1.83 + * The artifact factory to use.
1.84 + *
1.85 + * @component
1.86 + * @required
1.87 + * @readonly
1.88 + */
1.89 + private ArtifactFactory artifactFactory;
1.90 +
1.91 + /**
1.92 + * The artifact metadata source to use.
1.93 + *
1.94 + * @component
1.95 + * @required
1.96 + * @readonly
1.97 + */
1.98 + private ArtifactMetadataSource artifactMetadataSource;
1.99 +
1.100 + /**
1.101 + * The artifact collector to use.
1.102 + *
1.103 + * @component
1.104 + * @required
1.105 + * @readonly
1.106 + */
1.107 + private ArtifactCollector artifactCollector;
1.108 +
1.109 + /**
1.110 + * The dependency tree builder to use.
1.111 + *
1.112 + * @component
1.113 + * @required
1.114 + * @readonly
1.115 + */
1.116 + private DependencyTreeBuilder dependencyTreeBuilder;
1.117 +
1.118 + /**
1.119 + * If specified, this parameter will cause the dependency tree to be written to the path specified, instead of
1.120 + * writing to the console.
1.121 + * @deprecated use outputFile instead.
1.122 + * @parameter expression="${output}"
1.123 + */
1.124 + private File output;
1.125 +
1.126 + /**
1.127 + * If specified, this parameter will cause the dependency tree to be written to the path specified, instead of
1.128 + * writing to the console.
1.129 + * @parameter expression="${outputFile}"
1.130 + * @since 2.0-alpha-5
1.131 + */
1.132 + private File outputFile;
1.133 +
1.134 + /**
1.135 + * If specified, this parameter will cause the dependency tree to be written using the specified format. Currently
1.136 + * supported format are text, dot, graphml and tgf.
1.137 + *
1.138 + * These formats can be plotted to image files. An example of how to plot a dot file using
1.139 + * pygraphviz can be found <a href="http://networkx.lanl.gov/pygraphviz/tutorial.html#layout-and-drawing">here</a>
1.140 + *
1.141 + * @parameter expression="${outputType}" default-value="text"
1.142 + * @since 2.1
1.143 + */
1.144 + private String outputType;
1.145 +
1.146 + /**
1.147 + * The scope to filter by when resolving the dependency tree, or <code>null</code> to include dependencies from
1.148 + * all scopes. Note that this feature does not currently work due to MNG-3236.
1.149 + *
1.150 + * @since 2.0-alpha-5
1.151 + * @see <a href="http://jira.codehaus.org/browse/MNG-3236">MNG-3236</a>
1.152 + *
1.153 + * @parameter expression="${scope}"
1.154 + */
1.155 + private String scope;
1.156 +
1.157 + /**
1.158 + * Whether to include omitted nodes in the serialized dependency tree.
1.159 + *
1.160 + * @since 2.0-alpha-6
1.161 + *
1.162 + * @parameter expression="${verbose}" default-value="false"
1.163 + */
1.164 + private boolean verbose;
1.165 +
1.166 + /**
1.167 + * The token set name to use when outputting the dependency tree. Possible values are <code>whitespace</code>,
1.168 + * <code>standard</code> or <code>extended</code>, which use whitespace, standard or extended ASCII sets
1.169 + * respectively.
1.170 + *
1.171 + * @since 2.0-alpha-6
1.172 + *
1.173 + * @parameter expression="${tokens}" default-value="standard"
1.174 + */
1.175 + private String tokens;
1.176 +
1.177 + /**
1.178 + * A comma-separated list of artifacts to filter the serialized dependency tree by, or <code>null</code> not to
1.179 + * filter the dependency tree. The artifact syntax is defined by <code>StrictPatternIncludesArtifactFilter</code>.
1.180 + *
1.181 + * @see StrictPatternIncludesArtifactFilter
1.182 + * @since 2.0-alpha-6
1.183 + *
1.184 + * @parameter expression="${includes}"
1.185 + */
1.186 + private String includes;
1.187 +
1.188 + /**
1.189 + * A comma-separated list of artifacts to filter from the serialized dependency tree, or <code>null</code> not to
1.190 + * filter any artifacts from the dependency tree. The artifact syntax is defined by
1.191 + * <code>StrictPatternExcludesArtifactFilter</code>.
1.192 + *
1.193 + * @see StrictPatternExcludesArtifactFilter
1.194 + * @since 2.0-alpha-6
1.195 + *
1.196 + * @parameter expression="${excludes}"
1.197 + */
1.198 + private String excludes;
1.199 +
1.200 + /**
1.201 + * Runtime Information used to check the Maven version
1.202 + * @since 2.0
1.203 + * @component role="org.apache.maven.execution.RuntimeInformation"
1.204 + */
1.205 + private RuntimeInformation rti;
1.206 +
1.207 + /**
1.208 + * The computed dependency tree root node of the Maven project.
1.209 + */
1.210 + private DependencyNode rootNode;
1.211 +
1.212 + /**
1.213 + * Whether to append outputs into the output file or overwrite it.
1.214 + *
1.215 + * @parameter expression="${appendOutput}" default-value="false"
1.216 + * @since 2.2
1.217 + */
1.218 + private boolean appendOutput;
1.219 +
1.220 + // Mojo methods -----------------------------------------------------------
1.221 +
1.222 + /*
1.223 + * @see org.apache.maven.plugin.Mojo#execute()
1.224 + */
1.225 + public void execute() throws MojoExecutionException, MojoFailureException
1.226 + {
1.227 +
1.228 + ArtifactVersion detectedMavenVersion = rti.getApplicationVersion();
1.229 + VersionRange vr;
1.230 + try
1.231 + {
1.232 + vr = VersionRange.createFromVersionSpec( "[2.0.8,)" );
1.233 + if ( !containsVersion( vr, detectedMavenVersion ) )
1.234 + {
1.235 + getLog().warn(
1.236 + "The tree mojo requires at least Maven 2.0.8 to function properly. You may get eroneous results on earlier versions" );
1.237 + }
1.238 + }
1.239 + catch ( InvalidVersionSpecificationException e )
1.240 + {
1.241 + throw new MojoExecutionException( e.getLocalizedMessage() );
1.242 + }
1.243 +
1.244 +
1.245 + if ( output != null )
1.246 + {
1.247 + getLog().warn( "The parameter output is deprecated. Use outputFile instead." );
1.248 + this.outputFile = output;
1.249 + }
1.250 +
1.251 + ArtifactFilter artifactFilter = createResolvingArtifactFilter();
1.252 +
1.253 + try
1.254 + {
1.255 + // TODO: note that filter does not get applied due to MNG-3236
1.256 +
1.257 + rootNode =
1.258 + dependencyTreeBuilder.buildDependencyTree( project, localRepository, artifactFactory,
1.259 + artifactMetadataSource, artifactFilter, artifactCollector );
1.260 +
1.261 + String dependencyTreeString = serializeDependencyTree( rootNode );
1.262 +
1.263 + if ( outputFile != null )
1.264 + {
1.265 + DependencyUtil.write( dependencyTreeString, outputFile, this.appendOutput ,getLog() );
1.266 +
1.267 + getLog().info( "Wrote dependency tree to: " + outputFile );
1.268 + }
1.269 + else
1.270 + {
1.271 + DependencyUtil.log( dependencyTreeString, getLog() );
1.272 + }
1.273 + }
1.274 + catch ( DependencyTreeBuilderException exception )
1.275 + {
1.276 + throw new MojoExecutionException( "Cannot build project dependency tree", exception );
1.277 + }
1.278 + catch ( IOException exception )
1.279 + {
1.280 + throw new MojoExecutionException( "Cannot serialise project dependency tree", exception );
1.281 + }
1.282 + }
1.283 +
1.284 + // public methods ---------------------------------------------------------
1.285 +
1.286 + /**
1.287 + * Gets the Maven project used by this mojo.
1.288 + *
1.289 + * @return the Maven project
1.290 + */
1.291 + public MavenProject getProject()
1.292 + {
1.293 + return project;
1.294 + }
1.295 +
1.296 + /**
1.297 + * Gets the computed dependency tree root node for the Maven project.
1.298 + *
1.299 + * @return the dependency tree root node
1.300 + */
1.301 + public DependencyNode getDependencyTree()
1.302 + {
1.303 + return rootNode;
1.304 + }
1.305 +
1.306 + // private methods --------------------------------------------------------
1.307 +
1.308 + /**
1.309 + * Gets the artifact filter to use when resolving the dependency tree.
1.310 + *
1.311 + * @return the artifact filter
1.312 + */
1.313 + private ArtifactFilter createResolvingArtifactFilter()
1.314 + {
1.315 + ArtifactFilter filter;
1.316 +
1.317 + // filter scope
1.318 + if ( scope != null )
1.319 + {
1.320 + getLog().debug( "+ Resolving dependency tree for scope '" + scope + "'" );
1.321 +
1.322 + filter = new ScopeArtifactFilter( scope );
1.323 + }
1.324 + else
1.325 + {
1.326 + filter = null;
1.327 + }
1.328 +
1.329 + return filter;
1.330 + }
1.331 +
1.332 + /**
1.333 + * Serializes the specified dependency tree to a string.
1.334 + *
1.335 + * @param rootNode
1.336 + * the dependency tree root node to serialize
1.337 + * @return the serialized dependency tree
1.338 + */
1.339 + private String serializeDependencyTree( DependencyNode rootNode )
1.340 + {
1.341 + StringWriter writer = new StringWriter();
1.342 +
1.343 + DependencyNodeVisitor visitor = getSerializingDependencyNodeVisitor( writer );
1.344 +
1.345 + // TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
1.346 + visitor = new BuildingDependencyNodeVisitor( visitor );
1.347 +
1.348 + DependencyNodeFilter filter = createDependencyNodeFilter();
1.349 +
1.350 + if ( filter != null )
1.351 + {
1.352 + CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
1.353 + DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor( collectingVisitor, filter );
1.354 + rootNode.accept( firstPassVisitor );
1.355 +
1.356 + DependencyNodeFilter secondPassFilter = new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
1.357 + visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
1.358 + }
1.359 +
1.360 + rootNode.accept( visitor );
1.361 +
1.362 + return writer.toString();
1.363 + }
1.364 +
1.365 + public DependencyNodeVisitor getSerializingDependencyNodeVisitor( Writer writer )
1.366 + {
1.367 + if ( "graphml".equals( outputType ) )
1.368 + {
1.369 + return new GraphmlDependencyNodeVisitor( writer );
1.370 + }
1.371 + else if ( "tgf".equals( outputType ) )
1.372 + {
1.373 + return new TGFDependencyNodeVisitor( writer );
1.374 + }
1.375 + else if ( "dot".equals( outputType ) )
1.376 + {
1.377 + return new DOTDependencyNodeVisitor( writer ) ;
1.378 + }
1.379 + else
1.380 + {
1.381 + return new SerializingDependencyNodeVisitor( writer, toTreeTokens( tokens ) );
1.382 + }
1.383 + }
1.384 +
1.385 + /**
1.386 + * Gets the tree tokens instance for the specified name.
1.387 + *
1.388 + * @param tokens
1.389 + * the tree tokens name
1.390 + * @return the <code>TreeTokens</code> instance
1.391 + */
1.392 + private TreeTokens toTreeTokens( String tokens )
1.393 + {
1.394 + TreeTokens treeTokens;
1.395 +
1.396 + if ( "whitespace".equals( tokens ) )
1.397 + {
1.398 + getLog().debug( "+ Using whitespace tree tokens" );
1.399 +
1.400 + treeTokens = SerializingDependencyNodeVisitor.WHITESPACE_TOKENS;
1.401 + }
1.402 + else if ( "extended".equals( tokens ) )
1.403 + {
1.404 + getLog().debug( "+ Using extended tree tokens" );
1.405 +
1.406 + treeTokens = SerializingDependencyNodeVisitor.EXTENDED_TOKENS;
1.407 + }
1.408 + else
1.409 + {
1.410 + treeTokens = SerializingDependencyNodeVisitor.STANDARD_TOKENS;
1.411 + }
1.412 +
1.413 + return treeTokens;
1.414 + }
1.415 +
1.416 + /**
1.417 + * Gets the dependency node filter to use when serializing the dependency tree.
1.418 + *
1.419 + * @return the dependency node filter, or <code>null</code> if none required
1.420 + */
1.421 + private DependencyNodeFilter createDependencyNodeFilter()
1.422 + {
1.423 + List filters = new ArrayList();
1.424 +
1.425 + // filter node states
1.426 + if ( !verbose )
1.427 + {
1.428 + getLog().debug( "+ Filtering omitted nodes from dependency tree" );
1.429 +
1.430 + filters.add( StateDependencyNodeFilter.INCLUDED );
1.431 + }
1.432 +
1.433 + // filter includes
1.434 + if ( includes != null )
1.435 + {
1.436 + List patterns = Arrays.asList( includes.split( "," ) );
1.437 +
1.438 + getLog().debug( "+ Filtering dependency tree by artifact include patterns: " + patterns );
1.439 +
1.440 + ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter( patterns );
1.441 + filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
1.442 + }
1.443 +
1.444 + // filter excludes
1.445 + if ( excludes != null )
1.446 + {
1.447 + List patterns = Arrays.asList( excludes.split( "," ) );
1.448 +
1.449 + getLog().debug( "+ Filtering dependency tree by artifact exclude patterns: " + patterns );
1.450 +
1.451 + ArtifactFilter artifactFilter = new StrictPatternExcludesArtifactFilter( patterns );
1.452 + filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
1.453 + }
1.454 +
1.455 + return filters.isEmpty() ? null : new AndDependencyNodeFilter( filters );
1.456 + }
1.457 +
1.458 + //following is required because the version handling in maven code
1.459 + //doesn't work properly. I ripped it out of the enforcer rules.
1.460 +
1.461 +
1.462 +
1.463 + /**
1.464 + * Copied from Artifact.VersionRange. This is tweaked to handle singular ranges properly. Currently the default
1.465 + * containsVersion method assumes a singular version means allow everything. This method assumes that "2.0.4" ==
1.466 + * "[2.0.4,)"
1.467 + *
1.468 + * @param allowedRange range of allowed versions.
1.469 + * @param theVersion the version to be checked.
1.470 + * @return true if the version is contained by the range.
1.471 + */
1.472 + public static boolean containsVersion( VersionRange allowedRange, ArtifactVersion theVersion )
1.473 + {
1.474 + boolean matched = false;
1.475 + ArtifactVersion recommendedVersion = allowedRange.getRecommendedVersion();
1.476 + if ( recommendedVersion == null )
1.477 + {
1.478 +
1.479 + for ( Iterator i = allowedRange.getRestrictions().iterator(); i.hasNext() && !matched; )
1.480 + {
1.481 + Restriction restriction = (Restriction) i.next();
1.482 + if ( restriction.containsVersion( theVersion ) )
1.483 + {
1.484 + matched = true;
1.485 + }
1.486 + }
1.487 + }
1.488 + else
1.489 + {
1.490 + // only singular versions ever have a recommendedVersion
1.491 + int compareTo = recommendedVersion.compareTo( theVersion );
1.492 + matched = ( compareTo <= 0 );
1.493 + }
1.494 + return matched;
1.495 + }
1.496 }