From e28468f05f32bbf002f35ac05a0cfdae8d25b1da Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 14 Apr 2017 18:24:19 +0200 Subject: [PATCH 1/9] Added C# wrapper --- wrappers/csharp/abstractapi.py | 831 ++++++++++++++++++++++++++ wrappers/csharp/genwrapper.py | 590 ++++++++++++++++++ wrappers/csharp/wrapper_impl.mustache | 338 +++++++++++ 3 files changed, 1759 insertions(+) create mode 100644 wrappers/csharp/abstractapi.py create mode 100644 wrappers/csharp/genwrapper.py create mode 100644 wrappers/csharp/wrapper_impl.mustache diff --git a/wrappers/csharp/abstractapi.py b/wrappers/csharp/abstractapi.py new file mode 100644 index 000000000..05e44472b --- /dev/null +++ b/wrappers/csharp/abstractapi.py @@ -0,0 +1,831 @@ +# Copyright (C) 2017 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 re +import genapixml as CApi + + +class Error(RuntimeError): + pass + + +class BlacklistedException(Error): + pass + + +class Name(object): + camelCaseParsingRegex = re.compile('[A-Z][a-z0-9]*') + lowerCamelCaseSplitingRegex = re.compile('([a-z][a-z0-9]*)([A-Z][a-z0-9]*)') + + def __init__(self): + self.words = [] + self.prev = None + + def copy(self): + nameType = type(self) + name = nameType() + name.words = list(self.words) + name.prev = None if self.prev is None else self.prev.copy() + return name + + def delete_prefix(self, prefix): + it = self + _next = None + while it is not None and it.words != prefix.words: + _next = it + it = it.prev + + if it is None or it != prefix: + raise Error('no common prefix') + elif _next is not None: + _next.prev = None + + def _set_namespace(self, namespace): + self.prev = namespace + if self.prev is not None: + prefix = namespace.to_word_list() + i = 0 + while i 0: + suffix = self.words[-1] + if MethodName.regex.match(suffix) is not None: + self.overloadRef = int(suffix) + del self.words[-1] + + def to_c(self): + suffix = ('_' + str(self.overloadRef)) if self.overloadRef > 0 else '' + return self.to_snake_case(fullName=True) + suffix + + +class ArgName(Name): + def to_c(self): + return self.to_snake_case() + + +class PropertyName(ArgName): + pass + + +class NamespaceName(Name): + def __init__(self, *params): + Name.__init__(self) + if len(params) > 0: + self.words = params[0] + + +class Object(object): + def __init__(self, name): + self.name = name + self.parent = None + self.deprecated = False + + def find_first_ancestor_by_type(self, _type): + ancestor = self.parent + while ancestor is not None and type(ancestor) is not _type: + ancestor = ancestor.parent + return ancestor + + +class Type(Object): + def __init__(self, name, isconst=False, isref=False): + Object.__init__(self, name) + self.isconst = isconst + self.isref = isref + self.cname = None + + +class BaseType(Type): + def __init__(self, name, isconst=False, isref=False, size=None, isUnsigned=False): + Type.__init__(self, name, isconst=isconst, isref=isref) + self.size = size + self.isUnsigned = isUnsigned + + +class EnumType(Type): + def __init__(self, name, isconst=False, isref=False, enumDesc=None): + Type.__init__(self, name, isconst=isconst, isref=isref) + self.desc = enumDesc + + +class ClassType(Type): + def __init__(self, name, isconst=False, isref=False, classDesc=None): + Type.__init__(self, name, isconst=isconst, isref=isref) + self.desc = classDesc + + +class ListType(Type): + def __init__(self, containedTypeName, isconst=False, isref=False): + Type.__init__(self, 'list', isconst=isconst, isref=isref) + self.containedTypeName = containedTypeName + self._containedTypeDesc = None + + def set_contained_type_desc(self, desc): + self._containedTypeDesc = desc + desc.parent = self + + def get_contained_type_desc(self): + return self._containedTypeDesc + + containedTypeDesc = property(fset=set_contained_type_desc, fget=get_contained_type_desc) + + +class DocumentableObject(Object): + def __init__(self, name): + Object.__init__(self, name) + self.briefDescription = None + self.detailedDescription = None + self.deprecated = None + + def set_from_c(self, cObject, namespace=None): + self.briefDescription = cObject.briefDescription + self.detailedDescription = cObject.detailedDescription + self.deprecated = cObject.deprecated + self.parent = namespace + + def get_namespace_object(self): + if isinstance(self, (Namespace,Enum,Class)): + return self + elif self.parent is None: + raise Error('{0} is not attached to a namespace object'.format(self)) + else: + return self.parent.get_namespace_object() + + +class Namespace(DocumentableObject): + def __init__(self, name): + DocumentableObject.__init__(self, name) + self.children = [] + + def add_child(self, child): + self.children.append(child) + child.parent = self + + +class Flag: + def __init__(self, position): + self.position = position + + +class EnumValue(DocumentableObject): + def __init__(self, name): + DocumentableObject.__init__(self, name) + self.value = None + + def value_from_string(self, stringValue): + m = re.match('^1\s*<<\s*([0-9]+)$', stringValue) + if m is not None: + self.value = Flag(int(m.group(1))) + else: + self.value = int(stringValue, base=0) + + +class Enum(DocumentableObject): + def __init__(self, name): + DocumentableObject.__init__(self, name) + self.values = [] + + def add_value(self, value): + self.values.append(value) + value.parent = self + + def set_from_c(self, cEnum, namespace=None): + Object.set_from_c(self, cEnum, namespace=namespace) + + if 'associatedTypedef' in dir(cEnum): + name = cEnum.associatedTypedef.name + else: + name = cEnum.name + + self.name = EnumName() + self.name.prev = None if namespace is None else namespace.name + self.name.set_from_c(name) + + for cEnumValue in cEnum.values: + aEnumValue = EnumValue() + aEnumValue.set_from_c(cEnumValue, namespace=self) + self.add_value(aEnumValue) + + +class Argument(DocumentableObject): + def __init__(self, name, argType, optional=False, default=None): + DocumentableObject.__init__(self, name) + self._type = argType + argType.parent = self + self.optional = optional + self.default = default + + def _set_type(self, _type): + self._type = _type + _type.parent = self + + def _get_type(self): + return self._type + + type = property(fset=_set_type, fget=_get_type) + + +class Method(DocumentableObject): + class Type: + Instance = 0, + Class = 1 + + def __init__(self, name, type=Type.Instance): + DocumentableObject.__init__(self, name) + self.type = type + self.constMethod = False + self.args = [] + self._returnType = None + + def _set_return_type(self, returnType): + self._returnType = returnType + returnType.parent = self + + def _get_return_type(self): + return self._returnType + + def add_arguments(self, arg): + self.args.append(arg) + arg.parent = self + + returnType = property(fset=_set_return_type, fget=_get_return_type) + + +class Property(DocumentableObject): + def __init__(self, name): + DocumentableObject.__init__(self, name) + self._setter = None + self._getter = None + self._type = None + + def set_setter(self, setter): + self._setter = setter + setter.parent = self + + def get_setter(self): + return self._setter + + def set_getter(self, getter): + self._getter = getter + if self._type is None: + self._type = getter.returnType + getter.parent = self + + def get_getter(self): + return self._getter + + setter = property(fset=set_setter, fget=get_setter) + getter = property(fset=set_getter, fget=get_getter) + + +class Class(DocumentableObject): + def __init__(self, name): + DocumentableObject.__init__(self, name) + self.properties = [] + self.instanceMethods = [] + self.classMethods = [] + self._listenerInterface = None + self.multilistener = False + self.refcountable = False + + def add_property(self, property): + self.properties.append(property) + property.parent = self + + def add_instance_method(self, method): + self.instanceMethods.append(method) + method.parent = self + + def add_class_method(self, method): + self.classMethods.append(method) + method.parent = self + + def set_listener_interface(self, interface): + self._listenerInterface = interface + interface._listenedClass = self + + def get_listener_interface(self): + return self._listenerInterface + + listenerInterface = property(fget=get_listener_interface, fset=set_listener_interface) + + +class Interface(DocumentableObject): + def __init__(self, name): + DocumentableObject.__init__(self, name) + self.methods = [] + self._listenedClass = None + + def add_method(self, method): + self.methods.append(method) + method.parent = self + + def get_listened_class(self): + return self._listenedClass + + listenedClass = property(fget=get_listened_class) + + +class CParser(object): + def __init__(self, cProject): + self.cBaseType = ['void', 'bool_t', 'char', 'short', 'int', 'long', 'size_t', 'time_t', 'float', 'double', 'LinphoneStatus'] + self.cListType = 'bctbx_list_t' + self.regexFixedSizeInteger = '^(u?)int(\d?\d)_t$' + self.methodBl = ['ref', 'unref', 'new', 'destroy', 'getCurrentCallbacks', 'setUserData', 'getUserData'] + self.functionBl = [ + 'linphone_vcard_get_belcard'] # manualy wrapped + + self.classBl = ['LpConfig', + 'LinphonePlayer', + 'LinphoneIntRange', + 'LinphoneCallStats', + 'LinphoneCoreVTable', + 'LinphoneVideoPolicy', + 'LinphoneSipTransports'] # temporarly blacklisted + + # list of classes that must be concidered as refcountable even if + # they are no ref()/unref() methods + self.forcedRefcountableClasses = ['LinphoneFactory'] + + self.cProject = cProject + + self.enumsIndex = {} + for enum in self.cProject.enums: + if enum.associatedTypedef is None: + self.enumsIndex[enum.name] = None + else: + self.enumsIndex[enum.associatedTypedef.name] = None + + self.classesIndex = {} + self.interfacesIndex = {} + for _class in self.cProject.classes: + if _class.name not in self.classBl: + if _class.name.endswith('Cbs'): + self.interfacesIndex[_class.name] = None + else: + self.classesIndex[_class.name] = None + + name = NamespaceName() + name.from_snake_case('linphone') + + self.namespace = Namespace(name) + + def _is_blacklisted(self, name): + if type(name) is MethodName: + return name.to_camel_case(lower=True) in self.methodBl or name.to_c() in self.functionBl + elif type(name) is ClassName: + return name.to_c() in self.classBl + else: + return False + + def parse_all(self): + for enum in self.cProject.enums: + try: + self.parse_enum(enum) + except Error as e: + print('Could not parse \'{0}\' enum: {1}'.format(enum.name, e.args[0])) + + for _class in self.cProject.classes: + try: + self.parse_class(_class) + except BlacklistedException: + pass + except Error as e: + print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0])) + + self._fix_all_types() + + + def _class_is_refcountable(self, _class): + if _class.name in self.forcedRefcountableClasses: + return True + + for method in _class.instanceMethods: + if method.startswith(_class.cFunctionPrefix) and method[len(_class.cFunctionPrefix):] == 'ref': + return True + return False + + def _fix_all_types_in_class_or_interface(self, _class): + if _class is not None: + if type(_class) is Class: + self._fix_all_types_in_class(_class) + else: + self._fix_all_types_in_interface(_class) + + def _fix_all_types(self): + for _class in self.interfacesIndex.values(): + self._fix_all_types_in_class_or_interface(_class) + for _class in self.classesIndex.values(): + self._fix_all_types_in_class_or_interface(_class) + + def _fix_all_types_in_class(self, _class): + for property in _class.properties: + if property.setter is not None: + self._fix_all_types_in_method(property.setter) + if property.getter is not None: + self._fix_all_types_in_method(property.getter) + + for method in (_class.instanceMethods + _class.classMethods): + self._fix_all_types_in_method(method) + + def _fix_all_types_in_interface(self, interface): + for method in interface.methods: + self._fix_all_types_in_method(method) + + def _fix_all_types_in_method(self, method): + try: + self._fix_type(method.returnType) + for arg in method.args: + self._fix_type(arg.type) + except Error as e: + print('warning: some types could not be fixed in {0}() function: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0])) + + def _fix_type(self, _type): + if isinstance(_type, EnumType) and _type.desc is None: + _type.desc = self.enumsIndex[_type.name] + elif isinstance(_type, ClassType) and _type.desc is None: + if _type.name in self.classesIndex: + _type.desc = self.classesIndex[_type.name] + else: + _type.desc = self.interfacesIndex[_type.name] + elif isinstance(_type, ListType) and _type.containedTypeDesc is None: + if _type.containedTypeName in self.classesIndex: + _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.classesIndex[_type.containedTypeName]) + elif _type.containedTypeName in self.interfacesIndex: + _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.interfacesIndex[_type.containedTypeName]) + elif _type.containedTypeName in self.enumsIndex: + _type.containedTypeDesc = EnumType(_type.containedTypeName, enumDesc=self.enumsIndex[_type.containedTypeName]) + else: + if _type.containedTypeName is not None: + _type.containedTypeDesc = self.parse_c_base_type(_type.containedTypeName) + else: + raise Error('bctbx_list_t type without specified contained type') + + def parse_enum(self, cenum): + if 'associatedTypedef' in dir(cenum): + nameStr = cenum.associatedTypedef.name + else: + nameStr = cenum.name + + name = EnumName() + name.from_camel_case(nameStr, namespace=self.namespace.name) + enum = Enum(name) + self.namespace.add_child(enum) + + for cEnumValue in cenum.values: + valueName = EnumValueName() + valueName.from_camel_case(cEnumValue.name, namespace=name) + aEnumValue = EnumValue(valueName) + if cEnumValue.value is not None: + try: + aEnumValue.value_from_string(cEnumValue.value) + except ValueError: + raise Error('{0} enum value has an invalid definition ({1})'.format(cEnumValue.name, cEnumValue.value)) + enum.add_value(aEnumValue) + + self.enumsIndex[nameStr] = enum + return enum + + def parse_class(self, cclass): + if cclass.name in self.classBl: + raise BlacklistedException('{0} is blacklisted'.format(cclass.name)); + + if cclass.name.endswith('Cbs'): + _class = self._parse_listener(cclass) + self.interfacesIndex[cclass.name] = _class + else: + _class = self._parse_class(cclass) + self.classesIndex[cclass.name] = _class + self.namespace.add_child(_class) + return _class + + def _parse_class(self, cclass): + name = ClassName() + name.from_camel_case(cclass.name, namespace=self.namespace.name) + _class = Class(name) + _class.refcountable = self._class_is_refcountable(cclass) + + for cproperty in cclass.properties.values(): + try: + if cproperty.name != 'callbacks': + absProperty = self._parse_property(cproperty, namespace=name) + _class.add_property(absProperty) + else: + _class.listenerInterface = self.interfacesIndex[cproperty.getter.returnArgument.ctype] + except Error as e: + print('Could not parse {0} property in {1}: {2}'.format(cproperty.name, cclass.name, e.args[0])) + + for cMethod in cclass.instanceMethods.values(): + try: + method = self.parse_method(cMethod, namespace=name) + if method.name.to_snake_case() == 'add_callbacks' or method.name.to_snake_case() == 'remove_callbacks': + if _class.listenerInterface is None or not _class.multilistener: + _class.multilistener = True + _class.listenerInterface = self.interfacesIndex[_class.name.to_camel_case(fullName=True) + 'Cbs'] + elif isinstance(method.returnType, ClassType) and method.returnType.name.endswith('Cbs'): + pass + else: + _class.add_instance_method(method) + + except BlacklistedException: + pass + except Error as e: + print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0])) + + for cMethod in cclass.classMethods.values(): + try: + method = self.parse_method(cMethod, type=Method.Type.Class, namespace=name) + _class.add_class_method(method) + except BlacklistedException: + pass + except Error as e: + print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0])) + + return _class + + def _parse_property(self, cproperty, namespace=None): + name = PropertyName() + name.from_snake_case(cproperty.name) + if (cproperty.setter is not None and len(cproperty.setter.arguments) == 1) or (cproperty.getter is not None and len(cproperty.getter.arguments) == 0): + methodType = Method.Type.Class + else: + methodType = Method.Type.Instance + aproperty = Property(name) + if cproperty.setter is not None: + method = self.parse_method(cproperty.setter, namespace=namespace, type=methodType) + aproperty.setter = method + if cproperty.getter is not None: + method = self.parse_method(cproperty.getter, namespace=namespace, type=methodType) + aproperty.getter = method + return aproperty + + + def _parse_listener(self, cclass): + name = InterfaceName() + name.from_camel_case(cclass.name, namespace=self.namespace.name) + + if name.words[len(name.words)-1] == 'cbs': + name.words[len(name.words)-1] = 'listener' + else: + raise Error('{0} is not a listener'.format(cclass.name)) + + listener = Interface(name) + + for property in cclass.properties.values(): + if property.name != 'user_data': + try: + method = self._parse_listener_property(property, listener, cclass.events) + listener.add_method(method) + except Error as e: + print('Could not parse property \'{0}\' of listener \'{1}\': {2}'.format(property.name, cclass.name, e.args[0])) + + return listener + + def _parse_listener_property(self, property, listener, events): + methodName = MethodName() + methodName.from_snake_case(property.name) + methodName.words.insert(0, 'on') + methodName.prev = listener.name + + if property.getter is not None: + eventName = property.getter.returnArgument.ctype + elif property.setter is not None and len(property.setter.arguments) == 2: + eventName = property.setter.arguments[1].ctype + else: + raise Error('event name for {0} property of {1} listener not found'.format(property.name, listener.name.to_snake_case(fullName=True))) + + try: + event = events[eventName] + except KeyError: + raise Error('invalid event name \'{0}\''.format(eventName)) + + method = Method(methodName) + method.returnType = self.parse_type(event.returnArgument) + for arg in event.arguments: + argName = ArgName() + argName.from_snake_case(arg.name) + argument = Argument(argName, self.parse_type(arg)) + method.add_arguments(argument) + + return method + + def parse_method(self, cfunction, namespace, type=Method.Type.Instance): + name = MethodName() + name.from_snake_case(cfunction.name, namespace=namespace) + + if self._is_blacklisted(name): + raise BlacklistedException('{0} is blacklisted'.format(name.to_c())); + + method = Method(name, type=type) + method.deprecated = cfunction.deprecated + method.returnType = self.parse_type(cfunction.returnArgument) + + for arg in cfunction.arguments: + if type == Method.Type.Instance and arg is cfunction.arguments[0]: + method.constMethod = ('const' in arg.completeType.split(' ')) + else: + aType = self.parse_type(arg) + argName = ArgName() + argName.from_snake_case(arg.name) + absArg = Argument(argName, aType) + method.add_arguments(absArg) + + return method + + def parse_type(self, cType): + if cType.ctype in self.cBaseType or re.match(self.regexFixedSizeInteger, cType.ctype): + absType = self.parse_c_base_type(cType.completeType) + elif cType.ctype in self.enumsIndex: + absType = EnumType(cType.ctype, enumDesc=self.enumsIndex[cType.ctype]) + elif cType.ctype in self.classesIndex or cType.ctype in self.interfacesIndex: + absType = ClassType(cType.ctype) + absType.isconst = cType.completeType.startswith('const ') + absType.isref = cType.completeType.endswith('*') + elif cType.ctype == self.cListType: + absType = ListType(cType.containedType) + else: + raise Error('Unknown C type \'{0}\''.format(cType.ctype)) + + absType.cname = cType.completeType + return absType + + def parse_c_base_type(self, cDecl): + declElems = cDecl.split(' ') + param = {} + name = None + for elem in declElems: + if elem == 'const': + if name is None: + param['isconst'] = True + elif elem == 'unsigned': + param['isUnsigned'] = True + elif elem == 'char': + name = 'character' + elif elem == 'void': + name = 'void' + elif elem == 'bool_t': + name = 'boolean' + elif elem in ['short', 'long']: + param['size'] = elem + elif elem == 'int': + name = 'integer' + elif elem == 'float': + name = 'floatant' + param['size'] = 'float' + elif elem == 'size_t': + name = 'size' + elif elem == 'time_t': + name = 'time' + elif elem == 'double': + name = 'floatant' + if 'size' in param and param['size'] == 'long': + param['size'] = 'long double' + else: + param['size'] = 'double' + elif elem == 'LinphoneStatus': + name = 'status' + elif elem == '*': + if name is not None: + if name == 'character': + name = 'string' + elif name == 'string': + name = 'string_array' + elif 'isref' not in param or param['isref'] is False: + param['isref'] = True + else: + raise Error('Unhandled double-pointer') + else: + matchCtx = re.match(self.regexFixedSizeInteger, elem) + if matchCtx: + name = 'integer' + if matchCtx.group(1) == 'u': + param['isUnsigned'] = True + + param['size'] = int(matchCtx.group(2)) + if param['size'] not in [8, 16, 32, 64]: + raise Error('{0} C basic type has an invalid size ({1})'.format(cDecl, param['size'])) + + + if name is not None: + return BaseType(name, **param) + else: + raise Error('could not find type in \'{0}\''.format(cDecl)) diff --git a/wrappers/csharp/genwrapper.py b/wrappers/csharp/genwrapper.py new file mode 100644 index 000000000..4221bb202 --- /dev/null +++ b/wrappers/csharp/genwrapper.py @@ -0,0 +1,590 @@ +#!/usr/bin/python + +# Copyright (C) 2017 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 os +import sys +import pystache + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'tools')) +print sys.path +import genapixml as CApi +import abstractapi as AbsApi + +class CsharpTranslator(object): + def __init__(self): + self.ignore = [] + + def init_method_dict(self): + methodDict = {} + methodDict['has_impl'] = True + methodDict['impl'] = {} + methodDict['is_string_list'] = False + methodDict['is_class_list'] = False + methodDict['list_type'] = None + methodDict['is_string'] = False + methodDict['is_bool'] = False + methodDict['is_class'] = False + methodDict['is_enum'] = False + methodDict['is_generic'] = False + methodDict['takeRef'] = 'true' + return methodDict + + def get_class_array_type(self, name): + length = len(name) + if length > 11 and name[:11] == 'IEnumerable': + return name[12:length-1] + return None + + def is_generic(self, methodDict): + return not methodDict['is_string'] and not methodDict['is_bool'] and not methodDict['is_class'] and not methodDict['is_enum'] and methodDict['list_type'] == None + + def translate_method_name(self, name, recursive=False, topAncestor=None): + translatedName = name.to_camel_case(lower=True) + + if name.prev is None or not recursive or name.prev is topAncestor: + return translatedName + + def translate_argument_name(self, name): + argname = name.to_camel_case(lower=True) + arg = argname + if argname == "params": + arg = "parameters" + elif argname == "event": + arg = "ev" + elif argname == "ref": + arg = "reference" + elif argname == "value": + arg = "val" + return arg + + def translate_base_type(self, _type, isArg, dllImport=True): + if _type.name == 'void': + if _type.isref: + return 'IntPtr' + return 'void' + elif _type.name == 'status': + if dllImport: + return 'int' + else: + return 'void' + elif _type.name == 'boolean': + if dllImport: + res = 'int' # In C the bool_t is an integer + else: + res = 'bool' + elif _type.name == 'integer': + if _type.isUnsigned: + res = 'uint' + else: + res = 'int' + elif _type.name == 'string': + if dllImport: + if isArg: + return 'string' + else: + res = 'IntPtr' # Return as IntPtr and get string with Marshal.PtrToStringAnsi() + else: + return 'string' + elif _type.name == 'character': + if _type.isUnsigned: + res = 'byte' + else: + res = 'sbyte' + elif _type.name == 'time': + res = 'long' #TODO check + elif _type.name == 'size': + res = 'long' #TODO check + elif _type.name == 'floatant': + return 'float' + elif _type.name == 'string_array': + if dllImport or isArg: + return 'IntPtr' + else: + return 'IEnumerable' + else: + raise AbsApi.Error('\'{0}\' is not a base abstract type'.format(_type.name)) + + return res + + def is_linphone_type(self, _type, isArg, dllImport=True): + if type(_type) is AbsApi.ClassType: + return False if dllImport else True + elif type(_type) is AbsApi.EnumType: + return False if dllImport else True + + def translate_type(self, _type, isArg, dllImport=True): + if type(_type) is AbsApi.EnumType: + if dllImport and isArg: + return 'int' + return _type.desc.name.to_camel_case() + elif type(_type) is AbsApi.ClassType: + return "IntPtr" if dllImport else _type.desc.name.to_camel_case() + elif type(_type) is AbsApi.BaseType: + return self.translate_base_type(_type, isArg, dllImport) + elif type(_type) is AbsApi.ListType: + if isArg: + raise AbsApi.Error('Lists as params are not supported yet') + elif dllImport: + return 'IntPtr' + else: + if type(_type.containedTypeDesc) is AbsApi.BaseType: + if _type.containedTypeDesc.name == 'string': + return 'IEnumerable' + else: + raise AbsApi.Error('translation of bctbx_list_t of basic C types is not supported') + elif type(_type.containedTypeDesc) is AbsApi.ClassType: + ptrType = _type.containedTypeDesc.desc.name.to_camel_case() + return 'IEnumerable<' + ptrType + '>' + else: + if _type.containedTypeDesc: + raise AbsApi.Error('translation of bctbx_list_t of enums') + else: + raise AbsApi.Error('translation of bctbx_list_t of unknow type !') + + def translate_argument(self, arg, dllImport=True): + return '{0} {1}'.format(self.translate_type(arg.type, True, dllImport), self.translate_argument_name(arg.name)) + + def throws_exception(self, return_type): + if type(return_type) is AbsApi.BaseType: + if return_type.name == 'status': + return True + return False + + def translate_method(self, method, static=False, genImpl=True): + if method.name.to_snake_case(fullName=True) in self.ignore: + raise AbsApi.Error('{0} has been escaped'.format(method.name.to_snake_case(fullName=True))) + + methodElems = {} + methodElems['return'] = self.translate_type(method.returnType, False) + methodElems['name'] = method.name.to_c() + methodElems['params'] = '' if static else 'IntPtr thiz' + for arg in method.args: + if arg is not method.args[0] or not static: + methodElems['params'] += ', ' + methodElems['params'] += self.translate_argument(arg) + + methodDict = {} + methodDict['prototype'] = "static extern {return} {name}({params});".format(**methodElems) + + methodDict['has_impl'] = genImpl + if genImpl: + methodDict['impl'] = {} + methodDict['impl']['static'] = 'static ' if static else '' + methodDict['impl']['exception'] = self.throws_exception(method.returnType) + methodDict['impl']['type'] = self.translate_type(method.returnType, False, False) + methodDict['impl']['name'] = method.name.to_camel_case() + methodDict['impl']['override'] = 'override ' if method.name.to_camel_case() == 'ToString' else '' + methodDict['impl']['return'] = '' if methodDict['impl']['type'] == "void" else 'return ' + methodDict['impl']['c_name'] = method.name.to_c() + methodDict['impl']['nativePtr'] = '' if static else ('nativePtr, ' if len(method.args) > 0 else 'nativePtr') + + methodDict['list_type'] = self.get_class_array_type(methodDict['impl']['type']) + methodDict['is_string_list'] = methodDict['list_type'] == 'string' + methodDict['is_class_list'] = not methodDict['list_type'] == None and not methodDict['list_type'] == 'string' + + methodDict['is_string'] = methodDict['impl']['type'] == "string" + methodDict['is_bool'] = methodDict['impl']['type'] == "bool" + methodDict['is_class'] = self.is_linphone_type(method.returnType, False, False) and type(method.returnType) is AbsApi.ClassType + methodDict['is_enum'] = self.is_linphone_type(method.returnType, False, False) and type(method.returnType) is AbsApi.EnumType + methodDict['is_generic'] = self.is_generic(methodDict) + methodDict['takeRef'] = 'true' + if type(method.returnType.parent) is AbsApi.Method and len(method.returnType.parent.name.words) >=1: + if method.returnType.parent.name.words == ['new'] or method.returnType.parent.name.words[0] == 'create': + methodDict['takeRef'] = 'false' + + methodDict['impl']['args'] = '' + methodDict['impl']['c_args'] = '' + for arg in method.args: + if arg is not method.args[0]: + methodDict['impl']['args'] += ', ' + methodDict['impl']['c_args'] += ', ' + if self.is_linphone_type(arg.type, False, False): + if type(arg.type) is AbsApi.ClassType: + argname = self.translate_argument_name(arg.name) + methodDict['impl']['c_args'] += argname + " != null ? " + argname + ".nativePtr : IntPtr.Zero" + else: + methodDict['impl']['c_args'] += '(int)' + self.translate_argument_name(arg.name) + elif self.translate_type(arg.type, False, False) == "bool": + methodDict['impl']['c_args'] += self.translate_argument_name(arg.name) + " ? 1 : 0" + else: + methodDict['impl']['c_args'] += self.translate_argument_name(arg.name) + methodDict['impl']['args'] += self.translate_argument(arg, False) + + return methodDict + +########################################################################################################################################### + + def translate_property_getter(self, prop, name, static=False): + methodDict = self.translate_method(prop, static, False) + + methodDict['property_static'] = 'static ' if static else '' + methodDict['property_return'] = self.translate_type(prop.returnType, False, False) + methodDict['property_name'] = (name[3:] if len(name) > 3 else 'Instance') if name[:3] == "Get" else name + + methodDict['has_property'] = True + methodDict['has_getter'] = True + methodDict['has_setter'] = False + methodDict['return'] = methodDict['property_return'] + methodDict['exception'] = self.throws_exception(prop.returnType) + methodDict['getter_nativePtr'] = '' if static else 'nativePtr' + methodDict['getter_c_name'] = prop.name.to_c() + + methodDict['list_type'] = self.get_class_array_type(methodDict['return']) + methodDict['is_string_list'] = methodDict['list_type'] == 'string' + methodDict['is_class_list'] = not methodDict['list_type'] == None and not methodDict['list_type'] == 'string' + + methodDict['is_string'] = methodDict['return'] == "string" + methodDict['is_bool'] = methodDict['return'] == "bool" + methodDict['is_class'] = self.is_linphone_type(prop.returnType, False, False) and type(prop.returnType) is AbsApi.ClassType + methodDict['is_enum'] = self.is_linphone_type(prop.returnType, False, False) and type(prop.returnType) is AbsApi.EnumType + methodDict['is_generic'] = self.is_generic(methodDict) + methodDict['takeRef'] = 'true' + if type(prop.returnType.parent) is AbsApi.Method and len(prop.returnType.parent.name.words) >=1: + if prop.returnType.parent.name.words == ['new'] or prop.returnType.parent.name.words[0] == 'create': + methodDict['takeRef'] = 'false' + + return methodDict + + def translate_property_setter(self, prop, name, static=False): + methodDict = self.translate_method(prop, static, False) + + methodDict['property_static'] = 'static ' if static else '' + methodDict['property_return'] = self.translate_type(prop.args[0].type, True, False) + methodDict['property_name'] = (name[3:] if len(name) > 3 else 'Instance') if name[:3] == "Set" else name + + methodDict['has_property'] = True + methodDict['has_getter'] = False + methodDict['has_setter'] = True + methodDict['return'] = methodDict['property_return'] + methodDict['exception'] = self.throws_exception(prop.returnType) + methodDict['setter_nativePtr'] = '' if static else 'nativePtr, ' + methodDict['setter_c_name'] = prop.name.to_c() + + methodDict['list_type'] = self.get_class_array_type(methodDict['return']) + methodDict['is_string_list'] = methodDict['list_type'] == 'string' + methodDict['is_class_list'] = not methodDict['list_type'] == None and not methodDict['list_type'] == 'string' + + methodDict['is_string'] = methodDict['return'] == "string" + methodDict['is_bool'] = methodDict['return'] == "bool" + methodDict['is_class'] = self.is_linphone_type(prop.args[0].type, True, False) and type(prop.args[0].type) is AbsApi.ClassType + methodDict['is_enum'] = self.is_linphone_type(prop.args[0].type, True, False) and type(prop.args[0].type) is AbsApi.EnumType + methodDict['is_generic'] = self.is_generic(methodDict) + + return methodDict + + def translate_property_getter_setter(self, getter, setter, name, static=False): + methodDict = self.translate_property_getter(getter, name, static) + methodDictSet = self.translate_property_setter(setter, name, static) + + protoElems = {} + methodDict["prototype"] = methodDict['prototype'] + methodDict["has_second_prototype"] = True + methodDict["second_prototype"] = methodDictSet['prototype'] + + methodDict['has_setter'] = True + methodDict['exception'] = methodDictSet['exception'] + methodDict['setter_nativePtr'] = methodDictSet['setter_nativePtr'] + methodDict['setter_c_name'] = methodDictSet['setter_c_name'] + + return methodDict + + def translate_property(self, prop): + res = [] + name = prop.name.to_camel_case() + if prop.getter is not None: + if prop.setter is not None: + res.append(self.translate_property_getter_setter(prop.getter, prop.setter, name)) + else: + res.append(self.translate_property_getter(prop.getter, name)) + elif prop.setter is not None: + res.append(self.translate_property_setter(prop.setter, name)) + return res + +########################################################################################################################################### + + def translate_listener(self, _class, method): + listenedClass = method.find_first_ancestor_by_type(AbsApi.Interface).listenedClass + + listenerDict = {} + c_name_setter = listenedClass.name.to_snake_case(fullName=True) + '_cbs_set_' + method.name.to_snake_case()[3:] + listenerDict['cb_setter'] = {} + listenerDict['cb_setter']['name'] = c_name_setter + + listenerDict['delegate'] = {} + delegate_name_public = method.name.to_camel_case() + "Delegate" + delegate_name_private = delegate_name_public + "Private" + listenerDict['delegate']['name_public'] = delegate_name_public + listenerDict['delegate']['name_private'] = delegate_name_private + var_name_public = method.name.to_snake_case() + '_public' + var_name_private = method.name.to_snake_case() + '_private' + listenerDict['delegate']['var_public'] = var_name_public + listenerDict['delegate']['var_private'] = var_name_private + listenerDict['delegate']['cb_name'] = method.name.to_snake_case() + listenerDict['delegate']['name'] = method.name.to_camel_case() + + + listenerDict['delegate']['params_public'] = "" + listenerDict['delegate']['params_private'] = "" + listenerDict['delegate']['params'] = "" + for arg in method.args: + dllImportType = self.translate_type(arg.type, True, True) + normalType = self.translate_type(arg.type, True, False) + + argName = self.translate_argument_name(arg.name) + if arg != method.args[0]: + listenerDict['delegate']['params_public'] += ', ' + listenerDict['delegate']['params_private'] += ', ' + listenerDict['delegate']['params'] += ', ' + if normalType == dllImportType: + listenerDict['delegate']['params'] += argName + else: + if normalType == "bool": + listenerDict['delegate']['params'] += argName + " == 0" + elif self.is_linphone_type(arg.type, True, False) and type(arg.type) is AbsApi.ClassType: + listenerDict['delegate']['params'] += "fromNativePtr<" + normalType + ">(" + argName + ")" + elif self.is_linphone_type(arg.type, True, False) and type(arg.type) is AbsApi.EnumType: + listenerDict['delegate']['params'] += "(" + normalType + ")" + argName + "" + else: + raise("Error") + listenerDict['delegate']['params_public'] += normalType + " " + argName + listenerDict['delegate']['params_private'] += dllImportType + " " + argName + + listenerDict['delegate']["c_name_setter"] = c_name_setter + return listenerDict + + def generate_getter_for_listener_callbacks(self, _class, classname): + methodDict = self.init_method_dict() + c_name = _class.name.to_snake_case(fullName=True) + '_get_callbacks' + methodDict['prototype'] = "static extern IntPtr {c_name}(IntPtr thiz);".format(classname = classname, c_name = c_name) + + methodDict['has_impl'] = False + methodDict['has_property'] = True + methodDict['property_static'] = '' + methodDict['property_return'] = classname + methodDict['property_name'] = 'Listener' + methodDict['has_getter'] = True + methodDict['return'] = classname + methodDict['getter_nativePtr'] = 'nativePtr' + methodDict['getter_c_name'] = c_name + methodDict['is_class'] = True + + return methodDict + + def generate_add_for_listener_callbacks(self, _class, classname): + methodDict = self.init_method_dict() + c_name = _class.name.to_snake_case(fullName=True) + '_add_callbacks' + methodDict['prototype'] = "static extern void {c_name}(IntPtr thiz, IntPtr cbs);".format(classname = classname, c_name = c_name) + methodDict['impl']['static'] = '' + methodDict['impl']['type'] = 'void' + methodDict['impl']['name'] = 'AddListener' + methodDict['impl']['return'] = '' + methodDict['impl']['c_name'] = c_name + methodDict['impl']['nativePtr'] = 'nativePtr, ' + methodDict['impl']['args'] = classname + ' cbs' + methodDict['impl']['c_args'] = 'cbs != null ? cbs.nativePtr : IntPtr.Zero' + methodDict['is_generic'] = True + + return methodDict + + def generate_remove_for_listener_callbacks(self, _class, classname): + methodDict = self.init_method_dict() + c_name = _class.name.to_snake_case(fullName=True) + '_remove_callbacks' + methodDict['prototype'] = "static extern void {c_name}(IntPtr thiz, IntPtr cbs);".format(classname = classname, c_name = c_name) + methodDict['impl']['static'] = '' + methodDict['impl']['type'] = 'void' + methodDict['impl']['name'] = 'RemoveListener' + methodDict['impl']['return'] = '' + methodDict['impl']['c_name'] = c_name + methodDict['impl']['nativePtr'] = 'nativePtr, ' + methodDict['impl']['args'] = classname + ' cbs' + methodDict['impl']['c_args'] = 'cbs != null ? cbs.nativePtr : IntPtr.Zero' + methodDict['is_generic'] = True + + return methodDict + +########################################################################################################################################### + + def translate_enum(self, enum): + enumDict = {} + enumDict['enumName'] = enum.name.to_camel_case() + enumDict['values'] = [] + i = 0 + lastValue = None + for enumValue in enum.values: + enumValDict = {} + enumValDict['name'] = enumValue.name.to_camel_case() + if type(enumValue.value) is int: + lastValue = enumValue.value + enumValDict['value'] = str(enumValue.value) + elif type(enumValue.value) is AbsApi.Flag: + enumValDict['value'] = '1<<' + str(enumValue.value.position) + else: + if lastValue is not None: + enumValDict['value'] = lastValue + 1 + lastValue += 1 + else: + enumValDict['value'] = i + enumDict['values'].append(enumValDict) + i += 1 + return enumDict + + def translate_class(self, _class): + if _class.name.to_camel_case(fullName=True) in self.ignore: + raise AbsApi.Error('{0} has been escaped'.format(_class.name.to_camel_case(fullName=True))) + + classDict = {} + classDict['className'] = _class.name.to_camel_case() + classDict['isLinphoneFactory'] = _class.name.to_camel_case() == "Factory" + classDict['dllImports'] = [] + + islistenable = _class.listenerInterface is not None + ismonolistenable = (islistenable and not _class.multilistener) + if islistenable: + listenerName = _class.listenerInterface.name.to_camel_case() + if ismonolistenable: + classDict['dllImports'].append(self.generate_getter_for_listener_callbacks(_class, listenerName)) + else: + classDict['dllImports'].append(self.generate_add_for_listener_callbacks(_class, listenerName)) + classDict['dllImports'].append(self.generate_remove_for_listener_callbacks(_class, listenerName)) + + for method in _class.classMethods: + try: + if 'get' in method.name.to_word_list(): + methodDict = self.translate_property_getter(method, method.name.to_camel_case(), True) + #The following doesn't work because there a at least one method that has both getter and setter, + #and because it doesn't do both of them at once, property is declared twice + #elif 'set' in method.name.to_word_list(): + # methodDict = self.translate_property_setter(method, method.name.to_camel_case(), True) + else: + methodDict = self.translate_method(method, static=True, genImpl=True) + classDict['dllImports'].append(methodDict) + except AbsApi.Error as e: + print('Could not translate {0}: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0])) + + for prop in _class.properties: + try: + classDict['dllImports'] += self.translate_property(prop) + except AbsApi.Error as e: + print('error while translating {0} property: {1}'.format(prop.name.to_snake_case(), e.args[0])) + + for method in _class.instanceMethods: + try: + methodDict = self.translate_method(method, static=False, genImpl=True) + classDict['dllImports'].append(methodDict) + except AbsApi.Error as e: + print('Could not translate {0}: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0])) + + return classDict + + def translate_interface(self, interface): + if interface.name.to_camel_case(fullName=True) in self.ignore: + raise AbsApi.Error('{0} has been escaped'.format(interface.name.to_camel_case(fullName=True))) + + interfaceDict = {} + interfaceDict['interfaceName'] = interface.name.to_camel_case() + interfaceDict['methods'] = [] + for method in interface.methods: + interfaceDict['methods'].append(self.translate_listener(interface, method)) + + return interfaceDict + +########################################################################################################################################### + +class EnumImpl(object): + def __init__(self, enum, translator): + namespace = enum.find_first_ancestor_by_type(AbsApi.Namespace) + self.namespace = namespace.name.concatenate(fullName=True) if namespace is not None else None + self.enum = translator.translate_enum(enum) + +class ClassImpl(object): + def __init__(self, _class, translator): + namespace = _class.find_first_ancestor_by_type(AbsApi.Namespace) + self.namespace = namespace.name.concatenate(fullName=True) if namespace is not None else None + self._class = translator.translate_class(_class) + +class InterfaceImpl(object): + def __init__(self, interface, translator): + namespace = interface.find_first_ancestor_by_type(AbsApi.Namespace) + self.namespace = namespace.name.concatenate(fullName=True) if namespace is not None else None + self.interface = translator.translate_interface(interface) + +class WrapperImpl(object): + def __init__(self, enums, interfaces, classes): + self.enums = enums + self.interfaces = interfaces + self.classes = classes + +########################################################################################################################################### + +def render(renderer, item, path): + tmppath = path + '.tmp' + content = '' + with open(tmppath, mode='w') as f: + f.write(renderer.render(item)) + with open(tmppath, mode='rU') as f: + content = f.read() + with open(path, mode='w') as f: + f.write(content) + os.unlink(tmppath) + +def main(): + argparser = argparse.ArgumentParser(description='Generate source files for the C++ wrapper') + argparser.add_argument('xmldir', type=str, help='Directory where the XML documentation of the Linphone\'s API generated by Doxygen is placed') + argparser.add_argument('-o --output', type=str, help='the directory where to generate the source files', dest='outputdir', default='.') + argparser.add_argument('-n --name', type=str, help='the name of the genarated source file', dest='outputfile', default='LinphoneWrapper.cs') + args = argparser.parse_args() + + entries = os.listdir(args.outputdir) + + project = CApi.Project() + project.initFromDir(args.xmldir) + project.check() + + parser = AbsApi.CParser(project) + parser.parse_all() + translator = CsharpTranslator() + renderer = pystache.Renderer() + + enums = [] + for item in parser.enumsIndex.items(): + if item[1] is not None: + impl = EnumImpl(item[1], translator) + enums.append(impl) + else: + print('warning: {0} enum won\'t be translated because of parsing errors'.format(item[0])) + + interfaces = [] + classes = [] + for _class in parser.classesIndex.values() + parser.interfacesIndex.values(): + if _class is not None: + try: + if type(_class) is AbsApi.Class: + impl = ClassImpl(_class, translator) + classes.append(impl) + else: + impl = InterfaceImpl(_class, translator) + interfaces.append(impl) + except AbsApi.Error as e: + print('Could not translate {0}: {1}'.format(_class.name.to_camel_case(fullName=True), e.args[0])) + + wrapper = WrapperImpl(enums, interfaces, classes) + render(renderer, wrapper, args.outputdir + "/" + args.outputfile) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache new file mode 100644 index 000000000..5c5ec950d --- /dev/null +++ b/wrappers/csharp/wrapper_impl.mustache @@ -0,0 +1,338 @@ +/* +LinphoneWrapper.cs +Copyright (C) 2017 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. +*/ + +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace Linphone +{ + #region Wrapper specifics + public class LinphoneWrapper + { + public const string LIB_NAME = "linphone"; // With this, it automatically finds liblinphone.so + } + + [Serializable()] + public class LinphoneException : System.Exception + { + public LinphoneException() : base() { } + public LinphoneException(string message) : base(message) { } + public LinphoneException(string message, System.Exception inner) : base(message, inner) { } + protected LinphoneException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + + [StructLayout(LayoutKind.Sequential)] + public class LinphoneObject + { + internal IntPtr nativePtr; + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern int belle_sip_object_data_set(IntPtr ptr, string name, IntPtr data, IntPtr cb); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern IntPtr belle_sip_object_data_get(IntPtr ptr, string name); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern IntPtr belle_sip_object_ref(IntPtr ptr); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern void belle_sip_object_unref(IntPtr ptr); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern IntPtr bctbx_list_next(IntPtr ptr); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern IntPtr bctbx_list_get_data(IntPtr ptr); + + ~LinphoneObject() + { + Console.WriteLine("Destroying" + this.ToString()); + if (nativePtr != IntPtr.Zero) { + Console.WriteLine("Unreffing" + this.ToString()); + belle_sip_object_unref(nativePtr); + } + } + + internal static T fromNativePtr(IntPtr ptr, bool takeRef=true) where T : LinphoneObject, new() + { + if (ptr == IntPtr.Zero) return null; + IntPtr objPtr = belle_sip_object_data_get(ptr, "cs_obj"); + if (objPtr == IntPtr.Zero) + { + T obj = new T(); + Console.WriteLine("Creating" + obj.ToString()); + if (takeRef) + { + ptr = belle_sip_object_ref(ptr); + Console.WriteLine("Reffing" + obj.ToString()); + } + obj.nativePtr = ptr; + objPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); + Marshal.StructureToPtr(obj, objPtr, false); + belle_sip_object_data_set(ptr, "cs_obj", objPtr, IntPtr.Zero); + return obj; + } + else + { + T obj = Marshal.PtrToStructure(objPtr); + if (takeRef) + { + obj.nativePtr = belle_sip_object_ref(obj.nativePtr); + Console.WriteLine("Reffing" + obj.ToString()); + } + return obj; + } + } + + internal static IEnumerable MarshalStringArray(IntPtr arrayPtr) + { + if (arrayPtr != IntPtr.Zero) + { + IntPtr ptr = Marshal.ReadIntPtr(arrayPtr); + while (ptr != IntPtr.Zero) + { + string key = Marshal.PtrToStringAnsi(ptr); + yield return key; + arrayPtr = new IntPtr(arrayPtr.ToInt64() + IntPtr.Size); + ptr = Marshal.ReadIntPtr(arrayPtr); + } + } + } + + internal static IEnumerable MarshalBctbxList(IntPtr listPtr) where T : LinphoneObject, new() + { + if (listPtr != IntPtr.Zero) + { + IntPtr ptr = listPtr; + while (ptr != IntPtr.Zero) + { + IntPtr dataPtr = bctbx_list_get_data(ptr); + if (dataPtr == IntPtr.Zero) + { + break; + } + T obj = fromNativePtr(dataPtr); + yield return obj; + ptr = bctbx_list_next(ptr); + } + } + } + } + + public class LinphoneAndroid + { + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern void ms_set_jvm_from_env(IntPtr jnienv); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern void setAndroidLogHandler(); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern void setMediastreamerAndroidContext(IntPtr jnienv, IntPtr context); + + public static void setNativeLogHandler() + { + setAndroidLogHandler(); + } + + public static void setAndroidContext(IntPtr jnienv, IntPtr context) + { + ms_set_jvm_from_env(jnienv); + setMediastreamerAndroidContext(jnienv, context); + } + } + #endregion + + #region Enums + {{#enums}} + {{#enum}} + public enum {{enumName}} + { + {{#values}} + {{name}} = {{value}}, + {{/values}} + } + + {{/enum}} + {{/enums}} + #endregion + + #region Listeners + {{#interfaces}} + {{#interface}} + [StructLayout(LayoutKind.Sequential)] + public class {{interfaceName}} : LinphoneObject + { + {{#methods}} + [DllImport(LinphoneWrapper.LIB_NAME)] + {{#cb_setter}} + static extern void {{name}}(IntPtr thiz, IntPtr cb); + {{/cb_setter}} + + {{#delegate}} + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void {{name_private}}({{params_private}}); + + public delegate void {{name_public}}({{params_public}}); + private {{name_private}} {{var_private}}; + private {{name_public}} {{var_public}}; + + private void {{cb_name}}({{params_private}}) + { + {{var_public}}?.Invoke({{{params}}}); + } + public {{name_public}} {{name}} + { + set + { + {{var_private}} = {{cb_name}}; + {{var_public}} = value; + IntPtr cb = Marshal.GetFunctionPointerForDelegate({{var_private}}); + {{c_name_setter}}(nativePtr, cb); + } + } + {{/delegate}} + {{/methods}} + } + + {{/interface}} + {{/interfaces}} + #endregion + + #region Classes + {{#classes}} + {{#_class}} + [StructLayout(LayoutKind.Sequential)] + public class {{className}} : LinphoneObject + { + {{#isLinphoneFactory}} + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern IntPtr linphone_factory_create_core_cbs(IntPtr factory); + + public CoreListener CreateCoreListener() + { + IntPtr coreCbsPtr = linphone_factory_create_core_cbs(nativePtr); + return fromNativePtr(coreCbsPtr, false); + } + {{/isLinphoneFactory}} + {{#dllImports}} + [DllImport(LinphoneWrapper.LIB_NAME)] + {{{prototype}}} + {{#has_second_prototype}} + [DllImport(LinphoneWrapper.LIB_NAME)] + {{second_prototype}} + {{/has_second_prototype}} + + {{#has_property}} + {{property_static}}public {{{property_return}}} {{property_name}} + { + {{#has_getter}} + get + { + {{#is_string}} + IntPtr stringPtr = {{getter_c_name}}({{getter_nativePtr}}); + return Marshal.PtrToStringAnsi(stringPtr); + {{/is_string}} + {{#is_bool}} + return {{getter_c_name}}({{getter_nativePtr}}) == 0; + {{/is_bool}} + {{#is_class}} + IntPtr ptr = {{getter_c_name}}({{getter_nativePtr}}); + return fromNativePtr<{{return}}>(ptr, {{takeRef}}); + {{/is_class}} + {{#is_enum}} + return {{getter_c_name}}({{getter_nativePtr}}); + {{/is_enum}} + {{#is_generic}} + return {{getter_c_name}}({{getter_nativePtr}}); + {{/is_generic}} + {{#is_string_list}} + return MarshalStringArray({{getter_c_name}}({{getter_nativePtr}})); + {{/is_string_list}} + {{#is_class_list}} + return MarshalBctbxList<{{{list_type}}}>({{getter_c_name}}({{getter_nativePtr}})); + {{/is_class_list}} + } + {{/has_getter}} + {{#has_setter}} + set + { + {{#is_string}} + {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} + {{/is_string}} + {{#is_bool}} + {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value ? 1 : 0); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} + {{/is_bool}} + {{#is_class}} + {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value.nativePtr); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} + {{/is_class}} + {{#is_enum}} + {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}(int)value); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} + {{/is_enum}} + {{#is_generic}} + {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} + {{/is_generic}} + } + {{/has_setter}} + } + {{/has_property}} + {{#has_impl}} + {{#impl}} + public {{static}}{{override}}{{{type}}} {{name}}({{args}}) + { + {{#is_string}} + IntPtr stringPtr = {{c_name}}({{nativePtr}}{{c_args}}); + return Marshal.PtrToStringAnsi(stringPtr); + {{/is_string}} + {{#is_bool}} + {{return}}{{c_name}}({{nativePtr}}{{c_args}}) == 0 ? false : true; + {{/is_bool}} + {{#is_class}} + IntPtr ptr = {{c_name}}({{nativePtr}}{{c_args}}); + return fromNativePtr<{{type}}>(ptr, {{takeRef}}); + {{/is_class}} + {{#is_enum}} + {{#exception}}int exception_result = {{/exception}}{{return}}{{c_name}}({{nativePtr}}{{c_args}}); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{name}} returned value " + exception_result);{{/exception}} + {{/is_enum}} + {{#is_generic}} + {{#exception}}int exception_result = {{/exception}}{{return}}{{c_name}}({{nativePtr}}{{c_args}}); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{name}} returned value " + exception_result);{{/exception}} + {{/is_generic}} + {{#is_string_list}} + return MarshalStringArray({{c_name}}({{nativePtr}}{{c_args}})); + {{/is_string_list}} + {{#is_class_list}} + return MarshalBctbxList<{{{list_type}}}>({{c_name}}({{nativePtr}}{{c_args}})); + {{/is_class_list}} + } + {{/impl}} + {{/has_impl}} + {{/dllImports}} + } + {{/_class}} + {{/classes}} + #endregion +} \ No newline at end of file From 578bfd5f1afa0c3fd718cce7042d6051a5f595d0 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 20 Apr 2017 14:41:58 +0200 Subject: [PATCH 2/9] Removed not needed anymore some blacklisted classes --- wrappers/csharp/abstractapi.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/wrappers/csharp/abstractapi.py b/wrappers/csharp/abstractapi.py index 05e44472b..15b412484 100644 --- a/wrappers/csharp/abstractapi.py +++ b/wrappers/csharp/abstractapi.py @@ -456,11 +456,7 @@ class CParser(object): self.classBl = ['LpConfig', 'LinphonePlayer', - 'LinphoneIntRange', - 'LinphoneCallStats', - 'LinphoneCoreVTable', - 'LinphoneVideoPolicy', - 'LinphoneSipTransports'] # temporarly blacklisted + 'LinphoneCoreVTable',] # temporarly blacklisted # list of classes that must be concidered as refcountable even if # they are no ref()/unref() methods From 2915732e029985e05e96f5b56fcbe9144801add2 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 20 Apr 2017 17:58:45 +0200 Subject: [PATCH 3/9] Finished C# wrapper by adding methods having lists in params --- wrappers/csharp/genwrapper.py | 10 +++++--- wrappers/csharp/wrapper_impl.mustache | 36 ++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/wrappers/csharp/genwrapper.py b/wrappers/csharp/genwrapper.py index 4221bb202..d9d17727a 100644 --- a/wrappers/csharp/genwrapper.py +++ b/wrappers/csharp/genwrapper.py @@ -138,9 +138,7 @@ class CsharpTranslator(object): elif type(_type) is AbsApi.BaseType: return self.translate_base_type(_type, isArg, dllImport) elif type(_type) is AbsApi.ListType: - if isArg: - raise AbsApi.Error('Lists as params are not supported yet') - elif dllImport: + if dllImport: return 'IntPtr' else: if type(_type.containedTypeDesc) is AbsApi.BaseType: @@ -222,6 +220,12 @@ class CsharpTranslator(object): methodDict['impl']['c_args'] += '(int)' + self.translate_argument_name(arg.name) elif self.translate_type(arg.type, False, False) == "bool": methodDict['impl']['c_args'] += self.translate_argument_name(arg.name) + " ? 1 : 0" + elif self.get_class_array_type(self.translate_type(arg.type, False, False)) is not None: + listtype = self.get_class_array_type(self.translate_type(arg.type, False, False)) + if listtype == 'string': + methodDict['impl']['c_args'] += "StringArrayToBctbxList(" + self.translate_argument_name(arg.name) + ")" + else: + methodDict['impl']['c_args'] += "ObjectArrayToBctbxList<" + listtype + ">(" + self.translate_argument_name(arg.name) + ")" else: methodDict['impl']['c_args'] += self.translate_argument_name(arg.name) methodDict['impl']['args'] += self.translate_argument(arg, False) diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache index 5c5ec950d..fd5ce5b9c 100644 --- a/wrappers/csharp/wrapper_impl.mustache +++ b/wrappers/csharp/wrapper_impl.mustache @@ -60,6 +60,12 @@ namespace Linphone [DllImport(LinphoneWrapper.LIB_NAME)] static extern IntPtr bctbx_list_get_data(IntPtr ptr); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern IntPtr bctbx_list_append(IntPtr elem, string data); + + [DllImport(LinphoneWrapper.LIB_NAME)] + static extern IntPtr bctbx_list_append(IntPtr elem, IntPtr data); ~LinphoneObject() { @@ -134,6 +140,26 @@ namespace Linphone } } } + + internal static IntPtr StringArrayToBctbxList(IEnumerable stringlist) + { + IntPtr bctbx_list = IntPtr.Zero; + foreach (string s in stringlist) + { + bctbx_list = bctbx_list_append(bctbx_list, s); + } + return bctbx_list; + } + + internal static IntPtr ObjectArrayToBctbxList(IEnumerable objlist) where T : LinphoneObject, new() + { + IntPtr bctbx_list = IntPtr.Zero; + foreach (T ptr in objlist) + { + bctbx_list = bctbx_list_append(bctbx_list, ptr.nativePtr); + } + return bctbx_list; + } } public class LinphoneAndroid @@ -294,13 +320,21 @@ namespace Linphone {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_generic}} + {{#is_string_list}} + {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}StringArrayToBctbxList(value)); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} + {{/is_string_list}} + {{#is_class_list}} + {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}ObjectArrayToBctbxList<{{{list_type}}}>(value)); + {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} + {{/is_class_list}} } {{/has_setter}} } {{/has_property}} {{#has_impl}} {{#impl}} - public {{static}}{{override}}{{{type}}} {{name}}({{args}}) + public {{static}}{{override}}{{{type}}} {{name}}({{{args}}}) { {{#is_string}} IntPtr stringPtr = {{c_name}}({{nativePtr}}{{c_args}}); From 19cdddf7508f6c72d09f2a44bbc1440c0a266bf7 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 21 Apr 2017 11:28:39 +0200 Subject: [PATCH 4/9] Added getter for listeners in C# wrapper --- wrappers/csharp/wrapper_impl.mustache | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache index fd5ce5b9c..3ab0a5190 100644 --- a/wrappers/csharp/wrapper_impl.mustache +++ b/wrappers/csharp/wrapper_impl.mustache @@ -226,6 +226,10 @@ namespace Linphone } public {{name_public}} {{name}} { + get + { + return {{var_public}}; + } set { {{var_private}} = {{cb_name}}; From 13562a5002f06a3e90b97c7131b2c21f939ca65d Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 21 Apr 2017 11:37:38 +0200 Subject: [PATCH 5/9] Android specifics in C# wrapper now under ifdef --- wrappers/csharp/wrapper_impl.mustache | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache index 3ab0a5190..404f1d579 100644 --- a/wrappers/csharp/wrapper_impl.mustache +++ b/wrappers/csharp/wrapper_impl.mustache @@ -162,6 +162,7 @@ namespace Linphone } } + #if ANDROID public class LinphoneAndroid { [DllImport(LinphoneWrapper.LIB_NAME)] @@ -184,6 +185,7 @@ namespace Linphone setMediastreamerAndroidContext(jnienv, context); } } + #endif #endregion #region Enums From b5f095738d136a4951194b241c8adad566c8cea5 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 21 Apr 2017 15:25:09 +0200 Subject: [PATCH 6/9] Added brief doc to C# wrapper --- tools/metadoc.py | 4 ++-- wrappers/csharp/abstractapi.py | 8 ++++++-- wrappers/csharp/genwrapper.py | 5 +++++ wrappers/csharp/wrapper_impl.mustache | 15 +++++++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/tools/metadoc.py b/tools/metadoc.py index eb4b6e9d2..670341ca3 100644 --- a/tools/metadoc.py +++ b/tools/metadoc.py @@ -80,5 +80,5 @@ class DoxygenCppTranslator(Translator): class SandcastleCSharpTranslator(Translator): def _tag_as_brief(self, lines): if len(lines) > 0: - lines[0] = '' + lines[0] - lines[-1] = lines[-1] + '' + lines.insert(0, '') + lines.append('') diff --git a/wrappers/csharp/abstractapi.py b/wrappers/csharp/abstractapi.py index 15b412484..2757c62a4 100644 --- a/wrappers/csharp/abstractapi.py +++ b/wrappers/csharp/abstractapi.py @@ -455,8 +455,8 @@ class CParser(object): 'linphone_vcard_get_belcard'] # manualy wrapped self.classBl = ['LpConfig', - 'LinphonePlayer', - 'LinphoneCoreVTable',] # temporarly blacklisted + 'LinphoneCoreVTable', + 'LinphonePlayer'] # temporarly blacklisted # list of classes that must be concidered as refcountable even if # they are no ref()/unref() methods @@ -585,6 +585,7 @@ class CParser(object): name = EnumName() name.from_camel_case(nameStr, namespace=self.namespace.name) enum = Enum(name) + enum.briefDescription = cenum.briefDoc self.namespace.add_child(enum) for cEnumValue in cenum.values: @@ -618,6 +619,7 @@ class CParser(object): name = ClassName() name.from_camel_case(cclass.name, namespace=self.namespace.name) _class = Class(name) + _class.briefDescription = cclass.briefDoc _class.refcountable = self._class_is_refcountable(cclass) for cproperty in cclass.properties.values(): @@ -685,6 +687,7 @@ class CParser(object): raise Error('{0} is not a listener'.format(cclass.name)) listener = Interface(name) + listener.briefDescription = cclass.briefDoc for property in cclass.properties.values(): if property.name != 'user_data': @@ -732,6 +735,7 @@ class CParser(object): raise BlacklistedException('{0} is blacklisted'.format(name.to_c())); method = Method(name, type=type) + method.briefDescription = cfunction.briefDoc method.deprecated = cfunction.deprecated method.returnType = self.parse_type(cfunction.returnArgument) diff --git a/wrappers/csharp/genwrapper.py b/wrappers/csharp/genwrapper.py index d9d17727a..4f8e85d4b 100644 --- a/wrappers/csharp/genwrapper.py +++ b/wrappers/csharp/genwrapper.py @@ -25,10 +25,12 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'tools')) print sys.path import genapixml as CApi import abstractapi as AbsApi +import metadoc class CsharpTranslator(object): def __init__(self): self.ignore = [] + self.docTranslator = metadoc.SandcastleCSharpTranslator() def init_method_dict(self): methodDict = {} @@ -180,6 +182,8 @@ class CsharpTranslator(object): methodDict = {} methodDict['prototype'] = "static extern {return} {name}({params});".format(**methodElems) + methodDict['doc'] = self.docTranslator.translate(method.briefDescription) if method.briefDescription is not None else None + methodDict['has_impl'] = genImpl if genImpl: methodDict['impl'] = {} @@ -427,6 +431,7 @@ class CsharpTranslator(object): def translate_enum(self, enum): enumDict = {} enumDict['enumName'] = enum.name.to_camel_case() + enumDict['doc'] = self.docTranslator.translate(enum.briefDescription) if enum.briefDescription is not None else None enumDict['values'] = [] i = 0 lastValue = None diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache index 404f1d579..9d3f4ae88 100644 --- a/wrappers/csharp/wrapper_impl.mustache +++ b/wrappers/csharp/wrapper_impl.mustache @@ -191,6 +191,11 @@ namespace Linphone #region Enums {{#enums}} {{#enum}} + {{#doc}} + {{#lines}} + /// {{{line}}} + {{/lines}} + {{/doc}} public enum {{enumName}} { {{#values}} @@ -273,6 +278,11 @@ namespace Linphone {{/has_second_prototype}} {{#has_property}} + {{#doc}} + {{#lines}} + /// {{{line}}} + {{/lines}} + {{/doc}} {{property_static}}public {{{property_return}}} {{property_name}} { {{#has_getter}} @@ -340,6 +350,11 @@ namespace Linphone {{/has_property}} {{#has_impl}} {{#impl}} + {{#doc}} + {{#lines}} + /// {{{line}}} + {{/lines}} + {{/doc}} public {{static}}{{override}}{{{type}}} {{name}}({{{args}}}) { {{#is_string}} From fc765b3f1ce9542c7800f6292bd0d676988036a2 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 21 Apr 2017 15:31:52 +0200 Subject: [PATCH 7/9] Moved abstractapi to tools to prevent duplicating it in each wrapper --- {wrappers/cpp => tools}/abstractapi.py | 0 wrappers/csharp/abstractapi.py | 831 ------------------------- wrappers/csharp/genwrapper.py | 2 + 3 files changed, 2 insertions(+), 831 deletions(-) rename {wrappers/cpp => tools}/abstractapi.py (100%) delete mode 100644 wrappers/csharp/abstractapi.py diff --git a/wrappers/cpp/abstractapi.py b/tools/abstractapi.py similarity index 100% rename from wrappers/cpp/abstractapi.py rename to tools/abstractapi.py diff --git a/wrappers/csharp/abstractapi.py b/wrappers/csharp/abstractapi.py deleted file mode 100644 index 2757c62a4..000000000 --- a/wrappers/csharp/abstractapi.py +++ /dev/null @@ -1,831 +0,0 @@ -# Copyright (C) 2017 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 re -import genapixml as CApi - - -class Error(RuntimeError): - pass - - -class BlacklistedException(Error): - pass - - -class Name(object): - camelCaseParsingRegex = re.compile('[A-Z][a-z0-9]*') - lowerCamelCaseSplitingRegex = re.compile('([a-z][a-z0-9]*)([A-Z][a-z0-9]*)') - - def __init__(self): - self.words = [] - self.prev = None - - def copy(self): - nameType = type(self) - name = nameType() - name.words = list(self.words) - name.prev = None if self.prev is None else self.prev.copy() - return name - - def delete_prefix(self, prefix): - it = self - _next = None - while it is not None and it.words != prefix.words: - _next = it - it = it.prev - - if it is None or it != prefix: - raise Error('no common prefix') - elif _next is not None: - _next.prev = None - - def _set_namespace(self, namespace): - self.prev = namespace - if self.prev is not None: - prefix = namespace.to_word_list() - i = 0 - while i 0: - suffix = self.words[-1] - if MethodName.regex.match(suffix) is not None: - self.overloadRef = int(suffix) - del self.words[-1] - - def to_c(self): - suffix = ('_' + str(self.overloadRef)) if self.overloadRef > 0 else '' - return self.to_snake_case(fullName=True) + suffix - - -class ArgName(Name): - def to_c(self): - return self.to_snake_case() - - -class PropertyName(ArgName): - pass - - -class NamespaceName(Name): - def __init__(self, *params): - Name.__init__(self) - if len(params) > 0: - self.words = params[0] - - -class Object(object): - def __init__(self, name): - self.name = name - self.parent = None - self.deprecated = False - - def find_first_ancestor_by_type(self, _type): - ancestor = self.parent - while ancestor is not None and type(ancestor) is not _type: - ancestor = ancestor.parent - return ancestor - - -class Type(Object): - def __init__(self, name, isconst=False, isref=False): - Object.__init__(self, name) - self.isconst = isconst - self.isref = isref - self.cname = None - - -class BaseType(Type): - def __init__(self, name, isconst=False, isref=False, size=None, isUnsigned=False): - Type.__init__(self, name, isconst=isconst, isref=isref) - self.size = size - self.isUnsigned = isUnsigned - - -class EnumType(Type): - def __init__(self, name, isconst=False, isref=False, enumDesc=None): - Type.__init__(self, name, isconst=isconst, isref=isref) - self.desc = enumDesc - - -class ClassType(Type): - def __init__(self, name, isconst=False, isref=False, classDesc=None): - Type.__init__(self, name, isconst=isconst, isref=isref) - self.desc = classDesc - - -class ListType(Type): - def __init__(self, containedTypeName, isconst=False, isref=False): - Type.__init__(self, 'list', isconst=isconst, isref=isref) - self.containedTypeName = containedTypeName - self._containedTypeDesc = None - - def set_contained_type_desc(self, desc): - self._containedTypeDesc = desc - desc.parent = self - - def get_contained_type_desc(self): - return self._containedTypeDesc - - containedTypeDesc = property(fset=set_contained_type_desc, fget=get_contained_type_desc) - - -class DocumentableObject(Object): - def __init__(self, name): - Object.__init__(self, name) - self.briefDescription = None - self.detailedDescription = None - self.deprecated = None - - def set_from_c(self, cObject, namespace=None): - self.briefDescription = cObject.briefDescription - self.detailedDescription = cObject.detailedDescription - self.deprecated = cObject.deprecated - self.parent = namespace - - def get_namespace_object(self): - if isinstance(self, (Namespace,Enum,Class)): - return self - elif self.parent is None: - raise Error('{0} is not attached to a namespace object'.format(self)) - else: - return self.parent.get_namespace_object() - - -class Namespace(DocumentableObject): - def __init__(self, name): - DocumentableObject.__init__(self, name) - self.children = [] - - def add_child(self, child): - self.children.append(child) - child.parent = self - - -class Flag: - def __init__(self, position): - self.position = position - - -class EnumValue(DocumentableObject): - def __init__(self, name): - DocumentableObject.__init__(self, name) - self.value = None - - def value_from_string(self, stringValue): - m = re.match('^1\s*<<\s*([0-9]+)$', stringValue) - if m is not None: - self.value = Flag(int(m.group(1))) - else: - self.value = int(stringValue, base=0) - - -class Enum(DocumentableObject): - def __init__(self, name): - DocumentableObject.__init__(self, name) - self.values = [] - - def add_value(self, value): - self.values.append(value) - value.parent = self - - def set_from_c(self, cEnum, namespace=None): - Object.set_from_c(self, cEnum, namespace=namespace) - - if 'associatedTypedef' in dir(cEnum): - name = cEnum.associatedTypedef.name - else: - name = cEnum.name - - self.name = EnumName() - self.name.prev = None if namespace is None else namespace.name - self.name.set_from_c(name) - - for cEnumValue in cEnum.values: - aEnumValue = EnumValue() - aEnumValue.set_from_c(cEnumValue, namespace=self) - self.add_value(aEnumValue) - - -class Argument(DocumentableObject): - def __init__(self, name, argType, optional=False, default=None): - DocumentableObject.__init__(self, name) - self._type = argType - argType.parent = self - self.optional = optional - self.default = default - - def _set_type(self, _type): - self._type = _type - _type.parent = self - - def _get_type(self): - return self._type - - type = property(fset=_set_type, fget=_get_type) - - -class Method(DocumentableObject): - class Type: - Instance = 0, - Class = 1 - - def __init__(self, name, type=Type.Instance): - DocumentableObject.__init__(self, name) - self.type = type - self.constMethod = False - self.args = [] - self._returnType = None - - def _set_return_type(self, returnType): - self._returnType = returnType - returnType.parent = self - - def _get_return_type(self): - return self._returnType - - def add_arguments(self, arg): - self.args.append(arg) - arg.parent = self - - returnType = property(fset=_set_return_type, fget=_get_return_type) - - -class Property(DocumentableObject): - def __init__(self, name): - DocumentableObject.__init__(self, name) - self._setter = None - self._getter = None - self._type = None - - def set_setter(self, setter): - self._setter = setter - setter.parent = self - - def get_setter(self): - return self._setter - - def set_getter(self, getter): - self._getter = getter - if self._type is None: - self._type = getter.returnType - getter.parent = self - - def get_getter(self): - return self._getter - - setter = property(fset=set_setter, fget=get_setter) - getter = property(fset=set_getter, fget=get_getter) - - -class Class(DocumentableObject): - def __init__(self, name): - DocumentableObject.__init__(self, name) - self.properties = [] - self.instanceMethods = [] - self.classMethods = [] - self._listenerInterface = None - self.multilistener = False - self.refcountable = False - - def add_property(self, property): - self.properties.append(property) - property.parent = self - - def add_instance_method(self, method): - self.instanceMethods.append(method) - method.parent = self - - def add_class_method(self, method): - self.classMethods.append(method) - method.parent = self - - def set_listener_interface(self, interface): - self._listenerInterface = interface - interface._listenedClass = self - - def get_listener_interface(self): - return self._listenerInterface - - listenerInterface = property(fget=get_listener_interface, fset=set_listener_interface) - - -class Interface(DocumentableObject): - def __init__(self, name): - DocumentableObject.__init__(self, name) - self.methods = [] - self._listenedClass = None - - def add_method(self, method): - self.methods.append(method) - method.parent = self - - def get_listened_class(self): - return self._listenedClass - - listenedClass = property(fget=get_listened_class) - - -class CParser(object): - def __init__(self, cProject): - self.cBaseType = ['void', 'bool_t', 'char', 'short', 'int', 'long', 'size_t', 'time_t', 'float', 'double', 'LinphoneStatus'] - self.cListType = 'bctbx_list_t' - self.regexFixedSizeInteger = '^(u?)int(\d?\d)_t$' - self.methodBl = ['ref', 'unref', 'new', 'destroy', 'getCurrentCallbacks', 'setUserData', 'getUserData'] - self.functionBl = [ - 'linphone_vcard_get_belcard'] # manualy wrapped - - self.classBl = ['LpConfig', - 'LinphoneCoreVTable', - 'LinphonePlayer'] # temporarly blacklisted - - # list of classes that must be concidered as refcountable even if - # they are no ref()/unref() methods - self.forcedRefcountableClasses = ['LinphoneFactory'] - - self.cProject = cProject - - self.enumsIndex = {} - for enum in self.cProject.enums: - if enum.associatedTypedef is None: - self.enumsIndex[enum.name] = None - else: - self.enumsIndex[enum.associatedTypedef.name] = None - - self.classesIndex = {} - self.interfacesIndex = {} - for _class in self.cProject.classes: - if _class.name not in self.classBl: - if _class.name.endswith('Cbs'): - self.interfacesIndex[_class.name] = None - else: - self.classesIndex[_class.name] = None - - name = NamespaceName() - name.from_snake_case('linphone') - - self.namespace = Namespace(name) - - def _is_blacklisted(self, name): - if type(name) is MethodName: - return name.to_camel_case(lower=True) in self.methodBl or name.to_c() in self.functionBl - elif type(name) is ClassName: - return name.to_c() in self.classBl - else: - return False - - def parse_all(self): - for enum in self.cProject.enums: - try: - self.parse_enum(enum) - except Error as e: - print('Could not parse \'{0}\' enum: {1}'.format(enum.name, e.args[0])) - - for _class in self.cProject.classes: - try: - self.parse_class(_class) - except BlacklistedException: - pass - except Error as e: - print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0])) - - self._fix_all_types() - - - def _class_is_refcountable(self, _class): - if _class.name in self.forcedRefcountableClasses: - return True - - for method in _class.instanceMethods: - if method.startswith(_class.cFunctionPrefix) and method[len(_class.cFunctionPrefix):] == 'ref': - return True - return False - - def _fix_all_types_in_class_or_interface(self, _class): - if _class is not None: - if type(_class) is Class: - self._fix_all_types_in_class(_class) - else: - self._fix_all_types_in_interface(_class) - - def _fix_all_types(self): - for _class in self.interfacesIndex.values(): - self._fix_all_types_in_class_or_interface(_class) - for _class in self.classesIndex.values(): - self._fix_all_types_in_class_or_interface(_class) - - def _fix_all_types_in_class(self, _class): - for property in _class.properties: - if property.setter is not None: - self._fix_all_types_in_method(property.setter) - if property.getter is not None: - self._fix_all_types_in_method(property.getter) - - for method in (_class.instanceMethods + _class.classMethods): - self._fix_all_types_in_method(method) - - def _fix_all_types_in_interface(self, interface): - for method in interface.methods: - self._fix_all_types_in_method(method) - - def _fix_all_types_in_method(self, method): - try: - self._fix_type(method.returnType) - for arg in method.args: - self._fix_type(arg.type) - except Error as e: - print('warning: some types could not be fixed in {0}() function: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0])) - - def _fix_type(self, _type): - if isinstance(_type, EnumType) and _type.desc is None: - _type.desc = self.enumsIndex[_type.name] - elif isinstance(_type, ClassType) and _type.desc is None: - if _type.name in self.classesIndex: - _type.desc = self.classesIndex[_type.name] - else: - _type.desc = self.interfacesIndex[_type.name] - elif isinstance(_type, ListType) and _type.containedTypeDesc is None: - if _type.containedTypeName in self.classesIndex: - _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.classesIndex[_type.containedTypeName]) - elif _type.containedTypeName in self.interfacesIndex: - _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.interfacesIndex[_type.containedTypeName]) - elif _type.containedTypeName in self.enumsIndex: - _type.containedTypeDesc = EnumType(_type.containedTypeName, enumDesc=self.enumsIndex[_type.containedTypeName]) - else: - if _type.containedTypeName is not None: - _type.containedTypeDesc = self.parse_c_base_type(_type.containedTypeName) - else: - raise Error('bctbx_list_t type without specified contained type') - - def parse_enum(self, cenum): - if 'associatedTypedef' in dir(cenum): - nameStr = cenum.associatedTypedef.name - else: - nameStr = cenum.name - - name = EnumName() - name.from_camel_case(nameStr, namespace=self.namespace.name) - enum = Enum(name) - enum.briefDescription = cenum.briefDoc - self.namespace.add_child(enum) - - for cEnumValue in cenum.values: - valueName = EnumValueName() - valueName.from_camel_case(cEnumValue.name, namespace=name) - aEnumValue = EnumValue(valueName) - if cEnumValue.value is not None: - try: - aEnumValue.value_from_string(cEnumValue.value) - except ValueError: - raise Error('{0} enum value has an invalid definition ({1})'.format(cEnumValue.name, cEnumValue.value)) - enum.add_value(aEnumValue) - - self.enumsIndex[nameStr] = enum - return enum - - def parse_class(self, cclass): - if cclass.name in self.classBl: - raise BlacklistedException('{0} is blacklisted'.format(cclass.name)); - - if cclass.name.endswith('Cbs'): - _class = self._parse_listener(cclass) - self.interfacesIndex[cclass.name] = _class - else: - _class = self._parse_class(cclass) - self.classesIndex[cclass.name] = _class - self.namespace.add_child(_class) - return _class - - def _parse_class(self, cclass): - name = ClassName() - name.from_camel_case(cclass.name, namespace=self.namespace.name) - _class = Class(name) - _class.briefDescription = cclass.briefDoc - _class.refcountable = self._class_is_refcountable(cclass) - - for cproperty in cclass.properties.values(): - try: - if cproperty.name != 'callbacks': - absProperty = self._parse_property(cproperty, namespace=name) - _class.add_property(absProperty) - else: - _class.listenerInterface = self.interfacesIndex[cproperty.getter.returnArgument.ctype] - except Error as e: - print('Could not parse {0} property in {1}: {2}'.format(cproperty.name, cclass.name, e.args[0])) - - for cMethod in cclass.instanceMethods.values(): - try: - method = self.parse_method(cMethod, namespace=name) - if method.name.to_snake_case() == 'add_callbacks' or method.name.to_snake_case() == 'remove_callbacks': - if _class.listenerInterface is None or not _class.multilistener: - _class.multilistener = True - _class.listenerInterface = self.interfacesIndex[_class.name.to_camel_case(fullName=True) + 'Cbs'] - elif isinstance(method.returnType, ClassType) and method.returnType.name.endswith('Cbs'): - pass - else: - _class.add_instance_method(method) - - except BlacklistedException: - pass - except Error as e: - print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0])) - - for cMethod in cclass.classMethods.values(): - try: - method = self.parse_method(cMethod, type=Method.Type.Class, namespace=name) - _class.add_class_method(method) - except BlacklistedException: - pass - except Error as e: - print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0])) - - return _class - - def _parse_property(self, cproperty, namespace=None): - name = PropertyName() - name.from_snake_case(cproperty.name) - if (cproperty.setter is not None and len(cproperty.setter.arguments) == 1) or (cproperty.getter is not None and len(cproperty.getter.arguments) == 0): - methodType = Method.Type.Class - else: - methodType = Method.Type.Instance - aproperty = Property(name) - if cproperty.setter is not None: - method = self.parse_method(cproperty.setter, namespace=namespace, type=methodType) - aproperty.setter = method - if cproperty.getter is not None: - method = self.parse_method(cproperty.getter, namespace=namespace, type=methodType) - aproperty.getter = method - return aproperty - - - def _parse_listener(self, cclass): - name = InterfaceName() - name.from_camel_case(cclass.name, namespace=self.namespace.name) - - if name.words[len(name.words)-1] == 'cbs': - name.words[len(name.words)-1] = 'listener' - else: - raise Error('{0} is not a listener'.format(cclass.name)) - - listener = Interface(name) - listener.briefDescription = cclass.briefDoc - - for property in cclass.properties.values(): - if property.name != 'user_data': - try: - method = self._parse_listener_property(property, listener, cclass.events) - listener.add_method(method) - except Error as e: - print('Could not parse property \'{0}\' of listener \'{1}\': {2}'.format(property.name, cclass.name, e.args[0])) - - return listener - - def _parse_listener_property(self, property, listener, events): - methodName = MethodName() - methodName.from_snake_case(property.name) - methodName.words.insert(0, 'on') - methodName.prev = listener.name - - if property.getter is not None: - eventName = property.getter.returnArgument.ctype - elif property.setter is not None and len(property.setter.arguments) == 2: - eventName = property.setter.arguments[1].ctype - else: - raise Error('event name for {0} property of {1} listener not found'.format(property.name, listener.name.to_snake_case(fullName=True))) - - try: - event = events[eventName] - except KeyError: - raise Error('invalid event name \'{0}\''.format(eventName)) - - method = Method(methodName) - method.returnType = self.parse_type(event.returnArgument) - for arg in event.arguments: - argName = ArgName() - argName.from_snake_case(arg.name) - argument = Argument(argName, self.parse_type(arg)) - method.add_arguments(argument) - - return method - - def parse_method(self, cfunction, namespace, type=Method.Type.Instance): - name = MethodName() - name.from_snake_case(cfunction.name, namespace=namespace) - - if self._is_blacklisted(name): - raise BlacklistedException('{0} is blacklisted'.format(name.to_c())); - - method = Method(name, type=type) - method.briefDescription = cfunction.briefDoc - method.deprecated = cfunction.deprecated - method.returnType = self.parse_type(cfunction.returnArgument) - - for arg in cfunction.arguments: - if type == Method.Type.Instance and arg is cfunction.arguments[0]: - method.constMethod = ('const' in arg.completeType.split(' ')) - else: - aType = self.parse_type(arg) - argName = ArgName() - argName.from_snake_case(arg.name) - absArg = Argument(argName, aType) - method.add_arguments(absArg) - - return method - - def parse_type(self, cType): - if cType.ctype in self.cBaseType or re.match(self.regexFixedSizeInteger, cType.ctype): - absType = self.parse_c_base_type(cType.completeType) - elif cType.ctype in self.enumsIndex: - absType = EnumType(cType.ctype, enumDesc=self.enumsIndex[cType.ctype]) - elif cType.ctype in self.classesIndex or cType.ctype in self.interfacesIndex: - absType = ClassType(cType.ctype) - absType.isconst = cType.completeType.startswith('const ') - absType.isref = cType.completeType.endswith('*') - elif cType.ctype == self.cListType: - absType = ListType(cType.containedType) - else: - raise Error('Unknown C type \'{0}\''.format(cType.ctype)) - - absType.cname = cType.completeType - return absType - - def parse_c_base_type(self, cDecl): - declElems = cDecl.split(' ') - param = {} - name = None - for elem in declElems: - if elem == 'const': - if name is None: - param['isconst'] = True - elif elem == 'unsigned': - param['isUnsigned'] = True - elif elem == 'char': - name = 'character' - elif elem == 'void': - name = 'void' - elif elem == 'bool_t': - name = 'boolean' - elif elem in ['short', 'long']: - param['size'] = elem - elif elem == 'int': - name = 'integer' - elif elem == 'float': - name = 'floatant' - param['size'] = 'float' - elif elem == 'size_t': - name = 'size' - elif elem == 'time_t': - name = 'time' - elif elem == 'double': - name = 'floatant' - if 'size' in param and param['size'] == 'long': - param['size'] = 'long double' - else: - param['size'] = 'double' - elif elem == 'LinphoneStatus': - name = 'status' - elif elem == '*': - if name is not None: - if name == 'character': - name = 'string' - elif name == 'string': - name = 'string_array' - elif 'isref' not in param or param['isref'] is False: - param['isref'] = True - else: - raise Error('Unhandled double-pointer') - else: - matchCtx = re.match(self.regexFixedSizeInteger, elem) - if matchCtx: - name = 'integer' - if matchCtx.group(1) == 'u': - param['isUnsigned'] = True - - param['size'] = int(matchCtx.group(2)) - if param['size'] not in [8, 16, 32, 64]: - raise Error('{0} C basic type has an invalid size ({1})'.format(cDecl, param['size'])) - - - if name is not None: - return BaseType(name, **param) - else: - raise Error('could not find type in \'{0}\''.format(cDecl)) diff --git a/wrappers/csharp/genwrapper.py b/wrappers/csharp/genwrapper.py index 4f8e85d4b..68232f4b6 100644 --- a/wrappers/csharp/genwrapper.py +++ b/wrappers/csharp/genwrapper.py @@ -566,6 +566,8 @@ def main(): project.check() parser = AbsApi.CParser(project) + parser.functionBl = ['linphone_vcard_get_belcard'] + parser.classBl += 'LinphoneCoreVTable' parser.parse_all() translator = CsharpTranslator() renderer = pystache.Renderer() From 2b7111bb1c1c587ec3d5226f844a38d9ac96cf45 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 21 Apr 2017 15:52:39 +0200 Subject: [PATCH 8/9] Added doc to manually created methods and classes in C# wrapper + commented out some debug logs related to refs --- wrappers/csharp/genwrapper.py | 1 + wrappers/csharp/wrapper_impl.mustache | 34 +++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/wrappers/csharp/genwrapper.py b/wrappers/csharp/genwrapper.py index 68232f4b6..af1366467 100644 --- a/wrappers/csharp/genwrapper.py +++ b/wrappers/csharp/genwrapper.py @@ -460,6 +460,7 @@ class CsharpTranslator(object): classDict = {} classDict['className'] = _class.name.to_camel_case() classDict['isLinphoneFactory'] = _class.name.to_camel_case() == "Factory" + classDict['doc'] = self.docTranslator.translate(_class.briefDescription) if _class.briefDescription is not None else None classDict['dllImports'] = [] islistenable = _class.listenerInterface is not None diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache index 9d3f4ae88..4b12dfb7b 100644 --- a/wrappers/csharp/wrapper_impl.mustache +++ b/wrappers/csharp/wrapper_impl.mustache @@ -24,11 +24,17 @@ using System.Collections.Generic; namespace Linphone { #region Wrapper specifics + /// + /// Only contains the LIB_NAME value that represents the library in which all DllImport are made + /// public class LinphoneWrapper { public const string LIB_NAME = "linphone"; // With this, it automatically finds liblinphone.so } + /// + /// All methods that returns a LinphoneStatus with a value != 0 as an error code in C are translated in C# by throwing a LinphoneException + /// [Serializable()] public class LinphoneException : System.Exception { @@ -39,6 +45,9 @@ namespace Linphone } [StructLayout(LayoutKind.Sequential)] + /// + /// Parent class for a Linphone public objects + /// public class LinphoneObject { internal IntPtr nativePtr; @@ -69,9 +78,9 @@ namespace Linphone ~LinphoneObject() { - Console.WriteLine("Destroying" + this.ToString()); + //Console.WriteLine("Destroying" + this.ToString()); if (nativePtr != IntPtr.Zero) { - Console.WriteLine("Unreffing" + this.ToString()); + //Console.WriteLine("Unreffing" + this.ToString()); belle_sip_object_unref(nativePtr); } } @@ -83,11 +92,11 @@ namespace Linphone if (objPtr == IntPtr.Zero) { T obj = new T(); - Console.WriteLine("Creating" + obj.ToString()); + //Console.WriteLine("Creating" + obj.ToString()); if (takeRef) { ptr = belle_sip_object_ref(ptr); - Console.WriteLine("Reffing" + obj.ToString()); + //Console.WriteLine("Reffing" + obj.ToString()); } obj.nativePtr = ptr; objPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); @@ -101,7 +110,7 @@ namespace Linphone if (takeRef) { obj.nativePtr = belle_sip_object_ref(obj.nativePtr); - Console.WriteLine("Reffing" + obj.ToString()); + //Console.WriteLine("Reffing" + obj.ToString()); } return obj; } @@ -163,6 +172,9 @@ namespace Linphone } #if ANDROID + /// + /// Methods that are only found in Android version of Linphone libraries and related to JNI + /// public class LinphoneAndroid { [DllImport(LinphoneWrapper.LIB_NAME)] @@ -174,11 +186,18 @@ namespace Linphone [DllImport(LinphoneWrapper.LIB_NAME)] static extern void setMediastreamerAndroidContext(IntPtr jnienv, IntPtr context); + /// + /// Registers the Android log handler (adb logcat) in Linphone. + /// public static void setNativeLogHandler() { setAndroidLogHandler(); } + /// + /// Sets the JVM and JNI pointers in Linphone, required to be able to make JAVA upcalls. + /// Calling this method is mandatory and must be done as soon as possible ! + /// public static void setAndroidContext(IntPtr jnienv, IntPtr context) { ms_set_jvm_from_env(jnienv); @@ -256,6 +275,11 @@ namespace Linphone #region Classes {{#classes}} {{#_class}} + {{#doc}} + {{#lines}} + /// {{{line}}} + {{/lines}} + {{/doc}} [StructLayout(LayoutKind.Sequential)] public class {{className}} : LinphoneObject { From eaa9e7b107e85ed047dafb6bf14ba3247fa184e5 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 24 Apr 2017 15:27:55 +0200 Subject: [PATCH 9/9] Added doc on each enum value in C# wrapper --- wrappers/csharp/genwrapper.py | 7 ++++--- wrappers/csharp/wrapper_impl.mustache | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/wrappers/csharp/genwrapper.py b/wrappers/csharp/genwrapper.py index af1366467..05444e88c 100644 --- a/wrappers/csharp/genwrapper.py +++ b/wrappers/csharp/genwrapper.py @@ -182,7 +182,7 @@ class CsharpTranslator(object): methodDict = {} methodDict['prototype'] = "static extern {return} {name}({params});".format(**methodElems) - methodDict['doc'] = self.docTranslator.translate(method.briefDescription) if method.briefDescription is not None else None + methodDict['doc'] = self.docTranslator.translate(method.briefDescription) methodDict['has_impl'] = genImpl if genImpl: @@ -431,13 +431,14 @@ class CsharpTranslator(object): def translate_enum(self, enum): enumDict = {} enumDict['enumName'] = enum.name.to_camel_case() - enumDict['doc'] = self.docTranslator.translate(enum.briefDescription) if enum.briefDescription is not None else None + enumDict['doc'] = self.docTranslator.translate(enum.briefDescription) enumDict['values'] = [] i = 0 lastValue = None for enumValue in enum.values: enumValDict = {} enumValDict['name'] = enumValue.name.to_camel_case() + enumValDict['doc'] = self.docTranslator.translate(enumValue.briefDescription) if type(enumValue.value) is int: lastValue = enumValue.value enumValDict['value'] = str(enumValue.value) @@ -460,7 +461,7 @@ class CsharpTranslator(object): classDict = {} classDict['className'] = _class.name.to_camel_case() classDict['isLinphoneFactory'] = _class.name.to_camel_case() == "Factory" - classDict['doc'] = self.docTranslator.translate(_class.briefDescription) if _class.briefDescription is not None else None + classDict['doc'] = self.docTranslator.translate(_class.briefDescription) classDict['dllImports'] = [] islistenable = _class.listenerInterface is not None diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache index 4b12dfb7b..b3571e127 100644 --- a/wrappers/csharp/wrapper_impl.mustache +++ b/wrappers/csharp/wrapper_impl.mustache @@ -218,6 +218,11 @@ namespace Linphone public enum {{enumName}} { {{#values}} + {{#doc}} + {{#lines}} + /// {{{line}}} + {{/lines}} + {{/doc}} {{name}} = {{value}}, {{/values}} }