/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import java.util.Hashtable; /** * OPT_VCG implements the set of routines to output a graph * in VCG format. The graph should implement the OPT_VCGGraph interface, * and the graph nodes and edges should implement OPT_VCGNode and * OPT_VCGEdge interfaces respectively. * * @see OPT_VCGGraph * @see OPT_VCGNode * @see OPT_VCGEdge */ public final class OPT_VCG implements OPT_VCGConstants { /** * Returns a VCG representation for a given graph. * @param g the graph in question. */ public static String toVCG(OPT_VCGGraph g) { OPT_VCGGraph.GraphDesc gd = g.getVCGDescriptor(); StringBuilder res = new StringBuilder("graph: {\n"); /** * Emit graph header */ res.append(pair("title", quote(gd.getTitle()), 1)); // Options res.append(gd.getLayoutParameters()).append("\n"); res.append(pair("display_edge_labels", gd.displayEdgeLabels(), 1)); res.append(pair("late_edge_labels", gd.lateEdgeLabels(), 1)); res.append(pair("portsharing", gd.portSharing(), 1)); res.append(pair("node.width", gd.defaultNodeWidth(), 1)); res.append(pair("node.color", gd.defaultNodeColor(), 1)); res.append(pair("node.borderwidth", gd.defaultBorderWidth(), 1)); res.append(pair("edge.linestyle", gd.defaultEdgeStyle(), 1)); String[] eClasses = gd.getEdgeClasses(); if (eClasses != null) { for (int i = 0; i < eClasses.length; i++) { res.append(pair("classname " + i, quote(eClasses[i]), 1)); } res.append("\n"); } String[] eColors = gd.getEdgeColors(); /** * Process nodes */ Hashtable<OPT_VisNode, String> nodeNames = new Hashtable<OPT_VisNode, String>(); int nodenum = 0; for (Enumeration<OPT_VCGNode> nodes = g.nodes(); nodes.hasMoreElements();) { OPT_VCGNode node = nodes.nextElement(); OPT_VCGNode.NodeDesc nd = node.getVCGDescriptor(); res.append(indent("node: {", 1)); String name = nodeNames.get(node); if (name == null) { name = "Node " + (nodenum++); nodeNames.put(node, name); } res.append(pair("title", quote(name), 1)); String label = nd.getLabel(); if (label != null) res.append(pair("label", quote(label), 2)); String info1 = nd.getInfo1(); if (info1 != null) res.append(pair("info1", quote(info1), 2)); String info2 = nd.getInfo2(); if (info2 != null) res.append(pair("info2", quote(info2), 2)); String info3 = nd.getInfo3(); if (info3 != null) res.append(pair("info3", quote(info3), 2)); String shape = nd.getShape(); if (shape != null) res.append(pair("shape", shape, 2)); String color = nd.getColor(); if (color != null) res.append(pair("color", color, 2)); int bwidth = nd.getBorderWidth(); if (bwidth != 1) res.append(pair("borderwidth", bwidth, 2)); res.append(indent("}", 1)); res.append("\n"); /** * Process edges */ for (Enumeration<OPT_VisEdge> edges = node.edges(); edges.hasMoreElements();) { OPT_VCGEdge edge = (OPT_VCGEdge) edges.nextElement(); OPT_VCGEdge.EdgeDesc ed = edge.getVCGDescriptor(); String eName = edge.backEdge() ? "backedge" : "edge"; res.append(indent(eName + ": {", 1)); OPT_VisNode fromNode = edge.sourceNode(); String fromName = nodeNames.get(fromNode); if (fromName == null) { fromName = "Node " + (nodenum++); nodeNames.put(fromNode, fromName); } res.append(pair("sourcename", quote(fromName), 2)); OPT_VisNode toNode = edge.targetNode(); String toName = nodeNames.get(toNode); if (toName == null) { toName = "Node " + (nodenum++); nodeNames.put(toNode, toName); } res.append(pair("targetname", quote(toName), 2)); String eLabel = ed.getLabel(); if (eLabel != null) res.append(pair("label", quote(eLabel), 2)); int type = ed.getType(); String eColor = ed.getColor(); if (type != NONE) { res.append(pair("class", type, 2)); if (eColor == null && eColors != null) eColor = eColors[type]; } if (eColor != null) res.append(pair("color", eColor, 2)); int thickness = ed.getThickness(); if (thickness != 1) res.append(pair("thickness", thickness, 2)); String linestyle = ed.getStyle(); if (linestyle != null) res.append(pair("linestyle", linestyle, 2)); res.append(indent("}", 1)); res.append("\n"); } } /** * Emit graph footer */ res.append("}\n"); return res.toString(); } /** * Prints a VCG representation for a given graph to a file. * Overwrites the file. * * @param filename name of the file to append the graph to. * @param g the graph in question. */ public static void printVCG(String filename, OPT_VCGGraph g) { printVCG(filename, g, false); } /** * Prints a VCG representation for a given graph to a file. * * @param filename name of the file to append the graph to. * @param g the graph in question. * @param append should this be appended to the end of the file? */ public static void printVCG(String filename, OPT_VCGGraph g, boolean append) { writeToVCGFile(filename, toVCG(g), append); } /** * Initializes the vcg file for multiple graphs. * * @param filename name of the file. */ public static void headerVCG(String filename) { appendToVCGFile(filename, "graph: {\n\n"); } /** * Finalizes the vcg file after multiple graphs. * * @param filename name of the file. */ public static void footerVCG(String filename) { appendToVCGFile(filename, "\n}\n"); } /** * Appends a given string to a file. * * @param filename name of file * @param VCGOutput the string to write */ private static void appendToVCGFile(String filename, String VCGOutput) { writeToVCGFile(filename, VCGOutput, true); } /** * Writes a given string to a file (overwriting its contents). * * @param filename name of file * @param VCGOutput the string to write */ @SuppressWarnings("unused") // for debugging ?? private static void writeToVCGFile(String filename, String VCGOutput) { writeToVCGFile(filename, VCGOutput, false); } /** * Writes a given string to a file. * * @param filename name of file * @param VCGOutput the string to write * @param append should the string be appended to the end? */ private static void writeToVCGFile(String filename, String VCGOutput, boolean append) { try { PrintWriter out = new PrintWriter(new FileOutputStream(filename, append)); out.println(VCGOutput); out.close(); } catch (IOException e) { System.out.println("An error occurred: " + e); } } private static String[] indents = {"", " ", " ", " ", " ", " ",}; // Creates an {indent}value string // For internal use only. private static String indent(String value, int indent) { return indents[indent] + value + "\n"; } // Creates an {indent}name:value string // For internal use only. private static String pair(String name, String value, int indent) { return indents[indent] + name + ":" + value + "\n"; } private static String pair(String name, int value, int indent) { return indents[indent] + name + ":" + value + "\n"; } private static String pair(String name, boolean value, int indent) { return indents[indent] + name + ":" + (value ? "yes" : "no") + "\n"; } // Place value in quotes, quoting all special characters (only '"' for now) // For internal use only. private static String quote(String value) { int k = 0; if ((k = value.indexOf('\"')) != -1) { StringBuilder sb = new StringBuilder(); int s = k; for (; (k = value.indexOf('\"', s + 1)) != -1; s = k) { sb.append(value.substring(s, k - 1)).append('\\'); } sb.append(value.substring(s)); value = sb.toString(); } return "\"" + value + "\""; } // private constructor, so no objects can be created. private OPT_VCG() { } }