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