001: /* Soot - a J*va Optimization Framework
002: * Copyright (C) 2002 Sable Research Group
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the
016: * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
017: * Boston, MA 02111-1307, USA.
018: */
019:
020: /*
021: * Modified by the Sable Research Group and others 1997-1999.
022: * See the 'credits' file distributed with Soot for the complete list of
023: * contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot)
024: */
025:
026: /**
027: * DotGraph provides an interface to SOOT for generating DOT language
028: * for graphviz from ATT research lab.
029: *
030: * Intended usage: virtualize CFG, graphes, etc...
031: *
032: * @author Feng Qian
033: */package soot.util.dot;
034:
035: import java.io.*;
036: import java.util.*;
037:
038: public class DotGraph implements Renderable {
039:
040: /* allow a serialized drawing, following steps:
041: * 1. new DotGraph
042: * 2. draw(Directed)Edge / drawUndirectedEdge
043: * attachAttributes, addNode
044: * 3. plot
045: */
046: private String graphname;
047: private boolean isSubGraph;
048:
049: private HashMap<String, DotGraphNode> nodes;
050: /* draw elements are sub graphs, edges, commands */
051: private List<Renderable> drawElements;
052:
053: private List<DotGraphAttribute> attributes;
054:
055: /**
056: * The extension added to output files, exported so that
057: * clients can search for the filenames.
058: */
059: public final static String DOT_EXTENSION = ".dot";
060:
061: /**
062: * Creates a new graph for drawing.
063: * @param graphname, the name used to identify the graph in the dot source.
064: */
065: public DotGraph(String graphname) {
066: this .graphname = graphname;
067: this .isSubGraph = false;
068: this .nodes = new HashMap<String, DotGraphNode>(100);
069: this .drawElements = new LinkedList<Renderable>();
070: this .attributes = new LinkedList<DotGraphAttribute>();
071: }
072:
073: /**
074: * Generates the drawing on canvas to the dot file.
075: * @param filename the name for the output file. By convention, it should
076: * end with DOT_EXTENSION, but this is not enforced.
077: */
078: public void plot(String filename) {
079: try {
080: BufferedOutputStream out = new BufferedOutputStream(
081: new FileOutputStream(filename));
082:
083: render(out, 0);
084: out.close();
085: } catch (IOException ioe) {
086: }
087: }
088:
089: /**
090: * Draws a directed edge (including the source and end nodes,
091: * if they have not already been drawn).
092: * @param from, the source node
093: * @param to, the end node
094: * @return a graph edge
095: */
096: public DotGraphEdge drawEdge(String from, String to) {
097:
098: DotGraphNode src = drawNode(from);
099: DotGraphNode dst = drawNode(to);
100: DotGraphEdge edge = new DotGraphEdge(src, dst);
101:
102: this .drawElements.add(edge);
103:
104: return edge;
105: }
106:
107: /**
108: * Draws a node.
109: * @param name, the node to draw.
110: * @return the {@link DotGraphNode} corresponding to the
111: * specified name.
112: */
113: public DotGraphNode drawNode(String name) {
114: DotGraphNode node = getNode(name);
115:
116: if (node == null)
117: throw new RuntimeException("Assertion failed.");
118:
119: if (!this .drawElements.contains(node))
120: this .drawElements.add(node);
121:
122: return node;
123: }
124:
125: /**
126: * Gets the graph node by name.
127: * @param name, unique name of the node.
128: * @return the node with the specified name, or <code>null</code>
129: * if there is no such node.
130: */
131: public DotGraphNode getNode(String name) {
132: DotGraphNode node = nodes.get(name);
133: if (node == null) {
134: node = new DotGraphNode(name);
135: nodes.put(name, node);
136: }
137: return node;
138: }
139:
140: /**
141: * Sets all node shapes, see the list of node shapes in DotGraphConstants.
142: * @param shape, the node shape
143: */
144: public void setNodeShape(String shape) {
145: StringBuffer command = new StringBuffer("node [shape=");
146: command.append(shape);
147: command.append("];");
148: this .drawElements.add(new DotGraphCommand(new String(command)));
149: }
150:
151: /**
152: * Sets all node styles
153: * @param style, the node style
154: */
155: public void setNodeStyle(String style) {
156: StringBuffer command = new StringBuffer("node [style=");
157: command.append(style);
158: command.append("];");
159: this .drawElements.add(new DotGraphCommand(new String(command)));
160: }
161:
162: /**
163: * sets the size of drawing area, in inches
164: */
165: public void setGraphSize(double width, double height) {
166: String size = "\"" + width + "," + height + "\"";
167: this .setGraphAttribute("size", size);
168: }
169:
170: /**
171: * sets the pages size, once this is set, the generated graph
172: * will be broken into several pages.
173: */
174: public void setPageSize(double width, double height) {
175: String size = "\"" + width + ", " + height + "\"";
176: this .setGraphAttribute("page", size);
177: }
178:
179: /**
180: * sets the graph rotation angles
181: */
182: public void setOrientation(String orientation) {
183: this .setGraphAttribute("orientation", orientation);
184: }
185:
186: /**
187: * sets the graph label
188: */
189: public void setGraphLabel(String label) {
190: label = DotGraphUtility.replaceQuotes(label);
191: label = DotGraphUtility.replaceReturns(label);
192: this .setGraphAttribute("label", "\"" + label + "\"");
193: }
194:
195: /**
196: * sets any general attributes
197: * @param id is the attribute name.
198: * @param value is the attribute value.
199: */
200: public void setGraphAttribute(String id, String value) {
201: this .setGraphAttribute(new DotGraphAttribute(id, value));
202: }
203:
204: /**
205: * sets any general attributes
206: * @param attr a {@link DotGraphAttribute} specifying the
207: * attribute name and value.
208: */
209: public void setGraphAttribute(DotGraphAttribute attr) {
210: this .attributes.add(attr);
211: }
212:
213: /**
214: * draws an undirected edge
215: * @param label1, label2
216: */
217: public void drawUndirectedEdge(String label1, String label2) {
218: }
219:
220: /**
221: * creates a sub graph.
222: * @return the newly created sub graph.
223: */
224: public DotGraph createSubGraph(String label) {
225: // file name is used as label of sub graph.
226: DotGraph subgraph = new DotGraph(label);
227: subgraph.isSubGraph = true;
228:
229: this .drawElements.add(subgraph);
230:
231: return subgraph;
232: }
233:
234: /* implements renderable interface. */
235: public void render(OutputStream out, int indent) throws IOException {
236: // header
237: String graphname = this .graphname;
238:
239: if (!isSubGraph) {
240: DotGraphUtility.renderLine(out, "digraph \"" + graphname
241: + "\" {", indent);
242: } else {
243: DotGraphUtility.renderLine(out, "subgraph \"" + graphname
244: + "\" {", indent);
245: }
246:
247: /* render graph attributes */
248: Iterator<DotGraphAttribute> attrIt = this .attributes.iterator();
249: while (attrIt.hasNext()) {
250: DotGraphAttribute attr = attrIt.next();
251: DotGraphUtility.renderLine(out, attr.toString() + ";",
252: indent + 4);
253: }
254:
255: /* render elements */
256: Iterator<Renderable> elmntsIt = this .drawElements.iterator();
257: while (elmntsIt.hasNext()) {
258: Renderable element = elmntsIt.next();
259: element.render(out, indent + 4);
260: }
261:
262: // close the description
263: DotGraphUtility.renderLine(out, "}", indent);
264: }
265: }
|