package eu.stratosphere.util.dag; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Joiner; import eu.stratosphere.sopremo.operator.CompositeOperator; import eu.stratosphere.sopremo.operator.ElementaryOperator; import eu.stratosphere.sopremo.operator.SopremoModule; import eu.stratosphere.util.dag.GraphLevelPartitioner.Level; import eu.stratosphere.util.dag.TexTagProvider.CONSTANT; public class GraphTexPrinter<Node> { private boolean GENERATE_COMPLETE_DOKUMENT = false; private boolean COMPILE_RESULT = false; private String directory = System.getProperty("user.dir") + File.separatorChar; private SopremoModule plan; private Map<TexTagProvider.NODE, Integer> idMap = new HashMap<TexTagProvider.NODE, Integer>(); private TexTagProvider tags; private TexPictureConfiguration globalConfig; private NodeConfiguration nodeConfig; private String result; public GraphTexPrinter(SopremoModule plan) { this.initialize(); this.plan = plan; } public void generateCompleteDokument(boolean mode) { this.GENERATE_COMPLETE_DOKUMENT = mode; } public void compileResult(boolean mode) { this.COMPILE_RESULT = mode; } public void configure(TexPictureConfiguration conf) { this.globalConfig = conf; } public void configure(NodeConfiguration config) { this.nodeConfig = config; } public String convert(ConnectionNavigator<Node> navigator) { String result = this.convert((Iterable<? extends Node>) this.plan.getAllOutputs(), navigator); if (this.COMPILE_RESULT) { this.startLatexProcess(result); } return result; } public void changeFileDirectory(String newDirectory) { this.directory = newDirectory; } private void startLatexProcess(String tex) { String filename = "GeneratedTexFile" + new Date().getTime(); String texFileEnding = ".tex"; String pdfFileEnding = ".pdf"; File texFile = new File(this.directory + filename + texFileEnding); try { this.writeToFile(texFile, tex); this.compileTex(this.directory + filename + texFileEnding); this.showPdf(this.directory + filename + pdfFileEnding); } catch (IOException e) { e.printStackTrace(); } } private void showPdf(String filename) throws IOException { ProcessBuilder pdfViewer = new ProcessBuilder("gnome-open", filename); Process p = pdfViewer.start(); } private void writeToFile(File file, String tex) throws IOException { BufferedWriter bw = new BufferedWriter(new FileWriter(file)); bw.write(tex); bw.close(); } private void compileTex(String filename) throws IOException { ProcessBuilder pb = new ProcessBuilder("pdflatex", "-output-directory", this.directory, filename); Process p = pb.start(); try { p.waitFor(); } catch (InterruptedException e) { e.printStackTrace(); } } private void printProcessOutput(InputStream err) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(err)); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } private void initialize() { for (TexTagProvider.NODE nodeType : TexTagProvider.NODE.values()) { idMap.put(nodeType, 0); } this.globalConfig = new TexPictureConfiguration(); this.nodeConfig = new NodeConfiguration(); this.generateCompleteDokument(true); } private String convert(Iterable<? extends Node> startNodes, ConnectionNavigator<Node> navigator) { StringBuilder builder = new StringBuilder(); this.tags = new TexTagProvider(this.nodeConfig); try { this.print(builder, startNodes.iterator(), navigator); } catch (IOException e) { // cannot happen since we use a StringBuilder } return this.generateResult(builder); } private String generateResult(StringBuilder builder) { return (GENERATE_COMPLETE_DOKUMENT) ? this.tags.getDocumentWithContent(builder.toString()) : builder.toString(); } private void print(Appendable appendable, Iterator<? extends Node> startNodes, ConnectionNavigator<Node> navigator) throws IOException { this.createMacros(appendable); appendable.append(this.tags.getStartTag(this.globalConfig)).append("\n"); new PrintState(appendable, GraphLevelPartitioner.getLevels(startNodes, navigator)).printDAG(this.globalConfig .getOption(TexPictureConfiguration.OPTION.SCALE)); appendable.append(this.tags.getEndTag()); } private void createMacros(Appendable appendable) { this.tags.createMacros(appendable); } private class PrintState { private final Appendable appender; private Map<Object, String> ids = new IdentityHashMap<Object, String>(); private final List<Level<Object>> levels; @SuppressWarnings({ "unchecked", "rawtypes" }) private PrintState(final Appendable builder, final List<Level<Node>> levels) { this.appender = builder; this.levels = (List) levels; } private void printDAG(String params) throws IOException { for (int levelIndex = 0; levelIndex < this.levels.size(); levelIndex++) { final Level<Object> level = this.levels.get(levelIndex); for (int sourceIndex = 0; sourceIndex < level.getLevelNodes().size(); sourceIndex++) { final Object node = level.getLevelNodes().get(sourceIndex); this.printNode(node, sourceIndex, levelIndex, params); this.printEdges(level, node, levelIndex); } } // for (int levelIndex = this.levels.size() - 1; levelIndex > 0; levelIndex--) { // final Level<Object> level = this.levels.get(levelIndex); // for (int sourceIndex = 0; sourceIndex < level.getLevelNodes().size(); sourceIndex++) { // final Object node = level.getLevelNodes().get(sourceIndex); // this.printEdges(level, node, levelIndex); // } // } } private void printEdges(Level<Object> level, Object node, int levelIndex) throws IOException { // String id = String.valueOf(levelIndex - 1); // String id2 = String.valueOf(levelIndex); List<Object> edges = level.getLinks(node); for (Object edge : edges) { this.appender.append(String.format(GraphTexPrinter.this.tags.getEdgeTag(this.ids.get(edge), this.ids.get(node)))); this.appender.append('\n'); } } private void printNode(Object node, int sourceIndex, int levelIndex, String params) throws IOException { TexTagProvider.NODE nodeTemplate = this.findCorrectTexTemplate(node, levelIndex); // String id = String.valueOf(levelIndex); int counter = GraphTexPrinter.this.idMap.get(nodeTemplate); String id = this.getNameForNode(node) + "_" + counter; // if (levelIndex == 0 && sourceIndex == 0) { // this.appender.append(nodeTemplate.getTexRep(id, 0, 2 * (this.levels.size() - 1), // node.toString())); TexTagProvider.Position position; if (levelIndex == 0 && sourceIndex == 0) { // position = new TexTagProvider.AbsolutePosition(sourceIndex * 3, 2 * (-levelIndex // + this.levels.size() - 1)); position = new TexTagProvider.AbsolutePosition(0, 0); } else { String anchorId; TexTagProvider.POSITION_TAG positionTag; if (sourceIndex == 0) { anchorId = this.ids.get(this.levels.get(levelIndex - 1).getLevelNodes().get(sourceIndex)); positionTag = TexTagProvider.POSITION_TAG.BELOW; } else { anchorId = this.ids.get(this.levels.get(levelIndex).getLevelNodes().get(sourceIndex - 1)); positionTag = TexTagProvider.POSITION_TAG.RIGHT; } position = new TexTagProvider.RelativePosition(positionTag, anchorId); } if (nodeTemplate == TexTagProvider.NODE.OPERATOR) { this.appender.append(nodeTemplate.getTexRep(id, position, this.getNameForNode(node), this.escapeMathSymbols(node.toString()))); } else { this.appender.append(nodeTemplate.getTexRep(id, position, this.escapeMathSymbols(node.toString()))); } // } else { // TexTagProvider.POSITION pos; // String anchorID; // // if (sourceIndex == 0) { // pos = TexTagProvider.POSITION.BELOW; // anchorID = this.ids.get(this.levels.get(levelIndex - 1).getLevelNodes().get(0)); // } else { // pos = TexTagProvider.POSITION.RIGHT; // anchorID = this.ids.get(this.levels.get(levelIndex).getLevelNodes().get(sourceIndex - 1)); // } // // this.appender.append(nodeTemplate.getTexRep(id, node.toString(), pos, anchorID)); // } this.appender.append('\n'); GraphTexPrinter.this.idMap.put(nodeTemplate, counter + 1); this.ids.put(node, id); } private String escapeMathSymbols(String label) { if (label.contains("<")) label = label.replaceAll("<", Joiner.quoteReplacement("$<$")); if (label.contains(">")) label = label.replaceAll(">", Joiner.quoteReplacement("$>$")); return label; } private String getNameForNode(Object node) { if (node instanceof ElementaryOperator) { return ((ElementaryOperator) node).getName(); } else if (node instanceof CompositeOperator) { return ((CompositeOperator) node).getName(); } else { return "UNKNOWN OPERATOR: this operator is neither an eu.stratosphere.sopremo.operator.ElementaryOperator nor an eu.stratosphere.sopremo.operator.CompositeOperator"; } } private TexTagProvider.NODE findCorrectTexTemplate(Object node, int levelIndex) { TexTagProvider.NODE nodeTemplate; if (levelIndex == 0) { nodeTemplate = TexTagProvider.NODE.SOURCE; } else { boolean isSink = true; for (int i = levelIndex + 1; i < levels.size(); i++) { for (Object lvlNode : this.levels.get(i).getLevelNodes()) { if (this.levels.get(i).getLinks(lvlNode).contains(node)) { isSink = false; break; } } } if (isSink) { nodeTemplate = TexTagProvider.NODE.SINK; } else { nodeTemplate = TexTagProvider.NODE.OPERATOR; // // zum Debuggen // if (node instanceof ElementaryOperator) { // System.out.println(((ElementaryOperator) node).toString()); // } else if (node instanceof CompositeOperator) { // System.out.println(((CompositeOperator) node).toString()); // } // // ------------- } } return nodeTemplate; } } public static class TexPictureConfiguration { private Map<OPTION, Double> options = new HashMap<OPTION, Double>(); public enum OPTION { NODE_DISTANCE("node distance=%sem"), SCALE("scale=%s", "every node/.style={scale=%s}"); private String[] tex; private OPTION(String... tex) { this.tex = tex; } public String getTex(Double param) { StringBuilder result = new StringBuilder(); for (int i = 0; i < tex.length; i++) { result.append(String.format(tex[i], String.valueOf(param))); if (i != tex.length) { result.append(", "); } } return result.toString(); } } public void configure(OPTION option, double d) { this.options.put(option, d); } public String getOption(OPTION option) { double value = (this.options.containsKey(option)) ? this.options.get(option) : 1; return String.format(option.getTex(value)); } public String getOptions() { StringBuilder builder = new StringBuilder(); for (Entry<OPTION, Double> entry : this.options.entrySet()) { builder.append(", ").append(entry.getKey().getTex(entry.getValue())); } return builder.toString(); } } public static class NodeConfiguration { Map<CONSTANT, String> config = new HashMap<CONSTANT, String>(); public NodeConfiguration() { for (CONSTANT c : CONSTANT.values()) { this.config.put(c, c.getDefaultValue()); } } public void configure(CONSTANT constant, String value) { this.config.put(constant, value); } public String getValue(CONSTANT con) { return this.config.get(con); } } }