diff -r 000000000000 -r 42188c7ea2d9 Orb/python/doxygen/linkinserter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Orb/python/doxygen/linkinserter.py Thu Jan 21 17:29:01 2010 +0000 @@ -0,0 +1,474 @@ +# Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. +# This component and the accompanying materials are made available under the terms of the License +# "Eclipse Public License v1.0" which accompanies this distribution, +# and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Nokia Corporation - initial contribution. +# +# Contributors: +# +# Description: +# +import unittest +import sys +from cStringIO import StringIO +from optparse import OptionParser +from xml.etree import ElementTree as etree +import os.path +import logging +import shutil + +__version__ = "0.1" + + +class LinkFile(object): + """ + Parses string representations of cxxapiref XML files and returns an updated + string representation with inserted ids for linking to cxxFunctions. + """ + + def _get_cxxfunction_elems(self, elem): + """ + Takes an elements and generates a list of all child elements that are called cxxFunction + """ + return [e for e in elem.getiterator() if e.tag == "cxxFunction"] + + def _get_funcs_with_no_params(self, func_elem_list): + """ + Takes a list of cxxFunction elements and returns a list of those with no parameters. + """ + no_params = [] + for func in func_elem_list: + apiname = func.find("apiName").text + params_elem = func.find("cxxFunctionDetail/cxxFunctionDefinition/cxxFunctionParameters") + # if cxxFunctionParameters has no children + if len(params_elem.getiterator()) == 1: + no_params.append(apiname) + return no_params + + + def _filter_funcs_with_no_params(self, func_elem_list): + """ + Takes a list of cxxFunction elements and returns a list with parameterless functions + and all of their overloads removed. + """ + no_param_funcs = self._get_funcs_with_no_params(func_elem_list) + return [func_elem for func_elem in func_elem_list if not func_elem.find("apiName").text in no_param_funcs] + + def _filter_duplicate_funcs(self, func_elem_list): + """ + Takes a list of cxxFunction elements and returns a list ones with unique apiName text. + In the case of overloads the first instance found is taken and the rest are filtered. + """ + seen_func_names = [] + filtered = [] + for func_elem in func_elem_list: + this_apiname = func_elem.find("apiName").text + if not this_apiname in seen_func_names: + filtered.append(func_elem) + seen_func_names.append(this_apiname) + return filtered + + def _insert_id_into_cxxfunction_apiname(self, func_elem): + """ + Takes a cxxFunction element. Returns the element with an id inserted into the child apiName element. + """ + function_scoped_name=func_elem.find("cxxFunctionDetail/cxxFunctionDefinition/cxxFunctionScopedName").text + + if function_scoped_name == None: + function_scoped_name = "" + else: + function_scoped_name+="::" + + apiname_id = "".join([function_scoped_name,func_elem.find("apiName").text,"()"]) + + func_elem.find("apiName").attrib["id"] = apiname_id + return func_elem + + def get_func_elems_to_linkify(self, root): + cxxfunction_elems = self._get_cxxfunction_elems(root) + cxxfunction_elems = self._filter_funcs_with_no_params(cxxfunction_elems) + cxxfunction_elems = self._filter_duplicate_funcs(cxxfunction_elems) + return cxxfunction_elems + + def get_linkified(self, file_as_string): + """ + Takes a string representation of a cxxapiref file and returns the string + with inserted cxxFunction ids for any functions that fit the insertion rule. + + The id insertion rule is: + + If a function and none of its overloads have no arguments then insert an + id that represents a function with no arguments over the first function definition encountered. + The id is inserted into the apiName child element of the function. + """ + try: + root = etree.fromstring(file_as_string) + except Exception, e: + raise Exception("Failed to parse string as xml file error was %s" % e) + funcs_to_linkify = self.get_func_elems_to_linkify(root) + for index in xrange(0, len(funcs_to_linkify)): + func = self._insert_id_into_cxxfunction_apiname(funcs_to_linkify[index]) + return etree.tostring(root) + + +class LinkInserter(object): + + def __init__(self, link_file): + self.link_file = link_file + + def _handle_xml_file(self, xml_file): + """ + Runs linkify function on each file and writes the result to disk + """ + logging.info("Inserting links into %s" % xml_file) + content = open(xml_file, "r").read() + try: + linkified_contents = self.link_file.get_linkified(content) + except Exception,e: + logging.error("%s %s" %(e, xml_file)) + return + + try: + f = open(xml_file, "w") + except Exception, e: + raise IOError("Could not open xml file %s for writing, error was: %s" % (xml_file, e)) + else: + f.write(linkified_contents) + f.close() + + def _handle_xml_files(self, xml_files): + """ + Iterates over a list of files and calls _handle_xml_file on them + """ + for xml_file in xml_files: + self._handle_xml_file(xml_file) + + def _do_linkifying(self, dir): + """ + Takes a directory and calls a handler function on a list of xml files in that and sub directories. + """ + for root, dirs, files in os.walk(dir): + xml_file_paths = [os.path.join(root, f) for f in os.listdir(root) if os.path.splitext(f)[1].lower() == (".xml")] + self._handle_xml_files(xml_file_paths) + + def linkify_dir(self, dir): + if not os.path.exists(os.path.abspath(dir)): + raise IOError("Directory to linkify does not exist: %s" % dir) + self._do_linkifying(dir) + + +def insertlinks(xml_dir): + link_inserter = LinkInserter(LinkFile()) + link_inserter.linkify_dir(xml_dir) + + +def main(): + usage = "usage: %prog " + parser = OptionParser(usage, version='%prog ' + __version__) + (options, args) = parser.parse_args() + if len(args) < 1: + parser.print_help() + parser.error("Please supply the path to the XML content") + insertlinks(args[0]) + + +if __name__ == '__main__': + sys.exit(main()) + + +###################################### +# Test code +###################################### + +class TestLinkFile(unittest.TestCase): + + def setUp(self): + self.link_file = LinkFile() + + def test__get_funcs_with_no_params(self): + func = """ + + Init0 + + + + + + +""" + root = etree.fromstring(func) + func_list = [e for e in root.getiterator() if e.tag == "cxxFunction"] + expected = ["Init0"] + returned = self.link_file._get_funcs_with_no_params(func_list) + self.assertEqual(expected, returned) + + def test__get_funcs_with_no_params_ignores_a_func_with_params(self): + func = """ + + Init0 + + + + + + TUint32 + + a0 + + + + + + +""" + root = etree.fromstring(func) + func_list = [e for e in root.getiterator() if e.tag == "cxxFunction"] + expected = [] + returned = self.link_file._get_funcs_with_no_params(func_list) + self.assertEqual(expected, returned) + + def test_filter_duplicate_funcs_ignores_duplicate_funcs(self): + func = """ + + + Init0 + + + Init0 + + +""" + root = etree.fromstring(func) + func_list = [e for e in root.getiterator() if e.tag == "cxxFunction"] + expected = [func_list[0]] + returned = self.link_file._filter_duplicate_funcs(func_list) + self.assertEqual(expected, returned) + + def test__filter_funcs_with_no_params_filters_funcs_with_no_params(self): + func = """ + + + Init0 + + + + + + + + Init1 + + + + + + TUint32 + + a0 + + + + + + + +""" + root = etree.fromstring(func) + func_list = [e for e in root.getiterator() if e.tag == "cxxFunction"] + expected = [func_list[1]] + returned = self.link_file._filter_funcs_with_no_params(func_list) + self.assertEqual(expected, returned) + + def test__insert_id_into_cxxfunction_apiname(self): + func_str = """ + Init0 + + + + + + void + BTrace + static void Init0(TUint32 a0) + BTrace::Init0(TUint32 a0) + + + + TUint32 + + a0 + + + + + + + + + + + + + + """ + func_elem = etree.fromstring(func_str) + returned = self.link_file._insert_id_into_cxxfunction_apiname(func_elem) + self.assertEquals(returned.find("apiName").attrib["id"], "BTrace::Init0()") + + def test__insert_id_into_cxxfunction_apiname_when_the_cxxfunction_has_no_scoped_name(self): + func_str = """ + Init0 + + + + + + void + + static void Init0(TUint32 a0) + BTrace::Init0(TUint32 a0) + + + + TUint32 + + a0 + + + + + + + + + + + + + + """ + func_elem = etree.fromstring(func_str) + returned = self.link_file._insert_id_into_cxxfunction_apiname(func_elem) + self.assertEquals(returned.find("apiName").attrib["id"], "Init0()") + + def test_i_can_insert_an_id_to_a_cxxfunction_apiname(self): + file_as_string = """ + + + Init0 + + + + + + void + BTrace + static void Init0(TUint32 a0) + BTrace::Init0(TUint32 a0) + + + + TUint32 + + a0 + + + + + + + + + + + + + + + """ + returned = self.link_file.get_linkified(file_as_string) + self.assertEquals(etree.fromstring(returned).find("cxxFunction/apiName").attrib["id"], "BTrace::Init0()") + + +basic_class_file_str = """\ + + + Init0 + + + + + + void + BTrace + static void Init0() + BTrace::Init0() + + + + + + + + + + + + +""" + + +xml_files = { + "basic_xml_file_1.xml": """""", + "basic_xml_file_2.xml": """""", + } +no_xml_files = { + "non_xml_file.txt": """Some text""", + } + + +class DummyLinkFile(object): + + def __init__(self): + self.visited_files = [] + + def get_linkified(self, file_as_string): + self.visited_files.append(file_as_string) + return file_as_string + +class TestLinkInserter(unittest.TestCase): + + def setUp(self): + self.dummy_link_file = DummyLinkFile() + self.inserter = LinkInserter(self.dummy_link_file) + + def test__handle_xml_file_writes_to_a_file(self): + tmp_file_path = os.getcwd() + os.sep + "tmp_file.xml" + tmp_file = open(tmp_file_path, "w") + tmp_file.write(basic_class_file_str) + tmp_file.close() + self.inserter._handle_xml_file(tmp_file_path) + self.assertEquals(self.dummy_link_file.visited_files, [basic_class_file_str]) + os.remove(tmp_file_path) + + def test_i_raise_an_exception_when_dir_doesnt_exist(self): + self.assertRaises(IOError, self.inserter.linkify_dir, "non_existant_dir") + + def test_i_call_linkify_on_each_file_xml_file_in_a_dir(self): + basic_files = {} + basic_files.update(xml_files) + basic_files.update(no_xml_files) + test_dir = os.path.join(os.getcwd(), "test_dir") + os.mkdir(test_dir) + for filename,file_content in basic_files.items(): + handle = open(os.path.join(test_dir, filename),"w") + handle.write(file_content) + handle.close() + self.inserter._do_linkifying(test_dir) + self.dummy_link_file.visited_files.sort() + xml_files_input = xml_files.values() + xml_files_input.sort() + self.assertEquals(self.dummy_link_file.visited_files, xml_files_input) + shutil.rmtree(test_dir)