/*
* 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() { }
}