gen_java.py 28.0 KB
Newer Older
A
Andrey Kamaev 已提交
1 2 3 4 5 6 7 8
import sys, re, os.path
from string import Template

try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# c_type    : { java/jni correspondence }
type_dict = {
# "simple"  : { j_type : "?", jn_type : "?", jni_type : "?", suffix : "?" },
    ""        : { "j_type" : "", "jn_type" : "long", "jni_type" : "jlong" }, # c-tor ret_type
    "void"    : { "j_type" : "void", "jn_type" : "void", "jni_type" : "void" },
    "env"     : { "j_type" : "", "jn_type" : "", "jni_type" : "JNIEnv*"},
    "cls"     : { "j_type" : "", "jn_type" : "", "jni_type" : "jclass"},
    "bool"    : { "j_type" : "boolean", "jn_type" : "boolean", "jni_type" : "jboolean", "suffix" : "Z" },
    "int"     : { "j_type" : "int", "jn_type" : "int", "jni_type" : "int", "suffix" : "I" },
    "long"    : { "j_type" : "int", "jn_type" : "int", "jni_type" : "int", "suffix" : "I" },
    "float"   : { "j_type" : "float", "jn_type" : "float", "jni_type" : "jfloat", "suffix" : "F" },
    "double"  : { "j_type" : "double", "jn_type" : "double", "jni_type" : "jdouble", "suffix" : "D" },
    "size_t"  : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
    "__int64" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
# "complex" : { j_type : "?", jn_args : (("", ""),), jn_name : "", jni_var : "", jni_name : "", "suffix" : "?" },
    "Mat"     : { "j_type" : "Mat", "jn_type" : "long", "jn_args" : (("__int64", ".nativeObj"),),
25
                  "jni_var" : "Mat& %(n)s = *((Mat*)%(n)s_nativeObj)",
26
                  "jni_type" : "jlong", #"jni_name" : "*%(n)s",
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
                  "suffix" : "J" },
    "Point"   : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y")),
                  "jni_var" : "cv::Point %(n)s((int)%(n)s_x, (int)%(n)s_y)",
                  "suffix" : "DD"},
    "Point2f" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y")),
                  "jni_var" : "cv::Point2f %(n)s((float)%(n)s_x, (float)%(n)s_y)",
                  "suffix" : "DD"},
    "Point2d" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y")),
                  "jni_var" : "cv::Point2d %(n)s(%(n)s_x, %(n)s_y)",
                  "suffix" : "DD"},
    "Point3i" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y"), ("double", ".z")),
                  "jni_var" : "cv::Point3i %(n)s((int)%(n)s_x, (int)%(n)s_y, (int)%(n)s_z)",
                  "suffix" : "DDD"},
    "Point3f" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y"), ("double", ".z")),
                  "jni_var" : "cv::Point3f %(n)s((float)%(n)s_x, (float)%(n)s_y, (float)%(n)s_z)",
                  "suffix" : "DDD"},
    "Point3d" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y"), ("double", ".z")),
                  "jni_var" : "cv::Point3d %(n)s(%(n)s_x, %(n)s_y, %(n)s_z)",
                  "suffix" : "DDD"},
    "Rect"    : { "j_type" : "Rect",  "jn_args" : (("int", ".x"), ("int", ".y"), ("int", ".width"), ("int", ".height")),
                  "jni_var" : "cv::Rect %(n)s(%(n)s_x, %(n)s_y, %(n)s_width, %(n)s_height)",
                  "suffix" : "IIII"},
49 50 51 52 53 54 55 56 57
    "Size"    : { "j_type" : "Size",  "jn_args" : (("double", ".width"), ("double", ".height")),
                  "jni_var" : "cv::Size %(n)s((int)%(n)s_width, (int)%(n)s_height)",
                  "suffix" : "DD"},
    "Size2f"  : { "j_type" : "Size",  "jn_args" : (("double", ".width"), ("double", ".height")),
                  "jni_var" : "cv::Size2f %(n)s((float)%(n)s_width, (float)%(n)s_height)",
                  "suffix" : "DD"},
 "RotatedRect": { "j_type" : "RotatedRect",  "jn_args" : (("double", ".center.x"), ("double", ".center.y"), ("double", ".size.width"), ("double", ".size.height"), ("double", ".angle")),
                  "jni_var" : "cv::RotatedRect %(n)s(cv::Point2f(%(n)s_center_x, %(n)s_center_y), cv::Size2f(%(n)s_size_width, %(n)s_size_height), %(n)s_angle)",
                  "suffix" : "DDDDD"},
