view dottoxml/src/dot.py @ 4:4fa7bf58e914

first release
author dwinter
date Mon, 23 Jul 2012 09:58:50 +0200
parents d45d5c75afaa
children
line wrap: on
line source

# coding: latin-1
# Copyright (c) 2009,2010 Dirk Baechle.
# www: http://www.mydarc.de/dl9obn/programming/python/dottoxml
# mail: dl9obn AT darc.de
#
# 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.
"""
  Helper classes and functions for the dottoxml.py tool
"""

import re
import X11Colors

r_label = re.compile(r'label\s*=\s*"\s*\{[^\}]*\}\s*"\s*')
r_labelstart = re.compile(r'label\s*=\s*"\s*\{')
r_labelclose = re.compile(r'\}\s*"')

def compileAttributes(attribs):
    """ return the list of attributes as a DOT text string """
    atxt = ""
    first = True
    for key, value in attribs.iteritems():
        if not first:
            atxt += ", %s=\"%s\"" % (key, value)
        else:
            atxt += "%s=\"%s\"" % (key, value)
            first = False
            
    return "[%s]" % atxt

def parseAttributes(attribs):
    """ parse the attribute list and return a key/value dict for it """
    adict = {}
    tlist = []
    lmode = False
    ltext = ''
    # First pass: split entries by ,
    for a in attribs.split(','):
        if r_label.findall(a):
            tlist.append(a)
        elif r_labelstart.findall(a):
            ltext = a
            lmode = True
        else:
            if lmode:
                ltext += ",%s" % a
                if r_labelclose.findall(a):
                    lmode = False
                    tlist.append(ltext)
            else:
                tlist.append(a)

    # Second pass: split keys from values by =
    for t in tlist:
        apos = t.find('=')
        if apos > 0:
            adict[t[:apos].strip()] = t[apos+1:].strip().strip('"')

    return adict

def getLabelAttributes(label):
    """ return the sections of the label attributes in a list structure """
    sections = []
    slist = label.split('|')
    for s in slist:
        mlist = []
        s = s.replace('\\r','\\l')
        s = s.replace('\\n','\\l')
        alist = s.split('\\l')
        for a in alist:
            a = a.strip()
            if a != "":
                mlist.append(a)
        sections.append(mlist)
    return sections

def colorNameToRgb(fcol, defaultcol):
    """ convert the color name fcol to an RGB string, if required """
    if not fcol.startswith('#'):
        return X11Colors.color_map.get(fcol, defaultcol)
    else:
        return fcol
    
def getColorAttribute(attribs, key, defaultcol, conf):
    """ extract the color for the attribute key and convert it
        to RGB format if required
    """
    if conf.Colors:
        if attribs.has_key(key):
            return colorNameToRgb(attribs[key], defaultcol)
    return defaultcol

def escapeNewlines(label):
    """ convert the newline escape sequences in the given label """
    l = label.replace('\\n','\n')
    l = l.replace('\\l','\n')
    l = l.replace('\\r','\n')
    return l

