diff options
author | Radek Novacek <rnovacek@redhat.com> | 2013-03-18 09:52:42 +0100 |
---|---|---|
committer | Radek Novacek <rnovacek@redhat.com> | 2013-03-18 09:52:42 +0100 |
commit | b5e14dcdb73d3b70131b9a602efd209488922a4e (patch) | |
tree | 0a863922e2987f881d25efd02ca481614f218cb6 /tools | |
parent | e34b7108248ee50c3effbd76bdb030aec69a8adb (diff) | |
download | openlmi-providers-b5e14dcdb73d3b70131b9a602efd209488922a4e.tar.gz openlmi-providers-b5e14dcdb73d3b70131b9a602efd209488922a4e.tar.xz openlmi-providers-b5e14dcdb73d3b70131b9a602efd209488922a4e.zip |
tools: don't use CIMOM for generating documentation
class2rst.py and class2uml.py was altered to use konkretmof for parsing
MOFs instead of calling CIMOM. This two modules was also renamed to
openlmi-doc-class2* and will be installed to $prefix/bin.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/CMakeLists.txt | 3 | ||||
-rwxr-xr-x | tools/class2uml.py | 219 | ||||
-rw-r--r--[-rwxr-xr-x] | tools/openlmi-doc-class2rst (renamed from tools/class2rst.py) | 371 | ||||
-rw-r--r-- | tools/openlmi-doc-class2uml | 143 |
4 files changed, 337 insertions, 399 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..04d11b7 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,3 @@ + +install(PROGRAMS openlmi-doc-class2rst openlmi-doc-class2uml DESTINATION bin) + diff --git a/tools/class2uml.py b/tools/class2uml.py deleted file mode 100755 index e5e6ea4..0000000 --- a/tools/class2uml.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved. -# -# This library 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. -# -# This library 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 this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Authors: Jan Safranek <jsafrane@redhat.com> -# - -import os; -import pywbem; -import optparse -import re -import cgi -import sys - -class UmlExporter(object): - - def __init__(self, cliconn): - self.classcache = {} - self.cliconn = cliconn - # original list of classes - self.classes = set() - self.file = sys.stdout - - def load_class(self, classname): - if classname in self.classcache: - return self.classcache[classname] - - c = self.cliconn.GetClass(classname) - self.classcache[classname] = c - return c - - def display_type(self, param): - """ - Return displayable type of given parameter. - It adds [] if it's array and class name of referenced classes. - """ - if param.reference_class: - ptype = param.reference_class - else: - ptype = param.type - if param.is_array: - ptype = ptype + "[]" - return ptype - - def print_parameters(self, params): - """ - Print table of method parametes. - """ - if not params: - print >>self.file, "None" - return - - print >>self.file, "<table class=\"parameters\">" - for p in params.values(): - direction = set() - if p.qualifiers.has_key("Out"): - direction.add("OUT") - if p.qualifiers.has_key("In"): - direction.add("IN") - if not direction: - direction.add("IN") - direction = "/".join(sorted(direction)) - - print >>self.file, "<tr>" - print >>self.file, "<td>%s</td><td>%s</td><td><b>%s</b></td>" % (direction, self.display_type(p), p.name) - print >>self.file, "<td><table class=\"qualifiers\">" - self.print_qualifiers(p.qualifiers.values()) - print >>self.file, "</table></td>" - print >>self.file, "</tr>" - print >>self.file, "</table>" - - def print_qualifiers(self, qualifiers): - """ - Print contenf of table of qualifiers. - Only Deprecated, Description, Values and ValueMap are recognized. - """ - deprecated = "" - for q in sorted(qualifiers): - if q.name == "Deprecated": - deprecated = "<div class=\"deprecated\">DEPRECATED</div> " - if q.name == "Description": - print >>self.file, "<tr><td class=\"qualifiers\" colspan=\"2\">%s%s</td></tr>" % (deprecated, self.escape(q.value)) - if q.name == "Values": - print >>self.file, "<tr><td class=\"qualifiers\"><b>Values</b></td> <td class=\"qualifiers\"><table><tr><td class=\"qualifiers\">%s</td></tr></table></td></tr>" % ("</td></tr><tr><td class=\"qualifiers\">".join(q.value)) - if q.name == "ValueMap": - print >>self.file, "<tr><td class=\"qualifiers\"><b>ValueMap</b></td> <td class=\"qualifiers\"><table><tr><td class=\"qualifiers\">%s</td></tr></table></td></tr>" % ("</td></tr><tr><td class=\"qualifiers\">".join(q.value)) - - def compare_properties(self, p1, p2): - """ - Compare two properties if they should printed in Inherited properties. - Only Name, Description and Implemented are checked. - Returns False, if the property should be printed in Local. - """ - if p1.name != p2.name: - return False - d1 = p1.qualifiers.get("Description", None) - d2 = p2.qualifiers.get("Description", None) - if d1.value != d2.value: - return False - i1 = p1.qualifiers.get("Implemented", None) - i2 = p2.qualifiers.get("Implemented", None) - if i1 and i1.value and not (i2 and i2.value): - return False - return True - - def print_class(self, c, display_local = True, box_only = False): - """ - Print one class, inc. header. - """ - parent = None - if c.superclass: - parent = self.load_class(c.superclass) - - if c.superclass: - # draw arrow to parent - print >>self.file, "%s -down-|> %s" % (c.classname, c.superclass) - - if box_only: - self.file.write("class %s\n\n" % c.classname) - return - - local_props = [] - for name in sorted(c.properties.keys()): - if parent and parent.properties.has_key(name): - if not self.compare_properties(c.properties[name], parent.properties[name]): - # the property was overridden - local_props.append(c.properties[name]) - else: - local_props.append(c.properties[name]) - - local_methods = [] - for name in sorted(c.methods.keys()): - if parent and parent.methods.has_key(name): - if not self.compare_properties(c.methods[name], parent.methods[name]): - # the property was overridden - local_methods.append(c.methods[name]) - else: - local_methods.append(c.methods[name]) - self.file.write("class %s {\n" % c.classname) - - if display_local: - for prop in local_props: - self.file.write(" %s %s\n" % (self.display_type(prop), prop.name)) - - - if local_methods: - for m in local_methods: - self.file.write(" %s()\n" % (m.name)) - - self.file.write("}\n") - self.file.write("url of %s is [[%s.html]]\n" % (c.classname, c.classname)) - - def add_class(self, classname): - self.classes.add(classname) - - def export(self, shrink, noassoc = False): - """ - Print all classes and their parents. - """ - print >>self.file, "@startuml" - while self.classes: - c = self.classes.pop() - - cl = self.load_class(c) - if noassoc and cl.qualifiers.get("Association", False): - continue - - if shrink and shrink.match(c): - - self.print_class(cl, box_only = True) - else: - self.print_class(cl) - - print >>self.file, "@enduml" - -description = """ -Generate UML image for given classes. The tool connects to specified CIMOM -and reads class definition from there. Each class specified on command line -will be drawn as one box, containing locally defined or re-defined properties -and methods. Inheritance will be shown as arrow between a parent class and a -subclass. - -The generated file can be coverted to a picture by PlantUML tool. -""" - -parser = optparse.OptionParser(usage="usage: %prog [options] classname [classname ...]", description=description) -parser.add_option('-u', '--url', action='store', dest='addr', default='https://localhost:5989', help='URL of CIM server, default: https://localhost:5989') -parser.add_option('-U', '--user', action='store', dest='user', default=None, help='CIM user name') -parser.add_option('-s', '--shrink', action='store', dest='shrink', default=None, help='Regular expression pattern of CIM classes, which will be drawn only as boxes, without properties.') -parser.add_option('-A', '--no-associations', action='store_true', dest='noassoc', default=False, help='Skip association classes.') -parser.add_option('-P', '--password', action='store', dest='password', default=None, help='CIM password') -(options, args) = parser.parse_args() - -sys.stdout.softspace=0 - -shrink = None -if options.shrink: - shrink = re.compile(options.shrink) - -cliconn = pywbem.WBEMConnection(options.addr, (options.user, options.password)) -exporter = UmlExporter(cliconn) -for c in args: - exporter.add_class(c) -exporter.export(shrink = shrink, noassoc = options.noassoc) - diff --git a/tools/class2rst.py b/tools/openlmi-doc-class2rst index 12342f6..34d847d 100755..100644 --- a/tools/class2rst.py +++ b/tools/openlmi-doc-class2rst @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved. +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,24 +17,24 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Authors: Jan Safranek <jsafrane@redhat.com> +# Radek Novacek <rnovacek@redhat.com> # -import os; -import pywbem; +import os +import konkretmof import optparse import re import cgi import sys class HtmlExporter(object): - - def __init__(self, cliconn): + + def __init__(self): self.classcache = {} - self.cliconn = cliconn # original list of classes self.classes = [] - # set of all classes to print, i.e. incl. all parents - self.queue = set() + # list of all classes to print, i.e. incl. all parents + self.queue = [] self.file = None def link(self, classname, attributename = ""): @@ -42,23 +42,15 @@ class HtmlExporter(object): link = classname + "-" + attributename else: link = classname - link = link.replace("_","-") + link = str(link).replace("_","-") return link - + def print_escaped(self, string, prefix = ""): # replace \n with prefixes - lines = string.split("\\n") + lines = string.split("\n") for line in lines: print >>self.file, prefix + line print >>self.file, "" - - def load_class(self, classname): - if classname in self.classcache: - return self.classcache[classname] - - c = self.cliconn.GetClass(classname) - self.classcache[classname] = c - return c def source_class(self, c, param): """ @@ -67,21 +59,20 @@ class HtmlExporter(object): """ while True: - - if isinstance(param, pywbem.CIMMethod): - if not c.methods.has_key(param.name): + if isinstance(param, konkretmof.MOF_Method_Decl): + if not c.methods().has_key(param.name): break else: - if not c.properties.has_key(param.name): + if not c.properties().has_key(param.name): break - if not c.superclass: + if not c.super_class: # we're at the top class - return c.classname - + return c.name + parent = c - c = self.load_class(c.superclass) - return parent.classname + c = c.super_class + return parent.name def display_type(self, param): """ @@ -89,12 +80,17 @@ class HtmlExporter(object): It adds [] if it's array and class name of referenced classes. """ url = False - if param.reference_class: - ptype = param.reference_class + if isinstance(param, konkretmof.MOF_Reference_Decl): + ptype = param.class_name + url = True + elif isinstance(param, konkretmof.MOF_Parameter) and (param.data_type == konkretmof.TOK_REF): + ptype = param.ref_name + self.add_class(param.ref_name) url = True else: - ptype = param.type - if param.is_array: + ptype = param.type_name() + + if isinstance(param, (konkretmof.MOF_Property_Decl, konkretmof.MOF_Parameter)) and param.array_index != 0: ptype = ptype + "[]" if url: ptype = ":ref:`%s <%s>`" % (ptype, self.link(ptype)) @@ -111,33 +107,34 @@ class HtmlExporter(object): print >>self.file, "*None*" return - for p in params.values(): + for p in params: param_indent = " "*8 direction = set() - if p.qualifiers.has_key("Out"): + if p.qualifiers.has_key("out"): direction.add("*OUT*") - if p.qualifiers.has_key("In"): - if p.qualifiers["In"].value: + if p.qualifiers.has_key("in"): + if p.qualifiers.get("in").params.value(): direction.add("*IN*") if not direction: direction.add("*IN*") direction = ", ".join(sorted(direction)) print >>self.file, param_indent + "%s %s **%s**" % (direction, self.display_type(p), p.name) - self.print_qualifiers(p.qualifiers.values(), param_indent+ " "*4) + self.print_qualifiers(p.qualifiers, param_indent+ " "*4) print >>self.file, param_indent print >>self.file, function_indent def print_prototype(self, method): """ Print function prototype """ - self.file.write("``" + method.return_type + "`` ") + self.file.write("``" + method.type_name() + "`` ") self.file.write("**" + method.name + "** (") params = [] - for p in method.parameters.values(): - params.append((self.display_type(p) + " " + p.name)) + if method.parameters: + for p in method.parameters: + params.append((self.display_type(p) + " " + p.name)) self.file.write(", ".join(params)) - self.file.write("\n") - + self.file.write(")\n") + def print_table(self, col1, col2, indent): """ Print table with two columns. @@ -161,151 +158,160 @@ class HtmlExporter(object): values = None maps = None for q in sorted(qualifiers): - if q.name == "Deprecated": + if not q: + continue + qname = q.name.lower() + if qname == "deprecated": deprecated = "**Deprecated!** " - if q.name == "Description": + if qname == "description": if deprecated: print >>self.file, indent + deprecated - self.print_escaped(q.value, indent) - if q.name == "Values": - values = q.value - if q.name == "ValueMap": - maps = q.value + self.print_escaped(q.params.value(), indent) + if qname == "values": + values = [] + for p in q.params: + values.append(p.value()) + if qname == "valuemap": + maps = [] + for p in q.params: + maps.append(p.value()) print >>self.file, indent if maps and values: self.print_table(maps, values, indent) print >>self.file, indent - def compare_properties(self, p1, p2): - """ - Compare two properties if they should printed in Inherited properties. - Only Name, Description and Implemented are checked. - Returns False, if the property should be printed in Local. - """ - if p1.name != p2.name: - return False - d1 = p1.qualifiers.get("Description", None) - d2 = p2.qualifiers.get("Description", None) - if d1.value != d2.value: - return False - i1 = p1.qualifiers.get("Implemented", None) - i2 = p2.qualifiers.get("Implemented", None) - if i1 and i1.value and not (i2 and i2.value): - return False - return True - - def print_keys(self, c): + def print_keys(self, class_hiearchy): print >>self.file, "Key properties" print >>self.file, "^^^^^^^^^^^^^^" print >>self.file, "" - for prop in c.properties.values(): - if prop.qualifiers.has_key('Key'): - src = self.source_class(c, prop) - link = self.link(src, prop.name) - print >>self.file, "| :ref:`%s <%s>`" % (prop.name, link) + for cls in class_hiearchy: + for prop in cls.properties().values(): + if prop.qualifiers.has_key('key'): + src = self.source_class(cls, prop) + link = self.link(src, prop.name) + print >>self.file, "| :ref:`%s <%s>`" % (prop.name, link) print >>self.file, "" def print_class(self, c): """ Print one class, inc. header. """ + # We want to print following qualifiers for properties/methods + known_qualifiers = ("deprecated", "description", "values", "valuemap") parent = None - if c.superclass: - parent = self.load_class(c.superclass) - print >>self.file, "Subclass of :ref:`%s <%s>`" % (c.superclass, self.link(c.superclass)) + class_hiearchy = [c] + cc = c + while cc.super_class: + cc = cc.super_class + class_hiearchy.append(cc) + + if c.super_class: + parent = c.super_class + print >>self.file, "Subclass of :ref:`%s <%s>`" % (c.super_class_name, self.link(c.super_class_name)) print >>self.file, "" - description = c.qualifiers.get("Description", None) - if not description: - description = parent.qualifiers.get("Description", None) - if description: - self.print_escaped(description.value, "") + for cls in class_hiearchy: + description = cls.qualifiers.get("description") + if description: + self.print_escaped(description.params.value(), "") + break print >>self.file, "" - self.print_keys(c) - - local_props = [] - inherited_props = [] - for name in sorted(c.properties.keys()): - if parent and parent.properties.has_key(name): - inherited_props.append(c.properties[name]) - if not self.compare_properties(c.properties[name], parent.properties[name]): - # the property was overridden - local_props.append(c.properties[name]) - else: - local_props.append(c.properties[name]) - - local_methods = [] - inherited_methods = [] - for name in sorted(c.methods.keys()): - if parent and parent.methods.has_key(name): - inherited_methods.append(c.methods[name]) - if not self.compare_properties(c.methods[name], parent.methods[name]): - # the property was overridden - local_methods.append(c.methods[name]) - else: - local_methods.append(c.methods[name]) + self.print_keys(class_hiearchy) + + # Create dictionaries with properties and methods of given class and fill them + properties = {} + methods = {} + for cls in class_hiearchy: + for prop in cls.properties().values(): + if prop.name not in properties.keys(): + properties[prop.name] = prop + prop.cls = cls + prop.is_local = cls == c + prop.known_qualifiers = {} + for q in known_qualifiers: + prop.known_qualifiers[q] = prop.qualifiers.get(q) + else: + inherited_prop = properties[prop.name] + for q in known_qualifiers: + if not inherited_prop.known_qualifiers[q]: + inherited_prop.known_qualifiers[q] = prop.qualifiers.get(q) + for meth in cls.methods().values(): + if meth.name not in methods.keys(): + methods[meth.name] = meth + meth.cls = cls + meth.is_local = cls.name == c.name + meth.known_qualifiers = {} + for q in known_qualifiers: + meth.known_qualifiers[q] = meth.qualifiers.get(q) + else: + inherited_meth = methods[meth.name] + for q in known_qualifiers: + if not inherited_meth.known_qualifiers[q]: + inherited_meth.known_qualifiers[q] = meth.qualifiers.get(q) print >>self.file, "Local properties" print >>self.file, "^^^^^^^^^^^^^^^^" print >>self.file, "" - if local_props: - for prop in local_props: - prop_link = self.link(c.classname, prop.name) + present = False + for prop in properties.values(): + if prop.is_local: + present = True + prop_link = self.link(c.name, prop.name) print >>self.file, ".. _%s:" % (prop_link) print >>self.file, "" print >>self.file, "%s **%s**" % (self.display_type(prop), prop.name) print >>self.file, "" - self.print_qualifiers(prop.qualifiers.values(), " "*4) - print >>self.file, "" - else: + self.print_qualifiers(prop.known_qualifiers.values(), " "*4) + if not present: print >>self.file, "*None*" - print >>self.file, "" + print >>self.file, "" print >>self.file, "Local methods" print >>self.file, "^^^^^^^^^^^^^" print >>self.file, "" - if local_methods: - for m in local_methods: - link = self.link(c.classname, m.name) + present = False + for m in methods.values(): + if m.is_local: + present = True + link = self.link(c.name, m.name) print >>self.file, " .. _%s:" % (link) print >>self.file, "" self.print_prototype(m) print >>self.file, "" - self.print_qualifiers(m.qualifiers.values(), " "*4) + self.print_qualifiers(m.known_qualifiers.values(), " "*4) print >>self.file, " **Parameters**" print >>self.file, " " self.print_parameters(m.parameters) - print >>self.file, " " - else: + if not present: print >>self.file, "*None*" - print >>self.file, "" + print >>self.file, "" print >>self.file, "Inherited properties" print >>self.file, "^^^^^^^^^^^^^^^^^^^^" print >>self.file, "" - if inherited_props: - for p in inherited_props: - src = self.source_class(c, p) - link = self.link(src, p.name) - print >>self.file, "| %s :ref:`%s <%s>`" % (self.display_type(p), p.name, link) - print >>self.file, "" - else: + present = False + for prop in properties.values(): + if not prop.is_local: + present = True + link = self.link(prop.cls.name, prop.name) + print >>self.file, "| %s :ref:`%s <%s>`" % (self.display_type(prop), prop.name, link) + if not present: print >>self.file, "*None*" - print >>self.file, "" + print >>self.file, "" print >>self.file, "Inherited methods" print >>self.file, "^^^^^^^^^^^^^^^^^" print >>self.file, "" - if inherited_methods: - for m in inherited_methods: - src = self.source_class(c, m) - link = self.link(src, m.name) + present = False + for m in methods.values(): + if not m.is_local: + present = True + link = self.link(m.cls.name, m.name) print >>self.file, "| :ref:`%s <%s>`" % (m.name, link) - print >>self.file, "" - else: + if not present: print >>self.file, "*None*" - print >>self.file, "" + print >>self.file, "" def print_file(self, filename): @@ -315,46 +321,44 @@ class HtmlExporter(object): f.close() def print_page(self, c, header, footer): - print "exporting ", c.classname - self.file = open(c.classname + ".rst", "w") - print >>self.file, ".. _%s:" % self.link(c.classname) + print "exporting ", c.name + self.file = open(c.name + ".rst", "w") + print >>self.file, ".. _%s:" % self.link(c.name) print >>self.file, "" - print >>self.file, c.classname - print >>self.file, "-"*len(c.classname) + print >>self.file, c.name + print >>self.file, "-"*len(c.name) print >>self.file, "" -# if header: -# self.print_file(header) + if header: + self.print_file(header) self.print_class(c) -# if footer: -# self.print_file(footer) + if footer: + self.print_file(footer) self.file.close() - + def add_class(self, classname): + if classname in self.classes: + return + c = konkretmof.MOF_Class_Decl.list.fget().find(classname) + if not c: + print 'No such class "%s"' % classname + return self.classes.append(classname) - print "adding", classname - self.queue.add(classname) - # add all parents - c = self.load_class(classname) - parentname = c.superclass - while parentname: - if parentname in self.queue: - break - print "adding", parentname - self.queue.add(parentname) - c = self.load_class(parentname) - parentname = c.superclass + print "adding", c.name + self.queue.append(c) + if c.super_class: + self.add_class(c.super_class_name) def export(self, header=None, footer=None): """ Print everything, i.e. index.rst + all classes + all their parents. """ # remove duplicate classes from the queue (and sort them) - self.queue = sorted(self.queue) + self.queue.sort(key=lambda c:c.name) self.print_tree_rst(header, footer) self.print_index_rst(header, footer) while self.queue: c = self.queue.pop() - self.print_page(self.load_class(c), header, footer) + self.print_page(c, header, footer) def _do_print_tree(self, name, subtree, level): nbsp=" |nbsp| " @@ -378,24 +382,23 @@ class HtmlExporter(object): # hash classname -> nr. of its parents parents = {} # initialize the hash - for cname in self.queue: - subclasses[cname] = {} - parents[cname] = 0 + for c in self.queue: + subclasses[c.name] = {} + parents[c.name] = 0 # fill the hash - for cname in self.queue: - c = self.load_class(cname) - if c.superclass: - subclasses[c.superclass][cname] = subclasses[cname] - parents[cname] += 1 + for c in self.queue: + if c.super_class: + subclasses[c.super_class_name][c.name] = subclasses[c.name] + parents[c.name] += 1 print >>self.file, ".. |nbsp| unicode:: 0xA0" print >>self.file, " :trim:" print >>self.file, "" # print all top classes - for cname in self.queue: - if parents[cname] == 0: - self._do_print_tree(cname, subclasses[cname], 0) + for c in self.queue: + if parents[c.name] == 0: + self._do_print_tree(c.name, subclasses[c.name], 0) def print_index(self): """ @@ -406,8 +409,8 @@ class HtmlExporter(object): print >>self.file, " :maxdepth: 1" print >>self.file, "" # print all top classes - for cname in self.queue: - print >>self.file, " " + cname + for c in self.queue: + print >>self.file, " " + c.name def print_tree_rst(self, header, footer): @@ -431,8 +434,7 @@ class HtmlExporter(object): self.file.close() description = """ -Generate RST documentation for given CIM classes. The tool connects to specified CIMOM -and reads class definition from there. It generates separate RST file for each class +Generate RST documentation from given MOF files. It generates separate RST file for each class specified on command line and for all it's parents. The tool also generates tree.rst with inheritance tree and index.rst with table of content. @@ -444,17 +446,26 @@ Use Sphinx to generate html documentation from the RST files. """ parser = optparse.OptionParser(usage="usage: %prog [options] classname [classname ...]", description=description) -parser.add_option('-u', '--url', action='store', dest='addr', default='https://localhost:5989', help='URL of CIM server, default: https://localhost:5989') -parser.add_option('-U', '--user', action='store', dest='user', default=None, help='CIM user name') -parser.add_option('-P', '--password', action='store', dest='password', default=None, help='CIM password') +parser.add_option('-M', '--mof', action='append', dest='mof', default=[], help='MOF files to generate documentation from, can be used multiple times') +parser.add_option('-S', '--schema', action='append', dest='schema', default=[], help='MOF files to scan for dependencies, can be used multiple times') parser.add_option('-H', '--header', action='store', dest='header', default=None, help='File with HTML page header') parser.add_option('-F', '--footer', action='store', dest='footer', default=None, help='File with HTML page footer') (options, args) = parser.parse_args() -cliconn = pywbem.WBEMConnection(options.addr, (options.user, options.password)) -exporter = HtmlExporter(cliconn) -for c in args: - exporter.add_class(c) -exporter.export(options.header, options.footer) +exporter = HtmlExporter() + +for mof in options.schema: + konkretmof.MOF_add_include_path(os.path.dirname(mof)) + konkretmof.MOF_parse_file(mof) + +for mof in options.mof: + konkretmof.MOF_parse_file(mof) +for cls in konkretmof.MOF_Class_Decl.list.fget(): + if cls.file_name in options.mof: + exporter.add_class(cls.name) +for cls in args: + exporter.add_class(cls) + +exporter.export(options.header, options.footer) diff --git a/tools/openlmi-doc-class2uml b/tools/openlmi-doc-class2uml new file mode 100644 index 0000000..840ecfd --- /dev/null +++ b/tools/openlmi-doc-class2uml @@ -0,0 +1,143 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# +# This library 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. +# +# This library 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 this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Jan Safranek <jsafrane@redhat.com> +# Radek Novacek <rnovacek@redhat.com> +# + +import os +import konkretmof +import optparse +import re +import cgi +import sys + +class UmlExporter(object): + + def __init__(self): + self.classcache = {} + # original list of classes + self.classes = set() + self.file = sys.stdout + + def get_class(self, cls): + c = konkretmof.MOF_Class_Decl.list.fget().find(cls) + if c is None: + raise Exception("No such class: %s" % cls) + return c + + def display_type(self, param): + """ + Return displayable type of given parameter. + It adds [] if it's array and class name of referenced classes. + """ + if isinstance(param, konkretmof.MOF_Reference_Decl): + ptype = param.class_name + elif isinstance(param, konkretmof.MOF_Parameter) and (param.data_type == konkretmof.TOK_REF): + ptype = param.ref_name + else: + ptype = param.type_name() + + if isinstance(param, (konkretmof.MOF_Property_Decl, konkretmof.MOF_Parameter)) and param.array_index != 0: + ptype = ptype + "[]" + return ptype + + def print_class(self, c, display_local = True, box_only = False): + """ + Print one class, inc. header. + """ + parent = None + + if c.super_class: + # draw arrow to parent + print >>self.file, "%s -down-|> %s" % (c.name, c.super_class.name) + + if box_only: + self.file.write("class %s\n\n" % c.name) + return + + self.file.write("class %s {\n" % c.name) + + if display_local: + for prop in sorted(c.properties().values(), key=lambda x: x.name): + self.file.write(" %s %s\n" % (self.display_type(prop), prop.name)) + + + for m in sorted(c.methods().values(), key=lambda x: x.name): + self.file.write(" %s()\n" % (m.name)) + + self.file.write("}\n") + self.file.write("url of %s is [[%s.html]]\n" % (c.name, c.name)) + + def add_class(self, classname): + self.classes.add(classname) + + def export(self, shrink, noassoc = False): + """ + Print all classes and their parents. + """ + print >>self.file, "@startuml" + while self.classes: + c = self.classes.pop() + + cl = self.get_class(c) + if noassoc and cl.qualifiers.get("Association", False): + continue + + if shrink and shrink.match(c): + self.print_class(cl, box_only = True) + else: + self.print_class(cl) + + print >>self.file, "@enduml" + +description = """ +Generate UML image for given classes. Each class specified on command line +will be drawn as one box, containing locally defined or re-defined properties +and methods. Inheritance will be shown as arrow between a parent class and a +subclass. + +The generated file can be coverted to a picture by PlantUML tool. +""" + +parser = optparse.OptionParser(usage="usage: %prog [options] classname [classname ...]", description=description) +parser.add_option('-M', '--mof', action='append', dest='mof', default=[], help='MOF files to generate documentation from, can be used multiple times') +parser.add_option('-S', '--schema', action='append', dest='schema', default=[], help='MOF files to scan for dependencies, can be used multiple times') +parser.add_option('-s', '--shrink', action='store', dest='shrink', default=None, help='Regular expression pattern of CIM classes, which will be drawn only as boxes, without properties.') +parser.add_option('-A', '--no-associations', action='store_true', dest='noassoc', default=False, help='Skip association classes.') +(options, args) = parser.parse_args() + +sys.stdout.softspace=0 + +shrink = None +if options.shrink: + shrink = re.compile(options.shrink) + +for mof in options.schema: + konkretmof.MOF_add_include_path(os.path.dirname(mof)) + konkretmof.MOF_parse_file(mof) + +for mof in options.mof: + konkretmof.MOF_parse_file(mof) + +exporter = UmlExporter() + +for c in args: + exporter.add_class(c) + +exporter.export(shrink = shrink, noassoc = options.noassoc) |