diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dottoxml/src/dot.py	Mon Jul 23 09:58:50 2012 +0200
@@ -0,0 +1,445 @@
+# 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)