class Node:
    """ a single node in the graph """
    def __init__(self):
        self.label = ""
        self.id = 0
        self.attribs = {}
        self.referenced = False
        self.sections = []

    def initFromString(self, line):
        """ extract node info from the given text line """
        spos = line.find('[')
        atts = ""
        if spos >= 0:
            atts = line[spos+1:]
            line = line[:spos].strip()
        # Process label
        self.label = line.strip('"')
        # Process attributes
        if len(atts):
            spos = atts.rfind(']')
            if spos > 0:
                atts = atts[:spos]
                self.attribs = parseAttributes(atts)
        # Process sections
        if self.attribs.has_key("label"):
            tlabel = self.attribs["label"]
            if (tlabel != "" and     
                tlabel.startswith('{') and
                tlabel.endswith('}')):
                tlabel = tlabel[1:-1]
                self.sections = getLabelAttributes(tlabel)
        # HACK DW, no label in attrs, dann label in attrs = node.label
        else:
            self.attribs["label"]=self.label

    def getLabel(self, conf, multiline=False):
        """ return the label of the node """
        if conf.NodeLabels:
            if self.attribs.has_key('label'):
                if len(self.sections) > 0:
                    if multiline:
                        return '\n'.join(self.sections[0])
                    else:
                        return ','.join(self.sections[0])
                else:
                    return self.attribs['label']
            else:
                return self.label
        else:
            return ""

    def getLabelWidth(self, conf, multiline=False):
        """ return the maximum width label of the node label"""
        if conf.NodeLabels:
            if self.attribs.has_key('label'):
                if len(self.sections) > 0:
                    if multiline:
                        # Find maximum label width
                        width = 1
                        for s in self.sections[0]:
                            if len(s) > width:
                                width = len(s)
                        for s in self.sections[1]:
                            if len(s) > width:
                                width = len(s)
                        for s in self.sections[2]:
                            if len(s) > width:
                                width = len(s)
                        return width
                    else:
                        return len(','.join(self.sections[0]))
                else:
                    return len(self.attribs['label'])
            else:
                return len(self.label)
        else:
            return 0

    def complementAttributes(self, node):
        """ from node copy all new attributes, that do not exist in self """
        for a in node.attribs:
            if not self.attribs.has_key(a):
                self.attribs[a] = node.attribs[a]
                
    def exportDot(self, o, conf):
        """ write the node in DOT format to the given file """
        if len(self.attribs) > 0:
            o.write("\"%s\" %s;\n" % (self.label, compileAttributes(self.attribs)))
        else:
            o.write("\"%s\";\n" % (self.label))

    def exportGDF(self, o, conf):
        """ write the node in GDF format to the given file """
        tlabel = self.getLabel(conf)
        if tlabel == "":
            tlabel = "n%d" % self.id
        o.write("%s\n" % tlabel)

    def exportGML(self, o, conf):
        """ write the node in GML format to the given file """
        o.write("  node [\n")
        o.write("    id %d\n" % self.id)
        o.write("    label\n")
        o.write("    \"%s\"\n" % self.getLabel(conf))
        o.write("  ]\n")

    def exportGraphml(self, doc, parent, conf):        
        """ export the node in Graphml format and append it to the parent XML node """
        node = doc.createElement(u'node')
        node.setAttribute(u'id',u'n%d' % self.id)
        
        data0 = doc.createElement(u'data')
        data0.setAttribute(u'key', u'd0')

        exportUml = False
        if len(self.sections) > 0 and conf.NodeUml and not conf.LumpAttributes:
            exportUml = True
            snode = doc.createElement(u'y:UMLClassNode')
        else:
            snode = doc.createElement(u'y:ShapeNode')
        geom = doc.createElement(u'y:Geometry')
        geom.setAttribute(u'height',u'30.0')
        geom.setAttribute(u'width',u'30.0')
        geom.setAttribute(u'x',u'0.0')
        geom.setAttribute(u'y',u'0.0')
        snode.appendChild(geom)
        color = getColorAttribute(self.attribs, 'color', conf.DefaultNodeColor, conf)
        fill = doc.createElement(u'y:Fill')
        fill.setAttribute(u'color',u'%s' % color)
        fill.setAttribute(u'transparent',u'false')
        snode.appendChild(fill)
        border = doc.createElement(u'y:BorderStyle')
        border.setAttribute(u'color',u'#000000')
        border.setAttribute(u'type',u'line')
        border.setAttribute(u'width',u'1.0')
        snode.appendChild(border)
        color = getColorAttribute(self.attribs, 'fontcolor', conf.DefaultNodeTextColor, conf)        
        label = doc.createElement(u'y:NodeLabel')
        if conf.LumpAttributes:
            label.setAttribute(u'alignment',u'left')
        else:
            label.setAttribute(u'alignment',u'center')
        label.setAttribute(u'autoSizePolicy',u'content')
        label.setAttribute(u'fontFamily',u'Dialog')
        label.setAttribute(u'fontSize',u'12')
        label.setAttribute(u'fontStyle',u'plain')
        label.setAttribute(u'hasBackgroundColor',u'false')
        label.setAttribute(u'hasLineColor',u'false')
        label.setAttribute(u'modelName',u'internal')
        label.setAttribute(u'modelPosition',u'c')
        label.setAttribute(u'textColor',u'%s' % color)
        label.setAttribute(u'visible',u'true')
        nodeLabelText = escapeNewlines(self.getLabel(conf, True))
        if conf.LumpAttributes:
            # Find maximum label width
            width = self.getLabelWidth(conf, True)
            nodeLabelText += '\n' + conf.SepChar*width + '\n'
            nodeLabelText += u'%s\n' % '\n'.join(self.sections[1])
            nodeLabelText += conf.SepChar*width + '\n'
            nodeLabelText += u'%s' % '\n'.join(self.sections[2])
        label.appendChild(doc.createTextNode(u'%s' % nodeLabelText))        
        snode.appendChild(label)
        if exportUml and not conf.LumpAttributes:
            shape = doc.createElement(u'y:UML')
            shape.setAttribute(u'clipContent',u'true')
            shape.setAttribute(u'constraint',u'')
            shape.setAttribute(u'omitDetails',u'false')
            shape.setAttribute(u'stereotype',u'') 
            shape.setAttribute(u'use3DEffect',u'true')
     
            alabel = doc.createElement(u'y:AttributeLabel')
            alabel.appendChild(doc.createTextNode(u'%s' % '\n'.join(self.sections[1])))
            shape.appendChild(alabel)
            mlabel = doc.createElement(u'y:MethodLabel')
            mlabel.appendChild(doc.createTextNode(u'%s' % '\n'.join(self.sections[2])))
            shape.appendChild(mlabel)
        else:
            shape = doc.createElement(u'y:Shape')
            shape.setAttribute(u'type',u'rectangle')
        snode.appendChild(shape)
        data0.appendChild(snode)
        node.appendChild(data0)

        data1 = doc.createElement(u'data')
        data1.setAttribute(u'key', u'd1')
        node.appendChild(data1)
        
        if self.attribs.get("URL","")!="":
            
            data3 = doc.createElement(u'data')
            data3.setAttribute(u'key', u'd4')
            data3.appendChild(doc.createTextNode(u'%s' % self.attribs.get("URL","")))    
            node.appendChild(data3)
            
        parent.appendChild(node)

