genwrapper.py 35.1 KB
Newer Older
Sylvain Berfini's avatar
Sylvain Berfini committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#!/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
import errno

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

##########################################################################

34 35 36 37 38 39 40 41 42 43 44
ENUMS_LIST = {
    'AccountCreatorActivationCodeStatus': 'AccountCreator',
    'AccountCreatorDomainStatus': 'AccountCreator',
    'AccountCreatorEmailStatus': 'AccountCreator',
    'AccountCreatorLanguageStatus': 'AccountCreator',
    'AccountCreatorPasswordStatus': 'AccountCreator',
    'AccountCreatorPhoneNumberStatus': 'AccountCreator',
    'AccountCreatorStatus': 'AccountCreator',
    'AccountCreatorTransportStatus': 'AccountCreator',
    'AccountCreatorUsernameStatus': 'AccountCreator',
    'AddressFamily': 'CallStats',
45
    'AuthMethod': 'Core',
46 47 48 49 50 51
    'CallDir': 'Call',
    'CallState': 'Call',
    'CallStatus': 'CallLog',
    'ChatMessageState': 'ChatMessage',
    'ConfiguringState': 'Core',
    'CoreLogCollectionUploadState': 'Core',
52
    'EcCalibratorStatus': 'Core',
53 54 55 56 57 58 59 60 61 62
    'GlobalState': 'Core',
    'FriendListStatus': 'FriendList',
    'IceState': 'CallStats',
    'LimeState': 'Core',
    'MediaDirection': 'Core',
    'MediaEncryption': 'Core',
    'PlayerState': 'Player',
    'RegistrationState': 'Core',
    'SubscribePolicy': 'Friend',
    'TransportType': 'Address',
63
    'TunnelMode': 'Tunnel',
64 65
    'XmlRpcRequestArgType': 'XmlRpcRequest',
    'XmlRpcRequestStatus': 'XmlRpcRequest',
66 67 68 69
}

##########################################################################

Sylvain Berfini's avatar
Sylvain Berfini committed
70
class JavaTranslator(object):
71 72 73
    def __init__(self, packageName):
        package_dirs = packageName.split('.')
        self.jni_package = ''
74
        self.jni_path = ''
75 76
        for directory in package_dirs:
            self.jni_package += directory + '_'
77
            self.jni_path += directory + '/'
78

Sylvain Berfini's avatar
Sylvain Berfini committed
79 80 81 82 83 84 85 86 87 88 89
        self.docTranslator = metadoc.SandcastleJavaTranslator()

    def throws_exception(self, _type):
        if type(_type) is AbsApi.BaseType:
            if _type.name == 'status':
                return True
        return False

    def translate_argument_name(self, _argName):
        return _argName.to_snake_case()

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
    def translate_java_jni_enum_name(self, _enum):
        name = _enum.name.to_camel_case()
        if name in ENUMS_LIST:
            className = ENUMS_LIST[name]
            if name.startswith(className):
                name = name[len(className):]
            name = className + '$' + name
        return name

    def translate_java_jni_base_type_name(self, _type):
        if _type == 'string':
            return 'Ljava/lang/String;'
        elif _type == 'integer':
            return 'I'
        elif _type == 'boolean':
            return 'Z'
        elif _type == 'floatant':
            return 'F'
        elif _type == 'size':
            return 'I'
        elif _type == 'time':
            return 'I'
        elif _type == 'status':
            return 'I'
        elif _type == 'string_array':
            return '[Ljava/lang/String;'
        elif _type == 'character':
            return 'C'
        elif _type == 'void':
            return 'V'
        return _type

122 123
    def translate_as_c_base_type(self, t):
        _type = t.name
124
        if _type == 'string':
125
            return 'char *'
126
        elif _type == 'integer':
127 128 129 130 131 132 133 134 135 136
            if t.size is None:
                if t.isUnsigned:
                    return 'unsigned int'
                return 'int'
            inttype = 'int{0}_t'.format(t.size)
            if t.isUnsigned:
                inttype = 'u' + inttype
            if t.isref:
                inttype = inttype + ' *'
            return inttype
137 138 139 140 141 142 143 144 145 146 147
        elif _type == 'boolean':
            return 'bool_t'
        elif _type == 'floatant':
            return 'float'
        elif _type == 'size':
            return 'size_t'
        elif _type == 'time':
            return 'time_t'
        elif _type == 'status':
            return 'int'
        elif _type == 'string_array':
148
            return 'char **'
149 150 151
        elif _type == 'character':
            return 'char'
        elif _type == 'void':
152 153 154
            if t.isref:
                return 'void *'
            return 'void'
155 156
        return _type

Sylvain Berfini's avatar
Sylvain Berfini committed
157
    def translate_type(self, _type, native=False, jni=False, isReturn=False):
Sylvain Berfini's avatar
Sylvain Berfini committed
158
        if type(_type) is AbsApi.ListType:
159
            if jni:
