diff options
Diffstat (limited to 'utils/python/doc_gen/doxygen_extractor.py')
-rw-r--r-- | utils/python/doc_gen/doxygen_extractor.py | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/utils/python/doc_gen/doxygen_extractor.py b/utils/python/doc_gen/doxygen_extractor.py new file mode 100644 index 0000000..9207c9d --- /dev/null +++ b/utils/python/doc_gen/doxygen_extractor.py @@ -0,0 +1,242 @@ +import os +from system_utils import SystemUtils + +class DoxygenExtractor: + + md_special_chars =[ + { + "md_char": "*", + "replacement": "*" + }, + { + "md_char": "#", + "replacement": "#" + }, + { + "md_char": "`", + "replacement": "·" + } + ] + + #constructor + def __init__(self, root, header_paths, working_dir = "./temp", doxygen_xml_dest = "./xml"): + os.chdir(root) + self.header_paths = header_paths + self.utils = SystemUtils() + self.doxygen_xml_dest = doxygen_xml_dest + self.working_dir = working_dir + + ### + # this function copies headers recursively from a source director to a destination + # directory. + ### + def get_headers(self, from_dir, to_dir): + self.utils.copy_files(from_dir, to_dir, "*.h") + + ### + # Strips out reserved characters used in markdown notation, and replaces them + # with html character codes. + # + # @param text the text to strip and replace the md special characters + # + # @return the stripped text. + ### + def escape_md_chars(self, text): + for char in self.md_special_chars: + text = text.replace(char['md_char'], "\\" + char['md_char']) + return text + + + ### + # this function extracts data from an element tag ignoring the tag 'ref', but + # obtains the textual data it has inside the ref tag. + # + # @param element the element to process + # + # @return a list of extracted strings. + ### + def extract_ignoring_refs(self, element): + list = [] + + if element.text is not None: + list.append(element.text) + + for ref in element.iter(tag="ref"): + list.append(ref.text) + + return list + + ### + # this function extracts data from an element tag including all sub elements + # (recursive) + # + # @param element the element to process + # + # @return a list of extracted strings. + ### + def extract_with_subelements(self, element): + list = [] + + list.append(element.text or "") + + #if element.text is not None: + #list.append(element.text) + + for subelement in element: + if subelement is not None: + list = list + self.extract_with_subelements(subelement) + + list.append(element.tail or "") + + return list + + ### + # this function was at one point intended to fetch a value of a default parameter + # it is now only used to fetch the default parameters' name. + # + # @param document_root the root of the entire document + # @param element the element containing the default parameter + # + # @return a dictionary containing: + # { + # 'name':'', + # 'value':'' + # } + # + # @note this would be more useful if it return the value, it currently does not. + ### + def extract_default(self, element): + ref = element.find("ref") + return {'name':' '.join(element.itertext()), 'value':''} + + ### + # extracts a member function form the xml document + # + # @param root the document root + # @param xml_element the member function xml element. + # + # @return a function dictionary: + # { + # 'short_name':"", + # 'name':"", + # 'return_type':"", + # 'params':[], + # 'description':[], + # 'returns':"", + # 'notes':"", + # 'examples':"" + # } + ### + def extract_member_function(self, xml_element, function_filter = [], filter = True): + + function = { + 'short_name':"", + 'name':"", + 'return_type':"", + 'params':[], + 'description':[], + 'returns':"", + 'notes':"", + 'examples':"" + } + + function['name'] = xml_element.find('definition').text + function['short_name'] = xml_element.find('name').text + + if filter and any(filtered_func in function['short_name'] for filtered_func in function_filter): + print "Filtered out: " + function['short_name'] + return + + print "Generating documentation for: " + function['short_name'] + + if xml_element.find('type') is not None: + function['return_type'] = self.escape_md_chars(' '.join(self.extract_ignoring_refs(xml_element.find('type')))) + + #extract our parameters for this member function + for parameter in xml_element.iter('param'): + + type = "" + name = "" + + if parameter.find('type') is not None: + type = self.escape_md_chars(' '.join(parameter.find('type').itertext())) + + if parameter.find('declname') is not None: + name = ' '.join(self.extract_ignoring_refs(parameter.find('declname'))) + + param_object = { + 'type': type, + 'name': name, + 'default':{ + 'name':"", + 'value':"" + } + } + + if parameter.find('defval') is not None: + extracted = self.extract_default(parameter.find('defval')) + param_object['default']['name'] = extracted['name'] + param_object['default']['value'] = extracted['value'] + + function['params'].append(param_object) + + + detailed_description = xml_element.find('detaileddescription') + + if len(detailed_description.findall("para")) is not 0: + for para in detailed_description.findall("para"): + if len(para.findall("programlisting")) is 0 and len(para.findall("simplesect")) is 0: + function['description'] = function['description'] + self.extract_with_subelements(para) + + #para indicates a new paragraph - we should treat it as such... append \n! + function['description'] = function['description'] + ["\n\n"] + + if len(detailed_description.findall("para/simplesect[@kind='return']/para")) is not 0: + return_section = detailed_description.findall("para/simplesect[@kind='return']/para")[0] + function['returns'] = ' '.join(return_section.itertext()) + + if len(detailed_description.findall("para/simplesect[@kind='note']/para")) is not 0: + return_section = detailed_description.findall("para/simplesect[@kind='note']/para")[0] + function['notes'] = ' '.join(return_section.itertext()) + + examples = detailed_description.find('para/programlisting') + + if examples is not None: + function['examples'] = ''.join([('' if index is 0 else ' ')+word for index, word in enumerate(examples.itertext(),1) ]) + + param_list = detailed_description.findall('para/parameterlist') + + if len(param_list) is not 0: + for parameter_desc in param_list[0].findall('parameteritem'): + + param_descriptor = { + 'name':'', + 'description':'' + } + + param_name = parameter_desc.findall('parameternamelist/parametername') + additional = parameter_desc.findall('parameterdescription/para') + + if len(param_name) is not 0: + param_descriptor['name'] = param_name[0].text + + if len(additional) is not 0: + param_descriptor['description'] = ' '.join(additional[0].itertext()) + + for descriptor in function['params']: + if param_descriptor['name'] in descriptor['name']: + descriptor['description'] = param_descriptor['description'] + + return function + + def generate_doxygen(self): + self.utils.mk_dir(self.working_dir) + self.utils.clean_dir(self.working_dir) + + for path in self.header_paths: + self.get_headers(path, self.working_dir) + + if os.path.exists(self.doxygen_xml_dest): + self.utils.clean_dir(self.doxygen_xml_dest) + + os.system('doxygen doxy-config.cfg') |