58 59 60 61 62
    "Scalar"  : { "j_type" : "Scalar",  "jn_args" : (("double", ".v0"), ("double", ".v1"), ("double", ".v2"), ("double", ".v3")),
                  "jni_var" : "cv::Scalar %(n)s(%(n)s_v0, %(n)s_v1, %(n)s_v2, %(n)s_v3)",
                  "suffix" : "DDDD"},
    "string"  : { "j_type" : "java.lang.String",  "jn_type" : "java.lang.String",
                  "jni_type" : "jstring", "jni_name" : "n_%(n)s",
63
                  "jni_var" : 'const char* utf_%(n)s = env->GetStringUTFChars(%(n)s, 0); std::string n_%(n)s( utf_%(n)s ? utf_%(n)s : "" ); env->ReleaseStringUTFChars(%(n)s, utf_%(n)s)',
64
                  "suffix" : "Ljava_lang_String_2"},
65 66 67 68
    "String"  : { "j_type" : "java.lang.String",  "jn_type" : "java.lang.String",
                  "jni_type" : "jstring", "jni_name" : "n_%(n)s",
                  "jni_var" : 'const char* utf_%(n)s = env->GetStringUTFChars(%(n)s, 0); String n_%(n)s( utf_%(n)s ? utf_%(n)s : "" ); env->ReleaseStringUTFChars(%(n)s, utf_%(n)s)',
                  "suffix" : "Ljava_lang_String_2"},
A
Andrey Kamaev 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

}

class ConstInfo(object):
    def __init__(self, cname, name, val):
##        self.name = re.sub(r"^cv\.", "", name).replace(".", "_")
        self.cname = cname
        self.name =  re.sub(r"^Cv", "", name)
        #self.name = re.sub(r"([a-z])([A-Z])", r"\1_\2", name)
        #self.name = self.name.upper()
        self.value = val


class ClassInfo(object):
    def __init__(self, decl): # [ 'class/struct cname', [bases], [modlist] ]
        name = decl[0]
        name = name[name.find(" ")+1:].strip()
        self.cname = self.name = self.jname = re.sub(r"^cv\.", "", name)
        self.cname =self.cname.replace(".", "::")
88
        #self.jname =  re.sub(r"^Cv", "", self.jname)
A
Andrey Kamaev 已提交
89 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 122 123 124 125 126 127 128 129
        self.methods = {}
        self.consts = [] # using a list to save the occurence order
        for m in decl[2]:
            if m.startswith("="):
                self.jname = m[1:]


class ArgInfo(object):
    def __init__(self, arg_tuple): # [ ctype, name, def val, [mod], argno ]
        self.ctype = arg_tuple[0]
        self.name = arg_tuple[1]
        self.defval = arg_tuple[2]
        self.out = "/O" in arg_tuple[3] or "/IO" in arg_tuple[3]

##    def isbig(self):
##        return self.ctype == "Mat" or self.ctype == "vector_Mat"


class FuncInfo(object):
    def __init__(self, decl): # [ funcname, return_ctype, [modifiers], [args] ]
        name = re.sub(r"^cv\.", "", decl[0])
        self.cname = name.replace(".", "::")
        classname = ""
        dpos = name.rfind(".")
        if dpos >= 0:
            classname = name[:dpos]
            name = name[dpos+1:]
        self.classname = classname
        self.jname = self.name = name
        if "[" in name:
            self.jname = "getelem"
        for m in decl[2]:
            if m.startswith("="):
                self.jname = m[1:]
        self.jn_name = "n_" + self.jname
        self.jni_name= re.sub(r"_", "_1", self.jn_name)
        if self.classname:
            self.jni_name = "00024" + self.classname + "_" + self.jni_name
        self.static = ["","static"][ "/S" in decl[2] ]
        self.ctype = decl[1] or ""
        self.args = []