Sylvain Berfini's avatar
Sylvain Berfini committed
160 161 162
                if type(_type.containedTypeDesc) is AbsApi.ClassType:
                    return 'jobjectArray'
                elif type(_type.containedTypeDesc) is AbsApi.BaseType:
163 164
                    if _type.containedTypeDesc.name == 'string':
                        return 'jobjectArray'
Sylvain Berfini's avatar
Sylvain Berfini committed
165 166 167
                    return self.translate_type(_type.containedTypeDesc, jni=True) + 'Array'
                elif type(_type.containedTypeDesc) is AbsApi.EnumType:
                    ptrtype = self.translate_type(_type.containedTypeDesc, native)
Sylvain Berfini's avatar
Sylvain Berfini committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
            ptrtype = ''
            if type(_type.containedTypeDesc) is AbsApi.ClassType:
                ptrtype = self.translate_type(_type.containedTypeDesc, native)
            elif type(_type.containedTypeDesc) is AbsApi.BaseType:
                ptrtype = self.translate_type(_type.containedTypeDesc, native)
            elif type(_type.containedTypeDesc) is AbsApi.EnumType:
                ptrtype = self.translate_type(_type.containedTypeDesc, native)
            else:
                if _type.containedTypeDesc:
                    raise AbsApi.Error('translation of bctbx_list_t of ' + _type.containedTypeDesc.name)
                else:
                    raise AbsApi.Error('translation of bctbx_list_t of unknow type !')
            return ptrtype + '[]'
                
        elif type(_type) is AbsApi.ClassType:
183
            if jni:
184
                return 'jobject'
Sylvain Berfini's avatar
Sylvain Berfini committed
185 186 187 188
            return _type.desc.name.to_camel_case()
        elif type(_type) is AbsApi.EnumType:
            if native:
                return 'int'
189 190
            elif jni:
                return 'jint'
191 192 193 194
            name = _type.desc.name.to_camel_case()
            if name in ENUMS_LIST:
                className = ENUMS_LIST[name]
                if name.startswith(className):
195 196
                    name = name[len(className):]
                name = className + '.' + name
197
            return name
Sylvain Berfini's avatar
Sylvain Berfini committed
198 199
        elif type(_type) is AbsApi.BaseType:
            if _type.name == 'string':
200 201
                if jni:
                    return 'jstring'
Sylvain Berfini's avatar
Sylvain Berfini committed
202 203
                return 'String'
            elif _type.name == 'integer':
204 205
                if jni:
                    return 'jint'
Sylvain Berfini's avatar
Sylvain Berfini committed
206
                return 'int'
Sylvain Berfini's avatar
Sylvain Berfini committed
207 208 209 210
            elif _type.name == 'boolean':
                if jni:
                    return 'jboolean'
                return 'boolean'
Sylvain Berfini's avatar
Sylvain Berfini committed
211
            elif _type.name == 'floatant':
212 213
                if jni:
                    return 'jfloat'
Sylvain Berfini's avatar
Sylvain Berfini committed
214 215
                return 'float'
            elif _type.name == 'size':
216 217
                if jni:
                    return 'jint'
Sylvain Berfini's avatar
Sylvain Berfini committed
218
                return 'int'
Sylvain Berfini's avatar
Sylvain Berfini committed
219
            elif _type.name == 'time':
220 221
                if jni:
                    return 'jlong'
Sylvain Berfini's avatar
Sylvain Berfini committed
222
                return 'long'
223
            elif _type.name == 'status':
224 225
                if jni:
                    return 'jint'
226 227 228
                if native:
                    return 'int'
                return 'void'
229 230 231 232
            elif _type.name == 'string_array':
                if jni:
                    return 'jobjectArray'
                return 'String[]'
233 234 235 236
            elif _type.name == 'character':
                if jni:
                    return 'jchar'
                return 'char'
237
            elif _type.name == 'void':
238 239
                if isReturn:
                    return 'void'
240
                if jni:
241
                    return 'jobject'
242
                return 'Object'
Sylvain Berfini's avatar
Sylvain Berfini committed
243 244
            return _type.name

245 246
    def translate_argument(self, _arg, native=False, jni=False):
        return '{0} {1}'.format(self.translate_type(_arg.type, native, jni), self.translate_argument_name(_arg.name))
Sylvain Berfini's avatar
Sylvain Berfini committed
247 248 249 250 251 252 253 254 255

    def translate_property(self, _property):
        properties = []
        if _property.getter is not None:
            properties.append(self.translate_method(_property.getter))
        if _property.setter is not None:
            properties.append(self.translate_method(_property.setter))
        return properties

Sylvain Berfini's avatar
Sylvain Berfini committed
256
    def translate_jni_property(self, className, _property):
257 258
        properties = []
        if _property.getter is not None:
Sylvain Berfini's avatar
Sylvain Berfini committed
259
            properties.append(self.translate_jni_method(className, _property.getter))
260
        if _property.setter is not None:
Sylvain Berfini's avatar
Sylvain Berfini committed
261
            properties.append(self.translate_jni_method(className, _property.setter))
