+ code from dependency:tree default tip
authorindvdum
Fri, 13 May 2011 17:32:05 +0400
changeset 2231c4b87c6e7
parent 1 940c8f9a9fbc
+ code from dependency:tree
src/main/java/ru/indvdum/maven/plugin/wd/TreeMojo.java
     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  }