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 --------------------------------------------------------