1# Copyright (C) 2017 Belledonne Communications SARL
2#
3# This program is free software; you can redistribute it and/or
4# modify it under the terms of the GNU General Public License
5# as published by the Free Software Foundation; either version 2
6# of the License, or (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16
17
18import re
19import genapixml as CApi
20
21
22class Error(RuntimeError):
23	pass
24
25
26class BlacklistedException(Error):
27	pass
28
29
30class Name(object):
31	camelCaseParsingRegex = re.compile('[A-Z][a-z0-9]*')
32	lowerCamelCaseSplitingRegex = re.compile('([a-z][a-z0-9]*)([A-Z][a-z0-9]*)')
33
34	def __init__(self):
35		self.words = []
36		self.prev = None
37
38	def copy(self):
39		nameType = type(self)
40		name = nameType()
41		name.words = list(self.words)
42		name.prev = None if self.prev is None else self.prev.copy()
43		return name
44
45	def delete_prefix(self, prefix):
46		it = self
47		_next = None
48		while it is not None and it.words != prefix.words:
49			_next = it
50			it = it.prev
51
52		if it is None or it != prefix:
53			raise Error('no common prefix')
54		elif _next is not None:
55			_next.prev = None
56
57	def _set_namespace(self, namespace):
58		self.prev = namespace
59		if self.prev is not None:
60			prefix = namespace.to_word_list()
61			i = 0
62			while i<len(self.words) and i<len(prefix) and self.words[i] == prefix[i]:
63				i += 1
64			if i == len(self.words):
65				raise Error('name equal to namespace \'{0}\'', self.words)
66			else:
67				self.words = self.words[i:]
68
69	def _lower_all_words(self):
70		i = 0
71		while i<len(self.words):
72			self.words[i] = self.words[i].lower()
73			i += 1
74
75	def from_snake_case(self, name, namespace=None):
76		self.words = name.split('_')
77		Name._set_namespace(self, namespace)
78
79	def from_camel_case(self, name, islowercased=False, namespace=None):
80		if not islowercased:
81			self.words = Name.camelCaseParsingRegex.findall(name)
82		else:
83			match = Name.lowerCamelCaseSplitingRegex.match(name)
84			self.words = [match.group(1)]
85			self.words += Name.camelCaseParsingRegex.findall(match.group(2))
86
87		Name._lower_all_words(self)
88		Name._set_namespace(self, namespace)
89
90	def to_snake_case(self, fullName=False, upper=False):
91		if self.prev is None or not fullName:
92			res = '_'.join(self.words)
93			if upper:
94				res = res.upper()
95			return res
96		else:
97			return Name.to_snake_case(self.prev, fullName=True, upper=upper) + '_' + Name.to_snake_case(self, upper=upper)
98
99	def to_camel_case(self, lower=False, fullName=False):
100		if self.prev is None or not fullName:
101			res = ''
102			for elem in self.words:
103				if elem is self.words[0] and lower:
104					res += elem
105				else:
106					res += elem.title()
107			return res
108		else:
109			return Name.to_camel_case(self.prev, fullName=True, lower=lower) + Name.to_camel_case(self)
110
111	def concatenate(self, upper=False, fullName=False):
112		if self.prev is None or not fullName:
113			res = ''
114			for elem in self.words:
115				if upper:
116					res += elem.upper()
117				else:
118					res += elem
119			return res
120		else:
121			return Name.concatenate(self.prev, upper=upper, fullName=True) + Name.concatenate(self, upper=upper)
122
123	def to_word_list(self):
124		if self.prev is None:
125			return list(self.words)
126		else:
127			return Name.to_word_list(self.prev) + self.words
128
129	@staticmethod
130	def find_common_parent(name1, name2):
131		if name1.prev is None or name2.prev is None:
132			return None
133		elif name1.prev is name2.prev:
134			return name1.prev
135		else:
136			commonParent = Name.find_common_parent(name1.prev, name2)
137			if commonParent is not None:
138				return commonParent
139			else:
140				return Name.find_common_parent(name1, name2.prev)
141
142
143class ClassName(Name):
144	def to_c(self):
145		return Name.to_camel_case(self, fullName=True)
146
147
148class InterfaceName(ClassName):
149	def to_c(self):
150		return ClassName.to_c(self)[:-8] + 'Cbs'
151
152
153class EnumName(ClassName):
154	pass
155
156
157class EnumValueName(ClassName):
158	pass
159
160
161class MethodName(Name):
162	regex = re.compile('^\d+$')
163
164	def __init__(self):
165		self.overloadRef = 0
166
167	def from_snake_case(self, name, namespace=None):
168		Name.from_snake_case(self, name, namespace=namespace)
169		if len(self.words) > 0:
170			suffix = self.words[-1]
171			if MethodName.regex.match(suffix) is not None:
172				self.overloadRef = int(suffix)
173				del self.words[-1]
174
175	def to_c(self):
176		suffix = ('_' + str(self.overloadRef)) if self.overloadRef > 0 else ''
177		return self.to_snake_case(fullName=True) + suffix
178
179
180class ArgName(Name):
181	def to_c(self):
182		return self.to_snake_case()
183
184
185class PropertyName(ArgName):
186	pass
187
188
189class NamespaceName(Name):
190	def __init__(self, *params):
191		Name.__init__(self)
192		if len(params) > 0:
193			self.words = params[0]
194
195
196class Object(object):
197	def __init__(self, name):
198		self.name = name
199		self.parent = None
200		self.deprecated = False
201
202	def find_first_ancestor_by_type(self, _type):
203		ancestor = self.parent
204		while ancestor is not None and type(ancestor) is not _type:
205			ancestor = ancestor.parent
206		return ancestor
207
208
209class Type(Object):
210	def __init__(self, name, isconst=False, isref=False):
211		Object.__init__(self, name)
212		self.isconst = isconst
213		self.isref = isref
214		self.cname = None
215
216
217class BaseType(Type):
218	def __init__(self, name, isconst=False, isref=False, size=None, isUnsigned=False):
219		Type.__init__(self, name, isconst=isconst, isref=isref)
220		self.size = size
221		self.isUnsigned = isUnsigned
222
223
224class EnumType(Type):
225	def __init__(self, name, isconst=False, isref=False, enumDesc=None):
226		Type.__init__(self, name, isconst=isconst, isref=isref)
227		self.desc = enumDesc
228
229
230class ClassType(Type):
231	def __init__(self, name, isconst=False, isref=False, classDesc=None):
232		Type.__init__(self, name, isconst=isconst, isref=isref)
233		self.desc = classDesc
234
235
236class ListType(Type):
237	def __init__(self, containedTypeName, isconst=False, isref=False):
238		Type.__init__(self, 'list', isconst=isconst, isref=isref)
239		self.containedTypeName = containedTypeName
240		self._containedTypeDesc = None
241
242	def set_contained_type_desc(self, desc):
243		self._containedTypeDesc = desc
244		desc.parent = self
245
246	def get_contained_type_desc(self):
247		return self._containedTypeDesc
248
249	containedTypeDesc = property(fset=set_contained_type_desc, fget=get_contained_type_desc)
250
251
252class DocumentableObject(Object):
253	def __init__(self, name):
254		Object.__init__(self, name)
255		self.briefDescription = None
256		self.detailedDescription = None
257		self.deprecated = None
258
259	def set_from_c(self, cObject, namespace=None):
260		self.briefDescription = cObject.briefDescription
261		self.detailedDescription = cObject.detailedDescription
262		self.deprecated = cObject.deprecated
263		self.parent = namespace
264
265	def get_namespace_object(self):
266		if isinstance(self, (Namespace,Enum,Class)):
267			return self
268		elif self.parent is None:
269			raise Error('{0} is not attached to a namespace object'.format(self))
270		else:
271			return self.parent.get_namespace_object()
272
273
274class Namespace(DocumentableObject):
275	def __init__(self, name):
276		DocumentableObject.__init__(self, name)
277		self.children = []
278
279	def add_child(self, child):
280		self.children.append(child)
281		child.parent = self
282
283
284class Flag:
285	def __init__(self, position):
286		self.position = position
287
288
289class EnumValue(DocumentableObject):
290	def __init__(self, name):
291		DocumentableObject.__init__(self, name)
292		self.value = None
293
294	def value_from_string(self, stringValue):
295		m = re.match('^1\s*<<\s*([0-9]+)$', stringValue)
296		if m is not None:
297			self.value = Flag(int(m.group(1)))
298		else:
299			self.value = int(stringValue, base=0)
300
301
302class Enum(DocumentableObject):
303	def __init__(self, name):
304		DocumentableObject.__init__(self, name)
305		self.values = []
306
307	def add_value(self, value):
308		self.values.append(value)
309		value.parent = self
310
311	def set_from_c(self, cEnum, namespace=None):
312		Object.set_from_c(self, cEnum, namespace=namespace)
313
314		if 'associatedTypedef' in dir(cEnum):
315			name = cEnum.associatedTypedef.name
316		else:
317			name = cEnum.name
318
319		self.name = EnumName()
320		self.name.prev = None if namespace is None else namespace.name
321		self.name.set_from_c(name)
322
323		for cEnumValue in cEnum.values:
324			aEnumValue = EnumValue()
325			aEnumValue.set_from_c(cEnumValue, namespace=self)
326			self.add_value(aEnumValue)
327
328
329class Argument(DocumentableObject):
330	def __init__(self, name, argType, optional=False, default=None):
331		DocumentableObject.__init__(self, name)
332		self._type = argType
333		argType.parent = self
334		self.optional = optional
335		self.default = default
336
337	def _set_type(self, _type):
338		self._type = _type
339		_type.parent = self
340
341	def _get_type(self):
342		return self._type
343
344	type = property(fset=_set_type, fget=_get_type)
345
346
347class Method(DocumentableObject):
348	class Type:
349		Instance = 0,
350		Class = 1
351
352	def __init__(self, name, type=Type.Instance):
353		DocumentableObject.__init__(self, name)
354		self.type = type
355		self.constMethod = False
356		self.args = []
357		self._returnType = None
358
359	def _set_return_type(self, returnType):
360		self._returnType = returnType
361		returnType.parent = self
362
363	def _get_return_type(self):
364		return self._returnType
365
366	def add_arguments(self, arg):
367		self.args.append(arg)
368		arg.parent = self
369
370	returnType = property(fset=_set_return_type, fget=_get_return_type)
371
372
373class Property(DocumentableObject):
374	def __init__(self, name):
375		DocumentableObject.__init__(self, name)
376		self._setter = None
377		self._getter = None
378		self._type = None
379
380	def set_setter(self, setter):
381		self._setter = setter
382		setter.parent = self
383
384	def get_setter(self):
385		return self._setter
386
387	def set_getter(self, getter):
388		self._getter = getter
389		if self._type is None:
390			self._type = getter.returnType
391		getter.parent = self
392
393	def get_getter(self):
394		return self._getter
395
396	setter = property(fset=set_setter, fget=get_setter)
397	getter = property(fset=set_getter, fget=get_getter)
398
399
400class Class(DocumentableObject):
401	def __init__(self, name):
402		DocumentableObject.__init__(self, name)
403		self.properties = []
404		self.instanceMethods = []
405		self.classMethods = []
406		self._listenerInterface = None
407		self.multilistener = False
408		self.refcountable = False
409
410	def add_property(self, property):
411		self.properties.append(property)
412		property.parent = self
413
414	def add_instance_method(self, method):
415		self.instanceMethods.append(method)
416		method.parent = self
417
418	def add_class_method(self, method):
419		self.classMethods.append(method)
420		method.parent = self
421
422	def set_listener_interface(self, interface):
423		self._listenerInterface = interface
424		interface._listenedClass = self
425
426	def get_listener_interface(self):
427		return self._listenerInterface
428
429	listenerInterface = property(fget=get_listener_interface, fset=set_listener_interface)
430
431
432class Interface(DocumentableObject):
433	def __init__(self, name):
434		DocumentableObject.__init__(self, name)
435		self.methods = []
436		self._listenedClass = None
437
438	def add_method(self, method):
439		self.methods.append(method)
440		method.parent = self
441
442	def get_listened_class(self):
443		return self._listenedClass
444
445	listenedClass = property(fget=get_listened_class)
446
447
448class CParser(object):
449	def __init__(self, cProject):
450		self.cBaseType = ['void', 'bool_t', 'char', 'short', 'int', 'long', 'size_t', 'time_t', 'float', 'double', 'LinphoneStatus']
451		self.cListType = 'bctbx_list_t'
452		self.regexFixedSizeInteger = '^(u?)int(\d?\d)_t$'
453		self.methodBl = ['ref', 'unref', 'new', 'destroy', 'getCurrentCallbacks', 'setUserData', 'getUserData']
454		self.functionBl = [
455					   'linphone_factory_create_core', # manualy wrapped
456					   'linphone_factory_create_core_with_config', # manualy wrapped
457					   'linphone_vcard_get_belcard'] # manualy wrapped
458
459		self.classBl = ['LpConfig']  # temporarly blacklisted
460
461		# list of classes that must be concidered as refcountable even if
462		# they are no ref()/unref() methods
463		self.forcedRefcountableClasses = ['LinphoneFactory']
464
465		self.cProject = cProject
466
467		self.enumsIndex = {}
468		for enum in self.cProject.enums:
469			if enum.associatedTypedef is None:
470				self.enumsIndex[enum.name] = None
471			else:
472				self.enumsIndex[enum.associatedTypedef.name] = None
473
474		self.classesIndex = {}
475		self.interfacesIndex = {}
476		for _class in self.cProject.classes:
477			if _class.name not in self.classBl:
478				if _class.name.endswith('Cbs'):
479					self.interfacesIndex[_class.name] = None
480				else:
481					self.classesIndex[_class.name] = None
482
483		self.methodsIndex = {}
484		for _class in self.cProject.classes:
485			for funcname in _class.classMethods:
486				self.methodsIndex[funcname] = None
487			for funcname in _class.instanceMethods:
488				self.methodsIndex[funcname] = None
489			for _property in _class.properties.values():
490				if _property.setter is not None:
491					self.methodsIndex[_property.setter.name] = None
492				if _property.getter is not None:
493					self.methodsIndex[_property.getter.name] = None
494
495		name = NamespaceName()
496		name.from_snake_case('linphone')
497
498		self.namespace = Namespace(name)
499
500	def _is_blacklisted(self, name):
501		if type(name) is MethodName:
502			return name.to_camel_case(lower=True) in self.methodBl or name.to_c() in self.functionBl
503		elif type(name) is ClassName:
504			return name.to_c() in self.classBl
505		else:
506			return False
507
508	def parse_all(self):
509		for enum in self.cProject.enums:
510			try:
511				self.parse_enum(enum)
512			except Error as e:
513				print('Could not parse \'{0}\' enum: {1}'.format(enum.name, e.args[0]))
514
515		for _class in self.cProject.classes:
516			try:
517				self.parse_class(_class)
518			except BlacklistedException:
519				pass
520			except Error as e:
521				print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0]))
522
523
524		self._clean_all_indexes()
525		self._fix_all_types()
526		self._fix_all_docs()
527
528	def _clean_all_indexes(self):
529		for index in [self.classesIndex, self.interfacesIndex, self.methodsIndex]:
530			self._clean_index(index)
531
532	def _clean_index(self, index):
533		keysToRemove = []
534		for key in index.keys():
535			if index[key] is None:
536				keysToRemove.append(key)
537
538		for key in keysToRemove:
539			del index[key]
540
541	def _class_is_refcountable(self, _class):
542		if _class.name in self.forcedRefcountableClasses:
543			return True
544
545		for method in _class.instanceMethods:
546			if method.startswith(_class.cFunctionPrefix) and method[len(_class.cFunctionPrefix):] == 'ref':
547				return True
548		return False
549
550	def _fix_all_types_in_class_or_interface(self, _class):
551		if _class is not None:
552			if type(_class) is Class:
553				self._fix_all_types_in_class(_class)
554			else:
555				self._fix_all_types_in_interface(_class)
556
557	def _fix_all_types(self):
558		for _class in self.interfacesIndex.values():
559			self._fix_all_types_in_class_or_interface(_class)
560		for _class in self.classesIndex.values():
561			self._fix_all_types_in_class_or_interface(_class)
562
563	def _fix_all_types_in_class(self, _class):
564		for property in _class.properties:
565			if property.setter is not None:
566				self._fix_all_types_in_method(property.setter)
567			if property.getter is not None:
568				self._fix_all_types_in_method(property.getter)
569
570		for method in (_class.instanceMethods + _class.classMethods):
571			self._fix_all_types_in_method(method)
572
573	def _fix_all_types_in_interface(self, interface):
574		for method in interface.methods:
575			self._fix_all_types_in_method(method)
576
577	def _fix_all_types_in_method(self, method):
578		try:
579			self._fix_type(method.returnType)
580			for arg in method.args:
581				self._fix_type(arg.type)
582		except Error as e:
583			print('warning: some types could not be fixed in {0}() function: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0]))
584
585	def _fix_type(self, _type):
586		if isinstance(_type, EnumType) and _type.desc is None:
587			_type.desc = self.enumsIndex[_type.name]
588		elif isinstance(_type, ClassType) and _type.desc is None:
589			if _type.name in self.classesIndex:
590				_type.desc = self.classesIndex[_type.name]
591			else:
592				_type.desc = self.interfacesIndex[_type.name]
593		elif isinstance(_type, ListType) and _type.containedTypeDesc is None:
594			if _type.containedTypeName in self.classesIndex:
595				_type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.classesIndex[_type.containedTypeName])
596			elif _type.containedTypeName in self.interfacesIndex:
597				_type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.interfacesIndex[_type.containedTypeName])
598			elif _type.containedTypeName in self.enumsIndex:
599				_type.containedTypeDesc = EnumType(_type.containedTypeName, enumDesc=self.enumsIndex[_type.containedTypeName])
600			else:
601				if _type.containedTypeName is not None:
602					_type.containedTypeDesc = self.parse_c_base_type(_type.containedTypeName)
603				else:
604					raise Error('bctbx_list_t type without specified contained type')
605
606	def _fix_all_docs(self):
607		for _class in self.classesIndex.values():
608			if _class.briefDescription is not None:
609				_class.briefDescription.resolve_all_references(self)
610		for method in self.methodsIndex.values():
611			if method.briefDescription is not None:
612				method.briefDescription.resolve_all_references(self)
613
614	def parse_enum(self, cenum):
615		if 'associatedTypedef' in dir(cenum):
616			nameStr = cenum.associatedTypedef.name
617		else:
618			nameStr = cenum.name
619
620		name = EnumName()
621		name.from_camel_case(nameStr, namespace=self.namespace.name)
622		enum = Enum(name)
623		enum.briefDescription = cenum.briefDoc
624		self.namespace.add_child(enum)
625
626		for cEnumValue in cenum.values:
627			valueName = EnumValueName()
628			valueName.from_camel_case(cEnumValue.name, namespace=name)
629			aEnumValue = EnumValue(valueName)
630			aEnumValue.briefDescription = cEnumValue.briefDoc
631			if cEnumValue.value is not None:
632				try:
633					aEnumValue.value_from_string(cEnumValue.value)
634				except ValueError:
635					raise Error('{0} enum value has an invalid definition ({1})'.format(cEnumValue.name, cEnumValue.value))
636			enum.add_value(aEnumValue)
637
638		self.enumsIndex[nameStr] = enum
639		return enum
640
641	def parse_class(self, cclass):
642		if cclass.name in self.classBl:
643			raise BlacklistedException('{0} is blacklisted'.format(cclass.name));
644
645		if cclass.name.endswith('Cbs'):
646			_class = self._parse_listener(cclass)
647			self.interfacesIndex[cclass.name] = _class
648		else:
649			_class = self._parse_class(cclass)
650			self.classesIndex[cclass.name] = _class
651		self.namespace.add_child(_class)
652		return _class
653
654	def _parse_class(self, cclass):
655		name = ClassName()
656		name.from_camel_case(cclass.name, namespace=self.namespace.name)
657		_class = Class(name)
658		_class.briefDescription = cclass.briefDoc
659		_class.refcountable = self._class_is_refcountable(cclass)
660
661		for cproperty in cclass.properties.values():
662			try:
663				if cproperty.name != 'callbacks':
664					absProperty = self._parse_property(cproperty, namespace=name)
665					_class.add_property(absProperty)
666				else:
667					_class.listenerInterface = self.interfacesIndex[cproperty.getter.returnArgument.ctype]
668			except Error as e:
669				print('Could not parse {0} property in {1}: {2}'.format(cproperty.name, cclass.name, e.args[0]))
670
671		for cMethod in cclass.instanceMethods.values():
672			try:
673				method = self.parse_method(cMethod, namespace=name)
674				if method.name.to_snake_case() == 'add_callbacks' or method.name.to_snake_case() == 'remove_callbacks':
675					if _class.listenerInterface is None or not _class.multilistener:
676						_class.multilistener = True
677						_class.listenerInterface = self.interfacesIndex[_class.name.to_camel_case(fullName=True) + 'Cbs']
678				elif isinstance(method.returnType, ClassType) and method.returnType.name.endswith('Cbs'):
679					pass
680				else:
681					_class.add_instance_method(method)
682
683			except BlacklistedException:
684				pass
685			except Error as e:
686				print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0]))
687
688		for cMethod in cclass.classMethods.values():
689			try:
690				method = self.parse_method(cMethod, type=Method.Type.Class, namespace=name)
691				_class.add_class_method(method)
692			except BlacklistedException:
693				pass
694			except Error as e:
695				print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0]))
696
697		return _class
698
699	def _parse_property(self, cproperty, namespace=None):
700		name = PropertyName()
701		name.from_snake_case(cproperty.name)
702		if (cproperty.setter is not None and len(cproperty.setter.arguments) == 1) or (cproperty.getter is not None and len(cproperty.getter.arguments) == 0):
703			methodType = Method.Type.Class
704		else:
705			methodType = Method.Type.Instance
706		aproperty = Property(name)
707		if cproperty.setter is not None:
708			method = self.parse_method(cproperty.setter, namespace=namespace, type=methodType)
709			aproperty.setter = method
710		if cproperty.getter is not None:
711			method = self.parse_method(cproperty.getter, namespace=namespace, type=methodType)
712			aproperty.getter = method
713		return aproperty
714
715
716	def _parse_listener(self, cclass):
717		name = InterfaceName()
718		name.from_camel_case(cclass.name, namespace=self.namespace.name)
719
720		if name.words[len(name.words)-1] == 'cbs':
721			name.words[len(name.words)-1] = 'listener'
722		else:
723			raise Error('{0} is not a listener'.format(cclass.name))
724
725		listener = Interface(name)
726		listener.briefDescription = cclass.briefDoc
727
728		for property in cclass.properties.values():
729			if property.name != 'user_data':
730				try:
731					method = self._parse_listener_property(property, listener, cclass.events)
732					listener.add_method(method)
733				except Error as e:
734					print('Could not parse property \'{0}\' of listener \'{1}\': {2}'.format(property.name, cclass.name, e.args[0]))
735
736		return listener
737
738	def _parse_listener_property(self, property, listener, events):
739		methodName = MethodName()
740		methodName.from_snake_case(property.name)
741		methodName.words.insert(0, 'on')
742		methodName.prev = listener.name
743
744		if property.getter is not None:
745			eventName = property.getter.returnArgument.ctype
746		elif property.setter is not None and len(property.setter.arguments) == 2:
747			eventName = property.setter.arguments[1].ctype
748		else:
749			raise Error('event name for {0} property of {1} listener not found'.format(property.name, listener.name.to_snake_case(fullName=True)))
750
751		try:
752			event = events[eventName]
753		except KeyError:
754			raise Error('invalid event name \'{0}\''.format(eventName))
755
756		method = Method(methodName)
757		method.returnType = self.parse_type(event.returnArgument)
758		for arg in event.arguments:
759			argName = ArgName()
760			argName.from_snake_case(arg.name)
761			argument = Argument(argName, self.parse_type(arg))
762			method.add_arguments(argument)
763
764		return method
765
766	def parse_method(self, cfunction, namespace, type=Method.Type.Instance):
767		name = MethodName()
768		name.from_snake_case(cfunction.name, namespace=namespace)
769
770		if self._is_blacklisted(name):
771			raise BlacklistedException('{0} is blacklisted'.format(name.to_c()));
772
773		method = Method(name, type=type)
774		method.briefDescription = cfunction.briefDoc
775		method.deprecated = cfunction.deprecated
776		method.returnType = self.parse_type(cfunction.returnArgument)
777
778		for arg in cfunction.arguments:
779			if type == Method.Type.Instance and arg is cfunction.arguments[0]:
780				method.constMethod = ('const' in arg.completeType.split(' '))
781			else:
782				aType = self.parse_type(arg)
783				argName = ArgName()
784				argName.from_snake_case(arg.name)
785				absArg = Argument(argName, aType)
786				method.add_arguments(absArg)
787
788		self.methodsIndex[cfunction.name] = method
789		return method
790
791	def parse_type(self, cType):
792		if cType.ctype in self.cBaseType or re.match(self.regexFixedSizeInteger, cType.ctype):
793			absType = self.parse_c_base_type(cType.completeType)
794		elif cType.ctype in self.enumsIndex:
795			absType = EnumType(cType.ctype, enumDesc=self.enumsIndex[cType.ctype])
796		elif cType.ctype in self.classesIndex or cType.ctype in self.interfacesIndex:
797			absType = ClassType(cType.ctype)
798			absType.isconst = cType.completeType.startswith('const ')
799			absType.isref = cType.completeType.endswith('*')
800		elif cType.ctype == self.cListType:
801			absType = ListType(cType.containedType)
802		elif cType.ctype.endswith('Mask'):
803			absType = BaseType('integer', isUnsigned=True)
804		else:
805			raise Error('Unknown C type \'{0}\''.format(cType.ctype))
806
807		absType.cname = cType.completeType
808		return absType
809
810	def parse_c_base_type(self, cDecl):
811		declElems = cDecl.split(' ')
812		param = {}
813		name = None
814		for elem in declElems:
815			if elem == 'const':
816				if name is None:
817					param['isconst'] = True
818			elif elem == 'unsigned':
819				param['isUnsigned'] = True
820			elif elem == 'char':
821				name = 'character'
822			elif elem == 'void':
823				name = 'void'
824			elif elem == 'bool_t':
825				name = 'boolean'
826			elif elem in ['short', 'long']:
827				param['size'] = elem
828			elif elem == 'int':
829				name = 'integer'
830			elif elem == 'float':
831				name = 'floatant'
832				param['size'] = 'float'
833			elif elem == 'size_t':
834				name = 'size'
835			elif elem == 'time_t':
836				name = 'time'
837			elif elem == 'double':
838				name = 'floatant'
839				if 'size' in param and param['size'] == 'long':
840					param['size'] = 'long double'
841				else:
842					param['size'] = 'double'
843			elif elem == 'LinphoneStatus':
844				name = 'status'
845			elif elem == '*':
846				if name is not None:
847					if name == 'character':
848						name = 'string'
849					elif name == 'string':
850						name = 'string_array'
851					elif 'isref' not in param or param['isref'] is False:
852						param['isref'] = True
853					else:
854						raise Error('Unhandled double-pointer')
855			else:
856				matchCtx = re.match(self.regexFixedSizeInteger, elem)
857				if matchCtx:
858					name = 'integer'
859					if matchCtx.group(1) == 'u':
860						param['isUnsigned'] = True
861
862					param['size'] = int(matchCtx.group(2))
863					if param['size'] not in [8, 16, 32, 64]:
864						raise Error('{0} C basic type has an invalid size ({1})'.format(cDecl, param['size']))
865
866
867		if name is not None:
868			return BaseType(name, **param)
869		else:
870			raise Error('could not find type in \'{0}\''.format(cDecl))
871