package org.simantics.gnuplot; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * @author Tuukka Lehtonen * @since 1.24 */ public class Gnuplot { private static final String GNUPLOT_VERSION_STRING_STARTS_WITH = "gnuplot "; private static final String GNUPLOT_ENV = "GNUPLOT"; private static final String PATH_ENV = "PATH"; private Path executable; public Gnuplot(Path executable) { this.executable = executable; } public static Gnuplot detect() throws IOException { try { return new Gnuplot(findGnuplotPath()); } catch (InterruptedException e) { throw new IOException(e); } } private static String readInput(InputStream in) throws IOException { StringWriter sw = new StringWriter(); for (int l = 0; l < GNUPLOT_VERSION_STRING_STARTS_WITH.length(); ++l) { int c; c = in.read(); if(c <= 0) break; sw.write(c); } return sw.toString(); } private static boolean testExecutable(Path exe) throws IOException, InterruptedException { Process process = new ProcessBuilder(exe.toString(), "-V").start(); try { if (GNUPLOT_VERSION_STRING_STARTS_WITH.startsWith( readInput(process.getInputStream()) )) return true; return false; } finally { process.getInputStream().close(); process.getErrorStream().close(); process.waitFor(); } } private static Path findExecutable(String[] paths, String executableName, boolean fail) throws IOException, InterruptedException { for (String p : paths) { Path exe = Paths.get(p); if (executableName != null) exe = exe.resolve(executableName); if (Files.exists(exe) && Files.isExecutable(exe) && testExecutable(exe)) { return exe; } } if (fail) throw new UnsupportedOperationException("Couldn't find executable '" + executableName + "' on the system path."); return null; } private static Path findExecutable(String path, boolean splitPath, boolean fail) throws IOException, InterruptedException { switch (OSType.calculate()) { case APPLE: case SUN: case LINUX: return findExecutable(splitPath ? path.split(":") : new String[] { path }, "gnuplot", fail); case WINDOWS: return findExecutable(splitPath ? path.split(";") : new String[] { path }, "gnuplot.exe", fail); default: throw new UnsupportedOperationException("unsupported platform"); } } private static Path findGnuplotPath() throws IOException, InterruptedException { String path = System.getenv(GNUPLOT_ENV); if (path != null) { // Does GNUPLOT point directly to the executable? Path p = findExecutable(new String[] { path }, null, false); if (p != null) return p; // Does GNUPLOT point to the gnuplot installation bin directory? p = findExecutable(path, false, false); if (p != null) return p; // Does GNUPLOT point to the gnuplot installation root directory? p = findExecutable(path + "/bin", false, false); if (p != null) return p; } path = System.getenv(PATH_ENV); return findExecutable(path, true, true); } private void execute0(Path workingDirectory, Path stdout, Path stderr, String... cmdline) throws IOException { Process process = new ProcessBuilder(cmdline) .directory(workingDirectory.toFile()) .start(); process.getOutputStream().close(); Thread stdoutDumper = new Thread(new InputStreamToFileCopier(process.getInputStream(), stdout)); Thread stderrDumper = new Thread(new InputStreamToFileCopier(process.getErrorStream(), stderr)); stdoutDumper.start(); stderrDumper.start(); try { stdoutDumper.join(); stderrDumper.join(); process.waitFor(); } catch (InterruptedException e) { throw new IOException(e); } } public void execute(Path workingDirectory, Path stdout, Path stderr, Path script) throws IOException { execute0(workingDirectory, stdout, stderr, executable.toString(), "-c", script.toString()); } public void execute(Path workingDirectory, Path stdout, Path stderr, String... commands) throws IOException { StringBuilder e = new StringBuilder().append('"'); boolean first = true; for (String cmd : commands) { if (!first) e.append("; "); first = false; e.append(cmd); } e.append('"'); execute0(workingDirectory, stdout, stderr, executable.toString(), "-e", e.toString()); } public GnuplotSession newSession(Path workingDirectory, Path stdout, Path stderr) throws IOException { Process process = new ProcessBuilder(executable.toString()) .directory(workingDirectory.toFile()) .start(); return new GnuplotSession(workingDirectory, stdout, stderr, process); } static enum OSType { APPLE, LINUX, SUN, WINDOWS, UNKNOWN; public static OSType calculate() { String osName = System.getProperty("os.name"); assert osName != null; osName = osName.toLowerCase(); if (osName.startsWith("mac os x")) return APPLE; if (osName.startsWith("windows")) return WINDOWS; if (osName.startsWith("linux")) return LINUX; if (osName.startsWith("sun")) return SUN; return UNKNOWN; } } }