git-svn-id: https://svn.apache.org/repos/asf/maven/sandbox/branches/MDEP-145@744852 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/maven/plugin/dependency/TreeMojo.java b/src/main/java/org/apache/maven/plugin/dependency/TreeMojo.java
index 1ea7449..3404091 100644
--- a/src/main/java/org/apache/maven/plugin/dependency/TreeMojo.java
+++ b/src/main/java/org/apache/maven/plugin/dependency/TreeMojo.java
@@ -22,6 +22,7 @@
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
+import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -42,6 +43,9 @@
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.dependency.utils.DependencyUtil;
+import org.apache.maven.plugin.dependency.treeSerializers.GraphmlDependencyNodeVisitor;
+import org.apache.maven.plugin.dependency.treeSerializers.TGFDependencyNodeVisitor;
+import org.apache.maven.plugin.dependency.treeSerializers.DOTDependencyNodeVisitor;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
@@ -142,6 +146,18 @@
* @since 2.0-alpha-5
*/
private File outputFile;
+
+ /**
+ * If specified, this parameter will cause the dependency tree to be written using the specified format. Currently
+ * supported format are text, dot, graphml and tgf.
+ *
+ * These formats can be plotted to image files. An example of how to plot a dot file using
+ * pygraphviz can be found <a href="http://networkx.lanl.gov/pygraphviz/tutorial.html#layout-and-drawing">here</a>
+ *
+ * @parameter expression="${outputType}" default-value="text"
+ * @since 2.1
+ */
+ private String outputType;
/**
* The scope to filter by when resolving the dependency tree, or <code>null</code> to include dependencies from
@@ -333,7 +349,7 @@
StringWriter writer = new StringWriter();
TreeTokens treeTokens = toTreeTokens( tokens );
- DependencyNodeVisitor visitor = new SerializingDependencyNodeVisitor( writer, treeTokens );
+ DependencyNodeVisitor visitor = getSerializingDependencyNodeVisitor( writer );
// TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
visitor = new BuildingDependencyNodeVisitor( visitor );
@@ -355,6 +371,26 @@
return writer.toString();
}
+ public DependencyNodeVisitor getSerializingDependencyNodeVisitor( Writer writer )
+ {
+ if ( "graphml".equals( outputType ) )
+ {
+ return new GraphmlDependencyNodeVisitor( writer );
+ }
+ else if ( "tgf".equals( outputType ) )
+ {
+ return new TGFDependencyNodeVisitor( writer );
+ }
+ else if ( "dot".equals( outputType ) )
+ {
+ return new DOTDependencyNodeVisitor( writer ) ;
+ }
+ else
+ {
+ return new SerializingDependencyNodeVisitor( writer, toTreeTokens( tokens ) );
+ }
+ }
+
/**
* Gets the tree tokens instance for the specified name.
*
diff --git a/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/AbstractSerializingVisitor.java b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/AbstractSerializingVisitor.java
new file mode 100644
index 0000000..6c3c103
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/AbstractSerializingVisitor.java
@@ -0,0 +1,57 @@
+package org.apache.maven.plugin.dependency.treeSerializers ;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Abstract class for visitors performing serialization.
+ *
+ * @author <a href="mailto:jerome.creignou@gmail.com">Jerome Creignou</a>
+ */
+public abstract class AbstractSerializingVisitor
+{
+
+ /**
+ * The writer to serialize to.
+ */
+ protected final PrintWriter writer;
+
+ /**
+ * Constructor.
+ * <p>
+ * Build a new AbstractSerializingDependencyNodeVisitor with the writer to serialize to.
+ * </p>
+ *
+ * @param writer the writer to serialize to.
+ */
+ public AbstractSerializingVisitor( Writer writer )
+ {
+ if ( writer instanceof PrintWriter )
+ {
+ this.writer = (PrintWriter) writer;
+ }
+ else
+ {
+ this.writer = new PrintWriter( writer, true );
+ }
+ }
+}
diff --git a/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/DOTDependencyNodeVisitor.java b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/DOTDependencyNodeVisitor.java
new file mode 100644
index 0000000..b2d16ae
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/DOTDependencyNodeVisitor.java
@@ -0,0 +1,91 @@
+package org.apache.maven.plugin.dependency.treeSerializers;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.DependencyNode;
+import org.apache.maven.project.MavenProject;
+
+import java.io.Writer;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * A dependency node visitor that serializes visited nodes to DOT format
+ * http://en.wikipedia.org/wiki/DOT_language
+ *
+ * @author <a href="mailto:pi.songs@gmail.com">Pi Song</a>
+ * @since 2.1
+ */
+public class DOTDependencyNodeVisitor extends AbstractSerializingVisitor
+ implements DependencyNodeVisitor {
+
+ /**
+ * Constructor.
+ *
+ * @param writer the writer to write to.
+ */
+ public DOTDependencyNodeVisitor( Writer writer)
+ {
+ super( writer );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean visit( DependencyNode node )
+ {
+ if ( node.getParent() == null || node.getParent() == node )
+ {
+ writer.write( "digraph \"" + node.toNodeString() + "\" { \n" );
+ }
+
+ // Generate "currentNode -> Child" lines
+
+ List children = node.getChildren() ;
+
+ for ( Iterator it = children.iterator () ; it.hasNext (); )
+ {
+ StringBuffer sb = new StringBuffer() ;
+ sb.append( "\t\"" ) ;
+ sb.append(node.toNodeString()) ;
+ sb.append( "\" -> \"" ) ;
+ sb.append( ((DependencyNode)it.next()).toNodeString() ) ;
+ sb.append( "\" ; " ) ;
+ writer.println( sb.toString() ) ;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean endVisit( DependencyNode node )
+ {
+ if ( node.getParent() == null || node.getParent() == node )
+ {
+ writer.write( " } " );
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/GraphmlDependencyNodeVisitor.java b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/GraphmlDependencyNodeVisitor.java
new file mode 100644
index 0000000..9553f73
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/GraphmlDependencyNodeVisitor.java
@@ -0,0 +1,122 @@
+package org.apache.maven.plugin.dependency.treeSerializers ;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.shared.dependency.tree.DependencyNode;
+import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor;
+
+/**
+ * A dependency node visitor that serializes visited nodes to a writer using the graphml format.
+ * {@link http://graphml.graphdrawing.org/}
+ *
+ * @author <a href="mailto:jerome.creignou@gmail.com">Jerome Creignou</a>
+ * @since 2.1
+ */
+public class GraphmlDependencyNodeVisitor extends AbstractSerializingVisitor
+ implements DependencyNodeVisitor
+{
+
+ /**
+ * Graphml xml file header. Define Schema and root element. We also define 2 key as meta data.
+ */
+ private static final String GRAPHML_HEADER =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " + "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" "
+ + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
+ + "xmlns:y=\"http://www.yworks.com/xml/graphml\" "
+ + "xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns "
+ + "http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\n"
+ + " <key for=\"node\" id=\"d0\" yfiles.type=\"nodegraphics\"/> \n"
+ + " <key for=\"edge\" id=\"d1\" yfiles.type=\"edgegraphics\"/> \n"
+ + "<graph id=\"dependencies\" edgedefault=\"directed\">\n";
+
+ /**
+ * Graphml xml file footer.
+ */
+ private static final String GRAPHML_FOOTER = "</graph></graphml>";
+
+ /**
+ * Constructor.
+ *
+ * @param writer the writer to write to.
+ */
+ public GraphmlDependencyNodeVisitor( Writer writer )
+ {
+ super( writer );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean endVisit( DependencyNode node )
+ {
+ if ( node.getParent() == null || node.getParent() == node )
+ {
+ writer.write( GRAPHML_FOOTER );
+ }
+ else
+ {
+ DependencyNode p = node.getParent();
+ writer.print( "<edge source=\"" + generateId( p ) + "\" target=\"" + generateId( node ) + "\">" );
+ if ( node.getArtifact().getScope() != null )
+ {
+ // add Edge label
+ writer.print( "<data key=\"d1\"><y:PolyLineEdge><y:EdgeLabel>" + node.getArtifact().getScope()
+ + "</y:EdgeLabel></y:PolyLineEdge></data>" );
+ }
+ writer.println( "</edge>" );
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean visit( DependencyNode node )
+ {
+ if ( node.getParent() == null || node.getParent() == node )
+ {
+ writer.write( GRAPHML_HEADER );
+ }
+ // write node
+ writer.print( "<node id=\"" + generateId( node ) + "\">" );
+ // add node label
+ writer.print( "<data key=\"d0\"><y:ShapeNode><y:NodeLabel>" + node.toNodeString()
+ + "</y:NodeLabel></y:ShapeNode></data>" );
+ writer.println( "</node>" );
+ return true;
+ }
+
+ /**
+ * Generate a unique id from a DependencyNode.
+ * <p>
+ * Current implementation is rather simple and uses hashcode.
+ * </p>
+ *
+ * @param node the DependencyNode to use.
+ * @return the unique id.
+ */
+ private static String generateId( DependencyNode node )
+ {
+ return String.valueOf( node.hashCode() );
+ }
+}
diff --git a/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/TGFDependencyNodeVisitor.java b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/TGFDependencyNodeVisitor.java
new file mode 100644
index 0000000..ae30675
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugin/dependency/treeSerializers/TGFDependencyNodeVisitor.java
@@ -0,0 +1,158 @@
+package org.apache.maven.plugin.dependency.treeSerializers ;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.shared.dependency.tree.DependencyNode;
+import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
+
+/**
+ * A dependency node visitor that serializes visited nodes to a writer using the TGF format.
+ *
+ * http://en.wikipedia.org/wiki/Trivial_Graph_Format
+ *
+ * @author <a href="mailto:jerome.creignou@gmail.com">Jerome Creignou</a>
+ * @since 2.1
+ */
+public class TGFDependencyNodeVisitor extends AbstractSerializingVisitor
+ implements DependencyNodeVisitor
+{
+
+ /**
+ * Utiity class to write an Edge.
+ *
+ * @author <a href="mailto:jerome.creignou@gmail.com">Jerome Creignou</a>
+ */
+ final static class EdgeAppender
+ {
+ /**
+ * Edge start.
+ */
+ private DependencyNode from;
+
+ /**
+ * Edge end.
+ */
+ private DependencyNode to;
+
+ /**
+ * Edge label. (optional)
+ */
+ private String label;
+
+ /**
+ * Build a new EdgeAppender.
+ *
+ * @param from edge start.
+ * @param to edge end
+ * @param label optional label.
+ */
+ public EdgeAppender( DependencyNode from, DependencyNode to, String label )
+ {
+ super();
+ this.from = from;
+ this.to = to;
+ this.label = label;
+ }
+
+ /**
+ * build a string representing the edge.
+ */
+ public String toString()
+ {
+ StringBuffer result = new StringBuffer( generateId( from ) );
+ result.append( ' ' ).append( generateId( to ) );
+ if ( label != null )
+ {
+ result.append( ' ' ).append( label );
+ }
+ return result.toString();
+ }
+
+ }
+
+ /**
+ * List of edges.
+ */
+ private List edges = new ArrayList();
+
+ /**
+ * Constructor.
+ *
+ * @param writer the writer to write to.
+ */
+ public TGFDependencyNodeVisitor( Writer writer )
+ {
+ super( writer );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean endVisit( DependencyNode node )
+ {
+ if ( node.getParent() == null || node.getParent() == node )
+ {
+ // dump edges on last node endVisit
+ writer.println( "#" );
+ for ( Iterator iterator = edges.iterator(); iterator.hasNext(); )
+ {
+ writer.println( iterator.next().toString() );
+ }
+ }
+ else
+ {
+ DependencyNode p = node.getParent();
+ // using scope as edge label.
+ edges.add( new EdgeAppender( p, node, node.getArtifact().getScope() ) );
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean visit( DependencyNode node )
+ {
+ // write node
+ writer.write( generateId( node ) );
+ writer.write( " " );
+ writer.println( node.toNodeString() );
+ return true;
+ }
+
+ /**
+ * Generate a unique id from a DependencyNode.
+ * <p>
+ * Current implementation is rather simple and uses hashcode.
+ * </p>
+ *
+ * @param node the DependencyNode to use.
+ * @return the unique id.
+ */
+ private static String generateId( DependencyNode node )
+ {
+ return String.valueOf( node.hashCode() );
+ }
+}
diff --git a/src/test/java/org/apache/maven/plugin/dependency/TestTreeMojo.java b/src/test/java/org/apache/maven/plugin/dependency/TestTreeMojo.java
index 08eb228..4573b9d 100644
--- a/src/test/java/org/apache/maven/plugin/dependency/TestTreeMojo.java
+++ b/src/test/java/org/apache/maven/plugin/dependency/TestTreeMojo.java
@@ -20,7 +20,12 @@
*/
import java.io.File;
+import java.io.BufferedReader;
+import java.io.FileReader;
import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.testing.stubs.StubArtifactRepository;
@@ -83,6 +88,116 @@
assertChildNodeEquals( "testGroupId:snapshot:jar:2.0-SNAPSHOT:compile", rootNode, 0 );
assertChildNodeEquals( "testGroupId:release:jar:1.0:compile", rootNode, 1 );
}
+
+ /**
+ * Test the DOT format serialization
+ *
+ * @throws Exception
+ */
+ public void testTreeDotSerializing()
+ throws Exception
+ {
+ List contents = runTreeMojo("tree1.dot", "dot") ;
+ assertTrue(findString(contents, "digraph \"testGroupId:project:jar:1.0:compile\" {"));
+ assertTrue(findString(contents, "\"testGroupId:project:jar:1.0:compile\" -> \"testGroupId:snapshot:jar:2.0-SNAPSHOT:compile\""));
+ assertTrue(findString(contents, "\"testGroupId:project:jar:1.0:compile\" -> \"testGroupId:release:jar:1.0:compile\""));
+ }
+
+ /**
+ * Test the GraphML format serialization
+ *
+ * @throws Exception
+ */
+ public void testTreeGraphMLSerializing()
+ throws Exception
+ {
+ List contents = runTreeMojo("tree1.graphml", "graphml") ;
+
+ assertTrue(findString(contents, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
+ assertTrue(findString(contents, "<y:NodeLabel>testGroupId:project:jar:1.0:compile</y:NodeLabel>"));
+ assertTrue(findString(contents, "<y:NodeLabel>testGroupId:snapshot:jar:2.0-SNAPSHOT:compile</y:NodeLabel>"));
+ assertTrue(findString(contents, "<y:NodeLabel>testGroupId:release:jar:1.0:compile</y:NodeLabel>"));
+ assertTrue(findString(contents, "<key for=\"node\" id=\"d0\" yfiles.type=\"nodegraphics\"/>"));
+ assertTrue(findString(contents, "<key for=\"edge\" id=\"d1\" yfiles.type=\"edgegraphics\"/>"));
+ }
+
+ /**
+ * Test the TGF format serialization
+ *
+ * @throws Exception
+ */
+ public void testTreeTGFSerializing()
+ throws Exception
+ {
+ List contents = runTreeMojo("tree1.tgf", "tgf") ;
+ assertTrue(findString(contents, "testGroupId:project:jar:1.0:compile"));
+ assertTrue(findString(contents, "testGroupId:snapshot:jar:2.0-SNAPSHOT:compile"));
+ assertTrue(findString(contents, "testGroupId:release:jar:1.0:compile"));
+ }
+
+ /**
+ * Help finding content in the given list of string
+ * @param outputFile
+ * @param format
+ * @return list of strings in the output file
+ */
+ private List runTreeMojo(String outputFile, String format)
+ throws Exception
+ {
+ File testPom = new File( getBasedir(), "target/test-classes/unit/tree-test/plugin-config.xml" );
+ String outputFileName = testDir.getAbsolutePath() + outputFile ;
+ TreeMojo mojo = (TreeMojo) lookupMojo( "tree", testPom );
+ setVariableValueToObject( mojo, "localRepository", new StubArtifactRepository( testDir.getAbsolutePath() ) );
+ setVariableValueToObject( mojo, "outputType", format );
+ setVariableValueToObject( mojo, "outputFile", new File( outputFileName) );
+
+ assertNotNull( mojo );
+ assertNotNull( mojo.getProject() );
+ MavenProject project = mojo.getProject();
+ project.setArtifact( this.stubFactory.createArtifact( "testGroupId", "project", "1.0" ) );
+
+ Set artifacts = this.stubFactory.getScopedArtifacts();
+ Set directArtifacts = this.stubFactory.getReleaseAndSnapshotArtifacts();
+ artifacts.addAll( directArtifacts );
+
+ project.setArtifacts( artifacts );
+ project.setDependencyArtifacts( directArtifacts );
+
+ mojo.execute();
+
+ BufferedReader fp1 = new BufferedReader(new FileReader(outputFileName));
+ List contents = new ArrayList() ;
+
+ String line = null;
+ while ((line = fp1.readLine()) != null)
+ {
+ contents.add(line);
+ }
+ fp1.close();
+
+ return contents ;
+ }
+
+ /**
+ * Help finding content in the given list of string
+ * @param contents
+ * @param str
+ */
+ private boolean findString(List contents, String str)
+ {
+ for ( Iterator it = contents.iterator () ; it.hasNext (); )
+ {
+ String line = (String) it.next() ;
+ if ( line.indexOf(str) != -1 )
+ {
+ // if match then return here
+ return true ;
+ }
+ }
+
+ // in case no match for the whole list
+ return false ;
+ }
// private methods --------------------------------------------------------