130 131 132
        #self.jni_suffix = "__"
        #if self.classname and self.ctype and not self.static: # non-static class methods except c-tors
        #    self.jni_suffix += "J" # artifical 'self'
A
Andrey Kamaev 已提交
133 134 135
        for a in decl[3]:
            ai = ArgInfo(a)
            self.args.append(ai)
136
        #    self.jni_suffix += ctype2j.get(ai.ctype, ["","","",""])[3]
A
Andrey Kamaev 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162



class FuncFamilyInfo(object):
    def __init__(self, decl): # [ funcname, return_ctype, [modifiers], [args] ]
        self.funcs = []
        self.funcs.append( FuncInfo(decl) )
        self.jname = self.funcs[0].jname
        self.isconstructor = self.funcs[0].name == self.funcs[0].classname



    def add_func(self, fi):
        self.funcs.append( fi )


class JavaWrapperGenerator(object):
    def __init__(self):
        self.clear()

    def clear(self):
        self.classes = { "Mat" : ClassInfo([ 'class Mat', [], [] ]) }
        self.funcs = {}
        self.consts = [] # using a list to save the occurence order
        self.module = ""
        self.java_code = StringIO()
163
        self.jn_code = StringIO()
A
Andrey Kamaev 已提交
164 165
        self.cpp_code = StringIO()
        self.ported_func_counter = 0
166 167 168
        self.ported_func_list = []
        self.skipped_func_list = []
        self.total_func_counter = 0
A
Andrey Kamaev 已提交
169 170 171 172 173 174 175 176

    def add_class(self, decl):
        classinfo = ClassInfo(decl)
        if classinfo.name in self.classes:
            print "Generator error: class %s (%s) is duplicated" % \
                    (classinfo.name, classinfo.cname)
            sys.exit(-1)
        self.classes[classinfo.name] = classinfo
177
        if classinfo.name in type_dict:
A
Andrey Kamaev 已提交
178 179
            print "Duplicated class: " + classinfo.name
            sys.exit(-1)
180 181
        type_dict[classinfo.name] = \
            { "j_type" : classinfo.name,  "jn_args" : (("__int64", ".nativeObj"),),
182
              "jni_name" : "(*("+classinfo.name+"*)%(n)s_nativeObj)",
183
              "suffix" : "J" }
A
Andrey Kamaev 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251


    def add_const(self, decl): # [ "const cname", val, [], [] ]
        consts = self.consts
        name = decl[0].replace("const ", "").strip()
        name = re.sub(r"^cv\.", "", name)
        cname = name.replace(".", "::")
        # check if it's a class member
        dpos = name.rfind(".")
        if dpos >= 0:
            classname = name[:dpos]
            name = name[dpos+1:]
            if classname in self.classes:
                consts = self.classes[classname].consts
            else:
                # this class isn't wrapped
                # skipping this const
                return
        constinfo = ConstInfo(cname, name, decl[1])
        # checking duplication
        for c in consts:
            if c.name == constinfo.name:
                print "Generator error: constant %s (%s) is duplicated" \
                        % (constinfo.name, constinfo.cname)
                sys.exit(-1)
        consts.append(constinfo)

    def add_func(self, decl):
        ffi = FuncFamilyInfo(decl)
        func_map = self.funcs
        classname = ffi.funcs[0].classname
        if classname:
            if classname in self.classes:
                func_map = self.classes[classname].methods
            else:
                print "Generator error: the class %s for method %s is missing" % \
                        (classname, ffi.jname)
                sys.exit(-1)
        if ffi.jname in func_map:
            func_map[ffi.jname].add_func(ffi.funcs[0])
        else:
            func_map[ffi.jname] = ffi

    def save(self, path, name, buf):
        f = open(path + "/" + name, "wt")
        f.write(buf.getvalue())
        f.close()

    def gen(self, srcfiles, module, output_path):
        self.clear()
        self.module = module
        parser = hdr_parser.CppHeaderParser()

        # step 1: scan the headers and build more descriptive maps of classes, consts, functions
        for hdr in srcfiles:
            decls = parser.parse(hdr)
            for decl in decls:
                name = decl[0]
                if name.startswith("struct") or name.startswith("class"):
                    self.add_class(decl)
                    pass
                elif name.startswith("const"):
                    self.add_const(decl)
                else: # function
                    self.add_func(decl)
                    pass

        # java module header
