# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of logilab-common. # # logilab-common is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 2.1 of the License, or (at your option) any # later version. # # logilab-common is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with logilab-common. If not, see . """Functions to generate files readable with Georg Sander's vcg (Visualization of Compiler Graphs). You can download vcg at http://rw4.cs.uni-sb.de/~sander/html/gshome.html Note that vcg exists as a debian package. See vcg's documentation for explanation about the different values that maybe used for the functions parameters. """ __docformat__ = "restructuredtext en" import string ATTRS_VAL = { "algos": ( "dfs", "tree", "minbackward", "left_to_right", "right_to_left", "top_to_bottom", "bottom_to_top", "maxdepth", "maxdepthslow", "mindepth", "mindepthslow", "mindegree", "minindegree", "minoutdegree", "maxdegree", "maxindegree", "maxoutdegree", ), "booleans": ("yes", "no"), "colors": ( "black", "white", "blue", "red", "green", "yellow", "magenta", "lightgrey", "cyan", "darkgrey", "darkblue", "darkred", "darkgreen", "darkyellow", "darkmagenta", "darkcyan", "gold", "lightblue", "lightred", "lightgreen", "lightyellow", "lightmagenta", "lightcyan", "lilac", "turquoise", "aquamarine", "khaki", "purple", "yellowgreen", "pink", "orange", "orchid", ), "shapes": ("box", "ellipse", "rhomb", "triangle"), "textmodes": ("center", "left_justify", "right_justify"), "arrowstyles": ("solid", "line", "none"), "linestyles": ("continuous", "dashed", "dotted", "invisible"), } # meaning of possible values: # O -> string # 1 -> int # list -> value in list GRAPH_ATTRS = { "title": 0, "label": 0, "color": ATTRS_VAL["colors"], "textcolor": ATTRS_VAL["colors"], "bordercolor": ATTRS_VAL["colors"], "width": 1, "height": 1, "borderwidth": 1, "textmode": ATTRS_VAL["textmodes"], "shape": ATTRS_VAL["shapes"], "shrink": 1, "stretch": 1, "orientation": ATTRS_VAL["algos"], "vertical_order": 1, "horizontal_order": 1, "xspace": 1, "yspace": 1, "layoutalgorithm": ATTRS_VAL["algos"], "late_edge_labels": ATTRS_VAL["booleans"], "display_edge_labels": ATTRS_VAL["booleans"], "dirty_edge_labels": ATTRS_VAL["booleans"], "finetuning": ATTRS_VAL["booleans"], "manhattan_edges": ATTRS_VAL["booleans"], "smanhattan_edges": ATTRS_VAL["booleans"], "port_sharing": ATTRS_VAL["booleans"], "edges": ATTRS_VAL["booleans"], "nodes": ATTRS_VAL["booleans"], "splines": ATTRS_VAL["booleans"], } NODE_ATTRS = { "title": 0, "label": 0, "color": ATTRS_VAL["colors"], "textcolor": ATTRS_VAL["colors"], "bordercolor": ATTRS_VAL["colors"], "width": 1, "height": 1, "borderwidth": 1, "textmode": ATTRS_VAL["textmodes"], "shape": ATTRS_VAL["shapes"], "shrink": 1, "stretch": 1, "vertical_order": 1, "horizontal_order": 1, } EDGE_ATTRS = { "sourcename": 0, "targetname": 0, "label": 0, "linestyle": ATTRS_VAL["linestyles"], "class": 1, "thickness": 0, "color": ATTRS_VAL["colors"], "textcolor": ATTRS_VAL["colors"], "arrowcolor": ATTRS_VAL["colors"], "backarrowcolor": ATTRS_VAL["colors"], "arrowsize": 1, "backarrowsize": 1, "arrowstyle": ATTRS_VAL["arrowstyles"], "backarrowstyle": ATTRS_VAL["arrowstyles"], "textmode": ATTRS_VAL["textmodes"], "priority": 1, "anchor": 1, "horizontal_order": 1, } # Misc utilities ############################################################### def latin_to_vcg(st): """Convert latin characters using vcg escape sequence.""" for char in st: if char not in string.ascii_letters: try: num = ord(char) if num >= 192: st = st.replace(char, r"\fi%d" % ord(char)) except Exception: pass return st class VCGPrinter: """A vcg graph writer.""" def __init__(self, output_stream): self._stream = output_stream self._indent = "" def open_graph(self, **args): """open a vcg graph""" self._stream.write("%sgraph:{\n" % self._indent) self._inc_indent() self._write_attributes(GRAPH_ATTRS, **args) def close_graph(self): """close a vcg graph""" self._dec_indent() self._stream.write("%s}\n" % self._indent) def node(self, title, **args): """draw a node""" self._stream.write('%snode: {title:"%s"' % (self._indent, title)) self._write_attributes(NODE_ATTRS, **args) self._stream.write("}\n") def edge(self, from_node, to_node, edge_type="", **args): """draw an edge from a node to another.""" self._stream.write( '%s%sedge: {sourcename:"%s" targetname:"%s"' % (self._indent, edge_type, from_node, to_node) ) self._write_attributes(EDGE_ATTRS, **args) self._stream.write("}\n") # private ################################################################## def _write_attributes(self, attributes_dict, **args): """write graph, node or edge attributes""" for key, value in args.items(): try: _type = attributes_dict[key] except KeyError: raise Exception( """no such attribute %s possible attributes are %s""" % (key, attributes_dict.keys()) ) if not _type: self._stream.write('%s%s:"%s"\n' % (self._indent, key, value)) elif _type == 1: self._stream.write("%s%s:%s\n" % (self._indent, key, int(value))) elif value in _type: self._stream.write("%s%s:%s\n" % (self._indent, key, value)) else: raise Exception( """value %s isn\'t correct for attribute %s correct values are %s""" % (value, key, _type) ) def _inc_indent(self): """increment indentation""" self._indent = " %s" % self._indent def _dec_indent(self): """decrement indentation""" self._indent = self._indent[:-2]