262 263
        return properties

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
    def generate_listener(self, name, _class):
        methodDict = {}
        methodDict['return'] = 'void'
        methodDict['return_native'] = 'void'
        methodDict['return_keyword'] = ''
        methodDict['convertInputClassArrayToLongArray'] = False
        methodDict['name'] = name
        methodDict['exception'] = False
        methodDict['enumCast'] = False
        methodDict['classCast'] = False

        methodDict['params'] = _class.name.to_camel_case() + 'Listener listener'
        methodDict['native_params'] = 'long nativePtr, ' + _class.name.to_camel_case() + 'Listener listener'
        methodDict['static_native_params'] = ''
        methodDict['native_params_impl'] = ', listener'

        methodDict['deprecated'] = False
        methodDict['doc'] = None

        return methodDict

    def generate_add_listener(self, _class):
        return self.generate_listener('addListener', _class)

    def generate_remove_listener(self, _class):
        return self.generate_listener('removeListener', _class)

    def generate_set_listener(self, _class):
        return self.generate_listener('setListener', _class)

Sylvain Berfini's avatar
Sylvain Berfini committed
294 295 296
    def translate_method(self, _method):
        methodDict = {}

Sylvain Berfini's avatar
Sylvain Berfini committed
297 298
        methodDict['return'] = self.translate_type(_method.returnType, isReturn=True)
        methodDict['return_native'] = self.translate_type(_method.returnType, native=True, isReturn=True)
Sylvain Berfini's avatar
Sylvain Berfini committed
299 300 301 302 303 304 305
        methodDict['return_keyword'] = '' if methodDict['return'] == 'void' else 'return '

        methodDict['convertInputClassArrayToLongArray'] = False

        methodDict['name'] = _method.name.to_camel_case(lower=True)
        methodDict['exception'] = self.throws_exception(_method.returnType)

306
        methodDict['enumCast'] = type(_method.returnType) is AbsApi.EnumType
307
        methodDict['classCast'] = type(_method.returnType) is AbsApi.ClassType
308

Sylvain Berfini's avatar
Sylvain Berfini committed
309 310 311 312 313 314 315
        methodDict['params'] = ''
        methodDict['native_params'] = 'long nativePtr'
        methodDict['static_native_params'] = ''
        methodDict['native_params_impl'] = ''
        for arg in _method.args:
            if arg is not _method.args[0]:
                methodDict['params'] += ', '
316
                methodDict['static_native_params'] += ', '
Sylvain Berfini's avatar
Sylvain Berfini committed
317 318 319 320 321 322
            methodDict['native_params'] += ', '
            methodDict['native_params_impl'] += ', '

            methodDict['params'] += self.translate_argument(arg)
            methodDict['native_params'] += self.translate_argument(arg, True)
            methodDict['static_native_params'] += self.translate_argument(arg, True)
Sylvain Berfini's avatar
Sylvain Berfini committed
323
            if type(arg.type) is AbsApi.EnumType:
324
                methodDict['native_params_impl'] += self.translate_argument_name(arg.name) + '.toInt()'
Sylvain Berfini's avatar
Sylvain Berfini committed
325 326 327 328 329 330 331 332
            else:
                methodDict['native_params_impl'] += self.translate_argument_name(arg.name)

        methodDict['deprecated'] = _method.deprecated
        methodDict['doc'] = self.docTranslator.translate(_method.briefDescription) if _method.briefDescription is not None else None

        return methodDict

Sylvain Berfini's avatar
Sylvain Berfini committed
333
    def translate_jni_method(self, className, _method, static=False):
334
        methodDict = {}
Sylvain Berfini's avatar
Sylvain Berfini committed
335 336
        methodDict['classCName'] = 'Linphone' + className.to_camel_case()
        methodDict['className'] = className.to_camel_case()
337 338
        methodDict['classImplName'] = className.to_camel_case() + 'Impl'
        methodDict['jniPath'] = self.jni_path
Sylvain Berfini's avatar
Sylvain Berfini committed
339 340

        methodDict['return'] = self.translate_type(_method.returnType, jni=True, isReturn=True)
341 342
        methodDict['hasListReturn'] = methodDict['return'] == 'jobjectArray'
        methodDict['hasReturn'] = not methodDict['return'] == 'void' and not methodDict['hasListReturn']
343 344
        methodDict['hasStringReturn'] = methodDict['return'] == 'jstring'
        methodDict['hasNormalReturn'] = not methodDict['hasListReturn'] and not methodDict['hasStringReturn']
Sylvain Berfini's avatar
Sylvain Berfini committed
345 346 347 348 349
        methodDict['name'] = 'Java_' + self.jni_package + className.to_camel_case() + 'Impl_' + _method.name.to_camel_case(lower=True)
        methodDict['notStatic'] = not static
        methodDict['c_name'] = 'linphone_' + className.to_snake_case() + "_" + _method.name.to_snake_case()
        methodDict['returnObject'] = methodDict['hasReturn'] and type(_method.returnType) is AbsApi.ClassType
        methodDict['returnClassName'] = self.translate_type(_method.returnType)