252
        self.java_code.write("package org.opencv;\n\npublic class %s {\n" % module)
A
Andrey Kamaev 已提交
253

254 255 256 257
        if module == "core":
            self.java_code.write(\
"""
    private static final int
A
Andrey Kamaev 已提交
258 259 260 261 262 263 264
            CV_8U  = 0,
            CV_8S  = 1,
            CV_16U = 2,
            CV_16S = 3,
            CV_32S = 4,
            CV_32F = 5,
            CV_64F = 6,
265 266 267 268 269 270 271 272
            CV_USRTYPE1 = 7;

""" )

        if module == "imgproc":
            self.java_code.write(\
"""
    public static final int
A
Andrey Kamaev 已提交
273 274 275 276 277 278
            IPL_BORDER_CONSTANT = 0,
            IPL_BORDER_REPLICATE = 1,
            IPL_BORDER_REFLECT = 2,
            IPL_BORDER_WRAP = 3,
            IPL_BORDER_REFLECT_101 = 4,
            IPL_BORDER_TRANSPARENT = 5;
279
""" )
280

A
Andrey Pavlenko 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
        if module == "calib3d":
            self.java_code.write(\
"""
    public static final int
            CV_LMEDS = 4,
            CV_RANSAC = 8,
            CV_FM_LMEDS = CV_LMEDS,
            CV_FM_RANSAC = CV_RANSAC;

    public static final int
            CV_FM_7POINT = 1,
            CV_FM_8POINT = 2;

    public static final int
            CV_CALIB_USE_INTRINSIC_GUESS = 1,
            CV_CALIB_FIX_ASPECT_RATIO = 2,
            CV_CALIB_FIX_PRINCIPAL_POINT = 4,
            CV_CALIB_ZERO_TANGENT_DIST = 8,
            CV_CALIB_FIX_FOCAL_LENGTH = 16,
            CV_CALIB_FIX_K1 = 32,
            CV_CALIB_FIX_K2 = 64,
            CV_CALIB_FIX_K3 = 128,
            CV_CALIB_FIX_K4 = 2048,
            CV_CALIB_FIX_K5 = 4096,
            CV_CALIB_FIX_K6 = 8192,
            CV_CALIB_RATIONAL_MODEL = 16384,
            CV_CALIB_FIX_INTRINSIC = 256,
            CV_CALIB_SAME_FOCAL_LENGTH = 512,
            CV_CALIB_ZERO_DISPARITY = 1024;
""" )

312 313 314 315 316 317 318 319
        # java native stuff
        self.jn_code.write("""
    //
    // native stuff
    //
    static { System.loadLibrary("opencv_java");	}
""")

A
Andrey Kamaev 已提交
320 321
        # cpp module header
        self.cpp_code.write(\
322 323 324
"""//
// This file is auto-generated, please don't edit!
//
A
Andrey Kamaev 已提交
325 326

#include <jni.h>
327 328

#ifdef DEBUG
329
#include <android/log.h>
330 331
#define MODULE_LOG_TAG "OpenCV.%s"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, MODULE_LOG_TAG, __VA_ARGS__))
332
#endif // DEBUG
A
Andrey Kamaev 已提交
333

334
""" % module)
A
Andrey Kamaev 已提交
335 336
        self.cpp_code.write( "\n".join(['#include "opencv2/%s/%s"' % (module, os.path.basename(f)) \
                            for f in srcfiles]) )
337
        self.cpp_code.write('\nusing namespace cv;\n')
338
        self.cpp_code.write('\n\nextern "C" {\n\n')
A
Andrey Kamaev 已提交
339 340 341 342 343 344 345 346

        # step 2: generate the code for global constants
        self.gen_consts()

        # step 3: generate the code for all the global functions
        self.gen_funcs()

        # step 4: generate code for the classes
347
        self.gen_classes()
A
Andrey Kamaev 已提交
348

349
        # module tail
