summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Seguin <ixce@ed3n-m.(none)>2009-10-22 15:30:36 +0200
committerGuillaume Seguin <ixce@ed3n-m.(none)>2009-10-22 15:30:36 +0200
commitca60b5019f6b9ae092aeab87aa004f2ca2a15290 (patch)
tree563ee7cb815d882e3dcfa5de2c8b05cd4c095906
downloadumlpy-ca60b5019f6b9ae092aeab87aa004f2ca2a15290.tar.gz
umlpy-ca60b5019f6b9ae092aeab87aa004f2ca2a15290.tar.bz2
Import pyuml
-rw-r--r--grapher.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/grapher.py b/grapher.py
new file mode 100644
index 0000000..6e7629d
--- /dev/null
+++ b/grapher.py
@@ -0,0 +1,203 @@
+import epydoc.docparser
+import epydoc.docstringparser
+import epydoc.apidoc
+
+import gv
+
+import sys
+import os
+import re
+
+from optparse import OptionParser
+
+parser = OptionParser()
+parser.add_option("-v", dest = "debug", action = "store_true",
+ default = False,
+ help = "enables debug output")
+parser.add_option("-e", "--exclude", dest = "excludes", action = "append",
+ default = [],
+ help = "classes matching this regexp will be excluded \
+ from display")
+parser.add_option("-i", "--include", dest = "includes", action = "append",
+ default = [],
+ help = "classes matching this regexp will be included \
+ in display even if they were excluded by exclude \
+ regexps")
+parser.add_option("-f", "--force", dest = "forces", action = "append",
+ default = [],
+ help = "classes matching this regexp will be forced into \
+ display")
+
+(options, args) = parser.parse_args ()
+
+excludes = reduce (lambda x, y: x + y,
+ map (lambda s: s.split(","), options.excludes), [])
+includes = reduce (lambda x, y: x + y,
+ map (lambda s: s.split(","), options.includes), [])
+forces = reduce (lambda x, y: x + y,
+ map (lambda s: s.split(","), options.forces), [])
+
+print "Excludes :", excludes
+print "Includes :", includes
+print "Forces :", forces
+
+excludes = [re.compile (exp)
+ for exp in excludes]
+includes = [re.compile (exp)
+ for exp in includes]
+forces = [re.compile (exp)
+ for exp in forces]
+
+def is_excluded (class_name):
+ for exp in excludes:
+ if exp.match (class_name):
+ for exp in includes:
+ if exp.match (class_name):
+ return False
+ for exp in forces:
+ if exp.match (class_name):
+ return False
+ return True
+ else:
+ return False
+
+docs = []
+
+def get_var_type (var):
+ if var.type_descr != None:
+ return str (var.type_descr.to_plaintext ("").strip ())
+ else:
+ return None
+
+for path in args:
+ if os.path.isdir (path):
+ paths = [os.path.join (path, file)
+ for file in os.listdir (path) if file.endswith (".py")]
+ else:
+ paths = [path]
+ for path in paths:
+ docs.append (epydoc.docparser.parse_docs (path))
+
+classes = []
+bases_dict = {}
+vars_dict = {}
+
+for doc in docs:
+ for var in doc.variables.values ():
+ if type (var.value) != epydoc.apidoc.ClassDoc:
+ continue
+ var_val = var.value
+ var_name = str (var_val.canonical_name)
+ var_name = var_name.replace ("netnavalbattle.", "")
+ classes.append (var_name)
+ if options.debug:
+ print var_name
+ var_vars = var_val.variables.values ()
+ vars_dict[var_name] = []
+ for var_var in var_vars:
+ if type (var_var.value) != epydoc.apidoc.GenericValueDoc:
+ continue
+ epydoc.docstringparser.parse_docstring (var_var, None)
+ if options.debug:
+ print var_name, var_var.name, get_var_type (var_var)
+ var_var_name = str (var_var.name.replace ("netnavalbattle.", ""))
+ vars_dict[var_name].append ((var_var_name, get_var_type (var_var)))
+ bases = [str (base.canonical_name).replace ("netnavalbattle.", "")
+ for base in var_val.bases
+ if str (base.canonical_name) not in ("object", "<UNKNOWN>")]
+ if options.debug:
+ print bases
+ bases_dict[var_name] = bases
+
+nodes_dict = {}
+fields_dict = {}
+graph = gv.digraph ('g')
+gv.setv (graph, 'charset', 'utf-8')
+gv.setv (graph, 'overlap', 'false')
+gv.setv (graph, 'splines', 'true')
+item = gv.protoedge (graph)
+gv.setv (item, 'len', '2')
+item = gv.protonode (graph)
+gv.setv (item, 'shape', 'box')
+gv.setv (item, 'style', 'filled')
+
+def get_class_node (name):
+ if name not in nodes_dict:
+ node = gv.node (graph, name)
+ gv.setv (node, 'fillcolor', '#FF6262')
+ gv.setv (node, 'label', name)
+ fields_dict[name] = []
+ nodes_dict[name] = node
+ return nodes_dict[name]
+
+CLASS_COLOR = "#FF6262"
+FIELD_COLOR = "#63BDFF"
+
+def build_record_label (class_name):
+ label_base = """<
+<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR>
+ <TD BGCOLOR="%s" PORT="%s">%s</TD>
+ </TR>
+%s
+</TABLE>
+>"""
+ field_formatter = """ <TR>
+ <TD BGCOLOR="%s" PORT="%s">%s</TD>
+ </TR>"""
+ fields = "".join ([field_formatter % (FIELD_COLOR, field, field)
+ for field in fields_dict[class_name]])
+ return label_base % (CLASS_COLOR, class_name, class_name, fields)
+
+def add_var_field (class_name, var_name):
+ class_node = get_class_node (class_name)
+ if var_name not in fields_dict[class_name]:
+ fields_dict[class_name].append (var_name)
+ if gv.getv (class_node, 'shape') != 'plaintext':
+ gv.setv (class_node, 'shape', 'plaintext')
+ gv.setv (class_node, 'style', 'invis')
+ gv.setv (class_node, 'label', build_record_label (class_name))
+
+def add_super_edge (class_name, base_name):
+ class_node = get_class_node (class_name)
+ base_node = get_class_node (base_name)
+ edge = gv.edge (graph, class_name, base_name)
+ gv.setv (edge, 'arrowhead', 'normal')
+ if gv.getv (base_node, 'shape') == 'record':
+ gv.setv (edge, 'headport', "%s" % base_name)
+ # FIXME: edge style
+
+def add_var_edge (class_name, var_name, type_name):
+ if type_name == None:
+ return
+ add_var_field (class_name, var_name)
+ type_node = get_class_node (type_name)
+ edge = gv.edge (graph, type_name, class_name)
+ gv.setv (edge, 'headport', "%s:e" % var_name)
+ gv.setv (edge, 'arrowhead', 'diamond')
+ # FIXME: edge style
+
+for class_name in classes:
+ if is_excluded (class_name):
+ continue
+ for (var_name, type_name) in vars_dict[class_name]:
+ if not type_name or is_excluded (type_name):
+ continue
+ add_var_edge (class_name, var_name, type_name)
+
+for class_name in classes:
+ if is_excluded (class_name):
+ continue
+ for base_name in bases_dict[class_name]:
+ if is_excluded (base_name):
+ continue
+ add_super_edge (class_name, base_name)
+
+for class_name in classes:
+ for exp in forces:
+ if exp.match (class_name):
+ get_class_node (class_name)
+ break
+
+gv.layout (graph, 'dot')
+gv.render (graph, 'png', 'test.png')