350 351 352 353
        methodDict['isRealObjectArray'] = False
        methodDict['isStringObjectArray'] = False

        if methodDict['hasListReturn']:
354 355 356
            if type(_method.returnType) is AbsApi.BaseType and _method.returnType.name == 'string_array':
                methodDict['isStringObjectArray'] = True
            elif type(_method.returnType.containedTypeDesc) is AbsApi.BaseType:
357 358 359
                methodDict['isStringObjectArray'] = True
            elif type(_method.returnType.containedTypeDesc) is AbsApi.ClassType:
                methodDict['isRealObjectArray'] = True
360
                methodDict['objectCPrefix'] = 'linphone_' + _method.returnType.containedTypeDesc.desc.name.to_snake_case()
361 362 363
                methodDict['objectClassCName'] = 'Linphone' + _method.returnType.containedTypeDesc.desc.name.to_camel_case()
                methodDict['objectClassName'] = _method.returnType.containedTypeDesc.desc.name.to_camel_case()
                methodDict['objectClassImplName'] = _method.returnType.containedTypeDesc.desc.name.to_camel_case() + 'Impl'
Sylvain Berfini's avatar
Sylvain Berfini committed
364 365 366 367 368

        methodDict['params'] = 'JNIEnv *env, jobject thiz' if static else 'JNIEnv *env, jobject thiz, jlong ptr'
        methodDict['params_impl'] = ''
        methodDict['strings'] = []
        methodDict['objects'] = []
369 370
        methodDict['lists'] = []
        methodDict['array'] = []
Sylvain Berfini's avatar
Sylvain Berfini committed
371
        methodDict['returnedObjectGetter'] = ''
372
        for arg in _method.args:
Sylvain Berfini's avatar
Sylvain Berfini committed
373
            methodDict['params'] += ', '
374 375 376 377 378 379
            if static:
                if arg is not _method.args[0]:
                    methodDict['params_impl'] += ', '
            else:
                methodDict['params_impl'] += ', '

380
            methodDict['params'] += self.translate_argument(arg, jni=True)
Sylvain Berfini's avatar
Sylvain Berfini committed
381 382 383
            argname = self.translate_argument_name(arg.name)

            if type(arg.type) is AbsApi.ClassType:
384 385 386 387
                classCName = 'Linphone' + arg.type.desc.name.to_camel_case()
                if classCName[-8:] == 'Listener':
                   classCName = 'Linphone' + arg.type.desc.name.to_camel_case()[:-8] + 'Cbs'
                methodDict['objects'].append({'object': argname, 'objectClassCName': classCName})
Sylvain Berfini's avatar
Sylvain Berfini committed
388 389
                methodDict['params_impl'] += 'c_' + argname
                
390 391 392
            elif type(arg.type) is AbsApi.ListType:
                isStringList = type(arg.type.containedTypeDesc) is AbsApi.BaseType and arg.type.containedTypeDesc.name == 'string'
                isObjList = type(arg.type.containedTypeDesc) is AbsApi.ClassType
393
                methodDict['lists'].append({'list': argname, 'isStringList': isStringList, 'isObjList': isObjList, 'objectClassCName': arg.type.containedTypeDesc.name})
394
                methodDict['params_impl'] += 'bctbx_list_' + argname
395 396 397 398

            elif type(arg.type) is AbsApi.EnumType:
                argCType = arg.type.name
                methodDict['params_impl'] += '(' + argCType + ') ' + argname
399
                
Sylvain Berfini's avatar
Sylvain Berfini committed
400 401 402 403 404
            elif type(arg.type) is AbsApi.BaseType:
                if arg.type.name == 'string':
                    methodDict['strings'].append({'string': argname})
                    methodDict['params_impl'] += 'c_' + argname
                else:
405
                    methodDict['params_impl'] += '(' + self.translate_as_c_base_type(arg.type) + ')' + argname                        
Sylvain Berfini's avatar
Sylvain Berfini committed
406 407
            else:
                methodDict['params_impl'] += argname
408 409 410

        return methodDict

Sylvain Berfini's avatar
Sylvain Berfini committed
411 412
    def translate_class(self, _class):
        classDict = {
Sylvain Berfini's avatar
Sylvain Berfini committed
413 414 415
            'methods': [],
            'staticMethods': [],
            'jniMethods': [],
Sylvain Berfini's avatar
Sylvain Berfini committed
416 417 418 419 420 421 422 423
        }

        classDict['isLinphoneFactory'] = _class.name.to_camel_case() == "Factory"
        classDict['doc'] = self.docTranslator.translate(_class.briefDescription) if _class.briefDescription is not None else None

        for _property in _class.properties:
            try:
                classDict['methods'] += self.translate_property(_property)
Sylvain Berfini's avatar
Sylvain Berfini committed
424
                classDict['jniMethods'] += self.translate_jni_property(_class.name, _property)
Sylvain Berfini's avatar
Sylvain Berfini committed
425 426 427 428 429 430
            except AbsApi.Error as e:
                print('error while translating {0} property: {1}'.format(_property.name.to_snake_case(), e.args[0]))

        for method in _class.instanceMethods:
            try:
                methodDict = self.translate_method(method)