350
        self.java_code.write("\n\n" + self.jn_code.getvalue() + "\n")
A
Andrey Kamaev 已提交
351
        self.java_code.write("}\n")
352
        self.cpp_code.write('} // extern "C"\n')
A
Andrey Kamaev 已提交
353 354 355

        self.save(output_path, module+".java", self.java_code)
        self.save(output_path, module+".cpp",  self.cpp_code)
356 357 358 359 360 361 362 363 364 365 366
        # report
        report = StringIO()
        report.write("PORTED FUNCs LIST (%i of %i):\n\n" % \
            (self.ported_func_counter, self.total_func_counter) \
        )
        report.write("\n".join(self.ported_func_list))
        report.write("\n\nSKIPPED FUNCs LIST (%i of %i):\n\n" % \
            (self.total_func_counter - self.ported_func_counter, self.total_func_counter) \
        )
        report.write("".join(self.skipped_func_list))
        self.save(output_path, module+".txt", report)
A
Andrey Kamaev 已提交
367

368
        print "Done %i of %i funcs." % (self.ported_func_counter, self.total_func_counter)
A
Andrey Kamaev 已提交
369 370 371 372 373 374 375 376 377 378 379 380


    def gen_consts(self):
        # generate the code for global constants
        if self.consts:
            self.java_code.write("""
    public static final int
            """ + """,
            """.join(["%s = %s" % (c.name, c.value) for c in self.consts]) + \
            ";\n\n")


381
    def gen_func(self, fi, isoverload, jn_code):
382
        self.total_func_counter += 1
A
Andrey Kamaev 已提交
383

384
        # // C++: c_decl
A
Andrey Kamaev 已提交
385
        # e.g:
386
        # //  C++: void add(Mat src1, Mat src2, Mat dst, Mat mask = Mat(), int dtype = -1)
A
Andrey Kamaev 已提交
387 388 389
        c_decl = "%s %s %s(%s)" % \
            ( fi.static, fi.ctype, fi.cname, \
              ", ".join(a.ctype + " " + a.name + [""," = "+a.defval][bool(a.defval)] for a in fi.args) )
390
        indent = " " * 4
A
Andrey Kamaev 已提交
391
        if fi.classname:
392
            indent += " " * 4
393 394
        # java comment
        self.java_code.write( "\n%s// C++: %s\n" % (indent, c_decl) )
A
Andrey Kamaev 已提交
395
        # check if we 'know' all the types
396 397 398
        type_info = type_dict.get(fi.ctype)
        if not (type_info and type_info.get("jn_type")): # unsupported ret type
            msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype
399
            self.skipped_func_list.append(c_decl + "\n" + msg)
400
            self.java_code.write( indent + msg )
401
            #self.cpp_code.write( msg )
A
Andrey Kamaev 已提交
402 403 404
            print "SKIP:", c_decl, "\n\tdue to RET type", fi.ctype
            return
        for a in fi.args:
405 406
            if a.ctype not in type_dict:
                msg = "// Unknown type '%s', skipping the function\n\n" % a.ctype
407
                self.skipped_func_list.append(c_decl + "\n" + msg)
408
                self.java_code.write( indent + msg )
409
                #self.cpp_code.write( msg )
410
                print "SKIP:", c_decl, "\n\tdue to ARG type", a.ctype
A
Andrey Kamaev 已提交
411
                return
412 413
            if a.ctype != "Mat" and "jn_args" in type_dict[a.ctype] and a.out: # complex out args not yet supported
                msg = "// Unsupported type '%s&', skipping the function\n\n" % a.ctype
414
                self.skipped_func_list.append(c_decl + "\n" + msg)
415
                self.java_code.write( indent + msg )
416
                #self.cpp_code.write( msg )
417
                print "SKIP:", c_decl, "\n\tdue to OUT ARG of type", a.ctype
A
Andrey Kamaev 已提交
418 419 420
                return

        self.ported_func_counter += 1
421
        self.ported_func_list.append(c_decl)
422

423 424 425 426
        # jn & cpp comment
        jn_code.write( "\n%s// C++: %s\n" % (indent, c_decl) )
        self.cpp_code.write( "\n//\n// %s\n//\n" % c_decl )

