# Copyright (c) 2002-2004 LOGILAB S.A. (Paris, FRANCE). # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ Utilities for VCG diagrams output. """ __revision__ = "$Id: vcgutils.py,v 1.9 2005/04/15 11:00:46 syt Exp $" from logilab.common import astng from logilab.common.vcgutils import VCGPrinter from logilab.common.configuration import OptionsProviderMixIn from pyreverse.utils import info, FilterMixIn, \ is_interface, is_exception from pyreverse.extensions import diadefslib class VCGWriter(OptionsProviderMixIn, FilterMixIn): """write vcg graphs from a diagram definition and a project """ name = 'vcg' options = ( ("uml", {'action':"store_true", 'dest':"uml", 'default':0, 'help':"make diagram looks like an UML diagram"}), ) def __init__(self): FilterMixIn.__init__(self) self.options += FilterMixIn.options OptionsProviderMixIn.__init__(self) def write(self, project, diadefs): """write vcg files for according to """ self._files = [] for diagram in diadefs: if diagram.TYPE == 'class': self.write_classes_diagram(diagram) else: self.write_packages_diagram(diagram) def write_classes_diagram(self, diadef): """write a classes diagram to a file, using vcg syntax""" f = self._open_vcg(diadef.title) self._write_classes_diagram(diadef) self.printer.close_graph() f.close() def write_packages_diagram(self, diadef): """write a packages diagram to a file, using vcg syntax""" f = self._open_vcg(diadef.title) self._write_packages_diagram(diadef) self.printer.close_graph() f.close() def _open_vcg(self, name): """open a new vcg file""" name = name.strip() file = ('%s.vcg' % name.replace(' ', '_')) info('creating diagram %s'%file) f = open(file, 'w+') self._files.append(file) self.printer = VCGPrinter(f) self.printer.open_graph(title=name, layoutalgorithm='dfs', late_edge_labels='yes', port_sharing='no', manhattan_edges='yes') return f def _write_packages_diagram(self, diagram): """write a packages diagram using the VCGPrinter""" for obj in diagram.modules(): node = obj.node label = r'\fb%s\fn' % obj.title self.printer.node(obj.fig_id, label=label, shape='box') # package dependencies for rel in diagram.relationships.get('depends', ()): self.printer.edge(rel.from_object.fig_id, rel.to_object.fig_id, arrowstyle='solid', backarrowstyle='none', backarrowsize=0) def _write_classes_diagram(self, diagram): """write a classes diagram using the VCGPrinter""" uml = self.config.uml _filter = self.filter for obj in diagram.objects: node = obj.node if is_exception(node): label = r'\fb\f09%s\fn' % obj.title else: label = r'\fb%s\fn' % obj.title if obj.shape == 'interface': shape = 'ellipse' else: shape = 'box' methods = [m for m in node.locals.values() if isinstance(m, astng.Function) and _filter(m.name)] attrs = [] # prepare associations for name, value in node.instance_attrs_type.items(): if _filter(name): attrs.append(name) if uml: # calculate box size maxlen = len(obj.title) for method in methods: the_len = len(method.name) + 2 maxlen = max(maxlen, the_len) for attrname in attrs: the_len = len(attrname) maxlen = max(maxlen, the_len) label = r'%s\n\f05%s' % (label, "_" * maxlen) for attr in attrs: label = r'%s\n\f08%s' % (label, attr) # add a separator between attributes and methods in uml mode if uml and attrs: label = r'%s\n\f05%s' % (label, "_" * maxlen) for func in methods: label = r'%s\n\f10%s()' % (label, func.name) self.printer.node(obj.fig_id, label=label, shape=shape) # inheritance links for rel in diagram.relationships.get('specialization', ()): # ludal: pour les backarrow: # backarrowstyle='line',backarrowsize=10 la taille # vaut 0 par defaut self.printer.edge(rel.to_object.fig_id, rel.from_object.fig_id, arrowstyle='none', backarrowstyle='solid', backarrowsize=10) # implementation links for rel in diagram.relationships.get('implements', ()): self.printer.edge(rel.to_object.fig_id, rel.from_object.fig_id, arrowstyle='none', linestyle='dotted', backarrowstyle='solid', backarrowsize=10) # generate associations for rel in diagram.relationships.get('association', ()): self.printer.edge(rel.to_object.fig_id, rel.from_object.fig_id, label=rel.name, textcolor='black', arrowstyle='none', backarrowstyle='none')