linphone-ios/tools/genapixml.py

816 lines
29 KiB
Python
Executable file

#!/usr/bin/python
# Copyright (C) 2014 Belledonne Communications SARL
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import argparse
import logging
import os
import six
import string
import sys
import xml.etree.ElementTree as ET
import xml.dom.minidom as minidom
import metadoc
logger = logging.getLogger(__name__)
class CObject:
def __init__(self, name):
self.name = name.strip()
self.briefDescription = ''
self.detailedDescription = None
self.deprecated = False
self.briefDoc = None
self.detailedDoc = None
class CEnumValue(CObject):
def __init__(self, name):
CObject.__init__(self, name)
self.value = None
class CEnum(CObject):
def __init__(self, name):
CObject.__init__(self, name)
self.values = []
self.associatedTypedef = None
@property
def publicName(self):
return self.associatedTypedef.name if self.associatedTypedef is not None else self.name
def addValue(self, value):
self.values.append(value)
class CStructMember(CObject):
def __init__(self, name, t):
CObject.__init__(self, name)
self.ctype = t.strip()
class CStruct(CObject):
def __init__(self, name):
CObject.__init__(self, name)
self.members = []
self.associatedTypedef = None
def addMember(self, member):
self.members.append(member)
class CTypedef(CObject):
def __init__(self, name, definition):
CObject.__init__(self, name)
self.definition = definition.strip()
class CArgument(CObject):
def __init__(self, t, name = '', enums = [], structs = []):
CObject.__init__(self, name)
self.description = None
self.containedType = None
keywords = [ 'const', 'struct', 'enum', 'signed', 'unsigned', 'short', 'long', '*' ]
fullySplittedType = []
splittedType = t.strip().split(' ')
for s in splittedType:
if s.startswith('*'):
fullySplittedType.append('*')
if len(s) > 1:
fullySplittedType.append(s[1:])
elif s.endswith('*'):
fullySplittedType.append(s[:-1])
fullySplittedType.append('*')
else:
fullySplittedType.append(s)
if 'MS2_DEPRECATED' in fullySplittedType:
fullySplittedType.remove('MS2_DEPRECATED')
elif 'LINPHONE_DEPRECATED' in fullySplittedType:
fullySplittedType.remove('LINPHONE_DEPRECATED')
isStruct = False
isEnum = False
self.ctype = 'int' # Default to int so that the result is correct eg. for 'unsigned short'
for s in fullySplittedType:
if not s in keywords:
self.ctype = s
if s == 'struct':
isStruct = True
if s == 'enum':
isEnum = True
if isStruct:
for st in structs:
if st.associatedTypedef is not None:
self.ctype = st.associatedTypedef.name
elif isEnum:
for e in enums:
if e.associatedTypedef is not None:
self.ctype = e.associatedTypedef.name
if self.ctype == 'int' and 'int' not in fullySplittedType:
if fullySplittedType[-1] == '*':
fullySplittedType.insert(-1, 'int')
else:
fullySplittedType.append('int')
self.completeType = ' '.join(fullySplittedType)
def __str__(self):
return self.completeType + " " + self.name
class CArgumentsList:
def __init__(self):
self.arguments = []
def addArgument(self, arg):
self.arguments.append(arg)
def __len__(self):
return len(self.arguments)
def __getitem__(self, key):
return self.arguments[key]
def __str__(self):
argstr = []
for arg in self.arguments:
argstr.append(str(arg))
return ', '.join(argstr)
class CFunction(CObject):
def __init__(self, name, returnarg, argslist):
CObject.__init__(self, name)
self.returnArgument = returnarg
self.arguments = argslist
self.location = None
class CEvent(CFunction):
pass
class CProperty:
def __init__(self, name):
self.name = name
self.getter = None
self.setter = None
class CClass(CObject):
def __init__(self, st):
CObject.__init__(self, st.associatedTypedef.name)
if st.deprecated or st.associatedTypedef.deprecated:
self.deprecated = True
if len(st.associatedTypedef.briefDescription) > 0:
self.briefDescription = st.associatedTypedef.briefDescription
elif len(st.briefDescription) > 0:
self.briefDescription = st.briefDescription
if st.associatedTypedef.detailedDescription is not None:
self.detailedDescription = st.associatedTypedef.detailedDescription
elif st.detailedDescription is not None:
self.detailedDescription = st.detailedDescription
self.__struct = st
self.events = {}
self.classMethods = {}
self.instanceMethods = {}
self.properties = {}
self.__computeCFunctionPrefix()
def __computeCFunctionPrefix(self):
self.cFunctionPrefix = ''
first = True
for l in self.name:
if l.isupper() and not first:
self.cFunctionPrefix += '_'
self.cFunctionPrefix += l.lower()
first = False
self.cFunctionPrefix += '_'
def __addPropertyGetter(self, name, f):
if not name in self.properties:
prop = CProperty(name)
self.properties[name] = prop
self.properties[name].getter = f
def __addPropertySetter(self, name, f):
if not name in self.properties:
prop = CProperty(name)
self.properties[name] = prop
self.properties[name].setter = f
def __addClassMethod(self, f):
if not f.name in self.classMethods:
self.classMethods[f.name] = f
def __addInstanceMethod(self, f):
name = f.name[len(self.cFunctionPrefix):]
if name.startswith('get_') and len(f.arguments) == 1:
self.__addPropertyGetter(name[4:], f)
elif name.startswith('is_') and len(f.arguments) == 1 and f.returnArgument.ctype == 'bool_t':
self.__addPropertyGetter(name, f)
elif name.endswith('_enabled') and len(f.arguments) == 1 and f.returnArgument.ctype == 'bool_t':
self.__addPropertyGetter(name, f)
elif name.startswith('set_') and len(f.arguments) == 2:
self.__addPropertySetter(name[4:], f)
elif name.startswith('enable_') and len(f.arguments) == 2 and f.arguments[1].ctype == 'bool_t':
self.__addPropertySetter(name[7:] + '_enabled', f)
else:
if not f.name in self.instanceMethods:
self.instanceMethods[f.name] = f
def addEvent(self, ev):
if not ev.name in self.events:
self.events[ev.name] = ev
def addMethod(self, f):
if len(f.arguments) > 0 and f.arguments[0].ctype == self.name:
self.__addInstanceMethod(f)
else:
self.__addClassMethod(f)
class Project:
def __init__(self):
self.prettyPrint = False
self.enums = []
self.__structs = []
self.__typedefs = []
self.__events = []
self.__functions = []
self.classes = []
self.docparser = metadoc.Parser()
def add(self, elem):
if isinstance(elem, CClass):
logger.debug("Adding class " + elem.name)
self.classes.append(elem)
elif isinstance(elem, CEnum):
msg = 'Adding enum {0}'.format(elem.name)
for value in elem.values:
msg += ('\t{0}'.format(value))
logger.debug(msg)
self.enums.append(elem)
elif isinstance(elem, CStruct):
msg = "Adding struct " + elem.name
for sm in elem.members:
msg += ('\t{0} {1}'.format(sm.ctype, sm.name))
logger.debug(msg)
self.__structs.append(elem)
elif isinstance(elem, CTypedef):
logger.debug('Adding typedef {0}\t{1}'.format(elem.name, elem.definition))
self.__typedefs.append(elem)
elif isinstance(elem, CEvent):
logger.debug('Adding event {0}\tReturns: {1}\tArguments: {2}'.format(elem.name, elem.returnArgument.ctype, elem.arguments))
self.__events.append(elem)
elif isinstance(elem, CFunction):
logger.debug('Adding event {0}\tReturns: {1}\tArguments: {2}'.format(elem.name, elem.returnArgument.ctype, elem.arguments))
self.__functions.append(elem)
def __cleanDescription(self, descriptionNode):
for para in descriptionNode.findall('./para'):
for n in para.findall('./parameterlist'):
para.remove(n)
for n in para.findall("./simplesect[@kind='return']"):
para.remove(n)
for n in para.findall("./simplesect[@kind='see']"):
t = ''.join(n.itertext())
n.clear()
n.tag = 'see'
n.text = t
for n in para.findall("./simplesect[@kind='note']"):
n.tag = 'note'
n.attrib = {}
for n in para.findall(".//xrefsect"):
para.remove(n)
for n in para.findall('.//ref'):
n.attrib = {}
for n in para.findall(".//bctbx_list"):
para.remove(n)
if descriptionNode.tag == 'parameterdescription':
descriptionNode.tag = 'description'
if descriptionNode.tag == 'simplesect':
descriptionNode.tag = 'description'
descriptionNode.attrib = {}
return descriptionNode
def __canBeWrapped(self, node):
return node.find('./detaileddescription//donotwrap') is None
def __discoverClasses(self):
for td in self.__typedefs:
if td.definition.startswith('enum '):
for e in self.enums:
if (e.associatedTypedef is None) and td.definition[5:] == e.name:
e.associatedTypedef = td
break
elif td.definition.startswith('struct '):
structFound = False
for st in self.__structs:
if (st.associatedTypedef is None) and td.definition[7:] == st.name:
st.associatedTypedef = td
structFound = True
break
if not structFound:
name = td.definition[7:]
logger.warning("Structure with no associated typedef: " + name)
st = CStruct(name)
st.associatedTypedef = td
self.add(st)
for td in self.__typedefs:
if td.definition.startswith('struct '):
for st in self.__structs:
if st.associatedTypedef == td:
cclass = CClass(st)
cclass.briefDoc = td.briefDoc
cclass.detailedDoc = td.detailedDoc
self.add(cclass)
break
elif ('Linphone' + td.definition) == td.name:
st = CStruct(td.name)
st.associatedTypedef = td
cclass = CClass(st)
cclass.briefDoc = td.briefDoc
cclass.detailedDoc = td.detailedDoc
self.add(st)
self.add(cclass)
# Sort classes by length of name (longest first), so that methods are put in the right class
self.classes.sort(key = lambda c: len(c.name), reverse = True)
for e in self.__events:
eventAdded = False
for c in self.classes:
if c.name.endswith('Cbs') and e.name.startswith(c.name):
c.addEvent(e)
eventAdded = True
break
if not eventAdded:
for c in self.classes:
if e.name.startswith(c.name):
c.addEvent(e)
eventAdded = True
break
for f in self.__functions:
for c in self.classes:
if c.cFunctionPrefix == f.name[0 : len(c.cFunctionPrefix)]:
c.addMethod(f)
break
def __parseCEnumValueInitializer(self, initializer):
initializer = initializer.strip()
if not initializer.startswith('='):
return None
initializer = initializer[1:]
initializer.strip()
return initializer
def __parseCEnumValue(self, node):
ev = CEnumValue(node.find('./name').text)
initializerNode = node.find('./initializer')
if initializerNode is not None:
ev.value = self.__parseCEnumValueInitializer(initializerNode.text)
deprecatedNode = node.find(".//xrefsect[xreftitle='Deprecated']")
if deprecatedNode is not None:
ev.deprecated = True
ev.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
ev.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
ev.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
ev.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return ev
def __parseCEnumMemberdef(self, node):
if not Project.__canBeWrapped(self, node):
return None
e = CEnum(node.find('./name').text)
deprecatedNode = node.find(".//xrefsect[xreftitle='Deprecated']")
if deprecatedNode is not None:
e.deprecated = True
e.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
e.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
e.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
e.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
enumvalues = node.findall("enumvalue[@prot='public']")
for enumvalue in enumvalues:
ev = self.__parseCEnumValue(enumvalue)
e.addValue(ev)
return e
def __findCEnum(self, tree):
memberdefs = tree.findall("./compounddef[@kind='group']/sectiondef[@kind='enum']/memberdef[@kind='enum'][@prot='public']")
for m in memberdefs:
e = self.__parseCEnumMemberdef(m)
self.add(e)
def __parseCStructMember(self, node, structname):
name = node.find('./name').text
definition = node.find('./definition').text
t = definition[0:definition.find(structname + "::" + name)]
sm = CStructMember(name, t)
deprecatedNode = node.find(".//xrefsect[xreftitle='Deprecated']")
if deprecatedNode is not None:
sm.deprecated = True
sm.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
sm.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
sm.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
sm.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return sm
def __parseCStructCompounddef(self, node):
s = CStruct(node.find('./compoundname').text)
deprecatedNode = node.find(".//xrefsect[xreftitle='Deprecated']")
if deprecatedNode is not None:
s.deprecated = True
s.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
s.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
s.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
s.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
structmembers = node.findall("sectiondef/memberdef[@kind='variable'][@prot='public']")
for structmember in structmembers:
sm = self.__parseCStructMember(structmember, s.name)
s.addMember(sm)
return s
def __findCStruct(self, tree):
compounddefs = tree.findall("./compounddef[@kind='struct'][@prot='public']")
for c in compounddefs:
s = self.__parseCStructCompounddef(c)
self.add(s)
def __parseCTypedefMemberdef(self, node):
if not Project.__canBeWrapped(self, node):
return None
name = node.find('./name').text
definition = node.find('./definition').text
if definition.startswith('typedef '):
definition = definition[8 :]
if name.endswith('Cb'):
pos = definition.find("(*")
if pos == -1:
return None
returntype = definition[0:pos].strip()
returnarg = CArgument(returntype, enums = self.enums, structs = self.__structs)
returndesc = node.find("./detaileddescription/para/simplesect[@kind='return']")
if returndesc is not None:
if returnarg.ctype == 'MSList' or returnarg.ctype == 'bctbx_list_t':
n = returndesc.find('.//bctbxlist')
if n is not None:
returnarg.containedType = n.text
returnarg.description = self.__cleanDescription(returndesc)
elif returnarg.completeType != 'void':
missingDocWarning += "\tReturn value is not documented\n"
definition = definition[pos + 2 :]
pos = definition.find("(")
definition = definition[pos + 1 : -1]
argslist = CArgumentsList()
for argdef in definition.split(', '):
argType = ''
starPos = argdef.rfind('*')
spacePos = argdef.rfind(' ')
if starPos != -1:
argType = argdef[0 : starPos + 1]
argName = argdef[starPos + 1 :]
elif spacePos != -1:
argType = argdef[0 : spacePos]
argName = argdef[spacePos + 1 :]
arg = CArgument(argType, argName, self.enums, self.__structs)
if arg.ctype == 'MSList' or arg.ctype == 'bctbx_list_t':
for argentry in node.findall("detaileddescription/para/parameterlist[@kind='param']/*"):
if argentry.find("parameternamelist[parametername='{0}']".format(argName)) is not None:
containedType = argentry.find("parameterdescription//bctbxlist")
arg.containedType = containedType.text if containedType is not None else None
break
argslist.addArgument(arg)
if len(argslist) > 0:
paramdescs = node.findall("detaileddescription/para/parameterlist[@kind='param']/parameteritem")
if paramdescs:
for arg in argslist.arguments:
for paramdesc in paramdescs:
if arg.name == paramdesc.find('./parameternamelist').find('./parametername').text:
arg.description = self.__cleanDescription(paramdesc.find('./parameterdescription'))
missingDocWarning = ''
for arg in argslist.arguments:
if arg.description == None:
missingDocWarning += "\t'" + arg.name + "' parameter not documented\n";
if missingDocWarning != '':
logger.warning(name + ":\n" + missingDocWarning)
f = CEvent(name, returnarg, argslist)
deprecatedNode = node.find(".//xrefsect[xreftitle='Deprecated']")
if deprecatedNode is not None:
f.deprecated = True
f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
f.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
f.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return f
else:
pos = definition.rfind(" " + name)
if pos != -1:
definition = definition[0 : pos]
td = CTypedef(name, definition)
deprecatedNode = node.find(".//xrefsect[xreftitle='Deprecated']")
if deprecatedNode is not None:
td.deprecated = True
td.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
td.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
td.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
td.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return td
return None
def __findCTypedef(self, tree):
memberdefs = tree.findall("./compounddef[@kind='group']/sectiondef[@kind='typedef']/memberdef[@kind='typedef'][@prot='public']")
for m in memberdefs:
td = self.__parseCTypedefMemberdef(m)
self.add(td)
def __parseCFunctionMemberdef(self, node):
if not Project.__canBeWrapped(self, node):
return None
internal = node.find("./detaileddescription/internal")
if internal is not None:
return None
# The doc must be parsed here since the XML tree is to be modified in below code
briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
missingDocWarning = ''
name = node.find('./name').text
t = ''.join(node.find('./type').itertext())
returnarg = CArgument(t, enums = self.enums, structs = self.__structs)
returndesc = node.find("./detaileddescription/para/simplesect[@kind='return']")
if returndesc is not None:
if returnarg.ctype == 'MSList' or returnarg.ctype == 'bctbx_list_t':
n = returndesc.find('.//bctbxlist')
if n is not None:
returnarg.containedType = n.text
returnarg.description = self.__cleanDescription(returndesc)
elif returnarg.completeType != 'void':
missingDocWarning += "\tReturn value is not documented\n"
argslist = CArgumentsList()
argslistNode = node.findall('./param')
for argNode in argslistNode:
argType = ''.join(argNode.find('./type').itertext())
argName = ''
argNameNode = argNode.find('./declname')
if argNameNode is not None:
argName = ''.join(argNameNode.itertext())
if argType != 'void':
argslist.addArgument(CArgument(argType, argName, self.enums, self.__structs))
if len(argslist) > 0:
paramdescs = node.findall("./detaileddescription/para/parameterlist[@kind='param']/parameteritem")
if paramdescs:
for arg in argslist.arguments:
for paramdesc in paramdescs:
if arg.name == paramdesc.find('./parameternamelist').find('./parametername').text:
if arg.ctype == 'MSList' or arg.ctype == 'bctbx_list_t':
n = paramdesc.find('.//bctbxlist')
if n is not None:
arg.containedType = n.text
arg.description = self.__cleanDescription(paramdesc.find('./parameterdescription'))
missingDocWarning = ''
for arg in argslist.arguments:
if arg.description == None:
missingDocWarning += "\t'" + arg.name + "' parameter not documented\n";
f = CFunction(name, returnarg, argslist)
deprecatedNode = node.find(".//xrefsect[xreftitle='Deprecated']")
if deprecatedNode is not None:
f.deprecated = True
f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
if f.briefDescription == '' and ''.join(f.detailedDescription.itertext()).strip() == '':
return None
f.briefDoc = briefDoc
f.detailedDoc = detailedDoc
locationNode = node.find('./location')
if locationNode is not None:
f.location = locationNode.get('file')
if not f.location.endswith('.h'):
missingDocWarning += "\tNot documented in a header file ('" + f.location + "')\n";
if missingDocWarning != '':
logger.warning(name + ":\n" + missingDocWarning)
return f
def __findCFunction(self, tree):
memberdefs = tree.findall("./compounddef[@kind='group']/sectiondef[@kind='func']/memberdef[@kind='function'][@prot='public'][@static='no']")
for m in memberdefs:
f = self.__parseCFunctionMemberdef(m)
if f is not None:
self.add(f)
def initFromFiles(self, xmlfiles):
trees = []
for f in xmlfiles:
tree = None
try:
logger.debug("Parsing XML file: " + f)
tree = ET.parse(f)
except ET.ParseError as e:
logger.error(e)
if tree is not None:
trees.append(tree)
for tree in trees:
self.__findCEnum(tree)
for tree in trees:
self.__findCStruct(tree)
for tree in trees:
self.__findCTypedef(tree)
for tree in trees:
self.__findCFunction(tree)
self.__discoverClasses()
def initFromDir(self, xmldir):
files = [ os.path.join(xmldir, f) for f in os.listdir(xmldir) if (os.path.isfile(os.path.join(xmldir, f)) and f.endswith('.xml')) ]
self.initFromFiles(files)
def check(self):
for c in self.classes:
for name, p in six.iteritems(c.properties):
if p.getter is None and p.setter is not None:
logger.warning("Property '" + name + "' of class '" + c.name + "' has a setter but no getter")
class Generator:
def __init__(self, outputfile):
self.__outputfile = outputfile
def __generateEnum(self, cenum, enumsNode):
enumNodeAttributes = { 'name' : cenum.name, 'deprecated' : str(cenum.deprecated).lower() }
if cenum.associatedTypedef is not None:
enumNodeAttributes['name'] = cenum.associatedTypedef.name
enumNode = ET.SubElement(enumsNode, 'enum', enumNodeAttributes)
if cenum.briefDescription != '':
enumBriefDescriptionNode = ET.SubElement(enumNode, 'briefdescription')
enumBriefDescriptionNode.text = cenum.briefDescription
enumNode.append(cenum.detailedDescription)
if len(cenum.values) > 0:
enumValuesNode = ET.SubElement(enumNode, 'values')
for value in cenum.values:
enumValuesNodeAttributes = { 'name' : value.name, 'deprecated' : str(value.deprecated).lower() }
valueNode = ET.SubElement(enumValuesNode, 'value', enumValuesNodeAttributes)
if value.briefDescription != '':
valueBriefDescriptionNode = ET.SubElement(valueNode, 'briefdescription')
valueBriefDescriptionNode.text = value.briefDescription
valueNode.append(value.detailedDescription)
def __generateFunction(self, parentNode, nodeName, f):
functionAttributes = { 'name' : f.name, 'deprecated' : str(f.deprecated).lower() }
if f.location is not None:
functionAttributes['location'] = f.location
functionNode = ET.SubElement(parentNode, nodeName, functionAttributes)
returnValueAttributes = { 'type' : f.returnArgument.ctype, 'completetype' : f.returnArgument.completeType }
if f.returnArgument.containedType is not None:
returnValueAttributes['containedtype'] = f.returnArgument.containedType
returnValueNode = ET.SubElement(functionNode, 'return', returnValueAttributes)
if f.returnArgument.description is not None:
returnValueNode.append(f.returnArgument.description)
argumentsNode = ET.SubElement(functionNode, 'arguments')
for arg in f.arguments:
argumentNodeAttributes = { 'name' : arg.name, 'type' : arg.ctype, 'completetype' : arg.completeType }
if arg.containedType is not None:
argumentNodeAttributes['containedtype'] = arg.containedType
argumentNode = ET.SubElement(argumentsNode, 'argument', argumentNodeAttributes)
if arg.description is not None:
argumentNode.append(arg.description)
if f.briefDescription != '':
functionBriefDescriptionNode = ET.SubElement(functionNode, 'briefdescription')
functionBriefDescriptionNode.text = f.briefDescription
functionNode.append(f.detailedDescription)
def __generateClass(self, cclass, classesNode):
# Do not include classes that contain nothing
if len(cclass.events) == 0 and len(cclass.classMethods) == 0 and \
len(cclass.instanceMethods) == 0 and len(cclass.properties) == 0:
return
# Check the capabilities of the class
has_ref_method = False
has_unref_method = False
has_destroy_method = False
for methodname in cclass.instanceMethods:
methodname_without_prefix = methodname.replace(cclass.cFunctionPrefix, '')
if methodname_without_prefix == 'ref':
has_ref_method = True
elif methodname_without_prefix == 'unref':
has_unref_method = True
elif methodname_without_prefix == 'destroy':
has_destroy_method = True
refcountable = False
destroyable = False
if has_ref_method and has_unref_method:
refcountable = True
if has_destroy_method:
destroyable = True
classNodeAttributes = {
'name' : cclass.name,
'cfunctionprefix' : cclass.cFunctionPrefix,
'deprecated' : str(cclass.deprecated).lower(),
'refcountable' : str(refcountable).lower(),
'destroyable' : str(destroyable).lower()
}
# Generate the XML node for the class
classNode = ET.SubElement(classesNode, 'class', classNodeAttributes)
if len(cclass.events) > 0:
eventsNode = ET.SubElement(classNode, 'events')
eventnames = []
for eventname in cclass.events:
eventnames.append(eventname)
eventnames.sort()
for eventname in eventnames:
self.__generateFunction(eventsNode, 'event', cclass.events[eventname])
if len(cclass.classMethods) > 0:
classMethodsNode = ET.SubElement(classNode, 'classmethods')
methodnames = []
for methodname in cclass.classMethods:
methodnames.append(methodname)
methodnames.sort()
for methodname in methodnames:
self.__generateFunction(classMethodsNode, 'classmethod', cclass.classMethods[methodname])
if len(cclass.instanceMethods) > 0:
instanceMethodsNode = ET.SubElement(classNode, 'instancemethods')
methodnames = []
for methodname in cclass.instanceMethods:
methodnames.append(methodname)
methodnames.sort()
for methodname in methodnames:
self.__generateFunction(instanceMethodsNode, 'instancemethod', cclass.instanceMethods[methodname])
if len(cclass.properties) > 0:
propertiesNode = ET.SubElement(classNode, 'properties')
propnames = []
for propname in cclass.properties:
propnames.append(propname)
propnames.sort()
for propname in propnames:
propertyNodeAttributes = { 'name' : propname }
propertyNode = ET.SubElement(propertiesNode, 'property', propertyNodeAttributes)
if cclass.properties[propname].getter is not None:
self.__generateFunction(propertyNode, 'getter', cclass.properties[propname].getter)
if cclass.properties[propname].setter is not None:
self.__generateFunction(propertyNode, 'setter', cclass.properties[propname].setter)
if cclass.briefDescription != '':
classBriefDescriptionNode = ET.SubElement(classNode, 'briefdescription')
classBriefDescriptionNode.text = cclass.briefDescription
classNode.append(cclass.detailedDescription)
def generate(self, project):
logger.info("Generating XML document of Linphone API to '" + self.__outputfile.name + "'")
apiNode = ET.Element('api')
project.enums.sort(key = lambda e: e.name)
if len(project.enums) > 0:
enumsNode = ET.SubElement(apiNode, 'enums')
for cenum in project.enums:
self.__generateEnum(cenum, enumsNode)
if len(project.classes) > 0:
classesNode = ET.SubElement(apiNode, 'classes')
project.classes.sort(key = lambda c: c.name)
for cclass in project.classes:
self.__generateClass(cclass, classesNode)
s = '<?xml version="1.0" encoding="UTF-8" ?>\n'.encode('utf-8')
s += ET.tostring(apiNode, 'utf-8')
if project.prettyPrint:
s = minidom.parseString(s).toprettyxml(indent='\t')
self.__outputfile.write(s)
def main(argv = None):
if argv is None:
argv = sys.argv
argparser = argparse.ArgumentParser(description="Generate XML version of the Linphone API.")
argparser.add_argument('-o', '--outputfile', metavar='outputfile', type=argparse.FileType('w'), help="Output XML file describing the Linphone API.")
argparser.add_argument('--verbose', help="Increase output verbosity", action='store_true')
argparser.add_argument('--pretty', help="XML pretty print", action='store_true')
argparser.add_argument('xmldir', help="XML directory generated by doxygen.")
args = argparser.parse_args()
if args.outputfile == None:
args.outputfile = open('api.xml', 'w')
project = Project()
if args.verbose:
logger.setLogLevel(logging.DEBUG)
else:
logger.setLogLevel(logging.INFO)
if args.pretty:
project.prettyPrint = True
project.initFromDir(args.xmldir)
project.check()
gen = Generator(args.outputfile)
gen.generate(project)
if __name__ == "__main__":
sys.exit(main())