A
Andrey Kamaev 已提交
427
        # java args
428
        args = fi.args[:] # copy
A
Andrey Kamaev 已提交
429 430 431 432 433
        if args and args[-1].defval:
            isoverload = True

        while True:

434
             # java native method args
A
Andrey Kamaev 已提交
435 436 437
            jn_args = []
            # jni (cpp) function args
            jni_args = [ArgInfo([ "env", "env", "", [], "" ]), ArgInfo([ "cls", "cls", "", [], "" ])]
438 439 440 441 442 443
            suffix = "__"
            if fi.classname and fi.ctype and not fi.static: # non-static class method except c-tor
                # adding 'self'
                jn_args.append ( ArgInfo([ "__int64", "nativeObj", "", [], "" ]) )
                jni_args.append( ArgInfo([ "__int64", "self", "", [], "" ]) )
                suffix += "J"
A
Andrey Kamaev 已提交
444
            for a in args:
445 446 447
                suffix += type_dict[a.ctype].get("suffix") or ""
                fields = type_dict[a.ctype].get("jn_args") or []
                if fields: # complex type
A
Andrey Kamaev 已提交
448
                    for f in fields:
449
                        jn_args.append ( ArgInfo([ f[0], a.name + f[1], "", [], "" ]) )
A
Andrey Kamaev 已提交
450 451
                        jni_args.append( ArgInfo([ f[0], a.name + f[1].replace(".","_"), "", [], "" ]) )
                else:
452
                    jn_args.append(a)
A
Andrey Kamaev 已提交
453 454 455 456 457 458
                    jni_args.append(a)

            # java part:
            # private java NATIVE method decl
            # e.g.
            # private static native void n_add(long src1, long src2, long dst, long mask, int dtype);
459
            jn_code.write( Template(\
460
                "${indent}private static native $jn_type $jn_name($jn_args);\n").substitute(\
A
Andrey Kamaev 已提交
461
                indent = indent, \
462 463 464
                jn_type = type_dict[fi.ctype]["jn_type"], \
                jn_name = fi.jn_name, \
                jn_args = ", ".join(["%s %s" % (type_dict[a.ctype]["jn_type"], a.name.replace(".","_")) for a in jn_args])
A
Andrey Kamaev 已提交
465 466 467
            ) );

            # java part:
468 469 470 471 472 473 474 475 476

            #java doc comment
            f_name = fi.name
            if fi.classname:
                f_name = fi.classname + "::" + fi.name
            self.java_code.write(indent + "//javadoc: " + f_name + "(%s)\n" % \
                ", ".join([a.name for a in args])
            )

A
Andrey Kamaev 已提交
477 478 479 480
            # public java wrapper method impl (calling native one above)
            # e.g.
            # public static void add( Mat src1, Mat src2, Mat dst, Mat mask, int dtype )
            # { n_add( src1.nativeObj, src2.nativeObj, dst.nativeObj, mask.nativeObj, dtype );  }
481
            impl_code = "return $jn_name($jn_args_call);"
A
Andrey Kamaev 已提交
482
            if fi.ctype == "void":
483
                impl_code = "$jn_name($jn_args_call);"
A
Andrey Kamaev 已提交
484
            elif fi.ctype == "": # c-tor
485
                impl_code = "nativeObj = $jn_name($jn_args_call);"
A
Andrey Kamaev 已提交
486 487 488 489 490 491 492 493 494
            elif fi.ctype in self.classes: # wrapped class
                impl_code = " return new %s( $jn_name($jn_args_call) ); " % \
                    self.classes[fi.ctype].jname

            static = "static"
            if fi.classname:
                static = fi.static

            self.java_code.write( Template(\
495
                "${indent}public $static $j_type $j_name($j_args)").substitute(\
A
Andrey Kamaev 已提交
496 497
                indent = indent, \
                static=static, \
498
                j_type=type_dict[fi.ctype]["j_type"], \
A
Andrey Kamaev 已提交
499
                j_name=fi.jname, \
500
                j_args=", ".join(["%s %s" % (type_dict[a.ctype]["j_type"], a.name) for a in args]) \
A
Andrey Kamaev 已提交
501 502
            ) )

