Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
gen_java.py
1#!/usr/bin/env python
2
3import sys, re, os.path, errno, fnmatch
4import json
5import logging
6from shutil import copyfile
7from pprint import pformat
8from string import Template
9
10if sys.version_info[0] >= 3:
11 from io import StringIO
12else:
13 from cStringIO import StringIO
14
15SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
16
17# list of modules + files remap
18config = None
19ROOT_DIR = None
20FILES_REMAP = {}
22 path = os.path.realpath(path)
23 if path in FILES_REMAP:
24 return FILES_REMAP[path]
25 assert path[-3:] != '.in', path
26 return path
27
28
29total_files = 0
30updated_files = 0
31
32module_imports = []
33module_j_code = None
34module_jn_code = None
35
36# list of class names, which should be skipped by wrapper generator
37# the list is loaded from misc/java/gen_dict.json defined for the module and its dependencies
38class_ignore_list = []
39
40# list of constant names, which should be skipped by wrapper generator
41# ignored constants can be defined using regular expressions
42const_ignore_list = []
43
44# list of private constants
45const_private_list = []
46
47# { Module : { public : [[name, val],...], private : [[]...] } }
48missing_consts = {}
49
50# c_type : { java/jni correspondence }
51# Complex data types are configured for each module using misc/java/gen_dict.json
52
53type_dict = {
54 # "simple" : { j_type : "?", jn_type : "?", jni_type : "?", suffix : "?" },
55 "": {"j_type": "", "jn_type": "long", "jni_type": "jlong"}, # c-tor ret_type
56 "void": {"j_type": "void", "jn_type": "void", "jni_type": "void"},
57 "env": {"j_type": "", "jn_type": "", "jni_type": "JNIEnv*"},
58 "cls": {"j_type": "", "jn_type": "", "jni_type": "jclass"},
59 "bool": {"j_type": "boolean", "jn_type": "boolean", "jni_type": "jboolean", "suffix": "Z"},
60 "char": {"j_type": "char", "jn_type": "char", "jni_type": "jchar", "suffix": "C"},
61 "int": {"j_type": "int", "jn_type": "int", "jni_type": "jint", "suffix": "I"},
62 "long": {"j_type": "int", "jn_type": "int", "jni_type": "jint", "suffix": "I"},
63 "float": {"j_type": "float", "jn_type": "float", "jni_type": "jfloat", "suffix": "F"},
64 "double": {"j_type": "double", "jn_type": "double", "jni_type": "jdouble", "suffix": "D"},
65 "size_t": {"j_type": "long", "jn_type": "long", "jni_type": "jlong", "suffix": "J"},
66 "__int64": {"j_type": "long", "jn_type": "long", "jni_type": "jlong", "suffix": "J"},
67 "int64": {"j_type": "long", "jn_type": "long", "jni_type": "jlong", "suffix": "J"},
68 "double[]": {"j_type": "double[]", "jn_type": "double[]", "jni_type": "jdoubleArray", "suffix": "_3D"}
69}
70
71# { class : { func : {j_code, jn_code, cpp_code} } }
72ManualFuncs = {}
73ToStringSupport = []
74
75# { class : { func : { arg_name : {"ctype" : ctype, "attrib" : [attrib]} } } }
76func_arg_fix = {}
77
78'''
79 # INFO: Needs no comments :D
80'''
81
82
83def read_contents(fname):
84 with open(fname, 'r') as f:
85 data = f.read()
86 return data
87
88
89'''
90 # INFO: Create dir or a file if not created already
91'''
92
93
94def mkdir_p(path):
95 ''' mkdir -p '''
96 try:
97 os.makedirs(path)
98 except OSError as exc:
99 if exc.errno == errno.EEXIST and os.path.isdir(path):
100 pass
101 else:
102 raise
103
104
106 '''
107 turns vpHomoMatrix to VpHomoMatrix
108 '''
109 if len(s) > 0:
110 return s[0].upper() + s[1:]
111 else:
112 return s
113
114
116 '''
117 turns VpHomoMatrix to vpHomoMatrix
118 '''
119 if len(s) > 0:
120 return s[0].lower() + s[1:]
121 else:
122 return s
123
124
125T_JAVA_START_INHERITED = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_class_inherited.prolog'))
126T_JAVA_START_ORPHAN = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_class.prolog'))
127T_JAVA_START_MODULE = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_module.prolog'))
128T_CPP_MODULE = Template(read_contents(os.path.join(SCRIPT_DIR, 'templates/cpp_module.template')))
129
130
132 def __init__(self, type, decl, namespaces):
133 self.namespace, self.classpath, self.classname, self.name = self.parseName(decl[0], namespaces)
134
135 # parse doxygen comments
136 self.params = {}
137 self.annotation = []
138 if type == "class":
139 docstring = "// C++: class " + self.name + "\n//javadoc: " + self.name
140 else:
141 docstring = ""
142 if len(decl) > 5 and decl[5]:
143 # logging.info('docstring: %s', decl[5])
144 if re.search("(@|\\\\)deprecated", decl[5]):
145 self.annotation.append("@Deprecated")
146
147 self.docstring = docstring
148
149 def parseName(self, name, namespaces):
150 '''
151 input: full name and available namespaces
152 returns: (namespace, classpath, classname, name)
153 '''
154 name = name[name.find(" ") + 1:].strip() # remove struct/class/const prefix
155 spaceName = ""
156 localName = name # <classes>.<name>
157
158 if name.count('.') == 1 and len(namespaces) == 1:
159 namespaces = list(namespaces)
160 pieces = name.split(".")
161 return namespaces[0], ".".join(namespaces[:-1]), camelCase(pieces[0]), pieces[1]
162
163 for namespace in sorted(namespaces, key=len, reverse=True):
164 if name.startswith(namespace + "."):
165 spaceName = namespace
166 localName = name.replace(namespace + ".", "")
167 break
168 pieces = localName.split(".")
169 if len(pieces) > 2: # <class>.<class>.<class>.<name>
170 return spaceName, ".".join(pieces[:-1]), pieces[-2], pieces[-1]
171 elif len(pieces) == 2: # <class>.<name>
172 return spaceName, pieces[0], pieces[0], pieces[1]
173 elif len(pieces) == 1: # <name>
174 return spaceName, "", "", pieces[0]
175 else:
176 return spaceName, "", "" # error?!
177
178 def fullName(self, isCPP=False):
179 result = ".".join([self.fullClass(), reverseCamelCase(self.name)])
180 return result if not isCPP else result.replace(".", "::")
181
182 def fullClass(self, isCPP=False):
183 result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f) > 0])
184 return result if not isCPP else result.replace(".", "::")
185
186
188 def __init__(self, decl, addedManually=False, namespaces=[]):
189 GeneralInfo.__init__(self, "const", decl, namespaces)
190 self.cname = self.name.replace(".", "::")
191 self.value = decl[1]
192 self.addedManually = addedManually
193
194 def __repr__(self):
195 return Template("CONST $name=$value$manual").substitute(name=self.name,
196 value=self.value,
197 manual="(manual)" if self.addedManually else "")
198
199 def isIgnored(self):
200 for c in const_ignore_list:
201 if re.match(c, self.name):
202 return True
203 return False
204
205
207 def __init__(self, decl): # [f_ctype, f_name, '', '/RW']
208 self.ctype = decl[0]
209 self.name = decl[1]
210 self.rw = "/RW" in decl[3]
211
212 def __repr__(self):
213 return Template("PROP $ctype $name").substitute(ctype=self.ctype, name=self.name)
214
215
217 def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ]
218 GeneralInfo.__init__(self, "class", decl, namespaces)
219 self.cname = self.name.replace(".", "::")
220 self.methods = []
222 self.consts = [] # using a list to save the occurrence order
223 self.private_consts = [] # TODO: ViSP wont need these
224 self.imports = set()
225 self.props = []
226 self.jname = self.name
227 self.smart = None # True if class stores Ptr<T>* instead of T* in nativeObj field
228 self.j_code = None # java code stream
229 self.jn_code = None # jni code stream
230 self.cpp_code = None # cpp code stream
231 for m in decl[2]:
232 if m.startswith("="):
233 self.jname = m[1:]
234 self.base = ''
235 if decl[1]:
236 # self.base = re.sub(r"\b"+self.jname+r"\b", "", decl[1].replace(":", "")).strip()
237 self.base = re.sub(r"^.*:", "", decl[1].split(",")[0]).strip().replace(self.jname, "")
238
239 def __repr__(self):
240 return Template("CLASS $namespace::$classpath.$name : $base").substitute(**self.__dict__)
241
242 def getAllImports(self, module):
243 return ["import %s;" % c for c in sorted(self.imports) if not c.startswith('org.visp.' + module)]
244
245 def addImports(self, ctype):
246 if ctype in type_dict:
247 if "j_import" in type_dict[ctype]:
248 self.imports.add(type_dict[ctype]["j_import"])
249 if "v_type" in type_dict[ctype]:
250 self.imports.add("java.util.List")
251 self.imports.add("java.util.ArrayList")
252 self.imports.add("org.visp.utils.Converters")
253 if type_dict[ctype]["v_type"] in ("VpMatrix", "vector_Mat"):
254 self.imports.add("org.visp.core.VpMatrix")
255
256 def getAllMethods(self):
257 result = []
258 result.extend([fi for fi in sorted(self.methods) if fi.isconstructor])
259 result.extend([fi for fi in sorted(self.methods) if not fi.isconstructor])
260 return result
261
262 def addMethod(self, fi):
263 if fi not in self.methods: # Dont add duplicates
264 self.methods.append(fi)
265
266 def getConst(self, name):
267 for cand in self.consts + self.private_consts:
268 if cand.name == name:
269 return cand
270 return None
271
272 def addConst(self, constinfo):
273 # choose right list (public or private)
274 consts = self.consts
275 for c in const_private_list:
276 if re.match(c, constinfo.name):
277 consts = self.private_consts
278 break
279 consts.append(constinfo)
280
281 def initCodeStreams(self, Module):
282 self.j_code = StringIO()
283 self.jn_code = StringIO()
284 self.cpp_code = StringIO();
285
286 # INFO: All of these T_JAVA are templates for starting portion of Java classes
287 if self.base:
288 self.j_code.write(T_JAVA_START_INHERITED)
289 else:
290 if self.name != Module:
291 self.j_code.write(T_JAVA_START_ORPHAN)
292 else:
293 self.j_code.write(T_JAVA_START_MODULE)
294
295 # INFO: Misc folder handling for modules like Core, Imgproc
296 if self.name == Module:
297
298 # INFO: module_imports - read from gen_dict.json. Contains 'java.lang.String', 'java.lang.Character' etc
299 for i in module_imports or []:
300 self.imports.add(i)
301 if module_j_code:
302 self.j_code.write(module_j_code)
303 if module_jn_code:
304 self.jn_code.write(module_jn_code)
305
307 self.j_code.close()
308 self.jn_code.close()
309 self.cpp_code.close()
310
311 def generateJavaCode(self, m, M):
312 return Template(self.j_code.getvalue() + "\n\n" + \
313 self.jn_code.getvalue() + "\n}\n").substitute( \
314 module=m,
315 name=self.name,
316 jname=self.jname,
317 imports="\n".join(self.getAllImports(M)),
318 docs=self.docstring,
319 annotation="\n".join(self.annotation),
320 base=self.base)
321
323 return self.cpp_code.getvalue()
324
325
326class ArgInfo():
327 def __init__(self, arg_tuple): # [ ctype, name, def val, [mod], argno ]
328 self.pointer = False
329 ctype = arg_tuple[0]
330 if ctype.endswith("*"):
331 ctype = ctype[:-1]
332 self.pointer = True
333 self.ctype = ctype
334 self.name = arg_tuple[1]
335 self.defval = arg_tuple[2]
336 self.out = ""
337 if "/O" in arg_tuple[3]:
338 self.out = "O"
339 if "/IO" in arg_tuple[3]:
340 self.out = "IO"
341
342 def __repr__(self):
343 return Template("ARG $ctype$p $name=$defval").substitute(ctype=self.ctype,
344 p=" *" if self.pointer else "",
345 name=self.name,
346 defval=self.defval)
347 def __eq__(self, other):
348 return self.ctype == other.ctype
349
350
352 def __init__(self, decl, namespaces=[]): # [ funcname, return_ctype, [modifiers], [args] ]
353 GeneralInfo.__init__(self, "func", decl, namespaces)
354 self.cname = self.name.replace(".", "::")
355 self.jname = self.name
356 self.isconstructor = self.name == self.classname
357
358 # TODO open-cv didn't add camel case support
359 # I'm adding
362 if self.isconstructor:
363 self.name = camelCase(self.name)
364 self.jname = camelCase(self.jname)
365
366 if "[" in self.name:
367 self.jname = "getelem"
368 for m in decl[2]:
369 if m.startswith("="):
370 self.jname = m[1:]
371 self.static = ["", "static"]["/S" in decl[2]]
372 self.ctype = re.sub(r"^VpTermCriteria", "TermCriteria", decl[1] or "")
373 self.args = []
374 func_fix_map = func_arg_fix.get(self.jname, {})
375 for a in decl[3]:
376 arg = a[:]
377 arg_fix_map = func_fix_map.get(arg[1], {})
378 arg[0] = arg_fix_map.get('ctype', arg[0]) # fixing arg type
379 arg[3] = arg_fix_map.get('attrib', arg[3]) # fixing arg attrib
380 self.args.append(ArgInfo(arg))
381
382 def __repr__(self):
383 return Template("FUNC <$ctype $namespace.$classpath.$name $args>").substitute(**self.__dict__)
384
385 def __lt__(self, other):
386 return self.__repr__() < other.__repr__()
387
388 def __eq__(self, other):
389 return self.cname == other.cname and len(self.args) == len(other.args) and self.args == other.args
390
391
393 def __init__(self):
394 self.cpp_files = []
395 self.clear()
396
397 def clear(self):
398 self.namespaces = set(["vp"])
399 self.classes = {}
400 self.module = ""
401 self.Module = ""
404 self.def_args_hist = {} # { def_args_cnt : funcs_cnt }
405
406 def add_class(self, decl):
407 classinfo = ClassInfo(decl, namespaces=self.namespaces)
408 if classinfo.name in class_ignore_list:
409 logging.info('ignored: %s', classinfo)
410 return
411 name = classinfo.name
412 if self.isWrapped(name) and not classinfo.base:
413 logging.warning('duplicated: %s', classinfo)
414 return
415 self.classes[name] = classinfo
416 if name in type_dict and not classinfo.base:
417 logging.warning('duplicated: %s', classinfo)
418 return
419 type_dict.setdefault(name, {}).update(
420 {"j_type": classinfo.jname,
421 "jn_type": "long", "jn_args": (("__int64", ".nativeObj"),),
422 "jni_name": "(*(" + classinfo.fullName(isCPP=True) + "*)%(n)s_nativeObj)", "jni_type": "jlong",
423 "suffix": "J",
424 "j_import": "org.visp.%s.%s" % (self.module, classinfo.jname)
425 }
426 )
427 type_dict.setdefault(name + '*', {}).update(
428 {"j_type": classinfo.jname,
429 "jn_type": "long", "jn_args": (("__int64", ".nativeObj"),),
430 "jni_name": "(" + classinfo.fullName(isCPP=True) + "*)%(n)s_nativeObj", "jni_type": "jlong",
431 "suffix": "J",
432 "j_import": "org.visp.%s.%s" % (self.module, classinfo.jname)
433 }
434 )
435
436 # missing_consts { Module : { public : [[name, val],...], private : [[]...] } }
437 if name in missing_consts:
438 if 'private' in missing_consts[name]:
439 for (n, val) in missing_consts[name]['private']:
440 classinfo.private_consts.append(ConstInfo([n, val], addedManually=True))
441 if 'public' in missing_consts[name]:
442 for (n, val) in missing_consts[name]['public']:
443 classinfo.consts.append(ConstInfo([n, val], addedManually=True))
444
445 # class props
446 for p in decl[3]:
447 if True: # "vector" not in p[0]:
448 classinfo.props.append(ClassPropInfo(p))
449 else:
450 logging.warning("Skipped property: [%s]" % name, p)
451
452 if classinfo.base:
453 classinfo.addImports(classinfo.base)
454 type_dict.setdefault("Ptr_" + name, {}).update(
455 {"j_type": classinfo.jname,
456 "jn_type": "long", "jn_args": (("__int64", ".getNativeObjAddr()"),),
457 "jni_name": "*((Ptr<" + classinfo.fullName(isCPP=True) + ">*)%(n)s_nativeObj)", "jni_type": "jlong",
458 "suffix": "J",
459 "j_import": "org.visp.%s.%s" % (self.module, classinfo.jname)
460 }
461 )
462 logging.info('ok: class %s, name: %s, base: %s', classinfo, name, classinfo.base)
463
464 def add_const(self, decl): # [ "const cname", val, [], [] ]
465 constinfo = ConstInfo(decl, namespaces=self.namespaces)
466 if constinfo.isIgnored():
467 logging.info('ignored: %s', constinfo)
468 elif not self.isWrapped(constinfo.classname):
469 logging.info('class not found: %s', constinfo)
470 else:
471 ci = self.getClass(constinfo.classname)
472 duplicate = ci.getConst(constinfo.name)
473 if duplicate:
474 if duplicate.addedManually:
475 logging.info('manual: %s', constinfo)
476 else:
477 logging.warning('duplicated: %s', constinfo)
478 else:
479 ci.addConst(constinfo)
480 logging.info('ok: %s', constinfo)
481
482 # INFO: Some functions are to be ignored - either they can't exist in java or have to be implemtd manully
483 def add_func(self, decl):
484 fi = FuncInfo(decl, namespaces=self.namespaces)
485 classname = fi.classname or self.Module
486
487 # Workaround for imgrpoc module
488 if classname == 'Vp' and self.Module == 'Imgproc':
489 classname = 'VpImgproc'
490
491 if classname in class_ignore_list:
492 logging.info('ignored: %s', fi)
493 elif classname in ManualFuncs and fi.jname in ManualFuncs[classname]:
494 logging.info('manual: %s', fi)
495 elif not self.isWrapped(classname):
496 logging.warning('not found: %s', fi)
497 else:
498 self.getClass(classname).addMethod(fi)
499 logging.info('ok: %s', fi)
500 # calc args with def val
501 cnt = len([a for a in fi.args if a.defval])
502 self.def_args_hist[cnt] = self.def_args_hist.get(cnt, 0) + 1
503
504 def save(self, path, buf):
505 global total_files, updated_files
506 total_files += 1
507 if os.path.exists(path):
508 with open(path, "rt") as f:
509 content = f.read()
510 if content == buf:
511 return
512 with open(path, "wt") as f:
513 f.write(buf)
514 updated_files += 1
515
516 def gen(self, srcfiles, module, output_path, output_jni_path, output_java_path, common_headers):
517 self.clear()
518 self.module = module
519 self.Module = module.capitalize()
520
521 # INFO: In open-cv 4, hdr_parser is imported to gen2.py which is imported to gen_java
523
524 # INFO: Donno why open-cv was adding module in list of classes
525 # self.add_class( ['class ' + self.Module, '', [], []] ) # [ 'class/struct cname', ':bases', [modlist] [props] ]
526
527 # scan the headers and build more descriptive maps of classes, consts, functions
528 includes = [];
529
530 # INFO: Don't know yet what it does
531 for hdr in common_headers:
532 logging.info("\n===== Common header : %s =====", hdr)
533 includes.append('#include "' + hdr + '"')
534
535 # INFO: These are .hpp files containing enums, template classes and some VP_EXPORT functions
536 for hdr in srcfiles:
537
538 '''
539 # INFO: decls contains enum and const declared in .hpp files. It also contains function declarations,
540 but only those which have a VISP_EXPORT macro with them. Read a snippet from `hdr_parser.py` for more
541
542 Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>, original_return_type, docstring],
543 where each element of <list_of_arguments> is 4-element list itself:
544 [argtype, argname, default_value /* or "" if none */, <list_of_modifiers>]
545 where the list of modifiers is yet another nested list of strings
546 (currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods
547 and "/A value" for the plain C arrays with counters)
548 original_return_type is None if the original_return_type is the same as return_value_type
549
550 Ex:
551 ['const cv.Error.StsOk', '0', [], [], None, ''] , where cv and Error are namespaces
552 ['cv.cubeRoot', 'float', [], [['float', 'val', '', []]], 'float', '@brief Computes the cube root of an ...']
553
554 '''
555 decls = parser.parse(hdr)
556
557 # TODO: Above parse function detects functions but not classes.
558 # SO I'm adding classes assuming that the file name is a class name
559 # Find the last occurence of / in path/to/class/myClass.h
560 # -2 for removing `.h`
561 self.add_class(["class " + camelCase(hdr[hdr.rfind('/') + 1:-2]), '', [], []])
562
563 # INFO: List of all namespaces mentioned in the .hpp file. Like cv,ogl,cuda etc
564 self.namespaces = parser.namespaces
565 logging.info("\n\n===== Header: %s =====", hdr)
566 logging.info("Namespaces: %s", parser.namespaces)
567
568 if decls:
569 includes.append('#include "' + hdr + '"')
570 else:
571 logging.info("Ignore header: %s", hdr)
572
573 # INFO: Now split each declaration in categories - class, const or function
574 # INFO: Some functions .are to be ignored - either they can't exist in java or have to be implemtd manully
575 for decl in decls:
576 logging.info("\n--- Incoming ---\n%s", pformat(decl[:5], 4)) # without docstring
577 name = decl[0]
578
579 # ToDO ignore operator member functions
580 if decl[1] == 'operator':
581 print("Skipped operator function: %s" % name)
582 elif name.startswith("struct") or name.startswith("class"):
583 self.add_class(decl)
584 elif name.startswith("const"):
585 self.add_const(decl)
586 else: # function
587 self.add_func(decl)
588
589 logging.info("\n\n===== Generating... =====")
590 moduleCppCode = StringIO()
591 package_path = os.path.join(output_java_path, module)
592 mkdir_p(package_path)
593 for ci in self.classes.values():
594 # INFO: Ignore classes that are manually. For open-cv it was Mat, for Visp it'll be vpMat and vpImage
595 if ci.name in ["vpMatrix", "vpImageUChar", "vpImageRGBa", "vpArray2D"]:
596 continue
597 ci.initCodeStreams(self.Module)
598 self.gen_class(ci)
599 classJavaCode = ci.generateJavaCode(self.module, self.Module)
600 self.save("%s/%s/%s.java" % (output_java_path, module, ci.jname), classJavaCode)
601 moduleCppCode.write(ci.generateCppCode())
602 ci.cleanupCodeStreams()
603 cpp_file = os.path.abspath(os.path.join(output_jni_path, module + ".inl.hpp"))
604 self.cpp_files.append(cpp_file)
605 self.save(cpp_file, T_CPP_MODULE.substitute(m=module, M=module.upper(), code=moduleCppCode.getvalue(),
606 includes="\n".join(includes)))
607 self.save(os.path.join(output_path, module + ".txt"), self.makeReport())
608
609 def makeReport(self):
610 '''
611 Returns string with generator report
612 '''
613 report = StringIO()
614 total_count = len(self.ported_func_list) + len(self.skipped_func_list)
615 report.write("PORTED FUNCs LIST (%i of %i):\n\n" % (len(self.ported_func_list), total_count))
616 report.write("\n".join(self.ported_func_list))
617 report.write("\n\nSKIPPED FUNCs LIST (%i of %i):\n\n" % (len(self.skipped_func_list), total_count))
618 report.write("".join(self.skipped_func_list))
619 for i in self.def_args_hist.keys():
620 report.write("\n%i def args - %i funcs" % (i, self.def_args_hist[i]))
621 return report.getvalue()
622
623 def fullTypeName(self, t):
624 if self.isWrapped(t):
625 return self.getClass(t).fullName(isCPP=True)
626 else:
627 return t
628
629 def gen_func(self, ci, fi, prop_name=''):
630 logging.info("%s", fi)
631 j_code = ci.j_code
632 jn_code = ci.jn_code
633 cpp_code = ci.cpp_code
634
635 # c_decl
636 # e.g: void add(Mat src1, Mat src2, Mat dst, Mat mask = Mat(), int dtype = -1)
637 if prop_name:
638 c_decl = "%s %s::%s" % (fi.ctype, fi.classname, prop_name)
639 else:
640 decl_args = []
641 for a in fi.args: # INFO: Even arguments have their own class: [type, defval, name, isPointer(bool), out(ret type)]
642 s = a.ctype or ' _hidden_ '
643 if a.pointer:
644 s += "*"
645 elif a.out:
646 s += "&"
647 s += " " + a.name
648 if a.defval:
649 s += " = " + a.defval
650 decl_args.append(s)
651 c_decl = "%s %s %s(%s)" % (fi.static, fi.ctype, fi.cname, ", ".join(decl_args))
652
653 # INFO: This is the java comment above every function
654 j_code.write("\n //\n // C++: %s\n //\n\n" % c_decl)
655
656 # check if we 'know' all the types
657 if fi.ctype not in type_dict: # unsupported ret type
658 msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype
659 self.skipped_func_list.append(c_decl + "\n" + msg)
660 j_code.write(" " * 4 + msg)
661 logging.warning("SKIP:" + c_decl.strip() + "\t due to RET type" + fi.ctype)
662 return
663
664 for a in fi.args:
665 if a.ctype not in type_dict:
666 if not a.defval and a.ctype.endswith("*"):
667 a.defval = 0
668 if a.defval:
669 a.ctype = ''
670 continue
671 msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I")
672 self.skipped_func_list.append(c_decl + "\n" + msg)
673 j_code.write(" " * 4 + msg)
674 logging.warning("SKIP:" + c_decl.strip() + "\t due to ARG type" + a.ctype + "/" + (a.out or "I"))
675 return
676
677 self.ported_func_list.append(c_decl)
678
679 # jn & cpp comment
680 jn_code.write("\n // C++: %s\n" % c_decl)
681 cpp_code.write("\n//\n// %s\n//\n" % c_decl)
682
683 # INFO: Generating the JNI method signatures
684 args = fi.args[:] # copy
685 j_signatures = []
686 suffix_counter = int(ci.methods_suffixes.get(fi.jname, -1))
687 while True:
688 suffix_counter += 1
689 ci.methods_suffixes[fi.jname] = suffix_counter
690 # java native method args
691 jn_args = []
692 # jni (cpp) function args
693 jni_args = [ArgInfo(["env", "env", "", [], ""]), ArgInfo(["cls", "", "", [], ""])]
694 j_prologue = []
695 j_epilogue = []
696 c_prologue = []
697 c_epilogue = []
698
699 # Add 3rd party specific tags
700 # If Lapack, Eigen3 or OpenCV are missing, don't include them to prevent compilation error
701 if re.search("Lapack", fi.name, re.IGNORECASE):
702 c_prologue.append('#if defined(VISP_HAVE_LAPACK)')
703
704 if re.search("Eigen3", fi.name, re.IGNORECASE):
705 c_prologue.append('#if defined(VISP_HAVE_EIGEN3)')
706
707 if re.search("OpenCV", fi.name, re.IGNORECASE):
708 c_prologue.append('#if defined (VISP_HAVE_OPENCV)')
709
710 if re.search("Gsl", fi.name, re.IGNORECASE):
711 c_prologue.append('#if defined(VISP_BUILD_DEPRECATED_FUNCTIONS) && defined(VISP_HAVE_LAPACK)')
712
713 if re.search("json", fi.name, re.IGNORECASE) or fi.name in ['saveConfigFile']:
714 c_prologue.append('#if defined(VISP_HAVE_NLOHMANN_JSON)')
715
716
717 if type_dict[fi.ctype]["jni_type"] == "jdoubleArray" and type_dict[fi.ctype]["suffix"] != "[D":
718 fields = type_dict[fi.ctype]["jn_args"]
719 c_epilogue.append( \
720 ("jdoubleArray _da_retval_ = env->NewDoubleArray(%(cnt)i); " +
721 "jdouble _tmp_retval_[%(cnt)i] = {%(args)s}; " +
722 "env->SetDoubleArrayRegion(_da_retval_, 0, %(cnt)i, _tmp_retval_);") %
723 {"cnt": len(fields), "args": ", ".join(["(jdouble)_retval_" + f[1] for f in fields])})
724 if fi.classname and fi.ctype and not fi.static: # non-static class method except c-tor
725 # adding 'self'
726 jn_args.append(ArgInfo(["__int64", "nativeObj", "", [], ""]))
727 jni_args.append(ArgInfo(["__int64", "self", "", [], ""]))
728 ci.addImports(fi.ctype)
729 for a in args:
730 if not a.ctype: # hidden
731 continue
732 ci.addImports(a.ctype)
733 if "v_type" in type_dict[a.ctype]: # pass as vector
734 if type_dict[a.ctype]["v_type"] in ("Mat", "vector_Mat"): # pass as Mat or vector_Mat
735 jn_args.append(ArgInfo(["__int64", "%s_mat.nativeObj" % a.name, "", [], ""]))
736 jni_args.append(ArgInfo(["__int64", "%s_mat_nativeObj" % a.name, "", [], ""]))
737 c_prologue.append(type_dict[a.ctype]["jni_var"] % {"n": a.name} + ";")
738 c_prologue.append("Mat& %(n)s_mat = *((Mat*)%(n)s_mat_nativeObj)" % {"n": a.name} + ";")
739 if "I" in a.out or not a.out:
740 if type_dict[a.ctype]["v_type"] == "vector_Mat":
741 j_prologue.append(
742 "List<VpMatrix> %(n)s_tmplm = new ArrayList<VpMatrix>((%(n)s != null) ? %(n)s.size() : 0);" % {
743 "n": a.name})
744 j_prologue.append(
745 "Mat %(n)s_mat = Converters.%(t)s_to_Mat(%(n)s, %(n)s_tmplm);" % {"n": a.name,
746 "t": a.ctype})
747 else:
748 j_prologue.append("Mat %(n)s_mat = Converters.%(t)s_to_Mat(%(n)s);" % {"n": a.name, "t": a.ctype})
749 c_prologue.append("Mat_to_%(t)s( %(n)s_mat, %(n)s );" % {"n": a.name, "t": a.ctype})
750 else:
751 j_prologue.append("Mat %s_mat = new Mat();" % a.name)
752 if "O" in a.out:
753 j_epilogue.append("Converters.Mat_to_%(t)s(%(n)s_mat, %(n)s);" % {"t": a.ctype, "n": a.name})
754 j_epilogue.append("%s_mat.release();" % a.name)
755 c_epilogue.append("%(t)s_to_Mat( %(n)s, %(n)s_mat );" % {"n": a.name, "t": a.ctype})
756 elif type_dict[a.ctype]["v_type"] in ("std::vector<double>"):
757 c_prologue.append("std::vector<double> v_ = List_to_vector_double(env, v);")
758 elif type_dict[a.ctype]["v_type"] in ("std::vector<float>"):
759 c_prologue.append("std::vector<float> v_ = List_to_vector_float(env, v);")
760 else: # pass as list
761 jn_args.append(ArgInfo([a.ctype, a.name, "", [], ""]))
762 jni_args.append(ArgInfo([a.ctype, "%s_list" % a.name, "", [], ""]))
763 ci.addImports(a.ctype)
764 j_prologue.append("long[] "+a.name+" = Converters."+a.ctype+"_to_Array("+a.name+"_list_arr);")
765 a.name += '_list'
766 c_prologue.append(type_dict[a.ctype]["jni_var"] % {"n": a.name} + "_arr;")
767 if "I" in a.out or not a.out:
768 c_prologue.append("%(n)s_arr = List_to_%(t)s(env, %(n)s);" % {"n": a.name, "t": a.ctype})
769 if "O" in a.out:
770 c_epilogue.append("Copy_%s_to_List(env,%s,%s_list);" % (a.ctype, a.name, a.name))
771 a.name += '_arr'
772 else:
773 fields = type_dict[a.ctype].get("jn_args", ((a.ctype, ""),))
774 if "I" in a.out or not a.out or self.isWrapped(a.ctype): # input arg, pass by primitive fields
775 for f in fields:
776 jn_args.append(ArgInfo([f[0], a.name + f[1], "", [], ""]))
777 jni_args.append(ArgInfo([f[0], a.name + f[1].replace(".", "_").replace("[", "").replace("]",
778 "").replace(
779 "_getNativeObjAddr()", "_nativeObj"), "", [], ""]))
780 if "O" in a.out and not self.isWrapped(a.ctype): # out arg, pass as double[]
781 jn_args.append(ArgInfo(["double[]", "%s_out" % a.name, "", [], ""]))
782 jni_args.append(ArgInfo(["double[]", "%s_out" % a.name, "", [], ""]))
783 j_prologue.append("double[] %s_out = new double[%i];" % (a.name, len(fields)))
784 c_epilogue.append( \
785 "jdouble tmp_%(n)s[%(cnt)i] = {%(args)s}; env->SetDoubleArrayRegion(%(n)s_out, 0, %(cnt)i, tmp_%(n)s);" %
786 {"n": a.name, "cnt": len(fields),
787 "args": ", ".join(["(jdouble)" + a.name + f[1] for f in fields])})
788 if type_dict[a.ctype]["j_type"] in ('bool', 'int', 'long', 'float', 'double'):
789 j_epilogue.append('if(%(n)s!=null) %(n)s[0] = (%(t)s)%(n)s_out[0];' % {'n': a.name, 't':
790 type_dict[a.ctype]["j_type"]})
791 else:
792 set_vals = []
793 i = 0
794 for f in fields:
795 set_vals.append("%(n)s%(f)s = %(t)s%(n)s_out[%(i)i]" %
796 {"n": a.name,
797 "t": ("(" + type_dict[f[0]]["j_type"] + ")", "")[f[0] == "double"],
798 "f": f[1], "i": i}
799 )
800 i += 1
801 j_epilogue.append("if(" + a.name + "!=null){ " + "; ".join(set_vals) + "; } ")
802
803 # calculate java method signature to check for uniqueness
804 j_args = []
805 for a in args:
806 if not a.ctype: # hidden
807 continue
808 jt = type_dict[a.ctype]["j_type"]
809 if a.out and jt in ('bool', 'int', 'long', 'float', 'double'):
810 jt += '[]'
811 j_args.append(jt + ' ' + a.name)
812 j_signature = type_dict[fi.ctype]["j_type"] + " " + \
813 fi.jname + "(" + ", ".join(j_args) + ")"
814 logging.info("java: " + j_signature)
815
816 if (j_signature in j_signatures):
817 if args:
818 args.pop()
819 continue
820 else:
821 break
822
823 # java part:
824 # private java NATIVE method decl
825 # e.g.
826 # private static native void add_0(long src1, long src2, long dst, long mask, int dtype);
827 jn_code.write(Template( \
828 " private static native $type $name($args);\n").substitute( \
829 type=type_dict[fi.ctype].get("jn_type", "double[]"), \
830 name=fi.jname + '_' + str(suffix_counter), \
831 args=", ".join(["%s %s" % (type_dict[a.ctype]["jn_type"],
832 a.name.replace(".", "_").replace("[", "").replace("]", "").replace(
833 "_getNativeObjAddr()", "_nativeObj")) for a in jn_args])
834 ));
835
836 # java part:
837
838 # java doc comment
839 f_name = fi.name
840 if fi.classname:
841 f_name = fi.classname + "::" + fi.name
842 java_doc = "//javadoc: " + f_name + "(%s)" % ", ".join([a.name for a in args if a.ctype])
843 j_code.write(" " * 4 + java_doc + "\n")
844
845 if fi.docstring:
846 lines = StringIO(fi.docstring)
847 for line in lines:
848 j_code.write(" " * 4 + line + "\n")
849 if fi.annotation:
850 j_code.write(" " * 4 + "\n".join(fi.annotation) + "\n")
851
852 # public java wrapper method impl (calling native one above)
853 # e.g.
854 # public static void add( Mat src1, Mat src2, Mat dst, Mat mask, int dtype )
855 # { add_0( src1.nativeObj, src2.nativeObj, dst.nativeObj, mask.nativeObj, dtype ); }
856 ret_type = fi.ctype
857 if fi.ctype.endswith('*'):
858 ret_type = ret_type[:-1]
859 ret_val = type_dict[ret_type]["j_type"] + " retVal = "
860 tail = ""
861 ret = "return retVal;"
862 if "v_type" in type_dict[ret_type]:
863 j_type = type_dict[ret_type]["j_type"]
864 ret_val = "long[] addressList = "
865 j_epilogue.append(j_type + ' retVal = Converters.Array_to_' + ret_type + '(addressList);')
866 elif ret_type.startswith("Ptr_"):
867 ret_val = type_dict[fi.ctype]["j_type"] + " retVal = " + type_dict[ret_type]["j_type"] + ".__fromPtr__("
868 tail = ")"
869 elif ret_type == "void":
870 ret_val = ""
871 ret = "return;"
872 elif ret_type == "": # c-tor
873 if fi.classname and ci.base:
874 ret_val = "super( "
875 tail = " )"
876 else:
877 ret_val = "nativeObj = "
878 ret = "return;"
879 elif self.isWrapped(camelCase(ret_type)): # wrapped class
880 ret_val = type_dict[ret_type]["j_type"] + " retVal = new " + camelCase(
881 self.getClass(camelCase(ret_type)).jname) + "("
882 tail = ")"
883 elif "jn_type" not in type_dict[ret_type]:
884 ret_val = type_dict[fi.ctype]["j_type"] + " retVal = new " + camelCase(
885 type_dict[ret_type]["j_type"]) + "("
886 tail = ")"
887
888 static = "static"
889 if fi.classname:
890 static = fi.static
891
892 j_code.write(Template( \
893 """
894 public $static $j_type $j_name($j_args)
895 {
896 $prologue
897 $ret_val$jn_name($jn_args_call)$tail;
898 $epilogue
899 $ret
900 }
901 """
902 ).substitute( \
903 ret=ret, \
904 ret_val=ret_val, \
905 tail=tail, \
906 prologue="\n ".join(j_prologue), \
907 epilogue="\n ".join(j_epilogue), \
908 static=static, \
909 j_type=type_dict[fi.ctype]["j_type"], \
910 j_name=fi.jname, \
911 j_args=", ".join(j_args), \
912 jn_name=fi.jname + '_' + str(suffix_counter), \
913 jn_args_call=", ".join([a.name for a in jn_args]), \
914 )
915 )
916
917 # cpp part:
918 # jni_func(..) { _retval_ = vp_func(..); return _retval_; }
919 ret = "return _retval_;"
920 default = "return 0;"
921 if fi.ctype == "void":
922 ret = "return;"
923 default = "return;"
924 elif not fi.ctype: # c-tor
925 ret = "return (jlong) _retval_;"
926 elif "v_type" in type_dict[fi.ctype]: # c-tor
927 if type_dict[fi.ctype]["v_type"] in ("vpMatrix", "vector_vpMatrix"):
928 ret = "return (jlong) _retval_;"
929 else: # returned as jobject
930 ret = "return _retval_;"
931 elif fi.ctype == "String":
932 ret = "return env->NewStringUTF(_retval_.c_str());"
933 default = 'return env->NewStringUTF("");'
934 elif self.isWrapped(camelCase(fi.ctype)): # wrapped class:
935 ret = "return (jlong) new %s(_retval_);" % self.smartWrap(ci, self.fullTypeName(camelCase(fi.ctype)))
936 elif fi.ctype.startswith('Ptr_'):
937 c_prologue.append("typedef Ptr<%s> %s;" % (self.fullTypeName(fi.ctype[4:]), fi.ctype))
938 ret = "return (jlong)(new %(ctype)s(_retval_));" % {'ctype': fi.ctype}
939 elif self.isWrapped(camelCase(ret_type)): # pointer to wrapped class:
940 ret = "return (jlong) _retval_;"
941 elif type_dict[fi.ctype]["jni_type"] == "jdoubleArray":
942 ret = "return _da_retval_;"
943
944 # hack: replacing func call with property set/get
945 name = fi.name
946 if prop_name:
947 if args:
948 name = prop_name + " = "
949 else:
950 name = prop_name + ";//"
951
952 cvname = fi.fullName(isCPP=True)
953 retval = self.fullTypeName(fi.ctype) + " _retval_ = "
954 if fi.ctype == "void":
955 retval = ""
956 elif fi.ctype == "String":
957 retval = "vp::" + retval
958 elif "v_type" in type_dict[fi.ctype]: # vector is returned
959 retval = type_dict[fi.ctype]['jni_var'] % {"n": '_ret_val_vector_'} + " = "
960 c_epilogue.append("jlongArray _retval_ = " + fi.ctype + "_to_List(env, _ret_val_vector_);")
961 if len(fi.classname) > 0:
962 if not fi.ctype: # c-tor
963 retval = reverseCamelCase(fi.fullClass(isCPP=True)) + "* _retval_ = "
964 cvname = "new " + reverseCamelCase(fi.fullClass(isCPP=True))
965 elif fi.static:
966 cvname = reverseCamelCase(fi.fullName(isCPP=True))
967 else:
968 cvname = ("me->" if not self.isSmartClass(ci) else "(*me)->") + name
969 c_prologue.append( \
970 "%(cls)s* me = (%(cls)s*) self; //TODO: check for nullptr" \
971 % {"cls": reverseCamelCase(self.smartWrap(ci, fi.fullClass(isCPP=True)))} \
972 )
973 cvargs = []
974 for a in args:
975 if a.pointer:
976 jni_name = "&%(n)s"
977 else:
978 jni_name = "%(n)s"
979 if not a.out and not "jni_var" in type_dict[a.ctype]:
980 # explicit cast to C type to avoid ambiguous call error on platforms (mingw)
981 # where jni types are different from native types (e.g. jint is not the same as int)
982 jni_name = "(%s)%s" % (a.ctype, jni_name)
983 if not a.ctype: # hidden
984 jni_name = a.defval
985 cvargs.append(type_dict[a.ctype].get("jni_name", jni_name) % {"n": a.name})
986 if "v_type" not in type_dict[a.ctype]:
987 if ("I" in a.out or not a.out or self.isWrapped(a.ctype)) and "jni_var" in type_dict[a.ctype]: # complex type
988 if a.ctype in [ 'vector_double', 'vector_float' ]:
989 c_prologue.append(type_dict[a.ctype]["jni_var"] % {"n": a.name} + "_ = List_to_" + a.ctype + "(env, " + a.name + ");")
990 # add "_" suffix to the last argument
991 cvargs[len(cvargs) - 1] = cvargs[len(cvargs) - 1] + "_"
992 else:
993 c_prologue.append(type_dict[a.ctype]["jni_var"] % {"n": a.name} + ";")
994 if a.out and "I" not in a.out and not self.isWrapped(a.ctype) and a.ctype:
995 c_prologue.append("%s %s;" % (a.ctype, a.name))
996
997 # Add 3rd party specific tags
998 # If Lapack, Eigen3 or OpenCV are missing, don't include them to prevent compilation error
999 if re.search("Lapack", fi.name, re.IGNORECASE):
1000 ret += '\n #endif'
1001
1002 if re.search("Eigen3", fi.name, re.IGNORECASE):
1003 ret += '\n #endif'
1004
1005 if re.search("OpenCV", fi.name, re.IGNORECASE):
1006 ret += '\n #endif'
1007
1008 if re.search("Gsl", fi.name, re.IGNORECASE):
1009 ret += '\n #endif'
1010
1011 if re.search("json", fi.name, re.IGNORECASE) or fi.name in ['saveConfigFile']:
1012 ret += '\n #endif'
1013
1014 rtype = type_dict[fi.ctype].get("jni_type", "jdoubleArray")
1015 clazz = ci.jname
1016 cpp_code.write(Template( \
1017 """
1018${namespace}
1019JNIEXPORT $rtype JNICALL Java_org_visp_${module}_${clazz}_$fname ($argst);
1020
1021JNIEXPORT $rtype JNICALL Java_org_visp_${module}_${clazz}_$fname
1022 ($args)
1023{
1024 static const char method_name[] = "$module::$fname()";
1025 try {
1026 LOGD("%s", method_name);
1027 $prologue
1028 $retval$cvname( ${cvargs} );
1029 $epilogue$ret
1030 } catch(const std::exception &e) {
1031 throwJavaException(env, &e, method_name);
1032 } catch (...) {
1033 throwJavaException(env, 0, method_name);
1034 }
1035 $default
1036}
1037 """).substitute( \
1038 rtype=rtype, \
1039 module=self.module.replace('_', '_1'), \
1040 clazz=clazz.replace('_', '_1'), \
1041 fname=(fi.jname + '_' + str(suffix_counter)).replace('_', '_1'), \
1042 args=", ".join(["%s %s" % (type_dict[a.ctype].get("jni_type"), a.name) for a in jni_args]), \
1043 argst=", ".join([type_dict[a.ctype].get("jni_type") for a in jni_args]), \
1044 prologue="\n ".join(c_prologue), \
1045 epilogue=" ".join(c_epilogue) + ("\n " if c_epilogue else ""), \
1046 ret=ret, \
1047 cvname=cvname, \
1048 cvargs=", ".join(cvargs), \
1049 default=default, \
1050 retval=retval, \
1051 namespace=('using namespace ' + ci.namespace.replace('.', '::') + ';') if ci.namespace else ''
1052 ))
1053
1054 # adding method signature to dictionarry
1055 j_signatures.append(j_signature)
1056
1057 # processing args with default values
1058 if not args or not args[-1].defval:
1059 break
1060 while args and args[-1].defval:
1061 # 'smart' overloads filtering
1062 a = args.pop()
1063 if a.name in ('mask', 'dtype', 'ddepth', 'lineType', 'borderType', 'borderMode', 'criteria'):
1064 break
1065
1066 # INFO: Regex starts here
1067 def gen_class(self, ci):
1068 logging.info("%s", ci)
1069 # constants
1070 if ci.private_consts:
1071 logging.info("%s", ci.private_consts)
1072 ci.j_code.write("""
1073 private static final int
1074 %s;\n\n""" % (",\n" + " " * 12).join(["%s = %s" % (c.name, c.value) for c in ci.private_consts])
1075 )
1076 if ci.consts:
1077 logging.info("%s", ci.consts)
1078 ci.j_code.write("""
1079 public static final int
1080 %s;\n\n""" % (",\n" + " " * 12).join(["%s = %s" % (c.name, c.value) for c in ci.consts])
1081 )
1082 # methods
1083 for fi in ci.getAllMethods():
1084 self.gen_func(ci, fi)
1085 # props
1086 for pi in ci.props:
1087 # getter
1088 getter_name = ci.fullName() + ".get_" + pi.name
1089 fi = FuncInfo([getter_name, pi.ctype, [], []],
1090 self.namespaces) # [ funcname, return_ctype, [modifiers], [args] ]
1091 self.gen_func(ci, fi, pi.name)
1092 if pi.rw:
1093 # setter
1094 setter_name = ci.fullName() + ".set_" + pi.name
1095 fi = FuncInfo([setter_name, "void", [], [[pi.ctype, pi.name, "", [], ""]]], self.namespaces)
1096 self.gen_func(ci, fi, pi.name)
1097
1098 # manual ports
1099 if ci.name in ManualFuncs:
1100 for func in ManualFuncs[ci.name].keys():
1101 ci.j_code.write("\n\t")
1102 ci.jn_code.write("\n\t")
1103 ci.cpp_code.write("\n")
1104 ci.j_code.write("\n\t".join(ManualFuncs[ci.name][func]["j_code"]))
1105 ci.jn_code.write("\n\t".join(ManualFuncs[ci.name][func]["jn_code"]))
1106 ci.cpp_code.write("\n".join(ManualFuncs[ci.name][func]["cpp_code"]))
1107 ci.j_code.write("\n\t")
1108 ci.jn_code.write("\n\t")
1109 ci.cpp_code.write("\n")
1110
1111 # Add only classes that support << operator
1112 if ci.name in ToStringSupport:
1113 # toString
1114 ci.j_code.write(
1115 """
1116 @Override
1117 public String toString(){
1118 return toString(nativeObj);
1119 }
1120 """)
1121
1122 ci.jn_code.write(
1123 """
1124 // native support for java toString()
1125 private static native String toString(long nativeObj);
1126 """)
1127
1128 # native support for java toString()
1129 ci.cpp_code.write("""
1130//
1131// native support for java toString()
1132// static String %(cls)s::toString()
1133//
1134
1135JNIEXPORT jstring JNICALL Java_org_visp_%(module)s_%(j_cls)s_toString(JNIEnv*, jclass, jlong);
1136
1137JNIEXPORT jstring JNICALL Java_org_visp_%(module)s_%(j_cls)s_toString
1138 (JNIEnv* env, jclass, jlong self)
1139{
1140 %(cls)s* me = (%(cls)s*) self; //TODO: check for nullptr
1141 std::stringstream ss;
1142 ss << *me;
1143 return env->NewStringUTF(ss.str().c_str());
1144}
1145
1146 """ % {"module": module.replace('_', '_1'), "cls": self.smartWrap(ci, ci.fullName(isCPP=True)),
1147 "j_cls": ci.jname.replace('_', '_1')}
1148 )
1149
1150 if ci.name != 'VpImgproc' and ci.name != self.Module or ci.base:
1151 # finalize()
1152 # Note 2023.10.27 warning: [removal] finalize() in Object has been deprecated and marked for removal
1153 # Comment for now
1154# ci.j_code.write(
1155# """
1156# @Override
1157# protected void finalize() throws Throwable {
1158# delete(nativeObj);
1159# }
1160# """)
1161
1162 ci.jn_code.write(
1163 """
1164 // native support for java finalize()
1165 private static native void delete(long nativeObj);
1166 """)
1167
1168 # native support for java finalize()
1169 ci.cpp_code.write( \
1170 """
1171//
1172// native support for java finalize()
1173// static void %(cls)s::delete( __int64 self )
1174//
1175
1176JNIEXPORT void JNICALL Java_org_visp_%(module)s_%(j_cls)s_delete(JNIEnv*, jclass, jlong);
1177
1178JNIEXPORT void JNICALL Java_org_visp_%(module)s_%(j_cls)s_delete
1179 (JNIEnv*, jclass, jlong self)
1180{
1181 delete (%(cls)s*) self;
1182}
1183
1184 """ % {"module": module.replace('_', '_1'), "cls": self.smartWrap(ci, ci.fullName(isCPP=True)),
1185 "j_cls": ci.jname.replace('_', '_1')}
1186 )
1187
1188 def getClass(self, classname):
1189 return self.classes[classname or self.Module]
1190
1191 def isWrapped(self, classname):
1192 name = classname or self.Module
1193 return name in self.classes
1194
1195 def isSmartClass(self, ci):
1196 '''
1197 Check if class stores Ptr<T>* instead of T* in nativeObj field
1198 '''
1199 if ci.smart != None:
1200 return ci.smart
1201
1202 # if parents are smart (we hope) then children are!
1203 # if not we believe the class is smart if it has "create" method
1204 ci.smart = False
1205 if ci.base or ci.name == 'Algorithm':
1206 ci.smart = True
1207 else:
1208 for fi in ci.methods:
1209 if fi.name == "create":
1210 ci.smart = True
1211 break
1212
1213 return ci.smart
1214
1215 def smartWrap(self, ci, fullname):
1216 '''
1217 Wraps fullname with Ptr<> if needed
1218 '''
1219 if self.isSmartClass(ci):
1220 return "Ptr<" + fullname + ">"
1221 if fullname[0] == ':': # some classes miss namespace. so they are named ::<class>. That :: should be removed
1222 fullname = fullname[2:]
1223 return fullname
1224
1225 '''
1226 # INFO: Generate a visp_jni.hpp file. Contains #include tags for all
1227 modules that very to be built and specified at compile time
1228 '''
1229
1230 def finalize(self, output_jni_path):
1231 list_file = os.path.join(output_jni_path, "visp_jni.hpp")
1232 self.save(list_file, '\n'.join(['#include "%s"' % f for f in self.cpp_files]))
1233
1234
1235'''
1236 # INFO: As name suggests, copies some files specified at the desired location.
1237 Are the files edited as they are copied? No
1238'''
1239
1240
1241def copy_java_files(java_files_dir, java_base_path, default_package_path='org/visp/'):
1242 global total_files, updated_files
1243 java_files = []
1244 re_filter = re.compile(r'^.+\.(java|aidl)(.in)?$')
1245 for root, dirnames, filenames in os.walk(java_files_dir):
1246 java_files += [os.path.join(root, filename) for filename in filenames if re_filter.match(filename)]
1247 java_files = [f.replace('\\', '/') for f in java_files]
1248
1249 re_package = re.compile(r'^package +(.+);')
1250 re_prefix = re.compile(r'^.+[\+/]([^\+]+).(java|aidl)(.in)?$')
1251 for java_file in java_files:
1252
1253 '''
1254 # INFO: Not all files are copied directly. There's a set of files
1255 read in the `config.json`. Instead of copyong them, the code copies
1256 a diffrent set of files(also mentioned in gen_config.json, stored as
1257 a dict).
1258 '''
1259
1260 src = checkFileRemap(java_file)
1261 with open(src, 'r') as f:
1262 package_line = f.readline()
1263 m = re_prefix.match(java_file)
1264
1265 # INFO: using absolute path, get the filename.extension <- @a
1266 target_fname = (m.group(1) + '.' + m.group(2)) if m else os.path.basename(java_file)
1267
1268 # INFO: Using the package name mentioned at the top of java file
1269 # INFO: generate a path. Ex package org.visp.core to ./org/visp/core <- @b
1270 m = re_package.match(package_line)
1271 if m:
1272 package = m.group(1)
1273 package_path = package.replace('.', '/')
1274 else:
1275 package_path = default_package_path
1276
1277 # print(java_file, package_path, target_fname)
1278
1279 # INFO: dest path = ./gen/java/@b/@a
1280 # INFO: ./gen/java is given as function argument
1281 dest = os.path.join(java_base_path, os.path.join(package_path, target_fname))
1282 assert dest[-3:] != '.in', dest + ' | ' + target_fname
1283
1284 mkdir_p(os.path.dirname(dest))
1285 total_files += 1
1286
1287 if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1):
1288 copyfile(src, dest)
1289 updated_files += 1
1290
1291
1292if __name__ == "__main__":
1293 # initialize logger
1294 logging.basicConfig(filename='gen_java.log', format=None, filemode='w', level=logging.INFO)
1295 handler = logging.StreamHandler()
1296 handler.setLevel(logging.WARNING)
1297 logging.getLogger().addHandler(handler)
1298
1299 # parse command line parameters
1300 import argparse
1301
1302 arg_parser = argparse.ArgumentParser(description='ViSP Java Wrapper Generator')
1303 arg_parser.add_argument('-p', '--parser', required=True, help='ViSP header parser')
1304 arg_parser.add_argument('-c', '--config', required=True, help='ViSP modules config')
1305
1306 args = arg_parser.parse_args()
1307
1308 # INFO: There's a file called gen2.py. It contains some functions Arginfo
1309 # , GeneralINfo, FuncIfno and some template strings. I guess its called
1310 # header parsers. I guess thats used for extracting Class name, function name
1311 # and module name from a .cpp file. These values must be fed in the
1312 # template given above to create JNI code.
1313
1314 # import header parser
1315 hdr_parser_path = os.path.abspath(args.parser)
1316 if hdr_parser_path.endswith(".py"):
1317 hdr_parser_path = os.path.dirname(hdr_parser_path)
1318 sys.path.append(hdr_parser_path)
1319 import hdr_parser
1320
1321 with open(args.config) as f:
1322 config = json.load(f)
1323
1324 ROOT_DIR = config['rootdir'];
1325 assert os.path.exists(ROOT_DIR)
1326 FILES_REMAP = {os.path.realpath(os.path.join(ROOT_DIR, f['src'])): f['target'] for f in config['files_remap']}
1327 logging.info("\nRemapped configured files (%d):\n%s", len(FILES_REMAP), pformat(FILES_REMAP))
1328
1329 # INFO: Now we create some folders inside the <build> folder viz. gen/cpp, gen/java
1330
1331 dstdir = "./gen"
1332 jni_path = os.path.join(dstdir, 'cpp');
1333 mkdir_p(jni_path)
1334 java_base_path = os.path.join(dstdir, 'java');
1335 mkdir_p(java_base_path)
1336 java_test_base_path = os.path.join(dstdir, 'test');
1337 mkdir_p(java_test_base_path)
1338
1339 for (subdir, target_subdir) in [('src/java', 'java'), ('android/java', None), ('android-21/java', None)]:
1340 if target_subdir is None:
1341 target_subdir = subdir
1342 java_files_dir = os.path.join(SCRIPT_DIR, subdir)
1343 if os.path.exists(java_files_dir):
1344 target_path = os.path.join(dstdir, target_subdir);
1345 mkdir_p(target_path)
1346 copy_java_files(java_files_dir, target_path)
1347
1348 # launch Java Wrapper generator
1350
1351 gen_dict_files = []
1352
1353 # INFO: I'm not adding modules manually. That info is read from <buildPath>/modules/java_bindings_generator/gen_java.json
1354 # To add Java Wrapper for a module, find and change the line given below in <module>/CMakeLists.txt:
1355 # vp_add_module(<mod-name> ....) --> vp_add_module(<mod-name> .... WRAP java)
1356 # Also you need to add support for functions that have to implemented manually in misc/java/<mod-name> folder
1357 print("JAVA: Processing ViSP modules: %d" % len(config['modules']))
1358 for e in config['modules']:
1359
1360 # INFO: Get absolute location of the module
1361 (module, module_location) = (e['name'], os.path.join(ROOT_DIR, e['location']))
1362 logging.info("\n=== MODULE: %s (%s) ===\n" % (module, module_location))
1363
1364 java_path = os.path.join(java_base_path, 'org/visp')
1365 mkdir_p(java_path)
1366
1367 module_imports = []
1368 module_j_code = None
1369 module_jn_code = None
1370 srcfiles = []
1371 common_headers = []
1372
1373 misc_location = os.path.join(hdr_parser_path, '../misc/' + module)
1374
1375 # INFO: Get some specific .hpp files for the module. Not all modules contain the set of files
1376 srcfiles_fname = os.path.join(misc_location, 'filelist')
1377 if os.path.exists(srcfiles_fname):
1378 with open(srcfiles_fname) as f:
1379 srcfiles = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
1380 else:
1381 re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.details.hpp$|_winrt.hpp$|/cuda/)')
1382 # .h files before .hpp
1383 h_files = []
1384 hpp_files = []
1385 for root, dirnames, filenames in os.walk(os.path.join(module_location, 'include')):
1386 h_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.h')]
1387 hpp_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.hpp')]
1388 srcfiles = h_files + hpp_files
1389 srcfiles = [f for f in srcfiles if not re_bad.search(f.replace('\\', '/'))]
1390 logging.info("\nFiles (%d):\n%s", len(srcfiles), pformat(srcfiles))
1391
1392 # INFO: Again, get some more .hpp files. Not for all modules
1393 common_headers_fname = os.path.join(misc_location, 'filelist_common')
1394 if os.path.exists(common_headers_fname):
1395 with open(common_headers_fname) as f:
1396 common_headers = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if
1397 str(l).strip()]
1398 logging.info("\nCommon headers (%d):\n%s", len(common_headers), pformat(common_headers))
1399
1400 '''
1401 # INFO: Not all C++ files can be directly turned to Java/JNI files. Get all
1402 such files and classes here. Include classes/functions that are to be ignored,
1403 new classes to be added manually. Sometimes arguments are to be changed while the
1404 function can be used
1405
1406 Such files exist for a few root/core modules only like core, imgproc, calib
1407 '''
1408 gendict_fname = os.path.join(misc_location, 'gen_dict.json')
1409 if os.path.exists(gendict_fname):
1410 with open(gendict_fname) as f:
1411 gen_type_dict = json.load(f)
1412 class_ignore_list += gen_type_dict.get("class_ignore_list", [])
1413 const_ignore_list += gen_type_dict.get("const_ignore_list", [])
1414 const_private_list += gen_type_dict.get("const_private_list", [])
1415 missing_consts.update(gen_type_dict.get("missing_consts", {}))
1416 type_dict.update(gen_type_dict.get("type_dict", {}))
1417 ManualFuncs.update(gen_type_dict.get("ManualFuncs", {}))
1418 ToStringSupport += gen_type_dict.get("ToStringSupport", [])
1419 func_arg_fix.update(gen_type_dict.get("func_arg_fix", {}))
1420 if 'module_j_code' in gen_type_dict:
1421 module_j_code = read_contents(
1422 checkFileRemap(os.path.join(misc_location, gen_type_dict['module_j_code'])))
1423 if 'module_jn_code' in gen_type_dict:
1424 module_jn_code = read_contents(
1425 checkFileRemap(os.path.join(misc_location, gen_type_dict['module_jn_code'])))
1426 module_imports += gen_type_dict.get("module_imports", [])
1427
1428 '''
1429 # INFO: In light of above, copy the .java files that were manually created to dst folder
1430 Later machine will generate all other files
1431 '''
1432 java_files_dir = os.path.join(misc_location, 'src/java')
1433 if os.path.exists(java_files_dir):
1434 copy_java_files(java_files_dir, java_base_path, 'org/visp/' + module)
1435
1436 java_test_files_dir = os.path.join(misc_location, 'test')
1437 if os.path.exists(java_test_files_dir):
1438 copy_java_files(java_test_files_dir, java_test_base_path, 'org/visp/test/' + module)
1439
1440 # INFO: Here's the meat. Most important function of the whole file
1441 if len(srcfiles) > 0:
1442 generator.gen(srcfiles, module, dstdir, jni_path, java_path, common_headers)
1443 else:
1444 logging.info("No generated code for module: %s", module)
1445
1446 '''
1447 # INFO: Generate a visp_jni.hpp file. Contains #include tags for all
1448 modules that very to be built and specified at compile time
1449 '''
1450 generator.finalize(jni_path)
1451
1452 print('Generated files: %d (updated %d)' % (total_files, updated_files))
__init__(self, arg_tuple)
Definition gen_java.py:327
__eq__(self, other)
Definition gen_java.py:347
cleanupCodeStreams(self)
Definition gen_java.py:306
addMethod(self, fi)
Definition gen_java.py:262
getAllImports(self, module)
Definition gen_java.py:242
generateJavaCode(self, m, M)
Definition gen_java.py:311
initCodeStreams(self, Module)
Definition gen_java.py:281
addConst(self, constinfo)
Definition gen_java.py:272
addImports(self, ctype)
Definition gen_java.py:245
__init__(self, decl, namespaces=[])
Definition gen_java.py:217
getConst(self, name)
Definition gen_java.py:266
__init__(self, decl)
Definition gen_java.py:207
__init__(self, decl, addedManually=False, namespaces=[])
Definition gen_java.py:188
__lt__(self, other)
Definition gen_java.py:385
__init__(self, decl, namespaces=[])
Definition gen_java.py:352
__eq__(self, other)
Definition gen_java.py:388
fullName(self, isCPP=False)
Definition gen_java.py:178
fullClass(self, isCPP=False)
Definition gen_java.py:182
__init__(self, type, decl, namespaces)
Definition gen_java.py:132
parseName(self, name, namespaces)
Definition gen_java.py:149
gen(self, srcfiles, module, output_path, output_jni_path, output_java_path, common_headers)
Definition gen_java.py:516
finalize(self, output_jni_path)
Definition gen_java.py:1230
isWrapped(self, classname)
Definition gen_java.py:1191
gen_func(self, ci, fi, prop_name='')
Definition gen_java.py:629
getClass(self, classname)
Definition gen_java.py:1188
smartWrap(self, ci, fullname)
Definition gen_java.py:1215
checkFileRemap(path)
Definition gen_java.py:21
camelCase(s)
Definition gen_java.py:105
reverseCamelCase(s)
Definition gen_java.py:115
mkdir_p(path)
Definition gen_java.py:94
copy_java_files(java_files_dir, java_base_path, default_package_path='org/visp/')
Definition gen_java.py:1241
read_contents(fname)
Definition gen_java.py:83