doxy.py is a translator from Doxygen xml output into the restructuredText format.
authorZhanna Tsitkov <tsitkova@mit.edu>
Thu, 23 Jun 2011 19:03:34 +0000 (19:03 +0000)
committerZhanna Tsitkov <tsitkova@mit.edu>
Thu, 23 Jun 2011 19:03:34 +0000 (19:03 +0000)
The generated output may be used in Sphinx documentation project for the complete API and data type reference.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24983 dc483132-0cff-0310-8789-dd5450dbe970

doc/rst_tools/docmodel.py [new file with mode: 0644]
doc/rst_tools/doxy.py [new file with mode: 0644]
doc/rst_tools/doxybuilder_funcs.py [new file with mode: 0644]
doc/rst_tools/doxybuilder_types.py [new file with mode: 0644]
doc/rst_tools/func_document.tmpl [new file with mode: 0644]
doc/rst_tools/type_document.tmpl [new file with mode: 0644]

diff --git a/doc/rst_tools/docmodel.py b/doc/rst_tools/docmodel.py
new file mode 100644 (file)
index 0000000..6db5b1d
--- /dev/null
@@ -0,0 +1,222 @@
+'''
+Created on Nov 9, 2010
+
+@author: tsitkova
+'''
+import re
+
+from Cheetah.Template import Template
+
+class Attribute(object):
+    def __init__(self, **argkw):
+        self.name = argkw.get('name')
+        self.type = argkw.get('type')
+        self.typeId = argkw.get('typeId')
+        self.short_description = argkw.get('short_description')
+        self.long_description = argkw.get('long_description')
+        self.version = argkw.get('version')
+                
+    def __repr__(self):
+        result = list()
+        for (attr,value) in self.__dict__.iteritems():
+            result.append('%s=%s' % (attr,value))
+        return 'Attribute: %s' % ','.join(result)
+    
+
+class CompositeType():
+    def __init__(self, **argkw):
+        self.category = 'composite'
+        self.name = argkw.get('name')
+        self.Id = argkw.get('Id')
+        self.active = argkw.get('active', False)
+        self.version = argkw.get('version')
+        self.return_type = argkw.get('return_type')
+        self.short_description = argkw.get('short_description')
+        self.long_description = argkw.get('long_description')
+        self.friends = argkw.get('friends')
+        self.type = argkw.get('type')
+        self.attributes = self._setAttributes(argkw.get('attributes'))
+        
+    def __repr__(self):
+        result = list()
+        for (attr,value) in self.__dict__.iteritems():
+            if attr == 'attributes':
+                if value is not None:
+                    attributes = ['%s' % a for a in value]
+                    value = '\n  %s' % '\n  '.join(attributes)
+                
+            result.append('%s: %s' % (attr,value))        
+        result = '\n'.join(result)
+        
+        return result
+
+    def _setAttributes(self, attributes):
+        result = None
+        if attributes is not None:
+            result = list()
+            for a in attributes:
+                result.append(Attribute(**a))
+                
+        return result
+
+    def struct_reference(self, name):
+        result = re.sub(r'_', '-', name)
+        result = '_%s-struct' % result
+        
+        return result
+
+class Parameter(object):
+    def __init__(self, **argkw):
+        self.seqno = argkw.get('seqno')
+        self.name = argkw.get('name')
+        self.direction = argkw.get('direction')
+        self.type = argkw.get('type')
+        self.typeId = argkw.get('typeId')
+        self.description = argkw.get('description')
+        self.version = argkw.get('version')
+        
+    def __repr__(self):
+        content = (self.name,self.direction,self.seqno,self.type,self.typeId,self.description)
+        return 'Parameter: name=%s,direction=%s,seqno=%s,type=%s,typeId=%s,descr=%s' % content
+  
+class Function(object):
+    def __init__(self, **argkw):
+        self.category = 'function'
+        self.name = argkw.get('name')
+        self.Id = argkw.get('Id')
+        self.active = argkw.get('active', False)
+        self.version = argkw.get('version')
+        self.parameters = self._setParameters(argkw.get('parameters'))
+        self.return_type = argkw.get('return_type')
+        self.return_description = argkw.get('return_description')
+        self.retval_description = argkw.get('retval_description')
+        self.warn_description = argkw.get('warn_description')
+        self.sa_description = argkw.get('sa_description')
+        self.notes_description = argkw.get('notes_description')
+        self.short_description = argkw.get('short_description')
+        self.long_description = argkw.get('long_description')
+        self.deprecated_description = argkw.get('deprecated_description')
+        self.friends = argkw.get('friends')
+        
+    def _setParameters(self, parameters):
+        result = None
+        if parameters is not None:
+            result = list()
+            for p in parameters:
+                result.append(Parameter(**p))
+        
+        return result
+    
+    def getObjectRow(self):
+        result = [str(self.Id), 
+                  self.name,
+                  self.category]
+        
+        return ','.join(result)
+    
+    def getObjectDescriptionRow(self):
+        result = [self.Id, 
+                  self.active, 
+                  self.version, 
+                  self.short_description,
+                  self.long_description]
+
+        return ','.join(result)
+
+    def getParameterRows(self):
+        result = list()
+        for p in self.parameters:
+            p_row = [self.Id,
+                     p.name,
+                     p.seqno,
+                     p.type,
+                     p.typeId,
+                     p.description,
+                     p.version]
+            result.append(','.join(p_row))
+
+        return '\n'.join(result)
+
+    def __repr__(self):
+        lines = list()
+        lines.append('Category: %s' % self.category)
+        lines.append('Function name: %s' % self.name)
+        lines.append('Function Id: %s' % self.Id)
+        parameters = ['  %s' % p for p in self.parameters]
+        lines.append('Parameters:\n%s' % '\n'.join(parameters))
+        lines.append('Function return type: %s' % self.return_type)
+        lines.append('Function return type description:\n%s' % self.return_description)
+        lines.append('Function retval description:\n%s' % self.retval_description)
+        lines.append('Function short description:\n%s' % self.short_description)
+        lines.append('Function long description:\n%s' % self.long_description) 
+        lines.append('Warning description:\n%s' % self.warn_description)
+        lines.append('See also description:\n%s' % self.sa_description)
+        lines.append('NOTE description:\n%s' % self.notes_description) 
+        lines.append('Deprecated description:\n%s' % self.deprecated_description)
+        result = '\n'.join(lines)
+        
+        return result
+
+
+class DocModel(object):
+    def __init__(self, **argkw):
+        if len(argkw):
+            self.name = argkw['name']
+            if argkw['category'] == 'function':
+                self.category = 'function'
+                self.function = Function(**argkw)
+            elif argkw['category'] == 'composite':
+                self.category = 'composite'
+                self.composite = CompositeType(**argkw) 
+
+    def __repr__(self):
+        obj = getattr(self,self.category)
+        print type(obj)
+        return str(obj)
+                
+    def signature(self):
+        param_list = list()
+        for p in self.function.parameters:
+            if p.type is "... " :
+                param_list.append('%s %s' % (p.type,' '))
+            else:
+                param_list.append('%s %s' % (p.type, p.name))
+        param_list = ', '.join(param_list)
+        result = '%s %s(%s)' % (self.function.return_type,
+                                self.function.name, param_list)
+        
+        return result
+    
+    def save(self, path, template_path):
+        f = open(template_path, 'r')
+        t = Template(f.read(),self)
+        out = open(path, 'w')
+        out.write(str(t))
+        out.close()
+        f.close()
+
+
+class DocModelTest(DocModel):
+    def __init__(self):
+        doc_path = '../docutil/example.yml'
+        argkw = yaml.load(open(doc_path,'r'))
+        super(DocModelTest,self).__init__(**argkw)
+    
+    def run_tests(self):
+        self.test_save()
+
+    def test_print(self):
+        print 'testing'
+        print self
+        
+
+    def test_save(self):
+        template_path = '../docutil/function2edit.html'
+        
+        path = '/var/tsitkova/Sources/v10/trunk/documentation/test_doc.html'
+
+        self.save(path, template_path)       
+        
+if __name__ == '__main__':
+    tester = DocModelTest()
+    tester.run_tests()
diff --git a/doc/rst_tools/doxy.py b/doc/rst_tools/doxy.py
new file mode 100644 (file)
index 0000000..d343a49
--- /dev/null
@@ -0,0 +1,64 @@
+'''
+  Copyright 2011 by the Massachusetts
+  Institute of Technology.  All Rights Reserved.
+  Export of this software from the United States of America may
+  require a specific license from the United States Government.
+  It is the responsibility of any person or organization contemplating
+  export to obtain such a license before exporting.
+  WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+  distribute this software and its documentation for any purpose and
+  without fee is hereby granted, provided that the above copyright
+  notice appear in all copies and that both that copyright notice and
+  this permission notice appear in supporting documentation, and that
+  the name of M.I.T. not be used in advertising or publicity pertaining
+  to distribution of the software without specific, written prior
+  permission.  Furthermore if you modify this software you must label
+  your software as modified software and not distribute it in such a
+  fashion that it might be confused with the original M.I.T. software.
+  M.I.T. makes no representations about the suitability of
+  this software for any purpose.  It is provided "as is" without express
+  or implied warranty.
+'''
+import sys
+import os
+import re
+from optparse import OptionParser
+
+
+from doxybuilder_types import *
+from doxybuilder_funcs import *
+
+
+def processOptions():
+    usage = "\n\t\t%prog -t type -i in_dir -o out_dir"
+    description = "Description:\n\tProcess doxygen output for c-types and/or functions"
+    parser = OptionParser(usage=usage, description=description)
+
+    parser.add_option("-t", "--type",  type="string", dest="action_type", help="process typedef and/or function. Possible choices: typedef, func, all. Default: all.", default="all")
+    parser.add_option("-i", "--in",  type="string", dest="in_dir", help="input directory")
+    parser.add_option("-o", "--out",  type="string", dest= "out_dir", help="output directory. Note:  The subdirectory ./types will be created for typedef")
+
+    (options, args) = parser.parse_args()
+    action = options.action_type
+    in_dir = options.in_dir
+    out_dir = options.out_dir
+
+
+    if in_dir is None or out_dir is None:
+       parser.error("Input and output directories are required")
+    if action == "all" or action == "typedef":
+        tester = DoxyTypesTest(in_dir, out_dir)
+        tester.run_tests()
+
+    if action == "all" or action == "func" or action == "function":
+        tester = DoxyFuncsTest(in_dir, out_dir)
+        tester.run_tests()
+
+
+if __name__ == '__main__':
+    parser = processOptions()
+
+
diff --git a/doc/rst_tools/doxybuilder_funcs.py b/doc/rst_tools/doxybuilder_funcs.py
new file mode 100644 (file)
index 0000000..3f0b9c0
--- /dev/null
@@ -0,0 +1,565 @@
+'''
+  Copyright 2011 by the Massachusetts
+  Institute of Technology.  All Rights Reserved.
+  Export of this software from the United States of America may
+  require a specific license from the United States Government.
+  It is the responsibility of any person or organization contemplating
+  export to obtain such a license before exporting.
+  WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+  distribute this software and its documentation for any purpose and
+  without fee is hereby granted, provided that the above copyright
+  notice appear in all copies and that both that copyright notice and
+  this permission notice appear in supporting documentation, and that
+  the name of M.I.T. not be used in advertising or publicity pertaining
+  to distribution of the software without specific, written prior
+  permission.  Furthermore if you modify this software you must label
+  your software as modified software and not distribute it in such a
+  fashion that it might be confused with the original M.I.T. software.
+  M.I.T. makes no representations about the suitability of
+  this software for any purpose.  It is provided "as is" without express
+  or implied warranty.
+'''
+import sys
+import re
+
+from collections import defaultdict
+from xml.sax import make_parser 
+from xml.sax.handler import ContentHandler
+from docmodel import *
+
+class DocNode(object):
+    """
+    Represents the structure of xml node.
+    """
+    def __init__(self, name):
+        """
+        @param node: name - the name of a node.
+        @param attributes: a dictionary populated with attributes of a node
+        @param children: a dictionary with lists of children nodes. Nodes 
+            in lists are ordered as they appear in a document.
+        @param content: a content of xml node represented as a list of 
+            tuples [(type,value)] with type = ['char'|'element'].
+            If type is 'char' then the value is a character string otherwise 
+            it is a reference to a child node.
+        """
+        self.name = name
+        self.content = list()
+        self.attributes = dict()
+        self.children = defaultdict(list)
+        
+    def walk(self, decorators, sub_ws, stack=[]):
+        result = list()
+        decorator = decorators.get(self.name, decorators['default'])
+        stack.append(decorators['default'])
+        decorators['default'] = decorator
+
+        for (obj_type,obj) in self.content:
+            if obj_type == 'char':
+                if obj != '':
+                    result.append(obj)
+            else:
+                partial = obj.walk(decorators,1, stack)
+                if partial is not None:
+                    result.append(' %s ' % partial)
+        decorators['default'] = stack.pop()
+        result = decorator(self, ''.join(result))
+        if result is not None:
+            if sub_ws == 1:
+                result = re.sub(r'[ ]+', r' ', result)
+            else:
+                result = result.strip()
+
+        return result
+
+    def getContent(self):
+        decorators = {'default': lambda node,value: value}
+        result = self.walk(decorators, 1)
+        if len(result) == 0:
+            result = None
+        
+        return result
+
+    def __repr__(self):
+        result = ['Content: %s' % self.content]
+
+        for (key,value) in self.attributes.iteritems():
+            result.append('Attr: %s = %s' % (key,value))
+        for (key,value) in self.children.iteritems():
+            result.append('Child: %s,%i' % (key,len(value)))
+                          
+        return '\n'.join(result)
+
+class DoxyContenHandler(object, ContentHandler):
+    def __init__(self, builder):
+        self.builder = builder
+        self.counters = defaultdict(int)
+        self._nodes = None
+        self._current = None
+        
+    def startDocument(self):
+        pass
+    
+    def endDocument(self):
+        import sys
+        
+    def startElement(self, name, attrs):
+        if name == self.builder.toplevel:
+            self._nodes = []
+            
+        if name == 'memberdef':
+            kind = attrs.get('kind')
+            if kind is None:
+                raise ValueError('Kind is not defined')
+            self.counters[kind] += 1
+
+        if self._nodes is None:
+            return
+        
+        node = DocNode(name)
+        for (key,value) in attrs.items():
+            node.attributes[key] = value
+        if self._current is not None:
+            self._current.children[name].append(node)
+            self._nodes.append(self._current)
+        self._current = node
+            
+    def characters(self, content):
+            
+        if self._current is not None:
+            self._current.content.append(('char',content.strip()))
+                        
+    def endElement(self, name):
+        if name == self.builder.toplevel:
+            assert(len(self._nodes) == 0)
+            self._nodes = None
+            self.builder.document.append(self._current)
+            self._current = None
+        else:
+            if self._nodes is not None:
+                node = self._current
+                self._current = self._nodes.pop()
+                self._current.content.append(('element',node))
+
+
+class XML2AST(object):
+    """
+    Translates XML document into Abstract Syntax Tree like representation
+    The content of document is stored in self.document
+    """
+    def __init__(self, xmlpath, toplevel='doxygen'):
+        self.document = list()
+        self.toplevel = toplevel
+        self.parser = make_parser()
+        handler = DoxyContenHandler(self)
+        self.parser.setContentHandler(handler)
+        filename = 'krb5_8hin.xml'
+        filepath = '%s/%s' % (xmlpath,filename)
+        self.parser.parse(open(filepath,'r'))
+
+        
+class DoxyFuncs(XML2AST):
+    def __init__(self, path):
+        super(DoxyFuncs, self).__init__(path,toplevel='memberdef')
+        self.objects = list()
+
+    def run(self):
+        for node in self.document:
+            self.process(node)
+        print "\nnumber of functions processed ===> ",len(self.objects)
+
+    def process(self, node):
+        node_type = node.attributes['kind']
+        if node_type == 'function':
+            data = self._process_function_node(node)
+        else:
+            print 'not processing node: %s' % node_type
+            return
+        
+        self.objects.append(DocModel(**data))
+             
+    def save(self, templates, target_dir):
+        for obj in self.objects:
+            template_path = templates[obj.category]
+            outpath = '%s/%s.rst' % (target_dir,obj.name)
+            obj.save(outpath, template_path)
+
+
+    def _process_function_node(self, node):
+        f_name = node.children['name'][0].getContent()
+        print f_name
+        f_Id = node.attributes['id']
+        f_ret_type = self._process_type_node(node.children['type'][0])
+        f_brief = node.children['briefdescription'][0].getContent()
+        f_detailed = node.children['detaileddescription'][0]
+        detailed_description = self._process_description_node(f_detailed)
+        return_value_description = self._process_return_value_description(f_detailed)
+        retval_description = self._process_retval_description(f_detailed)
+        warning_description = self._process_warning_description(f_detailed)
+        seealso_description = self._process_seealso_description(f_detailed)
+        notes_description = self._process_notes_description(f_detailed)
+        deprecated_description = self._process_deprecated_description(f_detailed)
+        param_description_map = self.process_parameter_description(f_detailed)
+        f_definition = node.children['definition'][0].getContent()
+        f_argsstring = node.children['argsstring'][0].getContent()
+
+        function_descr = {'category': 'function',
+                          'name': f_name,
+                          'Id': f_Id,
+                          'return_type': f_ret_type[1],
+                          'return_description': return_value_description,
+                          'retval_description': retval_description,
+                          'sa_description': seealso_description,
+                          'warn_description': warning_description,
+                          'notes_description': notes_description,
+                          'short_description': f_brief,
+                          'long_description': detailed_description,
+                          'deprecated_description': deprecated_description,
+                          'parameters': list()}
+
+        parameters = function_descr['parameters']
+        for (i,p) in enumerate(node.children['param']):
+            type_node = p.children['type'][0]
+            p_type = self._process_type_node(type_node)            
+            if p_type[1].find('...') > -1 :
+                p_name = ''
+            else:
+                p_name = None
+            p_name_node = p.children.get('declname')
+            if p_name_node is not None:
+                p_name = p_name_node[0].getContent()            
+            (p_direction,p_descr) = param_description_map.get(p_name,(None,None))
+            
+            param_descr = {'seqno': i,
+                           'name': p_name,
+                           'direction': p_direction,
+                           'type': p_type[1],
+                           'typeId': p_type[0],
+                           'description': p_descr}
+            parameters.append(param_descr)
+        result = Function(**function_descr)
+        print >> self.tmp, result
+        
+        return function_descr
+
+    def _process_type_node(self, type_node):
+        """
+        Type node has form 
+            <type>type_string</type>
+        for build in types and
+            <type>
+              <ref refid='reference',kindref='member|compound'> 
+                  'type_name'
+              </ref></type>
+              postfix (ex. *, **m, etc.)
+            </type>
+        for user defined types.
+        """
+        type_ref_node = type_node.children.get('ref')
+        if type_ref_node is not None:
+            p_type_id = type_ref_node[0].attributes['refid']
+        else:
+            p_type_id = None
+        p_type = type_node.getContent()
+        # remove some macros
+        p_type = re.sub('KRB5_ATTR_DEPRECATED', '', p_type)
+        p_type = re.sub('KRB5_CALLCONV_C', '', p_type)
+        p_type = re.sub('KRB5_CALLCONV_WRONG', '', p_type)
+        p_type = re.sub('KRB5_CALLCONV', '', p_type)
+        p_type = p_type.strip()
+        
+        return (p_type_id, p_type)
+
+    def _process_description_node(self, node):
+        """
+        Description node is comprised of <para>...</para> sections
+        """
+        para = node.children.get('para')
+        result = list()
+        if para is not None:
+            decorators = {'default': self.paragraph_content_decorator}        
+            for e in para:
+                result.append(str(e.walk(decorators, 1)))
+                result.append('\n')
+        result = '\n'.join(result)
+
+        return result
+
+    def return_value_description_decorator(self, node, value):
+        if node.name == 'simplesect':
+            if node.attributes['kind'] == 'return':
+                cont = set()
+                cont = node.getContent()
+                return  value
+        else:
+            return None
+    def paragraph_content_decorator(self, node, value):
+        if node.name == 'para':
+            return value + '\n'
+        elif node.name == 'simplesect':
+            if node.attributes['kind'] == 'return':
+                return None
+        elif node.name == 'ref':
+            return ':c:func:' + '`' + value + '`'
+        elif node.name == 'emphasis':
+            return '*' + value + '*'
+        elif node.name == 'itemizedlist':
+            return '\n' + value
+        elif node.name == 'listitem':
+            return '\n\t - ' + value + '\n'
+        elif node.name == 'computeroutput':
+            return '**' + value + '**'
+        else:
+            return None
+
+    def parameter_name_decorator(self, node, value):
+        if node.name == 'parametername':
+            direction = node.attributes.get('direction')
+            if direction is not None:
+                value = '%s:%s' % (value,direction)
+            return value
+
+        elif node.name == 'parameterdescription':
+            return None
+        else:
+            return value
+        
+    def parameter_description_decorator(self, node, value):
+        if node.name == 'parameterdescription':
+            return value
+        elif node.name == 'parametername':
+            return None
+        else:
+            return value
+            
+    def process_parameter_description(self, node):
+        """
+        Parameter descriptions reside inside detailed description section.
+        """
+        para = node.children.get('para')
+        result = dict()
+        if para is not None:
+            for e in para:
+                
+                param_list = e.children.get('parameterlist')
+                if param_list is None:
+                    continue
+                param_items = param_list[0].children.get('parameteritem')
+                if param_items is None:
+                    continue
+                for it in param_items:
+                    decorators = {'default': self.parameter_name_decorator}        
+                    direction = None
+                    name = it.walk(decorators,0).split(':')
+                    if len(name) == 2:
+                        direction = name[1]
+                    
+                    decorators = {'default': self.parameter_description_decorator,
+                                  'para': self.paragraph_content_decorator}        
+                    description = it.walk(decorators, 0)                  
+                    result[name[0]] = (direction,description)
+        return result                    
+
+
+    def _process_return_value_description(self, node):
+        result = None
+        ret = list()
+
+        para = node.children.get('para')
+        if para is not None:
+            for p in para:
+                simplesect_list = p.children.get('simplesect')
+                if simplesect_list is None:
+                    continue
+                for it in simplesect_list:                    
+                    decorators = {'default': self.return_value_description_decorator,
+                                  'para': self.parameter_name_decorator}        
+                    result = it.walk(decorators, 1)
+                    if result is not None:
+                        ret.append(result)
+        return ret
+
+
+    def _process_retval_description(self, node):
+        """
+        retval descriptions reside inside detailed description section.
+        """
+        para = node.children.get('para')
+
+        result = None
+        ret = list()
+        if para is not None:
+
+            for e in para:
+                param_list = e.children.get('parameterlist')
+                if param_list is None:
+                    continue
+                for p in param_list:
+                    kind = p.attributes['kind']
+                    if kind == 'retval':
+
+                        param_items = p.children.get('parameteritem')
+                        if param_items is None:
+                            continue
+
+
+                        for it in param_items:
+                            param_descr = it.children.get('parameterdescription')
+                            if param_descr is not None:
+                                val = param_descr[0].children.get('para')
+
+                                if val is not None:
+                                    val_descr = val[0].getContent()
+
+                                else:
+                                    val_descr =''
+
+                            decorators = {'default': self.parameter_name_decorator}
+
+                            name = it.walk(decorators, 1).split(':')
+
+                            val = name[0]
+                            result = " %s  %s" % (val, val_descr)
+                            ret.append (result)
+        return ret
+        
+    def return_warning_decorator(self, node, value):
+        if node.name == 'simplesect':
+            if node.attributes['kind'] == 'warning':
+                return value
+        else:
+            return None
+
+    def _process_warning_description(self, node):
+        result = None
+        para = node.children.get('para')
+        if para is not None:
+            for p in para:
+                simplesect_list = p.children.get('simplesect')
+                if simplesect_list is None:
+                    continue
+                for it in simplesect_list:
+                    decorators = {'default': self.return_warning_decorator,
+                                  'para': self.paragraph_content_decorator}
+                    result = it.walk(decorators, 1)
+                    # Assuming that only one Warning per function
+                    if result is not None:
+                        return result
+        return result
+
+    def return_seealso_decorator(self, node, value):
+        if node.name == 'simplesect':
+            if node.attributes['kind'] == 'see':
+                return value
+        else:
+            return None
+
+    def _process_seealso_description(self, node):
+        result = None
+        para = node.children.get('para')
+        if para is not None:
+            for p in para:
+                simplesect_list = p.children.get('simplesect')
+                if simplesect_list is None:
+                    continue
+                for it in simplesect_list:
+                    decorators = {'default': self.return_seealso_decorator,
+                                  'para': self.paragraph_content_decorator}
+                    result = it.walk(decorators, 1)
+        return result
+
+    def return_notes_decorator(self, node, value):
+        if node.name == 'simplesect':
+            if node.attributes['kind'] == 'note':
+                return value
+        else:
+            return None
+
+    def _process_notes_description(self, node):
+        result = None
+        para = node.children.get('para')
+        if para is not None:
+            for p in para:
+                simplesect_list = p.children.get('simplesect')
+                if simplesect_list is None:
+                    continue
+                for it in simplesect_list:
+                    decorators = {'default': self.return_notes_decorator,
+                                  'para': self.paragraph_content_decorator}
+                    result = it.walk(decorators, 1)
+                    if result is not None:
+                        return result
+        return result
+
+    def return_deprecated_decorator(self, node, value):
+        if node.name == 'xrefsect':
+            if node.attributes['id'].find('deprecated_') > -1:
+                xreftitle = node.children.get('xreftitle')
+                if xreftitle[0] is not None:
+                    xrefdescr = node.children.get('xrefdescription')
+                    deprecated_descr = "DEPRECATED %s" % xrefdescr[0].getContent()
+                    return deprecated_descr
+        else:
+            return None
+
+    def _process_deprecated_description(self, node):
+        result = None
+        para = node.children.get('para')
+        if para is not None:
+            for p in para:
+                xrefsect_list = p.children.get('xrefsect')
+                if xrefsect_list is None:
+                    continue
+                for it in xrefsect_list:
+                    decorators = {'default': self.return_deprecated_decorator,
+                                  'para': self.paragraph_content_decorator}
+                    result = it.walk(decorators, 1)
+                    if result is not None:
+                        return result
+        return result
+
+    def break_into_lines(self, value, linelen=82):
+        breaks = range(0,len(value),linelen) + [len(value)]
+        result = list()
+        for (start,end) in zip(breaks[:-1],breaks[1:]):
+            result.append(value[start:end])
+        result = '\n'.join(result)
+        
+        return result
+        
+    def _save(self, table, path = None):
+        if path is None:
+            f = sys.stdout
+        else:
+            f = open(path, 'w')
+        for l in table:
+            f.write('%s\n' % ','.join(l))
+        if path is not None:
+            f.close()
+        
+
+    
+class DoxyFuncsTest(DoxyFuncs):
+    def __init__(self, xmlpath, rstpath):
+        super(DoxyFuncsTest,self).__init__(xmlpath)
+        self.target_dir = rstpath
+        outfile = '%s/%s' % (self.target_dir, 'out.txt')
+        self.tmp = open(outfile, 'w')
+
+    def run_tests(self):
+        self.test_save()
+    
+    def test_run(self):
+        self.run()
+        
+    def test_save(self):
+        self.run()
+        templates = {'function': 'func_document.tmpl'}
+        self.save(templates, self.target_dir)
+        
+if __name__ == '__main__':
+    tester = DoxyFuncsTest(xmlpath, rstpath)
+    tester.run_tests()
+        
diff --git a/doc/rst_tools/doxybuilder_types.py b/doc/rst_tools/doxybuilder_types.py
new file mode 100644 (file)
index 0000000..ae40ff7
--- /dev/null
@@ -0,0 +1,286 @@
+'''
+  Copyright 2011 by the Massachusetts
+  Institute of Technology.  All Rights Reserved.
+  Export of this software from the United States of America may
+  require a specific license from the United States Government.
+  It is the responsibility of any person or organization contemplating
+  export to obtain such a license before exporting.
+  WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+  distribute this software and its documentation for any purpose and
+  without fee is hereby granted, provided that the above copyright
+  notice appear in all copies and that both that copyright notice and
+  this permission notice appear in supporting documentation, and that
+  the name of M.I.T. not be used in advertising or publicity pertaining
+  to distribution of the software without specific, written prior
+  permission.  Furthermore if you modify this software you must label
+  your software as modified software and not distribute it in such a
+  fashion that it might be confused with the original M.I.T. software.
+  M.I.T. makes no representations about the suitability of
+  this software for any purpose.  It is provided "as is" without express
+  or implied warranty.
+'''
+import sys
+import os
+import re
+
+from lxml import etree
+
+from docmodel import *
+
+
+class DoxyTypes(object):
+    def __init__(self, xmlpath):
+        self.xmlpath = xmlpath
+        
+    def run_compound(self, filename, include=None):
+        path = '%s/%s' % (self.xmlpath,filename)    
+        tree = etree.parse(path)
+        root = tree.getroot()
+
+        brief_node = root.xpath('./compounddef/briefdescription')[0]        
+        brief_description = self._get_brief_description(brief_node)        
+        details_node = root.xpath('./compounddef/detaileddescription')[0]
+        detailed_description = self._get_detailed_description(details_node)
+        
+        fields = list()
+        for node in root.iterfind(".//memberdef[@kind]"):
+            data = {}
+            kind = node.attrib['kind']
+            if include is None or kind in include:
+                if kind == 'variable':
+                    data = self._process_variable_node(node)
+                else:
+                    pass
+                fields.append(data)
+                
+        result = {'brief_description': brief_description,
+                  'detailed_description': detailed_description,
+                  'attributes': fields}
+        
+        return result
+        
+        
+    
+    def run(self, filename, include=None):
+        """
+        Parses xml file generated by doxygen. 
+        
+        @param filename: doxygen xml file name
+        @param include: members sections to include, in None -- include all  
+        """
+        path = '%s/%s' % (self.xmlpath,filename)    
+        tree = etree.parse(path)
+        root = tree.getroot()
+        result = list()
+        for node in root.iterfind(".//memberdef[@kind]"):
+            data = {}
+            kind = node.attrib['kind']
+            if include is None or kind in include:
+                if kind == 'typedef':
+                    data = self._process_typedef_node(node)
+                elif kind == 'variable':
+                    data = self._process_variable_node(node)
+                result.append(data)
+        print "\nnumber of types processed ==> " , len(result)    
+        return result
+
+
+    def _process_typedef_node(self, node):
+        t_name = node.xpath('./name/text()')[0]
+        
+        print  t_name
+        
+        t_Id = node.attrib['id']
+        t_definition = node.xpath('./definition/text()')[0]
+        t_type = self._process_type_node(node.xpath("./type")[0])        
+        brief_node = node.xpath('./briefdescription')[0]
+        t_brief = self._get_brief_description(brief_node)
+        details_node = node.xpath('./detaileddescription')[0]
+        t_detailed = self._get_detailed_description(details_node)
+
+        typedef_descr = {'category': 'composite',
+                         'name': t_name,
+                         'Id': t_Id,
+                         'type': t_type[1],
+                         'short_description': t_brief,
+                         'long_description': t_detailed,
+                         'attributes': list()
+                          }
+        if t_type[0] is not None :
+            filename = '%s.xml' % t_type[0]
+            path = '%s/%s' % (self.xmlpath,filename)
+            if not os.path.exists(path):
+                # nothing can be done
+                return typedef_descr
+            
+            compound_info = self.run_compound(filename)            
+            if compound_info is not None:
+                brief_description =  compound_info.get('brief_description')
+                if brief_description is not None and len(brief_description):
+                    # override brief description
+                    typedef_descr['short_description'] = brief_description
+                detailed_description = compound_info.get('detailed_description')
+                if detailed_description is not None and len(detailed_description):
+                    # check if this is not a duplicate
+                    if detailed_description.find(t_detailed) < 0:
+                        typedef_descr['long_description'] = '%s\n%s' % \
+                            (detailed_description, 
+                             typedef_descr['long_description'])
+                typedef_descr['attributes'] = compound_info['attributes']
+        return typedef_descr
+
+    def _process_variable_node(self, node):
+        v_name = node.xpath('./name/text()')[0]
+        v_Id = node.attrib['id']
+        v_definition = node.xpath('./definition/text()')[0]
+        v_type = self._process_type_node(node.xpath("./type")[0])        
+        brief_node = node.xpath('./briefdescription')[0]
+        v_brief = self._get_brief_description(brief_node)
+        details_node = node.xpath('./detaileddescription')[0]
+        detailed_description = self._get_detailed_description(details_node)
+        
+        variable_descr = {'category': 'variable',
+                          'name': v_name,
+                          'Id': v_Id,
+                          'type': v_type[1],
+                          'short_description': v_brief,
+                          'long_description': detailed_description
+                          }
+
+        return variable_descr
+
+
+    def _get_brief_description(self, node):
+        result = list()
+        for p in node.xpath("./para"):
+                x = self._process_paragraph_content(p)
+                if x is not None and len(x):
+                    result.append(x)
+        result = '\n'.join(result)
+        
+        return result
+            
+
+    def _get_detailed_description(self, node):
+        """
+        Description node is comprised of <para>...</para> sections.
+        There are few types of these sections:
+           a) Content section
+           b) Return value section -- skip
+           c) Parameter list section -- skip
+        @param node: detailed description node 
+        """
+        result = list()
+        for p in node.xpath("./para"):
+            if len(p.xpath("./simplesect[@kind='return']")):
+                continue
+            elif len(p.xpath("./parameterlist[@kind='param']")):
+                continue
+            else:
+                x = self._process_paragraph_content(p)
+                result.append(x)
+        result = '\n'.join(result)
+
+        return result
+
+    def _process_paragraph_content(self, node):
+        
+        result = list()
+        content = node.xpath(".//text()")
+        for e in content:
+            if node is e.getparent():
+                result.append(e.strip())
+            elif e.getparent().tag == 'ref':
+                if e.is_tail:
+                    result.append(e.strip())
+                else:
+                    result.append(':c:type:`%s`' % e.strip())
+            elif e.getparent().tag == 'emphasis':
+                if e.is_tail:
+                    result.append(e.strip())
+                else:
+                    result.append('*%s*' % e.strip())
+            elif e.getparent().tag == 'computeroutput':
+                if e.is_tail:
+                    result.append(e.strip())
+                else:
+                    result.append('*%s*' % e.strip())
+        result = ' '.join(result)
+
+        return result
+
+    def _process_type_node(self, node):
+        """
+        Type node has form 
+            <type>type_string</type>
+        for build in types and
+            <type>
+              <ref refid='reference',kindref='member|compound'> 
+                  'type_name'
+              </ref></type>
+              postfix (ex. *, **m, etc.)
+            </type>
+        for user defined types.
+        """
+        p_id = node.xpath("./ref/@refid")
+        if len(p_id) == 1:
+            p_id = p_id[0]
+        elif len(p_id) == 0:
+            p_id = None
+        p_type = ' '.join(node.xpath(".//text()"))
+        
+        # remove  macros
+        p_type = re.sub('KRB5_CALLCONV_C', ' ', p_type)
+        p_type = re.sub('KRB5_CALLCONV', ' ', p_type)
+            
+        return (p_id,p_type)
+
+    def save(self, obj, templates, target_dir):
+        template_path = templates[obj.category]
+        outpath = '%s/%s.rst' % (target_dir,obj.name)
+        obj.save(outpath, template_path)
+            
+            
+        
+class DoxyTypesTest(DoxyTypes):
+    def __init__(self, xmlpath, rstpath):
+        self.templates = { 'composite': 'type_document.tmpl'}
+        self.target_dir = rstpath
+
+        super(DoxyTypesTest,self).__init__(xmlpath)
+
+    def run_tests(self):
+        self.test_process_typedef_node()
+        
+    # TESTS
+
+    def test_run(self):
+        filename = 'krb5_8hin.xml'
+        self.run(filename)
+
+        
+    def test_process_variable_node(self):
+        filename = 'struct__krb5__octet__data.xml'
+        result = self.run(filename, include=['variable'])
+
+    def test_process_typedef_node(self):
+        # run parser for typedefs
+        filename = 'krb5_8hin.xml'
+        result = self.run(filename, include=['typedef'])
+        target_dir = '%s/types' % (self.target_dir)
+        if not os.path.exists(target_dir):
+            os.makedirs(target_dir, 0755)
+        for t in result:
+            obj = DocModel(**t)
+            self.save(obj, self.templates, target_dir)
+
+    def test_run_compound(self):
+        filename = 'struct__krb5__context.xml'
+        result = self.run_compound(filename)
+        
+if __name__ == '__main__':
+    
+    tester = DoxyTypesTest( xml_inpath, rst_outpath)
+    tester.run_tests()
diff --git a/doc/rst_tools/func_document.tmpl b/doc/rst_tools/func_document.tmpl
new file mode 100644 (file)
index 0000000..830e5fc
--- /dev/null
@@ -0,0 +1,105 @@
+#if $function.short_description is not None
+       #set $title = $function.name + ' - ' + $function.short_description
+#else
+       #set $title = $function.name
+#end if
+$title
+#echo ''.join(['=']*len($title)) #
+
+:polyvox:`Doxygen reference to $function.name  <$function.name>`
+
+..
+
+.. c:function:: $signature
+
+..
+
+
+:param:
+        
+#for $param in $function.parameters:
+       #if $param.name is ''
+               #continue
+       #end if
+       #if $param.direction is not None
+           #set name_description = '**[%s]** **%s**' % ($param.direction, $param.name)
+       #else
+          #set name_description = '**%s**' % $param.name
+       #end if
+       #if $param.description is not None
+          #set $description= ' - ' + $param.description                     
+       #else
+          #set $description=''         
+       #end if
+                 $name_description$description
+                 
+#end for
+
+..
+
+#if len($function.retval_description) > 0 
+
+:retval:
+#for $retval in $function.retval_description:  
+         - $retval             
+#end for
+#end if
+
+#if len($function.return_description) > 0
+
+:return:
+#for $retval in $function.return_description:  
+         - $retval             
+#end for
+#end if
+  
+..
+        
+#if $function.deprecated_description is not None
+
+$function.deprecated_description
+#end if
+
+
+
+
+#if $function.long_description is not None
+
+
+$function.long_description
+
+#end if
+
+
+..
+
+#if $function.sa_description is not None
+.. seealso:: 
+        $function.sa_description
+#end if
+
+
+#if $function.warn_description is not None or  $function.notes_description is not None
+
+
+#if $function.warn_description is not None
+.. warning::
+       $function.warn_description
+#end if
+
+#if $function.notes_description is not None
+.. note::
+       $function.notes_description
+#end if
+
+#end if
+
+
+Feedback
+------------------
+
+#set $msg_subject = 'Documentation___' + $function.name 
+
+Please, provide your feedback on this document at krb5-bugs@mit.edu?subject=$msg_subject
+
diff --git a/doc/rst_tools/type_document.tmpl b/doc/rst_tools/type_document.tmpl
new file mode 100644 (file)
index 0000000..17a8b81
--- /dev/null
@@ -0,0 +1,62 @@
+.. highlightlang:: c
+
+.. $composite.struct_reference($composite.name):
+
+
+
+#if $composite.short_description is not None and len($composite.short_description)
+       #set $title = $composite.name + ' - ' + $composite.short_description
+#else
+       #set $title = $composite.name
+#end if
+$title
+#echo ''.join(['=']*len($title)) #
+
+
+
+:polyvox:`Doxygen reference to $composite.name  <$composite.name>`
+
+..
+.. c:type:: $composite.name
+..
+
+$composite.long_description
+
+
+#if $composite.Id is not None and $composite.name is not 'krb5_trace_callback'
+
+
+#if len($composite.attributes)
+
+Members
+---------
+
+#else 
+
+#if $composite.type is not None
+typedef :c:type:`$composite.type` $composite.name
+#end if
+
+#end if
+
+#for $attr in $composite.attributes:    
+#if $attr.name is not None
+.. c:member:: $attr.type       $composite.name.$attr.name 
+
+                                             $attr.short_description
+#if $attr.long_description is not None
+                                             $attr.long_description                 
+#end if
+                               
+#end if
+#end for
+#end if
+
+
+Feedback
+----------
+
+#set $msg_subject = 'Documentation___' + $composite.name 
+
+Please, provide your feedback on this document at krb5-bugs@mit.edu?subject=$msg_subject