# 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: 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]