503
            self.java_code.write( Template("\n$indent{ " + impl_code + " }\n").substitute(\
A
Andrey Kamaev 已提交
504 505
                indent = indent, \
                jn_name=fi.jn_name, \
506
                jn_args_call=", ".join( [a.name for a in jn_args] )\
A
Andrey Kamaev 已提交
507 508 509 510 511
            ) )

            # cpp part:
            # jni_func(..) { return cv_func(..); }
            ret = "return "
512
            ext = ""
513
            default = "return 0;"
A
Andrey Kamaev 已提交
514 515
            if fi.ctype == "void":
                ret = ""
516
                default = ""
517 518 519
            elif fi.ctype == "string":
                ret = "return env->NewStringUTF"
                ext = ".c_str()"
520
                default = 'return env->NewStringUTF("");'
521
            elif fi.ctype in self.classes: # wrapped class:
522
                ret = "return (jlong) new " + self.classes[fi.ctype].jname
A
Andrey Kamaev 已提交
523 524 525 526 527

            cvname = "cv::" + fi.name
            j2cvargs = []
            if fi.classname:
                if not fi.ctype: # c-tor
528
                    cvname = "(jlong) new " + fi.classname
A
Andrey Kamaev 已提交
529
                elif fi.static:
530
                    cvname = "%s::%s" % (fi.classname, fi.name)
A
Andrey Kamaev 已提交
531
                else:
532
                    cvname = "me->" + fi.name
A
Andrey Kamaev 已提交
533
                    j2cvargs.append(\
534
                        "%(cls)s* me = (%(cls)s*) self; //TODO: check for NULL" \
535
                            % { "cls" : fi.classname} \
A
Andrey Kamaev 已提交
536 537 538
                    )
            cvargs = []
            for a in args:
539 540 541
                cvargs.append( type_dict[a.ctype].get("jni_name", "%(n)s") % {"n" : a.name})
                if "jni_var" in type_dict[a.ctype]: # complex type
                    j2cvargs.append(type_dict[a.ctype]["jni_var"] % {"n" : a.name} + ";")
A
Andrey Kamaev 已提交
542

543
            rtype = type_dict[fi.ctype]["jni_type"]
A
Andrey Kamaev 已提交
544 545 546 547 548 549
            self.cpp_code.write ( Template( \
"""

JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_$fname
  ($args)
{
550
    try {
551 552 553
#ifdef DEBUG
        LOGD("$module::$fname()");
#endif // DEBUG
554 555 556
        $j2cv
        $ret( $cvname( $cvargs )$ext );
    } catch(cv::Exception e) {
557 558 559
#ifdef DEBUG
        LOGD("$module::$fname() catched cv::Exception: %s", e.what());
#endif // DEBUG
560 561 562
        jclass je = env->FindClass("org/opencv/CvException");
        if(!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
563
        $default
564
    } catch (...) {
565 566 567
#ifdef DEBUG
        LOGD("$module::$fname() catched unknown exception (...)");
#endif // DEBUG
568 569
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {$module::$fname()}");
570
        $default
571
    }
A
Andrey Kamaev 已提交
572 573
}

574

A
Andrey Kamaev 已提交
575 576 577 578
""" ).substitute( \
        rtype = rtype, \
        module = self.module, \
        fname = fi.jni_name + ["",suffix][isoverload], \
579
        args = ", ".join(["%s %s" % (type_dict[a.ctype].get("jni_type"), a.name) for a in jni_args]), \
580
        j2cv = "\n        ".join([a for a in j2cvargs]), \
A
Andrey Kamaev 已提交
581 582 583
        ret = ret, \
        cvname = cvname, \
        cvargs = ", ".join([a for a in cvargs]), \
584
        ext = ext, \
585
        default = default
A
Andrey Kamaev 已提交
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
    ) )

            # processing args with default values
            if args and args[-1].defval:
                a = args.pop()
            else:
                break



    def gen_funcs(self):
        # generate the code for all the global functions
        indent = "\t"
        fflist = self.funcs.items()
        fflist.sort()
        for name, ffi in fflist:
            assert not ffi.funcs[0].classname, "Error: global func is a class member - "+name
            for fi in ffi.funcs:
