/********************************************************************************************
* Copyright (c) 2009, 2016 Fabian Steeg, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Fabian Steeg - initial API and implementation (see bug #277380)
* Alexander Nyßen (itemis AG) - Fixed NPE (see bug #473011)
* Tamas Miklossy (itemis AG) - Refactoring of preferences (bug #446639)
* - Exporting *.dot files in different formats (bug #446647)
* - Naming of output file (bug #484198)
* Darius Jockel (itemis AG) - Fixed problems when calling dot on windows with large
* files (#492395)
*
*********************************************************************************************/
package org.eclipse.gef.dot.internal;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Class for drawing dot graphs by calling the dot executable.
*
* @author Fabian Steeg (fsteeg)
* @author Alexander Nyßen (anyssen)
* @author Darius Jockel
*/
final public class DotExecutableUtils {
private DotExecutableUtils() {
// should not be instantiated by clients
}
/**
* @param dotExecutablePath
* The path of the local Graphviz 'dot' executable, e.g.
* "C:\\Program Files (x86)\\Graphviz2.38\\bin\\dot.exe"
* @param dotInputFile
* The DOT content to render
* @param format
* The image format to export the graph to (e.g. 'pdf' or 'png')
* @param outputFile
* The output file or <code>null</code> if the input file name
* and location should be used (where only the file extension is
* changed dependent on the format)
* @param outputs
* A String array with two Strings, where the first contains the
* output of the input stream and the second contains the output
* of the error stream.
* @return The image file generated by rendering the dotInputFile with
* Graphviz, using the specified format
*/
public static File renderImage(final File dotExecutablePath,
final File dotInputFile, final String format, File outputFile,
String[] outputs) {
if (outputFile == null) {
String dotFile = dotInputFile.getName();
outputFile = new File(dotInputFile.getParent() + File.separator
+ dotFile.substring(0, dotFile.lastIndexOf('.') + 1)
+ format);
}
String[] localOutputs = executeDot(dotExecutablePath, false,
dotInputFile, outputFile, format);
if (!localOutputs[0].isEmpty()) {
System.out.println("Output from dot call: " + localOutputs[0]); //$NON-NLS-1$
}
if (!localOutputs[1].isEmpty()) {
System.err.println("Errors from dot call: " + localOutputs[1]); //$NON-NLS-1$
}
// make the local outputs array visible to the caller
if (outputs != null && outputs.length == 2) {
outputs[0] = localOutputs[0];
outputs[1] = localOutputs[1];
}
return outputFile;
}
/**
* Calls the Graphviz 'dot' executable with the given arguments.
*
* @param dotExecutablePath
* The path of the local Graphviz 'dot' executable, e.g.
* "C:\\Program Files (x86)\\Graphviz2.38\\bin\\dot.exe"
* @param invertYAxis
* Whether to invert the y-axis or not.
* @param dotInputFile
* The input file to pass to 'dot'.
* @param outputFile
* The output file to pass to 'dot' via the -o option. May be
* <code>null</code>.
* @param outputFormat
* The output format to pass to 'dot' via the -T option. May be
* <code>null</code>.
* @return A String array with two Strings, where the first contains the
* output of the input stream and the second contains the output of
* the error stream.
*/
public static String[] executeDot(final File dotExecutablePath,
final boolean invertYAxis, final File dotInputFile,
final File outputFile, final String outputFormat) {
File buffer = null;
boolean hasBuffer = false;
List<String> commands = new ArrayList<>();
commands.add(dotExecutablePath.getAbsolutePath());
if (invertYAxis) {
commands.add("-y");
}
if (outputFormat != null) {
commands.add("-T" + outputFormat);
}
if (outputFile != null) {
buffer = outputFile;
} else {
try {
buffer = File.createTempFile("tmpResult", ".dot");
hasBuffer = true;
} catch (IOException e) {
e.printStackTrace();
}
}
commands.add("-o" + buffer.toPath().toString());
commands.add(dotInputFile.toPath().toString());
String[] call = call(commands.toArray(new String[] {}));
if (hasBuffer) {
FileInputStream input;
try {
input = new FileInputStream(buffer);
call[0] = read(input);
input.close();
} catch (Exception e) {
System.out.println("failed to read temp dot file");
} finally {
buffer.delete();
}
return call;
} else {
return call;
}
}
/***
* @param dotExecutable
* path to the dot executable
* @return String array of the supported export formats
*/
public static String[] getSupportedExportFormats(String dotExecutable) {
String[] commands = { dotExecutable, "-T?" };
String[] outputs = call(commands);
String output = outputs[1];
if (!output.isEmpty()) {
String supportedFormats = output
.substring(output.lastIndexOf(": ") + 2);
supportedFormats = supportedFormats.trim();
return supportedFormats.split(" ");
} else {
return new String[] {};
}
}
/***
* @param commands
* commands to be executed
* @return String array with two Strings The first String contains the
* output of the input stream The second String contains the output
* of the error stream
*/
private static String[] call(final String[] commands) {
System.out.print("Calling '" + Arrays.asList(commands) + "'"); //$NON-NLS-1$ //$NON-NLS-2$
String[] outputs = { "", "" };
Runtime runtime = Runtime.getRuntime();
Process p = null;
try {
p = runtime.exec(commands);
p.waitFor();
System.out.println(
" resulted in exit status: " + p.exitValue() + "."); //$NON-NLS-1$//$NON-NLS-2$
} catch (Exception e) {
System.out
.println(" failed with exception " + e.getMessage() + "."); //$NON-NLS-1$ //$NON-NLS-2$
}
// handle input and error stream only if process succeeded.
if (p != null) {
String output = read(p.getInputStream());
outputs[0] = output;
String errors = read(p.getErrorStream());
outputs[1] = errors;
}
return outputs;
}
private static String read(InputStream is) {
try {
return DotFileUtils.read(is);
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}