package com.github.jabbalaci.graphviz;
// GraphViz.java - a simple API to call dot from Java programs
/*$Id$*/
/*
******************************************************************************
* *
* (c) Copyright Laszlo Szathmary *
* *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as published by *
* the Free Software Foundation; either version 2.1 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public *
* License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program; if not, write to the Free Software Foundation, *
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
* *
******************************************************************************
*/
import java.io.*;
import java.util.Properties;
/**
* <dl>
* <dt>Purpose: GraphViz Java API
* <dd>
* <p/>
* <dt>Description:
* <dd> With this Java class you can simply call dot
* from your Java programs.
* <dt>Example usage:
* <dd>
* <pre>
* GraphViz gv = new GraphViz();
* gv.addln(gv.start_graph());
* gv.addln("A -> B;");
* gv.addln("A -> C;");
* gv.addln(gv.end_graph());
* System.out.println(gv.getDotSource());
*
* String type = "gif";
* File out = new File("out." + type); // out.gif in this example
* gv.writeGraphToFile( gv.getGraph( gv.getDotSource(), type ), out );
* </pre>
* </dd>
* <p/>
* </dl>
*
* @author Laszlo Szathmary (<a href="jabba.laci@gmail.com">jabba.laci@gmail.com</a>)
* @version v0.1, 2003/12/04 (December) -- first release
*/
public class GraphViz {
/**
* Detects the client's operating system.
*/
private final static String osName = System.getProperty("os.name");
/**
* Load the config.properties file.
*/
private final static String CFG_PROP = "config.properties";
private final static String BALANCE_TREE_PROG = "balancetree.gv";
private final static Properties CONFIG_FILE = new Properties() {
private final static long serialVersionUID = 1L; {
try {
load(new FileInputStream(CFG_PROP));
} catch (Exception e) {
}
}
};
/**
* The dir. where temporary files will be created.
*/
private static String TEMP_DIR = osName.equals("Linux")
? CONFIG_FILE.getProperty("tempDirForLinux")
: CONFIG_FILE.getProperty("tempDirForWindows");
/**
* Where is your dot program located? It will be called externally.
*/
private static String DOT = osName.equals("Linux")
? CONFIG_FILE.getProperty("dotForLinux")
: CONFIG_FILE.getProperty("dotForWindows");
private static String GVPR = osName.equals("Linux")
? CONFIG_FILE.getProperty("gvprForLinux")
: CONFIG_FILE.getProperty("gvprForWindows");
private static String NEATO = osName.equals("Linux")
? CONFIG_FILE.getProperty("neatoForLinux")
: CONFIG_FILE.getProperty("neatoForWindows");
/**
* The image size in dpi. 96 dpi is normal size. Higher values are 10% higher each.
* Lower values 10% lower each.
* <p/>
* dpi patch by Peter Mueller
*/
private int[] dpiSizes = {46, 51, 57, 63, 70, 78, 86, 96, 106, 116, 128, 141, 155, 170, 187, 206, 226, 249};
/**
* Define the index in the image size array.
*/
private int currentDpiPos = 7;
/**
* The source of the graph written in dot language.
*/
private StringBuilder graph = new StringBuilder();
/**
* Constructor: creates a new GraphViz object that will contain
* a graph.
*/
public GraphViz() {
}
/**
* Increase the image size (dpi).
*/
public void increaseDpi() {
if (this.currentDpiPos < (this.dpiSizes.length - 1)) {
++this.currentDpiPos;
}
}
/**
* Decrease the image size (dpi).
*/
public void decreaseDpi() {
if (this.currentDpiPos > 0) {
--this.currentDpiPos;
}
}
public int getImageDpi() {
return this.dpiSizes[this.currentDpiPos];
}
/**
* Returns the graph's source description in dot language.
*
* @return Source of the graph in dot language.
*/
public String getDotSource() {
return this.graph.toString();
}
/**
* Adds a string to the graph's source (without newline).
*/
public void add(String line) {
this.graph.append(line);
}
/**
* Adds a string to the graph's source (with newline).
*/
public void addln(String line) {
this.graph.append(line + "\n");
}
/**
* Adds a newline to the graph's source.
*/
public void addln() {
this.graph.append('\n');
}
public void clearGraph() {
this.graph = new StringBuilder();
}
/**
* Returns the graph as an image in binary format.
*
* @param dot_source Source of the graph to be drawn.
* @param type Type of the output image to be produced, e.g.: gif, dot, fig, pdf, ps, svg, png.
* @return A byte array containing the image of the graph.
*/
public byte[] getGraph(String dot_source, String type) {
File dot;
byte[] img_stream = null;
try {
dot = writeDotSourceToFile(dot_source);
if (dot != null) {
img_stream = balancedTree(dot, type);
// img_stream = get_img_stream(dot, type);
if (dot.delete() == false)
System.err.println("Warning: " + dot.getAbsolutePath() + " could not be deleted!");
return img_stream;
}
return null;
} catch (java.io.IOException ioe) {
return null;
}
}
/**
* Writes the graph's image in a file.
*
* @param img A byte array containing the image of the graph.
* @param file Name of the file to where we want to write.
* @return Success: 1, Failure: -1
*/
public int writeGraphToFile(byte[] img, String file) {
File to = new File(file);
return writeGraphToFile(img, to);
}
/**
* Writes the graph's image in a file.
*
* @param img A byte array containing the image of the graph.
* @param to A File object to where we want to write.
* @return Success: 1, Failure: -1
*/
public int writeGraphToFile(byte[] img, File to) {
try {
FileOutputStream fos = new FileOutputStream(to);
fos.write(img);
fos.close();
} catch (java.io.IOException ioe) {
return -1;
}
return 1;
}
/**
* It will call the external dot program, and return the image in
* binary format.
*
* @param dot Source of the graph (in dot language).
* @param type Type of the output image to be produced, e.g.: gif, dot, fig, pdf, ps, svg, png.
* @return The image of the graph in .gif format.
*/
private byte[] get_img_stream(File dot, String type) {
File img;
byte[] img_stream = null;
try {
img = File.createTempFile("graph_", "." + type, new File(GraphViz.TEMP_DIR));
Runtime rt = Runtime.getRuntime();
// patch by Mike Chenault
String[] args = {DOT, "-T" + type, "-Gdpi=" + dpiSizes[this.currentDpiPos], dot.getAbsolutePath(), "-o", img.getAbsolutePath()};
Process p = rt.exec(args);
p.waitFor();
FileInputStream in = new FileInputStream(img.getAbsolutePath());
img_stream = new byte[in.available()];
in.read(img_stream);
// Close it if we need to
if (in != null) in.close();
if (img.delete() == false)
System.err.println("Warning: " + img.getAbsolutePath() + " could not be deleted!");
} catch (java.io.IOException ioe) {
System.err.println("Error: in I/O processing of tempfile in dir " + GraphViz.TEMP_DIR + "\n");
System.err.println(" or in calling external command");
ioe.printStackTrace();
} catch (java.lang.InterruptedException ie) {
System.err.println("Error: the execution of the external program was interrupted");
ie.printStackTrace();
}
return img_stream;
}
private byte[] balancedTree(File dot, String type) {
File img;
byte[] img_stream = null;
File gvprConfigFile = new File(BALANCE_TREE_PROG);
try {
img = File.createTempFile("graph_", "." + type, new File(GraphViz.TEMP_DIR));
// File tmp = File.createTempFile("tmp_", "." + type, new File(GraphViz.TEMP_DIR));
// File tmp2 = File.createTempFile("tmp2_", "." + type, new File(GraphViz.TEMP_DIR));
// File tmp3 = File.createTempFile("tmp3_", "." + type, new File(GraphViz.TEMP_DIR));
{
// patch by Mike Chenault
String[] args = new String[]{DOT, dot.getAbsolutePath()};
//"-T" + type, "-Gdpi=" + dpiSizes[this.currentDpiPos], dot.getAbsolutePath(), "-o", img.getAbsolutePath()};
ProcessBuilder builder = new ProcessBuilder(args);
builder.redirectErrorStream(true);
builder.redirectOutput(img);
Process p = builder.start();
p.waitFor();
}
{
String[] args = new String[]{GVPR, "-c", "-q", "-f" + BALANCE_TREE_PROG, img.getAbsolutePath(), "-o" + dot.getAbsolutePath()};
//"-T" + type, "-Gdpi=" + dpiSizes[this.currentDpiPos], dot.getAbsolutePath(), "-o", img.getAbsolutePath()};
ProcessBuilder builder = new ProcessBuilder(args);
builder.environment().put("GPRPATH", gvprConfigFile.getAbsoluteFile().getParent());
builder.redirectErrorStream(true);
//builder.redirectOutput(tmp2);
Process p = builder.start();
p.waitFor();
}
{
String[] args = new String[]{NEATO, "-n", "-T" + type, "-Gdpi=" + dpiSizes[this.currentDpiPos], dot.getAbsolutePath(), "-o" + img.getAbsolutePath()};
ProcessBuilder builder = new ProcessBuilder(args);
builder.redirectErrorStream(true);
// builder.redirectOutput(tmp3);
Process p = builder.start();
p.waitFor();
}
FileInputStream in = new FileInputStream(img.getAbsolutePath());
img_stream = new byte[in.available()];
in.read(img_stream);
// Close it if we need to
if (in != null) in.close();
if (img.delete() == false) {
System.err.println("Warning: " + img.getAbsolutePath() + " could not be deleted!");
}
// if (tmp.delete() == false){
// System.err.println("Warning: " + img.getAbsolutePath() + " could not be deleted!");
// }
} catch (java.io.IOException ioe) {
System.err.println("Error: in I/O processing of tempfile in dir " + GraphViz.TEMP_DIR + "\n");
System.err.println(" or in calling external command");
ioe.printStackTrace();
} catch (java.lang.InterruptedException ie) {
System.err.println("Error: the execution of the external program was interrupted");
ie.printStackTrace();
}
return img_stream;
}
/**
* Writes the source of the graph in a file, and returns the written file
* as a File object.
*
* @param str Source of the graph (in dot language).
* @return The file (as a File object) that contains the source of the graph.
*/
private File writeDotSourceToFile(String str) throws java.io.IOException {
File temp;
try {
temp = File.createTempFile("graph_", ".dot.tmp", new File(GraphViz.TEMP_DIR));
FileWriter fout = new FileWriter(temp);
fout.write(str);
fout.close();
} catch (Exception e) {
System.err.println("Error: I/O error while writing the dot source to temp file!");
return null;
}
return temp;
}
/**
* Returns a string that is used to start a graph.
*
* @return A string to open a graph.
*/
public String start_graph() {
return "digraph G {";
}
/**
* Returns a string that is used to end a graph.
*
* @return A string to close a graph.
*/
public String end_graph() {
return "}";
}
/**
* Takes the cluster or subgraph id as input parameter and returns a string
* that is used to start a subgraph.
*
* @return A string to open a subgraph.
*/
public String start_subgraph(int clusterid) {
return "subgraph cluster_" + clusterid + " {";
}
/**
* Returns a string that is used to end a graph.
*
* @return A string to close a graph.
*/
public String end_subgraph() {
return "}";
}
/**
* Read a DOT graph from a text file.
*
* @param input Input text file containing the DOT graph
* source.
*/
public void readSource(String input) {
StringBuilder sb = new StringBuilder();
try {
FileInputStream fis = new FileInputStream(input);
DataInputStream dis = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(dis));
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
dis.close();
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
this.graph = sb;
}
} // end of class GraphViz