summaryrefslogtreecommitdiffstats
path: root/doc/tools/doxybuilder_funcs.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tools/doxybuilder_funcs.py')
-rw-r--r--doc/tools/doxybuilder_funcs.py595
1 files changed, 595 insertions, 0 deletions
diff --git a/doc/tools/doxybuilder_funcs.py b/doc/tools/doxybuilder_funcs.py
new file mode 100644
index 0000000000..13331f8bad
--- /dev/null
+++ b/doc/tools/doxybuilder_funcs.py
@@ -0,0 +1,595 @@
+'''
+ 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)
+ f_version = self._process_version_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,
+ 'version_num': f_version,
+ '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':
+ if value.find('()') >= 0:
+ # functions
+ return ':c:func:' + '`' + value + '`'
+ else:
+ # macro's
+ return ':data:' + '`' + 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_version_decorator(self, node, value):
+ if node.name == 'simplesect':
+ if node.attributes['kind'] == 'version':
+ return value
+ else:
+ return None
+
+ def _process_version_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_version_decorator,
+ 'para': self.paragraph_content_decorator}
+ result = it.walk(decorators, 1)
+ if result is not None:
+ return result
+ 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()
+