001package com.basilv.examples.packagediagram;
002
003import java.io.*;
004import java.util.*;
005
006import jdepend.framework.*;
007
008public class PackageDiagramCreatorApp {
009
010  public static void main(String[] args) {
011    createPackageDependencyDiagram();
012    System.exit(0);
013  }
014  
015  public static void createPackageDependencyDiagram() {
016    Collection<JavaPackage> packages = analyzePackages();
017    StringBuilder builder = generateGraph(packages);
018    generateImage("packages", builder.toString());
019  }
020
021  @SuppressWarnings("unchecked")
022  private static Collection<JavaPackage> analyzePackages() {
023    JDepend jdepend = new JDepend();
024    try {
025      jdepend.addDirectory("dist/classes");
026    } catch (IOException e) {
027      throw new RuntimeException("Error adding directory for JDepend to analyze.", e);
028    }
029    Collection<JavaPackage> packages = jdepend.analyze();
030    return packages;
031  }
032
033  private static StringBuilder generateGraph(
034    Collection<JavaPackage> packages) {
035    StringBuilder builder = new StringBuilder();
036    builder.append("digraph packages {").append("\n");
037    builder.append("node [shape=box];").append("\n");
038    builder.append("rankdir=BT;").append("\n");
039    Set<String> drawnDependencies = new HashSet<String>();
040    for (JavaPackage javaPackage : packages) {
041      String packageNodeName = getGraphVizNodeForPackage(javaPackage);
042      if (packageNodeName == null) {
043        continue;
044      }
045      builder.append(packageNodeName).append("\n");
046
047      @SuppressWarnings("unchecked")
048      Collection<JavaPackage> dependencies = javaPackage.getEfferents();
049      
050      for (JavaPackage dependency : dependencies) {
051        String dependencyNodeName = getGraphVizNodeForPackage(dependency);
052        if (dependencyNodeName == null
053          || packageNodeName.equals(dependencyNodeName)) {
054          continue;
055        }
056        String dependencyKey = packageNodeName + "->"
057          + dependencyNodeName;
058        if (drawnDependencies.contains(dependencyKey)) {
059          continue;
060        }
061        builder.append(packageNodeName).append(" -> ").append(
062          dependencyNodeName).append(" [weight=4]").append("\n");
063        drawnDependencies.add(dependencyKey);
064      }
065    }
066    builder.append("}\n");
067    return builder;
068  }
069
070  private static String getGraphVizNodeForPackage(
071    JavaPackage javaPackage) {
072
073    String rootPackage = "com.basilv.examples.packagediagram";
074    String packageName = javaPackage.getName();
075    if (!packageName.startsWith(rootPackage)) {
076      return null;
077    }
078
079    return packageName.replace(".", "_");
080  }
081
082  private static void generateImage(String fileName,
083    String graphVizDotFormattedGraph) {
084    try {
085      File graphFile = createFileWithContents(fileName
086        + ".txt", graphVizDotFormattedGraph);
087
088      // This requires the GraphViz software to be installed -
089      // see http://graphviz.org/
090      String imageFileLocation = fileName + ".png";
091      Runtime.getRuntime().exec(
092        "dot -v -Tpng " + graphFile.getName() + " -o "
093          + imageFileLocation);
094
095      System.out.println("Image file available at "
096        + new File(imageFileLocation).getAbsolutePath());
097    } catch (IOException e) {
098      throw new RuntimeException("Error generating image " + fileName, e);
099    }
100  }
101
102  private static File createFileWithContents(
103    String fileName, String graphVizDotFormattedGraph)
104    throws IOException {
105    File graphFile = new File(fileName);
106    FileWriter writer = new FileWriter(graphFile, false);
107    try {
108      writer.append(graphVizDotFormattedGraph);
109    } finally {
110      writer.close();
111    }
112    return graphFile;
113  }
114
115}
116
117