From b5e14dcdb73d3b70131b9a602efd209488922a4e Mon Sep 17 00:00:00 2001 From: Radek Novacek Date: Mon, 18 Mar 2013 09:52:42 +0100 Subject: 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. --- tools/CMakeLists.txt | 3 + tools/class2rst.py | 460 ------------------------------------------ tools/class2uml.py | 219 -------------------- tools/openlmi-doc-class2rst | 471 ++++++++++++++++++++++++++++++++++++++++++++ tools/openlmi-doc-class2uml | 143 ++++++++++++++ 5 files changed, 617 insertions(+), 679 deletions(-) create mode 100644 tools/CMakeLists.txt delete mode 100755 tools/class2rst.py delete mode 100755 tools/class2uml.py create mode 100644 tools/openlmi-doc-class2rst create mode 100644 tools/openlmi-doc-class2uml (limited to 'tools') 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/class2rst.py b/tools/class2rst.py deleted file mode 100755 index 12342f6..0000000 --- a/tools/class2rst.py +++ /dev/null @@ -1,460 +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 -# - -import os; -import pywbem; -import optparse -import re -import cgi -import sys - -class HtmlExporter(object): - - def __init__(self, cliconn): - 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() - self.file = None - - def link(self, classname, attributename = ""): - if attributename: - link = classname + "-" + attributename - else: - link = classname - link = link.replace("_","-") - return link - - def print_escaped(self, string, prefix = ""): - # replace \n with prefixes - 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): - """ - Find a class where a CIMMethod or CIMProperty is first defined. - Start at class c and inspect all parents. - """ - - while True: - - if isinstance(param, pywbem.CIMMethod): - if not c.methods.has_key(param.name): - break - else: - if not c.properties.has_key(param.name): - break - - if not c.superclass: - # we're at the top class - return c.classname - - parent = c - c = self.load_class(c.superclass) - return parent.classname - - def display_type(self, param): - """ - Return displayable type of given parameter. - It adds [] if it's array and class name of referenced classes. - """ - url = False - if param.reference_class: - ptype = param.reference_class - url = True - else: - ptype = param.type - if param.is_array: - ptype = ptype + "[]" - if url: - ptype = ":ref:`%s <%s>`" % (ptype, self.link(ptype)) - else: - ptype = "``" + ptype + "``" - return ptype - - def print_parameters(self, params): - """ - Print table of method parametes. - """ - function_indent = " "*4 - if not params: - print >>self.file, "*None*" - return - - for p in params.values(): - param_indent = " "*8 - direction = set() - if p.qualifiers.has_key("Out"): - direction.add("*OUT*") - if p.qualifiers.has_key("In"): - if p.qualifiers["In"].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) - 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.name + "** (") - params = [] - for p in method.parameters.values(): - params.append((self.display_type(p) + " " + p.name)) - self.file.write(", ".join(params)) - self.file.write("\n") - - def print_table(self, col1, col2, indent): - """ - Print table with two columns. - """ - len1 = max(max(map(len, col1)), len("ValueMap")) - len2 = max(max(map(len, col2)), len("Values")) - separator = "="*len1 + " " + "="*len2 - print >>self.file, indent + separator - print >>self.file, indent + format("ValueMap", str(len1)) + " " + format("Values", str(len2)) - print >>self.file, indent + separator - for i in range(len(col1)): - print >>self.file, indent + format(col1[i], str(len1)) + " " + format(col2[i], str(len2)) - print >>self.file, indent + separator - - def print_qualifiers(self, qualifiers, indent): - """ - Print content of table of qualifiers. - Only Deprecated, Description, Values and ValueMap are recognized. - """ - deprecated = "" - values = None - maps = None - for q in sorted(qualifiers): - if q.name == "Deprecated": - deprecated = "**Deprecated!** " - if q.name == "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 - 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): - 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) - print >>self.file, "" - - def print_class(self, c): - """ - Print one class, inc. header. - """ - 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)) - 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, "") - 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]) - - 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) - 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: - print >>self.file, "*None*" - 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) - print >>self.file, " .. _%s:" % (link) - print >>self.file, "" - self.print_prototype(m) - print >>self.file, "" - self.print_qualifiers(m.qualifiers.values(), " "*4) - print >>self.file, " **Parameters**" - print >>self.file, " " - self.print_parameters(m.parameters) - print >>self.file, " " - else: - print >>self.file, "*None*" - 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: - print >>self.file, "*None*" - 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) - print >>self.file, "| :ref:`%s <%s>`" % (m.name, link) - print >>self.file, "" - else: - print >>self.file, "*None*" - print >>self.file, "" - - - def print_file(self, filename): - f = open(filename, "r") - data = f.read() - self.file.write(data) - 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 >>self.file, "" - print >>self.file, c.classname - print >>self.file, "-"*len(c.classname) - print >>self.file, "" -# if header: -# self.print_file(header) - self.print_class(c) -# if footer: -# self.print_file(footer) - self.file.close() - - def add_class(self, classname): - 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 - - 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.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) - - def _do_print_tree(self, name, subtree, level): - nbsp=" |nbsp| " - if level: - print >>self.file, "| " + ("\\|"+nbsp*7)*(level-1) + "\\|---", - else: - print >>self.file, "|", - if name in self.classes: - print >>self.file, "\*", - link = self.link(name) - print >>self.file, ":ref:`%s <%s>`" % (name, link) - for n in subtree.keys(): - self._do_print_tree(n, subtree[n], level+1) - - def print_tree(self): - """ - Create inheritance tree of classes. - """ - # hash classname -> list of hash of (direct) sublasses - subclasses = {} - # hash classname -> nr. of its parents - parents = {} - # initialize the hash - for cname in self.queue: - subclasses[cname] = {} - parents[cname] = 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 - - 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) - - def print_index(self): - """ - Create table of content. - """ - # hash classname -> list of hash of (direct) sublasses - print >>self.file, ".. toctree::" - print >>self.file, " :maxdepth: 1" - print >>self.file, "" - # print all top classes - for cname in self.queue: - print >>self.file, " " + cname - - - def print_tree_rst(self, header, footer): - print "exporting tree" - self.file = open("tree.rst", "w") - print >>self.file, "Inheritance tree" - print >>self.file, "================" - print >>self.file, "" - - self.print_tree() - self.file.close() - - def print_index_rst(self, header, footer): - print "exporting index" - self.file = open("index.rst", "w") - if header: - self.print_file(header) - self.print_index() - if footer: - self.print_file(footer) - 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 -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. - -The index.rst will optionally contain header and/or footer. Both must be valid -RST code snippets and will be inserted just at the beginning or at the end of the file. - -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('-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) - 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 -# - -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, "" - 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, "" - print >>self.file, "" % (direction, self.display_type(p), p.name) - print >>self.file, "" - print >>self.file, "" - print >>self.file, "
%s%s%s" - self.print_qualifiers(p.qualifiers.values()) - print >>self.file, "
" - - 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 = "
DEPRECATED
" - if q.name == "Description": - print >>self.file, "%s%s" % (deprecated, self.escape(q.value)) - if q.name == "Values": - print >>self.file, "Values
%s
" % ("".join(q.value)) - if q.name == "ValueMap": - print >>self.file, "ValueMap
%s
" % ("".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/openlmi-doc-class2rst b/tools/openlmi-doc-class2rst new file mode 100644 index 0000000..34d847d --- /dev/null +++ b/tools/openlmi-doc-class2rst @@ -0,0 +1,471 @@ +#!/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 +# Radek Novacek +# + +import os +import konkretmof +import optparse +import re +import cgi +import sys + +class HtmlExporter(object): + + def __init__(self): + self.classcache = {} + # original list of classes + self.classes = [] + # list of all classes to print, i.e. incl. all parents + self.queue = [] + self.file = None + + def link(self, classname, attributename = ""): + if attributename: + link = classname + "-" + attributename + else: + link = classname + link = str(link).replace("_","-") + return link + + def print_escaped(self, string, prefix = ""): + # replace \n with prefixes + lines = string.split("\n") + for line in lines: + print >>self.file, prefix + line + print >>self.file, "" + + def source_class(self, c, param): + """ + Find a class where a CIMMethod or CIMProperty is first defined. + Start at class c and inspect all parents. + """ + + while True: + 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): + break + + if not c.super_class: + # we're at the top class + return c.name + + parent = c + c = c.super_class + return parent.name + + def display_type(self, param): + """ + Return displayable type of given parameter. + It adds [] if it's array and class name of referenced classes. + """ + url = False + 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_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)) + else: + ptype = "``" + ptype + "``" + return ptype + + def print_parameters(self, params): + """ + Print table of method parametes. + """ + function_indent = " "*4 + if not params: + print >>self.file, "*None*" + return + + for p in params: + param_indent = " "*8 + direction = set() + if p.qualifiers.has_key("out"): + direction.add("*OUT*") + 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, 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.type_name() + "`` ") + self.file.write("**" + method.name + "** (") + params = [] + 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") + + def print_table(self, col1, col2, indent): + """ + Print table with two columns. + """ + len1 = max(max(map(len, col1)), len("ValueMap")) + len2 = max(max(map(len, col2)), len("Values")) + separator = "="*len1 + " " + "="*len2 + print >>self.file, indent + separator + print >>self.file, indent + format("ValueMap", str(len1)) + " " + format("Values", str(len2)) + print >>self.file, indent + separator + for i in range(len(col1)): + print >>self.file, indent + format(col1[i], str(len1)) + " " + format(col2[i], str(len2)) + print >>self.file, indent + separator + + def print_qualifiers(self, qualifiers, indent): + """ + Print content of table of qualifiers. + Only Deprecated, Description, Values and ValueMap are recognized. + """ + deprecated = "" + values = None + maps = None + for q in sorted(qualifiers): + if not q: + continue + qname = q.name.lower() + if qname == "deprecated": + deprecated = "**Deprecated!** " + if qname == "description": + if deprecated: + print >>self.file, indent + deprecated + 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 print_keys(self, class_hiearchy): + print >>self.file, "Key properties" + print >>self.file, "^^^^^^^^^^^^^^" + print >>self.file, "" + 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 + 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, "" + + 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(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, "" + 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.known_qualifiers.values(), " "*4) + if not present: + print >>self.file, "*None*" + print >>self.file, "" + + print >>self.file, "Local methods" + print >>self.file, "^^^^^^^^^^^^^" + print >>self.file, "" + 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.known_qualifiers.values(), " "*4) + print >>self.file, " **Parameters**" + print >>self.file, " " + self.print_parameters(m.parameters) + if not present: + print >>self.file, "*None*" + print >>self.file, "" + + print >>self.file, "Inherited properties" + print >>self.file, "^^^^^^^^^^^^^^^^^^^^" + print >>self.file, "" + 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, "Inherited methods" + print >>self.file, "^^^^^^^^^^^^^^^^^" + print >>self.file, "" + 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) + if not present: + print >>self.file, "*None*" + print >>self.file, "" + + + def print_file(self, filename): + f = open(filename, "r") + data = f.read() + self.file.write(data) + f.close() + + def print_page(self, c, header, footer): + 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.name + print >>self.file, "-"*len(c.name) + print >>self.file, "" + if header: + self.print_file(header) + self.print_class(c) + 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", 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.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(c, header, footer) + + def _do_print_tree(self, name, subtree, level): + nbsp=" |nbsp| " + if level: + print >>self.file, "| " + ("\\|"+nbsp*7)*(level-1) + "\\|---", + else: + print >>self.file, "|", + if name in self.classes: + print >>self.file, "\*", + link = self.link(name) + print >>self.file, ":ref:`%s <%s>`" % (name, link) + for n in subtree.keys(): + self._do_print_tree(n, subtree[n], level+1) + + def print_tree(self): + """ + Create inheritance tree of classes. + """ + # hash classname -> list of hash of (direct) sublasses + subclasses = {} + # hash classname -> nr. of its parents + parents = {} + # initialize the hash + for c in self.queue: + subclasses[c.name] = {} + parents[c.name] = 0 + + # fill the hash + 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 c in self.queue: + if parents[c.name] == 0: + self._do_print_tree(c.name, subclasses[c.name], 0) + + def print_index(self): + """ + Create table of content. + """ + # hash classname -> list of hash of (direct) sublasses + print >>self.file, ".. toctree::" + print >>self.file, " :maxdepth: 1" + print >>self.file, "" + # print all top classes + for c in self.queue: + print >>self.file, " " + c.name + + + def print_tree_rst(self, header, footer): + print "exporting tree" + self.file = open("tree.rst", "w") + print >>self.file, "Inheritance tree" + print >>self.file, "================" + print >>self.file, "" + + self.print_tree() + self.file.close() + + def print_index_rst(self, header, footer): + print "exporting index" + self.file = open("index.rst", "w") + if header: + self.print_file(header) + self.print_index() + if footer: + self.print_file(footer) + self.file.close() + +description = """ +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. + +The index.rst will optionally contain header and/or footer. Both must be valid +RST code snippets and will be inserted just at the beginning or at the end of the file. + +Use Sphinx to generate html documentation from the RST files. +""" + +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('-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() + +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 +# Radek Novacek +# + +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) -- cgit