class Edge:
    """ a single edge in the graph """
    def __init__(self):
        self.id = 0
        self.src = ""
        self.dest = ""
        self.attribs = {}

    def initFromString(self, line):
        """ extract edge info from the given text line """
        spos = line.find('[')
        atts = ""
        if spos >= 0:
            atts = line[spos+1:]
            line = line[:spos].strip()

        # Process labels
        ll = line.replace('->',' ').split()
        if len(ll) > 1:
            self.src = ll[0].strip('"')
            self.dest = ll[1].rstrip(';').strip('"')
        # Process attributes
        if len(atts):
            spos = atts.rfind(']')
            if spos > 0:
                atts = atts[:spos]
                self.attribs = parseAttributes(atts)
                        
    def getLabel(self, nodes, conf):
        """ return the label of the edge """
        if conf.EdgeLabels:
            if self.attribs.has_key('label'):
                return self.attribs['label']
            else:
                if conf.EdgeLabelsAutoComplete:
                    srclink = self.src
                    destlink = self.dest
                    if (nodes[self.src].attribs.has_key('label')):
                        srclink = nodes[self.src].attribs['label']
                    if (nodes[self.dest].attribs.has_key('label')):
                        destlink = nodes[self.dest].attribs['label']
                    return "%s -> %s" % (srclink, destlink)
                else:
                    return ""
        else:
            return ""

    def complementAttributes(self, edge):
        """ from edge copy all new attributes, that do not exist in self """
        for a in edge.attribs:
            if not self.attribs.has_key(a):
                self.attribs[a] = edge.attribs[a]
                
    def exportDot(self, o, nodes, conf):
        """ write the edge in DOT format to the given file """
        if len(self.attribs) > 0:
            o.write("\"%s\" -> \"%s\" %s;\n" % (self.src, self.dest, compileAttributes(self.attribs)))
        else:
            o.write("\"%s\" -> \"%s\";\n" % (self.src, self.dest))

    def exportGDF(self, o, nodes, conf):
        """ write the edge in GDF format to the given file """
        slabel = self.src.getLabel(conf)
        if slabel == "":
            slabel = "n%d" % self.src.id
        dlabel = self.dest.getLabel(conf)
        if dlabel == "":
            dlabel = "n%d" % self.dest.id
        o.write("%s,%s\n" % (slabel, dlabel))

    def exportGML(self, o, nodes, conf):
        """ write the edge in GML format to the given file """
        o.write("  edge [\n")
        o.write("    source %d\n" % nodes[self.src].id)
        o.write("    target %d\n" % nodes[self.dest].id)
        o.write("    label\n")
        o.write("    \"%s\"\n" % self.getLabel(nodes, conf))
        o.write("  ]\n")

    def exportGraphml(self, doc, parent, nodes, conf):
        """ export the edge in Graphml format and append it to the parent XML node """
        edge = doc.createElement(u'edge')
        edge.setAttribute(u'id',u'e%d' % self.id)
        edge.setAttribute(u'source',u'n%d' % nodes[self.src].id)
        edge.setAttribute(u'target',u'n%d' % nodes[self.dest].id)
        
        data2 = doc.createElement(u'data')
        data2.setAttribute(u'key', u'd2')

        pedge = doc.createElement(u'y:PolyLineEdge')
        line = doc.createElement(u'y:LineStyle')      
        color = getColorAttribute(self.attribs, 'color', conf.DefaultEdgeColor, conf)
        line.setAttribute(u'color',u'%s' % color)
        line.setAttribute(u'type', u'line')
        line.setAttribute(u'width', u'1.0')
        pedge.appendChild(line)
        arrow = doc.createElement(u'y:Arrows')
        arrow_tail = conf.DefaultArrowTail
        arrow_head = conf.DefaultArrowHead
        if conf.Arrows:
            if self.attribs.has_key('arrowtail'):
                arrow_head = self.attribs['arrowtail']
            if self.attribs.has_key('arrowhead'):
                arrow_tail = self.attribs['arrowhead']
        arrow.setAttribute(u'source',u'%s' % arrow_head)                
        arrow.setAttribute(u'target',u'%s' % arrow_tail)                
        pedge.appendChild(arrow)
        if conf.EdgeLabels:
            tlabel = self.getLabel(nodes, conf)
            if tlabel != "":
                label = doc.createElement(u'y:EdgeLabel')
                color = getColorAttribute(self.attribs, 'fontcolor', conf.DefaultEdgeTextColor, conf)
                label.setAttribute(u'alignment',u'center')
                label.setAttribute(u'distance',u'2.0')
                label.setAttribute(u'fontFamily',u'Dialog')
                label.setAttribute(u'fontSize',u'12')
                label.setAttribute(u'fontStyle',u'plain')
                label.setAttribute(u'hasBackgroundColor',u'false')
                label.setAttribute(u'hasLineColor',u'false')
                label.setAttribute(u'modelName',u'six_pos')
                label.setAttribute(u'modelPosition',u'tail')
                label.setAttribute(u'textColor',u'%s' % color)
                label.setAttribute(u'visible',u'true')
                label.setAttribute(u'preferredPlacement',u'anywhere')
                label.setAttribute(u'ratio',u'0.5')
                label.appendChild(doc.createTextNode(u'%s' % escapeNewlines(tlabel)))        
                pedge.appendChild(label)
        bend = doc.createElement(u'y:BendStyle')      
        bend.setAttribute(u'smoothed', u'false')
        pedge.appendChild(bend)
        data2.appendChild(pedge)
        edge.appendChild(data2)

        data3 = doc.createElement(u'data')
        data3.setAttribute(u'key', u'd3')
        edge.appendChild(data3)
        
        parent.appendChild(edge)