604
                self.gen_func(fi, len(ffi.funcs)>1, self.jn_code)
A
Andrey Kamaev 已提交
605 606 607 608


    def gen_classes(self):
        # generate code for the classes (their methods and consts)
609 610
        indent = " " * 4
        indent_m = indent + " " * 4
A
Andrey Kamaev 已提交
611 612 613
        classlist = self.classes.items()
        classlist.sort()
        for name, ci in classlist:
614 615 616
            if name == "Mat":
                continue
            self.java_code.write( "\n\n" + indent + "// C++: class %s" % (ci.cname) + "\n" )
617
            self.java_code.write( indent + "//javadoc: " + name + "\n" ) #java doc comment
A
Andrey Kamaev 已提交
618 619 620 621 622 623 624
            self.java_code.write( indent + "public static class %s {\n\n" % (ci.jname) )
            # self
            self.java_code.write( indent_m + "protected final long nativeObj;\n" )
            self.java_code.write( indent_m + "protected %s(long addr) { nativeObj = addr; }\n\n" \
                % name );
            # constants
            if ci.consts:
625
                prefix = "\n" + indent_m + "\t"
A
Andrey Kamaev 已提交
626 627 628
                s = indent_m + "public static final int" + prefix +\
                    ("," + prefix).join(["%s = %s" % (c.name, c.value) for c in ci.consts]) + ";\n\n"
                self.java_code.write( s )
629 630
            # methods
            jn_code = StringIO()
A
Andrey Kamaev 已提交
631 632 633 634 635 636
            # c-tors
            fflist = ci.methods.items()
            fflist.sort()
            for n, ffi in fflist:
                if ffi.isconstructor:
                    for fi in ffi.funcs:
637
                        self.gen_func(fi, len(ffi.funcs)>1, jn_code)
A
Andrey Kamaev 已提交
638 639 640 641
            self.java_code.write( "\n" )
            for n, ffi in fflist:
                if not ffi.isconstructor:
                    for fi in ffi.funcs:
642
                        self.gen_func(fi, len(ffi.funcs)>1, jn_code)
A
Andrey Kamaev 已提交
643

644 645 646 647 648 649 650 651 652 653 654 655 656
            # finalize()
            self.java_code.write(
"""
        @Override
        protected void finalize() throws Throwable {
            n_delete(nativeObj);
            super.finalize();
        }

"""
            )

            self.java_code.write(indent_m + "// native stuff\n\n")
657
            self.java_code.write(indent_m + 'static { System.loadLibrary("opencv_java"); }\n')
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
            self.java_code.write( jn_code.getvalue() )
            self.java_code.write(
"""
        // native support for java finalize()
        private static native void n_delete(long nativeObj);
"""
            )
            self.java_code.write("\n" + indent + "}\n\n")

            # native support for java finalize()
            self.cpp_code.write( \
"""
//
//  native support for java finalize()
//  static void %(cls)s::n_delete( __int64 self )
//

JNIEXPORT void JNICALL Java_org_opencv_%(module)s_00024%(cls)s_n_1delete
  (JNIEnv* env, jclass cls, jlong self)
{
    delete (%(cls)s*) self;
}

""" % {"module" : module, "cls" : name}
            )
A
Andrey Kamaev 已提交
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704


if __name__ == "__main__":
    if len(sys.argv) < 4:
        print "Usage:\n", \
            os.path.basename(sys.argv[0]), \
            "<full path to hdr_parser.py> <module name> <C++ header> [<C++ header>...]"
        print "Current args are: ", ", ".join(["'"+a+"'" for a in sys.argv])
        exit(0)

    dstdir = "."
    hdr_parser_path = os.path.abspath(sys.argv[1])
    if hdr_parser_path.endswith(".py"):
        hdr_parser_path = os.path.dirname(hdr_parser_path)
    sys.path.append(hdr_parser_path)
    import hdr_parser
    module = sys.argv[2]
    srcfiles = sys.argv[3:]
    print "Generating module '" + module + "' from headers:\n\t" + "\n\t".join(srcfiles)
    generator = JavaWrapperGenerator()
    generator.gen(srcfiles, module, dstdir)