Sylvain Berfini's avatar
Sylvain Berfini committed
431
                jniMethodDict = self.translate_jni_method(_class.name, method)
Sylvain Berfini's avatar
Sylvain Berfini committed
432
                classDict['methods'].append(methodDict)
433
                classDict['jniMethods'].append(jniMethodDict)
Sylvain Berfini's avatar
Sylvain Berfini committed
434 435 436 437 438 439
            except AbsApi.Error as e:
                print('Could not translate {0}: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0]))

        for method in _class.classMethods:
            try:
                methodDict = self.translate_method(method)
Sylvain Berfini's avatar
Sylvain Berfini committed
440
                jniMethodDict = self.translate_jni_method(_class.name, method, True)
Sylvain Berfini's avatar
Sylvain Berfini committed
441
                classDict['staticMethods'].append(methodDict)
442
                classDict['jniMethods'].append(jniMethodDict)
Sylvain Berfini's avatar
Sylvain Berfini committed
443 444 445
            except AbsApi.Error as e:
                print('Could not translate {0}: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0]))

446 447 448 449 450 451 452 453 454
        islistenable = _class.listenerInterface is not None
        if islistenable:
            isMultiListener = (_class.multilistener)
            if isMultiListener:
                classDict['methods'].append(self.generate_add_listener(_class))
                classDict['methods'].append(self.generate_remove_listener(_class))
            else:
                classDict['methods'].append(self.generate_set_listener(_class))

Sylvain Berfini's avatar
Sylvain Berfini committed
455 456
        return classDict

457
    def translate_jni_interface(self, _class, className, _method):
458 459 460 461 462 463
        methodDict = {}
        listenerName = 'Linphone' + className.to_camel_case()
        methodDict['classCName'] = listenerName[:-8] #Remove Listener at the end
        methodDict['className'] = className.to_camel_case()
        methodDict['classImplName'] = className.to_camel_case() + 'Impl'
        methodDict['jniPath'] = self.jni_path
464
        methodDict['cPrefix'] = 'linphone_' + className.to_snake_case()[:-9] # Remove _listener at the end
465 466
        methodDict['callbackName'] = methodDict['cPrefix'] + '_' + _method.name.to_snake_case()
        methodDict['jname'] = _method.name.to_camel_case(lower=True)
467 468 469 470 471
        methodDict['return'] = self.translate_as_c_base_type(_method.returnType)
        if type(_method.returnType) is AbsApi.ClassType:
            methodDict['return'] += '*'
        methodDict['returnIfFail'] = '' if  methodDict['return'] == 'void' else ' NULL' #TODO
        methodDict['hasReturn'] = not methodDict['return'] == 'void'
472 473 474
        methodDict['isSingleListener'] = not _class.multilistener
        methodDict['isMultiListener'] = _class.multilistener

475
        methodDict['firstParam'] = ''
476 477 478 479 480 481 482 483 484 485 486
        methodDict['jobjects'] = []
        methodDict['jenums'] = []
        methodDict['jstrings'] = []
        methodDict['params'] = ''
        methodDict['jparams'] = '('
        methodDict['params_impl'] = ''
        for arg in _method.args:
            argname = self.translate_argument_name(arg.name)
            if arg is not _method.args[0]:
                methodDict['params'] += ', '
                methodDict['params_impl'] += ', '
487 488 489 490 491
            else:
                 methodDict['firstParam'] = argname

            if (arg.type.isconst):
                methodDict['params'] += 'const '
492 493

            if type(arg.type) is AbsApi.ClassType:
494
                methodDict['params'] += 'Linphone' + arg.type.desc.name.to_camel_case() + ' *' + argname
495 496
                methodDict['jparams'] += 'L' + self.jni_path + arg.type.desc.name.to_camel_case() + ';'
                methodDict['params_impl'] += 'j_' + argname
497
                methodDict['jobjects'].append({'objectName': argname, 'className': arg.type.desc.name.to_camel_case(), })
498
            elif type(arg.type) is AbsApi.BaseType:
499
                methodDict['params'] += self.translate_as_c_base_type(arg.type) + ' ' + argname
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
                methodDict['jparams'] += self.translate_java_jni_base_type_name(arg.type.name)
                if arg.type.name == 'string':
                    methodDict['params_impl'] += 'j_' + argname
                    methodDict['jstrings'].append({'stringName': argname,})
                else:
                    methodDict['params_impl'] += argname
            elif type(arg.type) is AbsApi.EnumType:
                methodDict['params'] += 'Linphone' + arg.type.desc.name.to_camel_case() + ' ' + argname
                methodDict['jparams'] += 'L' + self.jni_path + self.translate_java_jni_enum_name(arg.type.desc) + ';'
                methodDict['params_impl'] += 'j_' + argname
                methodDict['jenums'].append({'enumName': argname, 'cEnumPrefix': 'linphone_' + arg.type.desc.name.to_snake_case()})

        methodDict['jparams'] += ')'
        if (methodDict['return'] == 'void'):
            methodDict['jparams'] += 'V'
        else:
            pass #TODO

        return methodDict

Sylvain Berfini's avatar
Sylvain Berfini committed
520 521
    def translate_interface(self, _class):
        interfaceDict = {
Sylvain Berfini's avatar
Sylvain Berfini committed
522
            'methods': [],
523
            'jniMethods': [],
Sylvain Berfini's avatar
Sylvain Berfini committed
524 525 526 527 528 529
        }

        interfaceDict['doc'] = self.docTranslator.translate(_class.briefDescription)

        for method in _class.methods:
            interfaceDict['methods'].append(self.translate_method(method))
530
            interfaceDict['jniMethods'].append(self.translate_jni_interface(_class.listenedClass, _class.name, method))
Sylvain Berfini's avatar
Sylvain Berfini committed
531 532 533 534

        return interfaceDict

    def translate_enum(self, _class):
535 536 537
        enumDict = {
            'jniMethods': [],
        }
Sylvain Berfini's avatar
Sylvain Berfini committed
538 539 540 541 542 543 544

        enumDict['name'] = _class.name.to_camel_case()
        enumDict['doc'] = self.docTranslator.translate(_class.briefDescription)
        enumDict['values'] = []
        i = 0
        lastValue = None

545 546
        enumDict['jniPath'] = self.jni_path

Sylvain Berfini's avatar
Sylvain Berfini committed
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
        for enumValue in _class.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)
            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
            i += 1
            enumValDict['commarorsemicolon'] = ';' if i == len(_class.values) else ','
            enumDict['values'].append(enumValDict)

        return enumDict

##########################################################################

class JavaEnum(object):
571
    def __init__(self, package, _enum, translator):
Sylvain Berfini's avatar
Sylvain Berfini committed
572
        self._class = translator.translate_enum(_enum)
573
        self.packageName = package
Sylvain Berfini's avatar
Sylvain Berfini committed
574
        self.className = _enum.name.to_camel_case()
575 576 577 578
        if self.className == 'XmlRpcArgType':
            self.className = 'XmlRpcRequestArgType'
        elif self.className == 'XmlRpcStatus':
            self.className = 'XmlRpcRequestStatus'
579
        self.cPrefix = 'linphone_' + _enum.name.to_snake_case()
Sylvain Berfini's avatar
Sylvain Berfini committed
580 581 582
        self.filename = self.className + ".java"
        self.values = self._class['values']
        self.doc = self._class['doc']
583
        self.jniName = translator.translate_java_jni_enum_name(_enum)
584 585 586 587
        if self.className == 'XmlRpcRequestArgType':
            self.jniName = 'XmlRpcRequest$ArgType'
        elif self.className == 'XmlRpcRequestStatus':
            self.jniName = 'XmlRpcRequest$Status'
Sylvain Berfini's avatar
Sylvain Berfini committed
588

589 590 591 592 593 594 595 596 597
class JniInterface(object):
    def __init__(self, javaClass, apiClass):
        self.isSingleListener = (not apiClass.multilistener)
        self.isMultiListener = (apiClass.multilistener)
        self.classCName = javaClass.cName
        self.cPrefix = javaClass.cPrefix
        self.callbacks = []
        listener = apiClass.listenerInterface
        for method in listener.methods:
598
            cb = 'linphone_' + listener.name.to_snake_case()[:-9] # Remove _listener at the end
599 600 601 602 603 604
            cbName = cb + '_' + method.name.to_snake_case()
            self.callbacks.append({
                'callbackName': cbName,
                'callback': method.name.to_snake_case()[3:], # Remove the on_
            })

Sylvain Berfini's avatar
Sylvain Berfini committed
605
class JavaInterface(object):
606
    def __init__(self, package, _interface, translator):
Sylvain Berfini's avatar
Sylvain Berfini committed
607
        self._class = translator.translate_interface(_interface)
608
        self.packageName = package
Sylvain Berfini's avatar
Sylvain Berfini committed
609 610
        self.className = _interface.name.to_camel_case()
        self.filename = self.className + ".java"
611
        self.cPrefix = 'linphone_' + _interface.name.to_snake_case()
Sylvain Berfini's avatar
Sylvain Berfini committed
612 613 614
        self.imports = []
        self.methods = self._class['methods']
        self.doc = self._class['doc']
615
        self.jniMethods = self._class['jniMethods']
Sylvain Berfini's avatar
Sylvain Berfini committed
616 617 618 619 620 621 622 623 624 625

class JavaInterfaceStub(object):
    def __init__(self, _interface):
        self.packageName = _interface.packageName
        self.className = _interface.className
        self.classNameStub =  self.className + "Stub"
        self.filename = self.className + "Stub.java"
        self.methods = _interface.methods

class JavaClass(object):
626
    def __init__(self, package, _class, translator):
Sylvain Berfini's avatar
Sylvain Berfini committed
627 628
        self._class = translator.translate_class(_class)
        self.isLinphoneFactory = self._class['isLinphoneFactory']
629
        self.isNotLinphoneFactory = not self.isLinphoneFactory
Sylvain Berfini's avatar
Sylvain Berfini committed
630 631
        self.cName = 'Linphone' + _class.name.to_camel_case()
        self.cPrefix = 'linphone_' + _class.name.to_snake_case()
632
        self.packageName = package
Sylvain Berfini's avatar
Sylvain Berfini committed
633 634
        self.className = _class.name.to_camel_case()
        self.classImplName = self.className + "Impl"
635
        self.factoryName = _class.name.to_snake_case()
Sylvain Berfini's avatar
Sylvain Berfini committed
636 637 638 639
        self.filename = self.className + ".java"
        self.imports = []
        self.methods = self._class['methods']
        self.staticMethods = self._class['staticMethods']
640
        self.jniMethods = self._class['jniMethods']
Sylvain Berfini's avatar
Sylvain Berfini committed
641 642
        self.doc = self._class['doc']
        self.enums = []
643 644 645
        self.jniInterface = None
        if _class.listenerInterface is not None:
            self.jniInterface = JniInterface(self, _class)
Sylvain Berfini's avatar
Sylvain Berfini committed
646 647 648 649 650 651 652

    def add_enum(self, enum):
        if enum.className.startswith(self.className):
            enum.className = enum.className[len(self.className):]
        self.enums.append(enum)

class Jni(object):
Sylvain Berfini's avatar
Sylvain Berfini committed
653
    def __init__(self, package):
654
        self.enums = []
655
        self.interfaces = []
656
        self.callbacks = []
657
        self.objects = []
Sylvain Berfini's avatar
Sylvain Berfini committed
658 659 660 661 662 663 664 665
        self.methods = []
        self.jni_package = ''
        self.jni_path = ''
        package_dirs = package.split('.')
        for directory in package_dirs:
            self.jni_package += directory + '_'
            self.jni_path += directory + '/'

666 667 668 669 670 671 672 673 674 675
    def add_enum(self, javaEnum):
        obj = {
            'jniPrefix': self.jni_package,
            'jniPath': self.jni_path,
            'jniName': javaEnum.jniName,
            'cPrefix': javaEnum.cPrefix,
            'className': javaEnum.className,
        }
        self.enums.append(obj)

Sylvain Berfini's avatar
Sylvain Berfini committed
676
    def add_object(self, javaClass):
677 678
        if javaClass.className == 'Factory':
            return
Sylvain Berfini's avatar
Sylvain Berfini committed
679 680 681 682 683 684 685 686 687
        obj = {
            'jniPrefix': self.jni_package,
            'jniPath': self.jni_path,
            'cPrefix': javaClass.cPrefix,
            'className': javaClass.className,
            'classCName': javaClass.cName,
            'classImplName': javaClass.classImplName,
        }
        self.objects.append(obj)
Sylvain Berfini's avatar
Sylvain Berfini committed
688

689 690 691 692 693 694 695 696 697 698 699 700 701
        jniInterface = javaClass.jniInterface
        if jniInterface is not None:
            interface = {
                'isSingleListener': jniInterface.isSingleListener,
                'isMultiListener': jniInterface.isMultiListener,
                'classCName': jniInterface.classCName,
                'cPrefix': jniInterface.cPrefix,
                'jniPackage': self.jni_package,
                'factoryName': javaClass.factoryName,
                'callbacksList': []
            }
            for callback in jniInterface.callbacks:
                interface['callbacksList'].append(callback)
702 703
            self.interfaces.append(interface)

704 705 706 707
    def add_callbacks(self, name, callbacks):
        for callback in callbacks:
            self.callbacks.append(callback)

Sylvain Berfini's avatar
Sylvain Berfini committed
708
    def add_methods(self, name, methods):
Sylvain Berfini's avatar
Sylvain Berfini committed
709 710
        for method in methods:
            self.methods.append(method)
Sylvain Berfini's avatar
Sylvain Berfini committed
711 712 713 714

##########################################################################

class GenWrapper(object):
715
    def __init__(self, srcdir, javadir, package, xmldir):
Sylvain Berfini's avatar
Sylvain Berfini committed
716 717
        self.srcdir = srcdir
        self.javadir = javadir
718
        self.package = package
Sylvain Berfini's avatar
Sylvain Berfini committed
719 720 721 722 723

        project = CApi.Project()
        project.initFromDir(xmldir)
        project.check()

724
        self.parser = AbsApi.CParser(project, ['LinphoneBuffer'])
725 726 727 728
        self.parser.functionBl = \
            ['linphone_vcard_get_belcard',\
            'linphone_core_get_current_vtable',\
            'linphone_factory_get',\
729 730 731
            'linphone_factory_clean',\
            'linphone_call_zoom_video',\
            'linphone_config_get_range']
Sylvain Berfini's avatar
Sylvain Berfini committed
732
        self.parser.parse_all()
733
        self.translator = JavaTranslator(package)
Sylvain Berfini's avatar
Sylvain Berfini committed
734
        self.renderer = pystache.Renderer()
Sylvain Berfini's avatar
Sylvain Berfini committed
735
        self.jni = Jni(package)
Sylvain Berfini's avatar
Sylvain Berfini committed
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751

        self.enums = {}
        self.interfaces = {}
        self.classes = {}
        self.enums_to_remove = []

    def render_all(self):
        for _interface in self.parser.interfacesIndex.values():
            self.render_java_interface(_interface)
        for _class in self.parser.classesIndex.values():
            self.render_java_class(_class)
        for _enum in self.parser.enumsIndex.items():
            if _enum[1] is not None:
                self.render_java_enum(_enum[1])

        for name, value in self.enums.iteritems():
752
            self.jni.add_enum(value)
753 754
            if name in ENUMS_LIST:
                className = ENUMS_LIST[name]
Sylvain Berfini's avatar
Sylvain Berfini committed
755 756 757 758 759 760 761 762 763 764 765 766 767
                print 'Enum ' + name + ' belongs to class ' + className
                self.classes[className].add_enum(value)
                self.enums_to_remove.append(name)

        for enum in self.enums_to_remove:
            self.enums.pop(enum, None)

        for name, value in self.enums.iteritems():
            self.render(value, self.javadir + '/' + value.filename)
        for name, value in self.interfaces.iteritems():
            self.render(value, self.javadir + '/' + value.filename)
        for name, value in self.classes.iteritems():
            self.render(value, self.javadir + '/' + value.filename)
Sylvain Berfini's avatar
Sylvain Berfini committed
768
            self.jni.add_object(value)
Sylvain Berfini's avatar
Sylvain Berfini committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785

        self.render(self.jni, self.srcdir + '/linphone_jni.cc')

    def render(self, item, path):
        tmppath = path + '.tmp'
        content = ''
        with open(tmppath, mode='w') as f:
            f.write(self.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 render_java_enum(self, _class):
        if _class is not None:
            try:
786
                javaenum = JavaEnum(self.package, _class, self.translator)
Sylvain Berfini's avatar
Sylvain Berfini committed
787 788 789 790 791 792 793
                self.enums[javaenum.className] = javaenum
            except AbsApi.Error as e:
                print('Could not translate {0}: {1}'.format(_class.name.to_camel_case(fullName=True), e.args[0]))

    def render_java_interface(self, _class):
        if _class is not None:
            try:
794
                javainterface = JavaInterface(self.package, _class, self.translator)
Sylvain Berfini's avatar
Sylvain Berfini committed
795 796
                self.interfaces[javainterface.className] = javainterface
                javaInterfaceStub = JavaInterfaceStub(javainterface)
797
                self.interfaces[javaInterfaceStub.classNameStub] = javaInterfaceStub
Sylvain Berfini's avatar
Sylvain Berfini committed
798 799
            except AbsApi.Error as e:
                print('Could not translate {0}: {1}'.format(_class.name.to_camel_case(fullName=True), e.args[0]))
800
            self.jni.add_callbacks(javainterface.className, javainterface.jniMethods)
Sylvain Berfini's avatar
Sylvain Berfini committed
801 802 803 804

    def render_java_class(self, _class):
        if _class is not None:
            try:
805
                javaclass = JavaClass(self.package, _class, self.translator)
Sylvain Berfini's avatar
Sylvain Berfini committed
806 807 808 809 810 811 812 813 814 815 816
                self.classes[javaclass.className] = javaclass
            except AbsApi.Error as e:
                print('Could not translate {0}: {1}'.format(_class.name.to_camel_case(fullName=True), e.args[0]))
            self.jni.add_methods(javaclass.className, javaclass.jniMethods)

##########################################################################

def main():
    argparser = argparse.ArgumentParser(description='Generate source files for the Java 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='.')
817
    argparser.add_argument('-p --package', type=str, help='the package name for the wrapper', dest='package', default='org.linphone.core')
818
    argparser.add_argument('-n --name', type=str, help='the name of the genarated source file', dest='name', default='linphone_jni.cc')
Sylvain Berfini's avatar
Sylvain Berfini committed
819 820 821 822
    args = argparser.parse_args()

    srcdir = args.outputdir + '/src'
    javadir = args.outputdir + '/java'
823 824 825
    package_dirs = args.package.split('.')
    for directory in package_dirs:
        javadir += '/' + directory
Sylvain Berfini's avatar
Sylvain Berfini committed
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840

    try:
        os.makedirs(srcdir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            print("Cannot create '{0}' dircetory: {1}".format(srcdir, e.strerror))
            sys.exit(1)

    try:
        os.makedirs(javadir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            print("Cannot create '{0}' dircetory: {1}".format(javadir, e.strerror))
            sys.exit(1)

841
    genwrapper = GenWrapper(srcdir, javadir, args.package, args.xmldir)
Sylvain Berfini's avatar
Sylvain Berfini committed
842 843 844 845
    genwrapper.render_all()

if __name__ == '__main__':
    main()