gen_java.py 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569
  1. #!/usr/bin/env python
  2. import sys, re, os.path, errno, fnmatch
  3. import json
  4. import logging
  5. import codecs
  6. from shutil import copyfile
  7. from pprint import pformat
  8. from string import Template
  9. if sys.version_info[0] >= 3:
  10. from io import StringIO
  11. else:
  12. import io
  13. class StringIO(io.StringIO):
  14. def write(self, s):
  15. if isinstance(s, str):
  16. s = unicode(s) # noqa: F821
  17. return super(StringIO, self).write(s)
  18. SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
  19. # list of modules + files remap
  20. config = None
  21. ROOT_DIR = None
  22. USE_CLEANERS = True
  23. FILES_REMAP = {}
  24. def checkFileRemap(path):
  25. path = os.path.realpath(path)
  26. if path in FILES_REMAP:
  27. return FILES_REMAP[path]
  28. assert path[-3:] != '.in', path
  29. return path
  30. total_files = 0
  31. updated_files = 0
  32. module_imports = []
  33. module_j_code = None
  34. module_jn_code = None
  35. # list of class names, which should be skipped by wrapper generator
  36. # the list is loaded from misc/java/gen_dict.json defined for the module and its dependencies
  37. class_ignore_list = []
  38. # list of constant names, which should be skipped by wrapper generator
  39. # ignored constants can be defined using regular expressions
  40. const_ignore_list = []
  41. # list of private constants
  42. const_private_list = []
  43. # { Module : { public : [[name, val],...], private : [[]...] } }
  44. missing_consts = {}
  45. # c_type : { java/jni correspondence }
  46. # Complex data types are configured for each module using misc/java/gen_dict.json
  47. type_dict = {
  48. # "simple" : { j_type : "?", jn_type : "?", jni_type : "?", suffix : "?" },
  49. "" : { "j_type" : "", "jn_type" : "long", "jni_type" : "jlong" }, # c-tor ret_type
  50. "void" : { "j_type" : "void", "jn_type" : "void", "jni_type" : "void" },
  51. "env" : { "j_type" : "", "jn_type" : "", "jni_type" : "JNIEnv*"},
  52. "cls" : { "j_type" : "", "jn_type" : "", "jni_type" : "jclass"},
  53. "bool" : { "j_type" : "boolean", "jn_type" : "boolean", "jni_type" : "jboolean", "suffix" : "Z" },
  54. "char" : { "j_type" : "char", "jn_type" : "char", "jni_type" : "jchar", "suffix" : "C" },
  55. "int" : { "j_type" : "int", "jn_type" : "int", "jni_type" : "jint", "suffix" : "I" },
  56. "long" : { "j_type" : "int", "jn_type" : "int", "jni_type" : "jint", "suffix" : "I" },
  57. "long long" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
  58. "float" : { "j_type" : "float", "jn_type" : "float", "jni_type" : "jfloat", "suffix" : "F" },
  59. "double" : { "j_type" : "double", "jn_type" : "double", "jni_type" : "jdouble", "suffix" : "D" },
  60. "size_t" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
  61. "__int64" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
  62. "int64" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
  63. "double[]": { "j_type" : "double[]", "jn_type" : "double[]", "jni_type" : "jdoubleArray", "suffix" : "_3D" },
  64. 'string' : { # std::string, see "String" in modules/core/misc/java/gen_dict.json
  65. 'j_type': 'String',
  66. 'jn_type': 'String',
  67. 'jni_name': 'n_%(n)s',
  68. 'jni_type': 'jstring',
  69. '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)',
  70. 'suffix': 'Ljava_lang_String_2',
  71. 'j_import': 'java.lang.String'
  72. },
  73. 'vector_string': { # std::vector<std::string>, see "vector_String" in modules/core/misc/java/gen_dict.json
  74. 'j_type': 'List<String>',
  75. 'jn_type': 'List<String>',
  76. 'jni_type': 'jobject',
  77. 'jni_var': 'std::vector< std::string > %(n)s',
  78. 'suffix': 'Ljava_util_List',
  79. 'v_type': 'string',
  80. 'j_import': 'java.lang.String'
  81. },
  82. "byte[]": {
  83. "j_type" : "byte[]",
  84. "jn_type": "byte[]",
  85. "jni_type": "jbyteArray",
  86. "jni_name": "n_%(n)s",
  87. "jni_var": "char* n_%(n)s = reinterpret_cast<char*>(env->GetByteArrayElements(%(n)s, NULL))",
  88. },
  89. }
  90. # Defines a rule to add extra prefixes for names from specific namespaces.
  91. # In example, cv::fisheye::stereoRectify from namespace fisheye is wrapped as fisheye_stereoRectify
  92. namespaces_dict = {}
  93. # { class : { func : {j_code, jn_code, cpp_code} } }
  94. ManualFuncs = {}
  95. # { class : { func : { arg_name : {"ctype" : ctype, "attrib" : [attrib]} } } }
  96. func_arg_fix = {}
  97. def read_contents(fname):
  98. with open(fname, 'r') as f:
  99. data = f.read()
  100. return data
  101. def mkdir_p(path):
  102. ''' mkdir -p '''
  103. try:
  104. os.makedirs(path)
  105. except OSError as exc:
  106. if exc.errno == errno.EEXIST and os.path.isdir(path):
  107. pass
  108. else:
  109. raise
  110. T_JAVA_START_INHERITED = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_class_inherited.prolog'))
  111. T_JAVA_START_ORPHAN = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_class.prolog'))
  112. T_JAVA_START_MODULE = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_module.prolog'))
  113. T_CPP_MODULE = Template(read_contents(os.path.join(SCRIPT_DIR, 'templates/cpp_module.template')))
  114. class GeneralInfo():
  115. def __init__(self, type, decl, namespaces):
  116. self.symbol_id, self.parent_id, self.namespace, self.classpath, self.classname, self.name = self.parseName(decl[0], namespaces)
  117. self.cname = get_cname(self.symbol_id)
  118. # parse doxygen comments
  119. self.params={}
  120. self.annotation=[]
  121. if type == "class":
  122. docstring="// C++: class " + self.name + "\n"
  123. else:
  124. docstring=""
  125. if len(decl)>5 and decl[5]:
  126. doc = decl[5]
  127. #logging.info('docstring: %s', doc)
  128. if re.search("(@|\\\\)deprecated", doc):
  129. self.annotation.append("@Deprecated")
  130. docstring += sanitize_java_documentation_string(doc, type)
  131. self.docstring = docstring
  132. def parseName(self, name, namespaces):
  133. '''
  134. input: full name and available namespaces
  135. returns: (namespace, classpath, classname, name)
  136. '''
  137. name = name[name.find(" ")+1:].strip() # remove struct/class/const prefix
  138. parent = name[:name.rfind('.')].strip()
  139. if len(parent) == 0:
  140. parent = None
  141. spaceName = ""
  142. localName = name # <classes>.<name>
  143. for namespace in sorted(namespaces, key=len, reverse=True):
  144. if name.startswith(namespace + "."):
  145. spaceName = namespace
  146. localName = name.replace(namespace + ".", "")
  147. break
  148. pieces = localName.split(".")
  149. if len(pieces) > 2: # <class>.<class>.<class>.<name>
  150. return name, parent, spaceName, ".".join(pieces[:-1]), pieces[-2], pieces[-1]
  151. elif len(pieces) == 2: # <class>.<name>
  152. return name, parent, spaceName, pieces[0], pieces[0], pieces[1]
  153. elif len(pieces) == 1: # <name>
  154. return name, parent, spaceName, "", "", pieces[0]
  155. else:
  156. return name, parent, spaceName, "", "" # error?!
  157. def fullNameOrigin(self):
  158. result = self.symbol_id
  159. return result
  160. def fullNameJAVA(self):
  161. result = '.'.join([self.fullParentNameJAVA(), self.jname])
  162. return result
  163. def fullNameCPP(self):
  164. result = self.cname
  165. return result
  166. def fullParentNameJAVA(self):
  167. result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f)>0])
  168. return result
  169. def fullParentNameCPP(self):
  170. result = get_cname(self.parent_id)
  171. return result
  172. class ConstInfo(GeneralInfo):
  173. def __init__(self, decl, addedManually=False, namespaces=[], enumType=None):
  174. GeneralInfo.__init__(self, "const", decl, namespaces)
  175. self.value = decl[1]
  176. self.enumType = enumType
  177. self.addedManually = addedManually
  178. if self.namespace in namespaces_dict:
  179. prefix = namespaces_dict[self.namespace]
  180. if prefix:
  181. self.name = '%s_%s' % (prefix, self.name)
  182. def __repr__(self):
  183. return Template("CONST $name=$value$manual").substitute(name=self.name,
  184. value=self.value,
  185. manual="(manual)" if self.addedManually else "")
  186. def isIgnored(self):
  187. for c in const_ignore_list:
  188. if re.match(c, self.name):
  189. return True
  190. return False
  191. def normalize_field_name(name):
  192. return name.replace(".","_").replace("[","").replace("]","").replace("_getNativeObjAddr()","_nativeObj")
  193. def normalize_class_name(name):
  194. return re.sub(r"^cv\.", "", name).replace(".", "_")
  195. def get_cname(name):
  196. return name.replace(".", "::")
  197. def cast_from(t):
  198. if t in type_dict and "cast_from" in type_dict[t]:
  199. return type_dict[t]["cast_from"]
  200. return t
  201. def cast_to(t):
  202. if t in type_dict and "cast_to" in type_dict[t]:
  203. return type_dict[t]["cast_to"]
  204. return t
  205. class ClassPropInfo():
  206. def __init__(self, decl): # [f_ctype, f_name, '', '/RW']
  207. self.ctype = decl[0]
  208. self.name = decl[1]
  209. self.rw = "/RW" in decl[3]
  210. def __repr__(self):
  211. return Template("PROP $ctype $name").substitute(ctype=self.ctype, name=self.name)
  212. class ClassInfo(GeneralInfo):
  213. def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ]
  214. GeneralInfo.__init__(self, "class", decl, namespaces)
  215. self.methods = []
  216. self.methods_suffixes = {}
  217. self.consts = [] # using a list to save the occurrence order
  218. self.private_consts = []
  219. self.imports = set()
  220. self.props= []
  221. self.jname = self.name
  222. self.smart = None # True if class stores Ptr<T>* instead of T* in nativeObj field
  223. self.j_code = None # java code stream
  224. self.jn_code = None # jni code stream
  225. self.cpp_code = None # cpp code stream
  226. for m in decl[2]:
  227. if m.startswith("="):
  228. self.jname = m[1:]
  229. if m == '/Simple':
  230. self.smart = False
  231. if self.classpath:
  232. prefix = self.classpath.replace('.', '_')
  233. self.name = '%s_%s' % (prefix, self.name)
  234. self.jname = '%s_%s' % (prefix, self.jname)
  235. if self.namespace in namespaces_dict:
  236. prefix = namespaces_dict[self.namespace]
  237. if prefix:
  238. self.name = '%s_%s' % (prefix, self.name)
  239. self.jname = '%s_%s' % (prefix, self.jname)
  240. self.base = ''
  241. if decl[1]:
  242. # FIXIT Use generator to find type properly instead of hacks below
  243. base_class = re.sub(r"^: ", "", decl[1])
  244. base_class = re.sub(r"^cv::", "", base_class)
  245. base_class = base_class.replace('::', '.')
  246. base_info = ClassInfo(('class {}'.format(base_class), '', [], [], None, None), [self.namespace])
  247. base_type_name = base_info.name
  248. if not base_type_name in type_dict:
  249. base_type_name = re.sub(r"^.*:", "", decl[1].split(",")[0]).strip().replace(self.jname, "")
  250. self.base = base_type_name
  251. self.addImports(self.base)
  252. def __repr__(self):
  253. return Template("CLASS $namespace::$classpath.$name : $base").substitute(**self.__dict__)
  254. def getAllImports(self, module):
  255. return ["import %s;" % c for c in sorted(self.imports) if not c.startswith('org.opencv.'+module)
  256. and (not c.startswith('java.lang.') or c.count('.') != 2)]
  257. def addImports(self, ctype):
  258. if ctype in type_dict:
  259. if "j_import" in type_dict[ctype]:
  260. self.imports.add(type_dict[ctype]["j_import"])
  261. if "v_type" in type_dict[ctype]:
  262. self.imports.add("java.util.List")
  263. self.imports.add("java.util.ArrayList")
  264. self.imports.add("org.opencv.utils.Converters")
  265. if type_dict[ctype]["v_type"] in ("Mat", "vector_Mat"):
  266. self.imports.add("org.opencv.core.Mat")
  267. def getAllMethods(self):
  268. result = []
  269. result += [fi for fi in self.methods if fi.isconstructor]
  270. result += [fi for fi in self.methods if not fi.isconstructor]
  271. return result
  272. def addMethod(self, fi):
  273. self.methods.append(fi)
  274. def getConst(self, name):
  275. for cand in self.consts + self.private_consts:
  276. if cand.name == name:
  277. return cand
  278. return None
  279. def addConst(self, constinfo):
  280. # choose right list (public or private)
  281. consts = self.consts
  282. for c in const_private_list:
  283. if re.match(c, constinfo.name):
  284. consts = self.private_consts
  285. break
  286. consts.append(constinfo)
  287. def initCodeStreams(self, Module):
  288. self.j_code = StringIO()
  289. self.jn_code = StringIO()
  290. self.cpp_code = StringIO()
  291. if self.base:
  292. self.j_code.write(T_JAVA_START_INHERITED)
  293. else:
  294. if self.name != Module:
  295. self.j_code.write(T_JAVA_START_ORPHAN)
  296. else:
  297. self.j_code.write(T_JAVA_START_MODULE)
  298. # misc handling
  299. if self.name == Module:
  300. for i in module_imports or []:
  301. self.imports.add(i)
  302. if module_j_code:
  303. self.j_code.write(module_j_code)
  304. if module_jn_code:
  305. self.jn_code.write(module_jn_code)
  306. def cleanupCodeStreams(self):
  307. self.j_code.close()
  308. self.jn_code.close()
  309. self.cpp_code.close()
  310. def generateJavaCode(self, m, M):
  311. return Template(self.j_code.getvalue() + "\n\n" +
  312. self.jn_code.getvalue() + "\n}\n").substitute(
  313. module = m,
  314. name = self.name,
  315. jname = self.jname,
  316. jcleaner = "long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy));" if USE_CLEANERS else "",
  317. imports = "\n".join(self.getAllImports(M)),
  318. docs = self.docstring,
  319. annotation = "\n" + "\n".join(self.annotation) if self.annotation else "",
  320. base = self.base)
  321. def generateCppCode(self):
  322. return self.cpp_code.getvalue()
  323. class ArgInfo():
  324. def __init__(self, arg_tuple): # [ ctype, name, def val, [mod], argno ]
  325. self.pointer = False
  326. ctype = arg_tuple[0]
  327. if ctype.endswith("*"):
  328. ctype = ctype[:-1]
  329. self.pointer = True
  330. self.ctype = ctype
  331. self.name = arg_tuple[1]
  332. self.defval = arg_tuple[2]
  333. self.out = ""
  334. if "/O" in arg_tuple[3]:
  335. self.out = "O"
  336. if "/IO" in arg_tuple[3]:
  337. self.out = "IO"
  338. def __repr__(self):
  339. return Template("ARG $ctype$p $name=$defval").substitute(ctype=self.ctype,
  340. p=" *" if self.pointer else "",
  341. name=self.name,
  342. defval=self.defval)
  343. class FuncInfo(GeneralInfo):
  344. def __init__(self, decl, namespaces=[]): # [ funcname, return_ctype, [modifiers], [args] ]
  345. GeneralInfo.__init__(self, "func", decl, namespaces)
  346. self.cname = get_cname(decl[0])
  347. self.jname = self.name
  348. self.isconstructor = self.name == self.classname
  349. if "[" in self.name:
  350. self.jname = "getelem"
  351. for m in decl[2]:
  352. if m.startswith("="): # alias from WRAP_AS
  353. self.jname = m[1:]
  354. if self.classpath and self.classname != self.classpath:
  355. prefix = self.classpath.replace('.', '_')
  356. self.classname = prefix #'%s_%s' % (prefix, self.classname)
  357. if self.isconstructor:
  358. self.name = prefix #'%s_%s' % (prefix, self.name)
  359. self.jname = prefix #'%s_%s' % (prefix, self.jname)
  360. if self.namespace in namespaces_dict:
  361. prefix = namespaces_dict[self.namespace]
  362. if prefix:
  363. if self.classname:
  364. self.classname = '%s_%s' % (prefix, self.classname)
  365. if self.isconstructor:
  366. self.jname = '%s_%s' % (prefix, self.jname)
  367. else:
  368. self.jname = '%s_%s' % (prefix, self.jname)
  369. self.static = ["","static"][ "/S" in decl[2] ]
  370. self.ctype = re.sub(r"^CvTermCriteria", "TermCriteria", decl[1] or "")
  371. self.args = []
  372. func_fix_map = func_arg_fix.get(self.jname, {})
  373. for a in decl[3]:
  374. arg = a[:]
  375. arg_fix_map = func_fix_map.get(arg[1], {})
  376. arg[0] = arg_fix_map.get('ctype', arg[0]) #fixing arg type
  377. arg[3] = arg_fix_map.get('attrib', arg[3]) #fixing arg attrib
  378. if arg[0] == 'dnn_Net':
  379. arg[0] = 'Net'
  380. self.args.append(ArgInfo(arg))
  381. def fullClassJAVA(self):
  382. return self.fullParentNameJAVA()
  383. def fullClassCPP(self):
  384. return self.fullParentNameCPP()
  385. def __repr__(self):
  386. return Template("FUNC <$ctype $namespace.$classpath.$name $args>").substitute(**self.__dict__)
  387. def __lt__(self, other):
  388. return self.__repr__() < other.__repr__()
  389. class JavaWrapperGenerator(object):
  390. def __init__(self):
  391. self.cpp_files = []
  392. self.clear()
  393. def clear(self):
  394. self.namespaces = ["cv"]
  395. classinfo_Mat = ClassInfo([ 'class cv.Mat', '', ['/Simple'], [] ], self.namespaces)
  396. self.classes = { "Mat" : classinfo_Mat }
  397. self.module = ""
  398. self.Module = ""
  399. self.ported_func_list = []
  400. self.skipped_func_list = []
  401. self.def_args_hist = {} # { def_args_cnt : funcs_cnt }
  402. def add_class(self, decl):
  403. classinfo = ClassInfo(decl, namespaces=self.namespaces)
  404. if classinfo.name in class_ignore_list:
  405. logging.info('ignored: %s', classinfo)
  406. return
  407. name = classinfo.name
  408. if self.isWrapped(name) and not classinfo.base:
  409. logging.warning('duplicated: %s', classinfo)
  410. return
  411. self.classes[name] = classinfo
  412. if name in type_dict and not classinfo.base:
  413. logging.warning('duplicated: %s', classinfo)
  414. return
  415. if self.isSmartClass(classinfo):
  416. jni_name = "*((*(Ptr<"+classinfo.fullNameCPP()+">*)%(n)s_nativeObj).get())"
  417. else:
  418. jni_name = "(*("+classinfo.fullNameCPP()+"*)%(n)s_nativeObj)"
  419. type_dict.setdefault(name, {}).update(
  420. { "j_type" : classinfo.jname,
  421. "jn_type" : "long", "jn_args" : (("__int64", ".getNativeObjAddr()"),),
  422. "jni_name" : jni_name,
  423. "jni_type" : "jlong",
  424. "suffix" : "J",
  425. "j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname)
  426. }
  427. )
  428. type_dict.setdefault(name+'*', {}).update(
  429. { "j_type" : classinfo.jname,
  430. "jn_type" : "long", "jn_args" : (("__int64", ".getNativeObjAddr()"),),
  431. "jni_name" : "&("+jni_name+")",
  432. "jni_type" : "jlong",
  433. "suffix" : "J",
  434. "j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname)
  435. }
  436. )
  437. # missing_consts { Module : { public : [[name, val],...], private : [[]...] } }
  438. if name in missing_consts:
  439. if 'private' in missing_consts[name]:
  440. for (n, val) in missing_consts[name]['private']:
  441. classinfo.private_consts.append( ConstInfo([n, val], addedManually=True) )
  442. if 'public' in missing_consts[name]:
  443. for (n, val) in missing_consts[name]['public']:
  444. classinfo.consts.append( ConstInfo([n, val], addedManually=True) )
  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. if classinfo.base:
  452. classinfo.addImports(classinfo.base)
  453. if ("Ptr_"+name) not in type_dict:
  454. type_dict["Ptr_"+name] = {
  455. "j_type" : classinfo.jname,
  456. "jn_type" : "long", "jn_args" : (("__int64", ".getNativeObjAddr()"),),
  457. "jni_name" : "*((Ptr<"+classinfo.fullNameCPP()+">*)%(n)s_nativeObj)", "jni_type" : "jlong",
  458. "suffix" : "J",
  459. "j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname)
  460. }
  461. logging.info('ok: class %s, name: %s, base: %s', classinfo, name, classinfo.base)
  462. def add_const(self, decl, enumType=None): # [ "const cname", val, [], [] ]
  463. constinfo = ConstInfo(decl, namespaces=self.namespaces, enumType=enumType)
  464. if constinfo.isIgnored():
  465. logging.info('ignored: %s', constinfo)
  466. else:
  467. if not self.isWrapped(constinfo.classname):
  468. logging.info('class not found: %s', constinfo)
  469. constinfo.name = constinfo.classname + '_' + constinfo.name
  470. constinfo.classname = ''
  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. def add_enum(self, decl): # [ "enum cname", "", [], [] ]
  482. enumType = decl[0].rsplit(" ", 1)[1]
  483. if enumType.endswith("<unnamed>"):
  484. enumType = None
  485. else:
  486. ctype = normalize_class_name(enumType)
  487. type_dict[ctype] = { "cast_from" : "int", "cast_to" : get_cname(enumType), "j_type" : "int", "jn_type" : "int", "jni_type" : "jint", "suffix" : "I" }
  488. const_decls = decl[3]
  489. for decl in const_decls:
  490. self.add_const(decl, enumType)
  491. def add_func(self, decl):
  492. fi = FuncInfo(decl, namespaces=self.namespaces)
  493. classname = fi.classname or self.Module
  494. class_symbol_id = classname if self.isWrapped(classname) else fi.classpath.replace('.', '_') #('.'.join([fi.namespace, fi.classpath])[3:])
  495. if classname in class_ignore_list:
  496. logging.info('ignored: %s', fi)
  497. elif classname in ManualFuncs and fi.jname in ManualFuncs[classname]:
  498. logging.info('manual: %s', fi)
  499. elif not self.isWrapped(class_symbol_id):
  500. logging.warning('not found: %s', fi)
  501. else:
  502. self.getClass(class_symbol_id).addMethod(fi)
  503. logging.info('ok: %s', fi)
  504. # calc args with def val
  505. cnt = len([a for a in fi.args if a.defval])
  506. self.def_args_hist[cnt] = self.def_args_hist.get(cnt, 0) + 1
  507. def save(self, path, buf):
  508. global total_files, updated_files
  509. total_files += 1
  510. if os.path.exists(path):
  511. with open(path, "rt") as f:
  512. content = f.read()
  513. if content == buf:
  514. return
  515. with codecs.open(path, "w", "utf-8") as f:
  516. f.write(buf)
  517. updated_files += 1
  518. def gen(self, srcfiles, module, output_path, output_jni_path, output_java_path, common_headers,
  519. preprocessor_definitions=None):
  520. self.clear()
  521. self.module = module
  522. self.Module = module.capitalize()
  523. # TODO: support UMat versions of declarations (implement UMat-wrapper for Java)
  524. parser = hdr_parser.CppHeaderParser(
  525. generate_umat_decls=False,
  526. preprocessor_definitions=preprocessor_definitions
  527. )
  528. self.add_class( ['class cv.' + self.Module, '', [], []] ) # [ 'class/struct cname', ':bases', [modlist] [props] ]
  529. # scan the headers and build more descriptive maps of classes, consts, functions
  530. includes = []
  531. for hdr in common_headers:
  532. logging.info("\n===== Common header : %s =====", hdr)
  533. includes.append('#include "' + hdr + '"')
  534. for hdr in srcfiles:
  535. decls = parser.parse(hdr)
  536. self.namespaces = sorted(parser.namespaces)
  537. logging.info("\n\n===== Header: %s =====", hdr)
  538. logging.info("Namespaces: %s", sorted(parser.namespaces))
  539. if decls:
  540. includes.append('#include "' + hdr + '"')
  541. else:
  542. logging.info("Ignore header: %s", hdr)
  543. for decl in decls:
  544. logging.info("\n--- Incoming ---\n%s", pformat(decl[:5], 4)) # without docstring
  545. name = decl[0]
  546. if name.startswith("struct") or name.startswith("class"):
  547. self.add_class(decl)
  548. elif name.startswith("const"):
  549. self.add_const(decl)
  550. elif name.startswith("enum"):
  551. # enum
  552. self.add_enum(decl)
  553. else: # function
  554. self.add_func(decl)
  555. logging.info("\n\n===== Generating... =====")
  556. moduleCppCode = StringIO()
  557. package_path = os.path.join(output_java_path, module)
  558. mkdir_p(package_path)
  559. for ci in sorted(self.classes.values(), key=lambda x: x.symbol_id):
  560. if ci.name == "Mat":
  561. continue
  562. ci.initCodeStreams(self.Module)
  563. self.gen_class(ci)
  564. classJavaCode = ci.generateJavaCode(self.module, self.Module)
  565. self.save("%s/%s/%s.java" % (output_java_path, module, ci.jname), classJavaCode)
  566. moduleCppCode.write(ci.generateCppCode())
  567. ci.cleanupCodeStreams()
  568. cpp_file = os.path.abspath(os.path.join(output_jni_path, module + ".inl.hpp"))
  569. self.cpp_files.append(cpp_file)
  570. self.save(cpp_file, T_CPP_MODULE.substitute(m = module, M = module.upper(), code = moduleCppCode.getvalue(), includes = "\n".join(includes)))
  571. self.save(os.path.join(output_path, module+".txt"), self.makeReport())
  572. def makeReport(self):
  573. '''
  574. Returns string with generator report
  575. '''
  576. report = StringIO()
  577. total_count = len(self.ported_func_list)+ len(self.skipped_func_list)
  578. report.write("PORTED FUNCs LIST (%i of %i):\n\n" % (len(self.ported_func_list), total_count))
  579. report.write("\n".join(self.ported_func_list))
  580. report.write("\n\nSKIPPED FUNCs LIST (%i of %i):\n\n" % (len(self.skipped_func_list), total_count))
  581. report.write("".join(self.skipped_func_list))
  582. for i in sorted(self.def_args_hist.keys()):
  583. report.write("\n%i def args - %i funcs" % (i, self.def_args_hist[i]))
  584. return report.getvalue()
  585. def fullTypeNameCPP(self, t):
  586. if self.isWrapped(t):
  587. return self.getClass(t).fullNameCPP()
  588. else:
  589. return cast_from(t)
  590. def gen_func(self, ci, fi, prop_name=''):
  591. logging.info("%s", fi)
  592. j_code = ci.j_code
  593. jn_code = ci.jn_code
  594. cpp_code = ci.cpp_code
  595. # c_decl
  596. # e.g: void add(Mat src1, Mat src2, Mat dst, Mat mask = Mat(), int dtype = -1)
  597. if prop_name:
  598. c_decl = "%s %s::%s" % (fi.ctype, fi.classname, prop_name)
  599. else:
  600. decl_args = []
  601. for a in fi.args:
  602. s = a.ctype or ' _hidden_ '
  603. if a.pointer:
  604. s += "*"
  605. elif a.out:
  606. s += "&"
  607. s += " " + a.name
  608. if a.defval:
  609. s += " = "+a.defval
  610. decl_args.append(s)
  611. c_decl = "%s %s %s(%s)" % ( fi.static, fi.ctype, fi.cname, ", ".join(decl_args) )
  612. # java comment
  613. j_code.write( "\n //\n // C++: %s\n //\n\n" % c_decl )
  614. # check if we 'know' all the types
  615. if fi.ctype not in type_dict: # unsupported ret type
  616. msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype
  617. self.skipped_func_list.append(c_decl + "\n" + msg)
  618. j_code.write( " "*4 + msg )
  619. logging.info("SKIP:" + c_decl.strip() + "\t due to RET type " + fi.ctype)
  620. return
  621. for a in fi.args:
  622. if a.ctype not in type_dict:
  623. if not a.defval and a.ctype.endswith("*"):
  624. a.defval = 0
  625. if a.defval:
  626. a.ctype = ''
  627. continue
  628. msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I")
  629. self.skipped_func_list.append(c_decl + "\n" + msg)
  630. j_code.write( " "*4 + msg )
  631. logging.info("SKIP:" + c_decl.strip() + "\t due to ARG type " + a.ctype + "/" + (a.out or "I"))
  632. return
  633. self.ported_func_list.append(c_decl)
  634. # jn & cpp comment
  635. jn_code.write( "\n // C++: %s\n" % c_decl )
  636. cpp_code.write( "\n//\n// %s\n//\n" % c_decl )
  637. # java args
  638. args = fi.args[:] # copy
  639. j_signatures=[]
  640. suffix_counter = int(ci.methods_suffixes.get(fi.jname, -1))
  641. while True:
  642. suffix_counter += 1
  643. ci.methods_suffixes[fi.jname] = suffix_counter
  644. # java native method args
  645. jn_args = []
  646. # jni (cpp) function args
  647. jni_args = [ArgInfo([ "env", "env", "", [], "" ]), ArgInfo([ "cls", "", "", [], "" ])]
  648. j_prologue = []
  649. j_epilogue = []
  650. c_prologue = []
  651. c_epilogue = []
  652. if type_dict[fi.ctype]["jni_type"] == "jdoubleArray":
  653. fields = type_dict[fi.ctype]["jn_args"]
  654. c_epilogue.append( \
  655. ("jdoubleArray _da_retval_ = env->NewDoubleArray(%(cnt)i); " +
  656. "jdouble _tmp_retval_[%(cnt)i] = {%(args)s}; " +
  657. "env->SetDoubleArrayRegion(_da_retval_, 0, %(cnt)i, _tmp_retval_);") %
  658. { "cnt" : len(fields), "args" : ", ".join(["(jdouble)_retval_" + f[1] for f in fields]) } )
  659. elif type_dict[fi.ctype]["jni_type"] == "jintArray":
  660. fields = type_dict[fi.ctype]["jn_args"]
  661. c_epilogue.append(
  662. ("jintArray _ia_retval_ = env->NewIntArray(%(cnt)i); " +
  663. "jint _tmp_retval_[%(cnt)i] = {%(args)s}; " +
  664. "env->SetIntArrayRegion(_ia_retval_, 0, %(cnt)i, _tmp_retval_);") %
  665. { "cnt" : len(fields), "args" : ", ".join(["(jint)_retval_" + f[1] for f in fields]) } )
  666. if fi.classname and fi.ctype and not fi.static: # non-static class method except c-tor
  667. # adding 'self'
  668. jn_args.append ( ArgInfo([ "__int64", "nativeObj", "", [], "" ]) )
  669. jni_args.append( ArgInfo([ "__int64", "self", "", [], "" ]) )
  670. ci.addImports(fi.ctype)
  671. for a in args:
  672. if not a.ctype: # hidden
  673. continue
  674. ci.addImports(a.ctype)
  675. if "v_type" in type_dict[a.ctype]: # pass as vector
  676. if type_dict[a.ctype]["v_type"] in ("Mat", "vector_Mat"): #pass as Mat or vector_Mat
  677. jn_args.append ( ArgInfo([ "__int64", "%s_mat.nativeObj" % a.name, "", [], "" ]) )
  678. jni_args.append ( ArgInfo([ "__int64", "%s_mat_nativeObj" % a.name, "", [], "" ]) )
  679. c_prologue.append( type_dict[a.ctype]["jni_var"] % {"n" : a.name} + ";" )
  680. c_prologue.append( "Mat& %(n)s_mat = *((Mat*)%(n)s_mat_nativeObj)" % {"n" : a.name} + ";" )
  681. if "I" in a.out or not a.out:
  682. if type_dict[a.ctype]["v_type"] == "vector_Mat":
  683. j_prologue.append( "List<Mat> %(n)s_tmplm = new ArrayList<Mat>((%(n)s != null) ? %(n)s.size() : 0);" % {"n" : a.name } )
  684. j_prologue.append( "Mat %(n)s_mat = Converters.%(t)s_to_Mat(%(n)s, %(n)s_tmplm);" % {"n" : a.name, "t" : a.ctype} )
  685. else:
  686. if not type_dict[a.ctype]["j_type"].startswith("MatOf"):
  687. j_prologue.append( "Mat %(n)s_mat = Converters.%(t)s_to_Mat(%(n)s);" % {"n" : a.name, "t" : a.ctype} )
  688. else:
  689. j_prologue.append( "Mat %s_mat = %s;" % (a.name, a.name) )
  690. c_prologue.append( "Mat_to_%(t)s( %(n)s_mat, %(n)s );" % {"n" : a.name, "t" : a.ctype} )
  691. else:
  692. if not type_dict[a.ctype]["j_type"].startswith("MatOf"):
  693. j_prologue.append( "Mat %s_mat = new Mat();" % a.name )
  694. else:
  695. j_prologue.append( "Mat %s_mat = %s;" % (a.name, a.name) )
  696. if "O" in a.out:
  697. if not type_dict[a.ctype]["j_type"].startswith("MatOf"):
  698. j_epilogue.append("Converters.Mat_to_%(t)s(%(n)s_mat, %(n)s);" % {"t" : a.ctype, "n" : a.name})
  699. j_epilogue.append( "%s_mat.release();" % a.name )
  700. c_epilogue.append( "%(t)s_to_Mat( %(n)s, %(n)s_mat );" % {"n" : a.name, "t" : a.ctype} )
  701. else: #pass as list
  702. jn_args.append ( ArgInfo([ a.ctype, a.name, "", [], "" ]) )
  703. jni_args.append ( ArgInfo([ a.ctype, "%s_list" % a.name , "", [], "" ]) )
  704. c_prologue.append(type_dict[a.ctype]["jni_var"] % {"n" : a.name} + ";")
  705. if "I" in a.out or not a.out:
  706. c_prologue.append("%(n)s = List_to_%(t)s(env, %(n)s_list);" % {"n" : a.name, "t" : a.ctype})
  707. if "O" in a.out:
  708. c_epilogue.append("Copy_%s_to_List(env,%s,%s_list);" % (a.ctype, a.name, a.name))
  709. else:
  710. fields = type_dict[a.ctype].get("jn_args", ((a.ctype, ""),))
  711. if "I" in a.out or not a.out or self.isWrapped(a.ctype): # input arg, pass by primitive fields
  712. for f in fields:
  713. # Use array access format for Java code when jn_type is array type
  714. if type_dict[a.ctype].get("jn_type", "").endswith("[]"):
  715. # For Java code: convert .val[0] format to [0] format
  716. jn_args.append ( ArgInfo([ f[0], a.name + f[1].replace(".val[", "["), "", [], "" ]) )
  717. else:
  718. # For non-array types, use conventional format
  719. jn_args.append ( ArgInfo([ f[0], a.name + f[1], "", [], "" ]) )
  720. # For C++ code: use conventional format as is
  721. jni_args.append( ArgInfo([ f[0], a.name + normalize_field_name(f[1]), "", [], "" ]) )
  722. if "O" in a.out and not self.isWrapped(a.ctype): # out arg, pass as double[]
  723. jn_args.append ( ArgInfo([ "double[]", "%s_out" % a.name, "", [], "" ]) )
  724. jni_args.append ( ArgInfo([ "double[]", "%s_out" % a.name, "", [], "" ]) )
  725. j_prologue.append( "double[] %s_out = new double[%i];" % (a.name, len(fields)) )
  726. c_epilogue.append(
  727. "jdouble tmp_%(n)s[%(cnt)i] = {%(args)s}; env->SetDoubleArrayRegion(%(n)s_out, 0, %(cnt)i, tmp_%(n)s);" %
  728. { "n" : a.name, "cnt" : len(fields), "args" : ", ".join(["(jdouble)" + a.name + f[1] for f in fields]) } )
  729. if type_dict[a.ctype]["j_type"] in ('bool', 'int', 'long', 'float', 'double'):
  730. j_epilogue.append('if(%(n)s!=null) %(n)s[0] = (%(t)s)%(n)s_out[0];' % {'n':a.name,'t':type_dict[a.ctype]["j_type"]})
  731. else:
  732. set_vals = []
  733. i = 0
  734. for f in fields:
  735. # Use array access format for Java code when jn_type is array type
  736. if type_dict[a.ctype].get("jn_type", "").endswith("[]"):
  737. # For Java code: convert .val[0] format to [0] format
  738. set_vals.append( "%(n)s%(f)s = %(t)s%(n)s_out[%(i)i]" %
  739. {"n" : a.name, "t": ("("+type_dict[f[0]]["j_type"]+")", "")[f[0]=="double"], "f" : f[1].replace(".val[", "["), "i" : i}
  740. )
  741. else:
  742. set_vals.append( "%(n)s%(f)s = %(t)s%(n)s_out[%(i)i]" %
  743. {"n" : a.name, "t": ("("+type_dict[f[0]]["j_type"]+")", "")[f[0]=="double"], "f" : f[1], "i" : i}
  744. )
  745. i += 1
  746. j_epilogue.append( "if("+a.name+"!=null){ " + "; ".join(set_vals) + "; } ")
  747. # calculate java method signature to check for uniqueness
  748. j_args = []
  749. for a in args:
  750. if not a.ctype: #hidden
  751. continue
  752. jt = type_dict[a.ctype]["j_type"]
  753. if a.out and jt in ('bool', 'int', 'long', 'float', 'double'):
  754. jt += '[]'
  755. j_args.append( jt + ' ' + a.name )
  756. j_signature = type_dict[fi.ctype]["j_type"] + " " + \
  757. fi.jname + "(" + ", ".join(j_args) + ")"
  758. logging.info("java: " + j_signature)
  759. if j_signature in j_signatures:
  760. if args:
  761. args.pop()
  762. continue
  763. else:
  764. break
  765. # java part:
  766. # private java NATIVE method decl
  767. # e.g.
  768. # private static native void add_0(long src1, long src2, long dst, long mask, int dtype);
  769. jn_code.write( Template(
  770. " private static native $type $name($args);\n").substitute(
  771. type = type_dict[fi.ctype].get("jn_type", "double[]"),
  772. name = fi.jname + '_' + str(suffix_counter),
  773. args = ", ".join(["%s %s" % (type_dict[a.ctype]["jn_type"], normalize_field_name(a.name)) for a in jn_args])
  774. ) )
  775. # java part:
  776. #java doc comment
  777. if fi.docstring:
  778. lines = fi.docstring.splitlines()
  779. returnTag = False
  780. javadocParams = []
  781. toWrite = []
  782. inCode = False
  783. for index, line in enumerate(lines):
  784. p0 = line.find("@param")
  785. if p0 != -1:
  786. p0 += 7
  787. p1 = line.find(' ', p0)
  788. p1 = len(line) if p1 == -1 else p1
  789. name = line[p0:p1]
  790. javadocParams.append(name)
  791. for arg in j_args:
  792. if arg.endswith(" " + name):
  793. toWrite.append(line);
  794. break
  795. else:
  796. if "<code>" in line:
  797. inCode = True
  798. if "</code>" in line:
  799. inCode = False
  800. line = line.replace('@result ', '@return ') # @result is valid in Doxygen, but invalid in Javadoc
  801. if "@return " in line:
  802. returnTag = True
  803. if (not inCode and toWrite and not toWrite[-1] and
  804. line and not line.startswith("\\") and not line.startswith("<ul>") and not line.startswith("@param")):
  805. toWrite.append("<p>");
  806. if index == len(lines) - 1:
  807. for arg in j_args:
  808. name = arg[arg.rfind(' ') + 1:]
  809. if not name in javadocParams:
  810. toWrite.append(" * @param " + name + " automatically generated");
  811. if type_dict[fi.ctype]["j_type"] and not returnTag and fi.ctype != "void":
  812. toWrite.append(" * @return automatically generated");
  813. toWrite.append(line);
  814. for line in toWrite:
  815. j_code.write(" "*4 + line + "\n")
  816. if fi.annotation:
  817. j_code.write(" "*4 + "\n".join(fi.annotation) + "\n")
  818. # public java wrapper method impl (calling native one above)
  819. # e.g.
  820. # public static void add( Mat src1, Mat src2, Mat dst, Mat mask, int dtype )
  821. # { add_0( src1.nativeObj, src2.nativeObj, dst.nativeObj, mask.nativeObj, dtype ); }
  822. ret_type = fi.ctype
  823. if fi.ctype.endswith('*'):
  824. ret_type = ret_type[:-1]
  825. ret_val = type_dict[ret_type]["j_type"] + " retVal = " if j_epilogue else "return "
  826. tail = ""
  827. ret = "return retVal;" if j_epilogue else ""
  828. if "v_type" in type_dict[ret_type]:
  829. j_type = type_dict[ret_type]["j_type"]
  830. if type_dict[ret_type]["v_type"] in ("Mat", "vector_Mat"):
  831. tail = ")"
  832. if j_type.startswith('MatOf'):
  833. ret_val += j_type + ".fromNativeAddr("
  834. else:
  835. ret_val = "Mat retValMat = new Mat("
  836. j_prologue.append( j_type + ' retVal = new Array' + j_type+'();')
  837. j_epilogue.append('Converters.Mat_to_' + ret_type + '(retValMat, retVal);')
  838. ret = "return retVal;"
  839. elif ret_type.startswith("Ptr_"):
  840. constructor = type_dict[ret_type]["j_type"] + ".__fromPtr__("
  841. if j_epilogue:
  842. ret_val = type_dict[fi.ctype]["j_type"] + " retVal = " + constructor
  843. else:
  844. ret_val = "return " + constructor
  845. tail = ")"
  846. elif ret_type == "void":
  847. ret_val = ""
  848. ret = ""
  849. elif ret_type == "": # c-tor
  850. if fi.classname and ci.base:
  851. ret_val = "super("
  852. tail = ")"
  853. else:
  854. ret_val = "nativeObj = "
  855. tail = ";\n long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy))" if USE_CLEANERS else ""
  856. ret = ""
  857. elif self.isWrapped(ret_type): # wrapped class
  858. constructor = self.getClass(ret_type).jname + "("
  859. if j_epilogue:
  860. ret_val = type_dict[ret_type]["j_type"] + " retVal = new " + constructor
  861. else:
  862. ret_val = "return new " + constructor
  863. tail = ")"
  864. elif "jn_type" not in type_dict[ret_type]:
  865. constructor = type_dict[ret_type]["j_type"] + "("
  866. if j_epilogue:
  867. ret_val = type_dict[fi.ctype]["j_type"] + " retVal = new " + constructor
  868. else:
  869. ret_val = "return new " + constructor
  870. tail = ")"
  871. static = "static"
  872. if fi.classname:
  873. static = fi.static
  874. j_code.write( Template(
  875. """ public $static$j_type$j_name($j_args) {$prologue
  876. $ret_val$jn_name($jn_args_call)$tail;$epilogue$ret
  877. }
  878. """
  879. ).substitute(
  880. ret = "\n " + ret if ret else "",
  881. ret_val = ret_val,
  882. tail = tail,
  883. prologue = "\n " + "\n ".join(j_prologue) if j_prologue else "",
  884. epilogue = "\n " + "\n ".join(j_epilogue) if j_epilogue else "",
  885. static = static + " " if static else "",
  886. j_type=type_dict[fi.ctype]["j_type"] + " " if type_dict[fi.ctype]["j_type"] else "",
  887. j_name=fi.jname,
  888. j_args=", ".join(j_args),
  889. jn_name=fi.jname + '_' + str(suffix_counter),
  890. jn_args_call=", ".join( [a.name for a in jn_args] ),
  891. )
  892. )
  893. # cpp part:
  894. # jni_func(..) { _retval_ = cv_func(..); return _retval_; }
  895. ret = "return _retval_;" if c_epilogue else ""
  896. default = "return 0;"
  897. if fi.ctype == "void":
  898. ret = ""
  899. default = ""
  900. elif not fi.ctype: # c-tor
  901. if self.isSmartClass(ci):
  902. ret = "return (jlong)(new Ptr<%(ctype)s>(_retval_));" % { 'ctype': fi.fullClassCPP() }
  903. else:
  904. ret = "return (jlong) _retval_;"
  905. elif "v_type" in type_dict[fi.ctype]: # c-tor
  906. if type_dict[fi.ctype]["v_type"] in ("Mat", "vector_Mat"):
  907. ret = "return (jlong) _retval_;"
  908. elif fi.ctype in ['String', 'string']:
  909. ret = "return env->NewStringUTF(_retval_.c_str());"
  910. default = 'return env->NewStringUTF("");'
  911. elif self.isWrapped(fi.ctype): # wrapped class:
  912. ret = None
  913. if fi.ctype in self.classes:
  914. ret_ci = self.classes[fi.ctype]
  915. if self.isSmartClass(ret_ci):
  916. ret = "return (jlong)(new Ptr<%(ctype)s>(new %(ctype)s(_retval_)));" % { 'ctype': ret_ci.fullNameCPP() }
  917. if ret is None:
  918. ret = "return (jlong) new %s(_retval_);" % self.fullTypeNameCPP(fi.ctype)
  919. elif fi.ctype.startswith('Ptr_'):
  920. c_prologue.append("typedef Ptr<%s> %s;" % (self.fullTypeNameCPP(fi.ctype[4:]), fi.ctype))
  921. ret = "return (jlong)(new %(ctype)s(_retval_));" % { 'ctype':fi.ctype }
  922. elif self.isWrapped(ret_type): # pointer to wrapped class:
  923. ret = "return (jlong) _retval_;"
  924. elif type_dict[fi.ctype]["jni_type"] == "jdoubleArray":
  925. ret = "return _da_retval_;"
  926. elif type_dict[fi.ctype]["jni_type"] == "jintArray":
  927. ret = "return _ia_retval_;"
  928. elif "jni_var" in type_dict[ret_type]:
  929. c_epilogue.append(type_dict[ret_type]["jni_var"] % {"n" : '_retval_'})
  930. ret = f"return {type_dict[ret_type]['jni_name'] % {'n' : '_retval_'}};"
  931. # hack: replacing func call with property set/get
  932. name = fi.name
  933. if prop_name:
  934. if args:
  935. name = prop_name + " = "
  936. else:
  937. name = prop_name + ";//"
  938. cvname = fi.fullNameCPP()
  939. retval = self.fullTypeNameCPP(fi.ctype) + " _retval_ = " if ret else "return "
  940. if fi.ctype == "void":
  941. retval = ""
  942. elif fi.ctype == "String":
  943. retval = "cv::" + self.fullTypeNameCPP(fi.ctype) + " _retval_ = "
  944. elif fi.ctype == "string":
  945. retval = "std::string _retval_ = "
  946. elif "v_type" in type_dict[fi.ctype]: # vector is returned
  947. retval = type_dict[fi.ctype]['jni_var'] % {"n" : '_ret_val_vector_'} + " = "
  948. if type_dict[fi.ctype]["v_type"] in ("Mat", "vector_Mat"):
  949. c_epilogue.append("Mat* _retval_ = new Mat();")
  950. c_epilogue.append(fi.ctype+"_to_Mat(_ret_val_vector_, *_retval_);")
  951. else:
  952. if ret:
  953. c_epilogue.append("jobject _retval_ = " + fi.ctype + "_to_List(env, _ret_val_vector_);")
  954. else:
  955. c_epilogue.append("return " + fi.ctype + "_to_List(env, _ret_val_vector_);")
  956. if fi.classname:
  957. if not fi.ctype: # c-tor
  958. if self.isSmartClass(ci):
  959. retval = self.smartWrap(ci, fi.fullClassCPP()) + " _retval_ = "
  960. cvname = "makePtr<" + fi.fullClassCPP() +">"
  961. else:
  962. retval = fi.fullClassCPP() + "* _retval_ = "
  963. cvname = "new " + fi.fullClassCPP()
  964. elif fi.static:
  965. cvname = fi.fullNameCPP()
  966. else:
  967. cvname = ("me->" if not self.isSmartClass(ci) else "(*me)->") + name
  968. c_prologue.append(
  969. "%(cls)s* me = (%(cls)s*) self; //TODO: check for NULL"
  970. % { "cls" : self.smartWrap(ci, fi.fullClassCPP())}
  971. )
  972. cvargs = []
  973. for a in args:
  974. if a.pointer:
  975. jni_name = "&%(n)s"
  976. else:
  977. jni_name = "%(n)s"
  978. if not a.out and not "jni_var" in type_dict[a.ctype]:
  979. # explicit cast to C type to avoid ambiguous call error on platforms (mingw)
  980. # where jni types are different from native types (e.g. jint is not the same as int)
  981. jni_name = "(%s)%s" % (cast_to(a.ctype), jni_name)
  982. if not a.ctype: # hidden
  983. jni_name = a.defval
  984. cvargs.append( type_dict[a.ctype].get("jni_name", jni_name) % {"n" : a.name})
  985. if "v_type" not in type_dict[a.ctype]:
  986. if ("I" in a.out or not a.out or self.isWrapped(a.ctype)) and "jni_var" in type_dict[a.ctype]: # complex type
  987. c_prologue.append(type_dict[a.ctype]["jni_var"] % {"n" : a.name} + ";")
  988. if a.out and "I" not in a.out and not self.isWrapped(a.ctype) and a.ctype:
  989. c_prologue.append("%s %s;" % (a.ctype, a.name))
  990. rtype = type_dict[fi.ctype].get("jni_type", "jdoubleArray")
  991. clazz = ci.jname
  992. cpp_code.write ( Template(
  993. """
  994. JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname ($argst);
  995. JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname
  996. ($args)
  997. {
  998. ${namespace}
  999. static const char method_name[] = "$module::$fname()";
  1000. try {
  1001. LOGD("%s", method_name);$prologue
  1002. $retval$cvname($cvargs);$epilogue$ret
  1003. } catch(const std::exception &e) {
  1004. throwJavaException(env, &e, method_name);
  1005. } catch (...) {
  1006. throwJavaException(env, 0, method_name);
  1007. }$default
  1008. }
  1009. """ ).substitute(
  1010. rtype = rtype,
  1011. module = self.module.replace('_', '_1'),
  1012. clazz = clazz.replace('_', '_1'),
  1013. fname = (fi.jname + '_' + str(suffix_counter)).replace('_', '_1'),
  1014. args = ", ".join(["%s %s" % (type_dict[a.ctype].get("jni_type"), a.name) for a in jni_args]),
  1015. argst = ", ".join([type_dict[a.ctype].get("jni_type") for a in jni_args]),
  1016. prologue = "\n " + "\n ".join(c_prologue) if c_prologue else "",
  1017. epilogue = "\n " + "\n ".join(c_epilogue) if c_epilogue else "",
  1018. ret = "\n " + ret if ret else "",
  1019. cvname = cvname,
  1020. cvargs = " " + ", ".join(cvargs) + " " if cvargs else "",
  1021. default = "\n " + default if default else "",
  1022. retval = retval,
  1023. namespace = ('using namespace ' + ci.namespace.replace('.', '::') + ';') if ci.namespace and ci.namespace != 'cv' else ''
  1024. ) )
  1025. # adding method signature to dictionary
  1026. j_signatures.append(j_signature)
  1027. # processing args with default values
  1028. if args and args[-1].defval:
  1029. args.pop()
  1030. else:
  1031. break
  1032. def gen_class(self, ci):
  1033. logging.info("%s", ci)
  1034. # constants
  1035. consts_map = {c.name: c for c in ci.private_consts}
  1036. consts_map.update({c.name: c for c in ci.consts})
  1037. def const_value(v):
  1038. if v in consts_map:
  1039. target = consts_map[v]
  1040. assert target.value != v
  1041. return const_value(target.value)
  1042. return v
  1043. if ci.private_consts:
  1044. logging.info("%s", ci.private_consts)
  1045. ci.j_code.write("""
  1046. private static final int
  1047. %s;\n\n""" % (",\n"+" "*12).join(["%s = %s" % (c.name, const_value(c.value)) for c in ci.private_consts])
  1048. )
  1049. if ci.consts:
  1050. enumTypes = set(map(lambda c: c.enumType, ci.consts))
  1051. grouped_consts = {enumType: [c for c in ci.consts if c.enumType == enumType] for enumType in enumTypes}
  1052. for typeName in sorted(grouped_consts.keys(), key=lambda x: str(x) if x is not None else ""):
  1053. consts = grouped_consts[typeName]
  1054. logging.info("%s", consts)
  1055. if typeName:
  1056. typeNameShort = typeName.rsplit(".", 1)[-1]
  1057. ###################### Utilize Java enums ######################
  1058. # ci.j_code.write("""
  1059. # public enum {1} {{
  1060. # {0};
  1061. #
  1062. # private final int id;
  1063. # {1}(int id) {{ this.id = id; }}
  1064. # {1}({1} _this) {{ this.id = _this.id; }}
  1065. # public int getValue() {{ return id; }}
  1066. # }}\n\n""".format((",\n"+" "*8).join(["%s(%s)" % (c.name, c.value) for c in consts]), typeName)
  1067. # )
  1068. ################################################################
  1069. ci.j_code.write("""
  1070. // C++: enum {1} ({2})
  1071. public static final int
  1072. {0};\n\n""".format((",\n"+" "*12).join(["%s = %s" % (c.name, c.value) for c in consts]), typeNameShort, typeName)
  1073. )
  1074. else:
  1075. ci.j_code.write("""
  1076. // C++: enum <unnamed>
  1077. public static final int
  1078. {0};\n\n""".format((",\n"+" "*12).join(["%s = %s" % (c.name, c.value) for c in consts]))
  1079. )
  1080. # methods
  1081. for fi in ci.getAllMethods():
  1082. self.gen_func(ci, fi)
  1083. # props
  1084. for pi in ci.props:
  1085. basename = ci.fullNameOrigin()
  1086. # getter
  1087. getter_name = basename + ".get_" + pi.name
  1088. fi = FuncInfo( [getter_name, pi.ctype, [], []], self.namespaces ) # [ funcname, return_ctype, [modifiers], [args] ]
  1089. self.gen_func(ci, fi, pi.name)
  1090. if pi.rw:
  1091. #setter
  1092. setter_name = basename + ".set_" + pi.name
  1093. fi = FuncInfo( [ setter_name, "void", [], [ [pi.ctype, pi.name, "", [], ""] ] ], self.namespaces)
  1094. self.gen_func(ci, fi, pi.name)
  1095. # manual ports
  1096. if ci.name in ManualFuncs:
  1097. for func in sorted(ManualFuncs[ci.name].keys()):
  1098. logging.info("manual function: %s", func)
  1099. fn = ManualFuncs[ci.name][func]
  1100. ci.j_code.write("\n".join(fn["j_code"]))
  1101. ci.jn_code.write("\n".join(fn["jn_code"]))
  1102. ci.cpp_code.write("\n".join(fn["cpp_code"]))
  1103. if ci.name != self.Module or ci.base:
  1104. # finalize() for old Java
  1105. if not USE_CLEANERS:
  1106. ci.j_code.write(
  1107. """
  1108. @Override
  1109. protected void finalize() throws Throwable {
  1110. delete(nativeObj);
  1111. }
  1112. """ )
  1113. ci.jn_code.write(
  1114. """
  1115. // native support for java finalize() or cleaner
  1116. private static native void delete(long nativeObj);
  1117. """ )
  1118. # native support for java finalize()
  1119. ci.cpp_code.write(
  1120. """
  1121. //
  1122. // native support for java finalize() or cleaner
  1123. // static void %(cls)s::delete( __int64 self )
  1124. //
  1125. JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete(JNIEnv*, jclass, jlong);
  1126. JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete
  1127. (JNIEnv*, jclass, jlong self)
  1128. {
  1129. delete (%(cls)s*) self;
  1130. }
  1131. """ % {"module" : module.replace('_', '_1'), "cls" : self.smartWrap(ci, ci.fullNameCPP()), "j_cls" : ci.jname.replace('_', '_1')}
  1132. )
  1133. def getClass(self, classname):
  1134. return self.classes[classname or self.Module]
  1135. def isWrapped(self, classname):
  1136. name = classname or self.Module
  1137. return name in self.classes
  1138. def isSmartClass(self, ci):
  1139. '''
  1140. Check if class stores Ptr<T>* instead of T* in nativeObj field
  1141. '''
  1142. if ci.smart != None:
  1143. return ci.smart
  1144. ci.smart = True # smart class is not properly handled in case of base/derived classes
  1145. return ci.smart
  1146. def smartWrap(self, ci, fullname):
  1147. '''
  1148. Wraps fullname with Ptr<> if needed
  1149. '''
  1150. if self.isSmartClass(ci):
  1151. return "Ptr<" + fullname + ">"
  1152. return fullname
  1153. def finalize(self, output_jni_path):
  1154. list_file = os.path.join(output_jni_path, "opencv_jni.hpp")
  1155. self.save(list_file, '\n'.join(['#include "%s"' % f for f in self.cpp_files]))
  1156. def copy_java_files(java_files_dir, java_base_path, default_package_path='org/opencv/'):
  1157. global total_files, updated_files
  1158. java_files = []
  1159. re_filter = re.compile(r'^.+\.(java|kt)(.in)?$')
  1160. for root, dirnames, filenames in os.walk(java_files_dir):
  1161. java_files += [os.path.join(root, filename) for filename in filenames if re_filter.match(filename)]
  1162. java_files = [f.replace('\\', '/') for f in java_files]
  1163. re_package = re.compile(r'^package +(.+);')
  1164. re_prefix = re.compile(r'^.+[\+/]([^\+]+).(java|kt)(.in)?$')
  1165. for java_file in java_files:
  1166. src = checkFileRemap(java_file)
  1167. with open(src, 'r') as f:
  1168. package_line = f.readline()
  1169. m = re_prefix.match(java_file)
  1170. target_fname = (m.group(1) + '.' + m.group(2)) if m else os.path.basename(java_file)
  1171. m = re_package.match(package_line)
  1172. if m:
  1173. package = m.group(1)
  1174. package_path = package.replace('.', '/')
  1175. else:
  1176. package_path = default_package_path
  1177. #print(java_file, package_path, target_fname)
  1178. dest = os.path.join(java_base_path, os.path.join(package_path, target_fname))
  1179. assert dest[-3:] != '.in', dest + ' | ' + target_fname
  1180. mkdir_p(os.path.dirname(dest))
  1181. total_files += 1
  1182. if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1):
  1183. copyfile(src, dest)
  1184. updated_files += 1
  1185. def sanitize_java_documentation_string(doc, type):
  1186. if type == "class":
  1187. doc = doc.replace("@param ", "")
  1188. doc = re.sub(re.compile('\\\\f\\$(.*?)\\\\f\\$', re.DOTALL), '\\(' + r'\1' + '\\)', doc)
  1189. doc = re.sub(re.compile('\\\\f\\[(.*?)\\\\f\\]', re.DOTALL), '\\(' + r'\1' + '\\)', doc)
  1190. doc = re.sub(re.compile('\\\\f\\{(.*?)\\\\f\\}', re.DOTALL), '\\(' + r'\1' + '\\)', doc)
  1191. doc = doc.replace("&", "&amp;") \
  1192. .replace("\\<", "&lt;") \
  1193. .replace("\\>", "&gt;") \
  1194. .replace("<", "&lt;") \
  1195. .replace(">", "&gt;") \
  1196. .replace("$", "$$") \
  1197. .replace("@anchor", "") \
  1198. .replace("@brief ", "").replace("\\brief ", "") \
  1199. .replace("@cite", "CITE:") \
  1200. .replace("@code{.cpp}", "<code>") \
  1201. .replace("@code{.txt}", "<code>") \
  1202. .replace("@code", "<code>") \
  1203. .replace("@copydoc", "") \
  1204. .replace("@copybrief", "") \
  1205. .replace("@date", "") \
  1206. .replace("@defgroup", "") \
  1207. .replace("@details ", "") \
  1208. .replace("@endcode", "</code>") \
  1209. .replace("@endinternal", "") \
  1210. .replace("@file", "") \
  1211. .replace("@include", "INCLUDE:") \
  1212. .replace("@ingroup", "") \
  1213. .replace("@internal", "") \
  1214. .replace("@overload", "") \
  1215. .replace("@param[in]", "@param") \
  1216. .replace("@param[out]", "@param") \
  1217. .replace("@ref", "REF:") \
  1218. .replace("@returns", "@return") \
  1219. .replace("@sa", "SEE:") \
  1220. .replace("@see", "SEE:") \
  1221. .replace("@snippet", "SNIPPET:") \
  1222. .replace("@todo", "TODO:") \
  1223. .replace("@warning ", "WARNING: ")
  1224. doc = re.sub(re.compile('\\*\\*([^\\*]+?)\\*\\*', re.DOTALL), '<b>' + r'\1' + '</b>', doc)
  1225. lines = doc.splitlines()
  1226. lines = list(map(lambda x: x[x.find('*'):].strip() if x.lstrip().startswith("*") else x, lines))
  1227. listInd = [];
  1228. indexDiff = 0;
  1229. for index, line in enumerate(lines[:]):
  1230. if line.strip().startswith("-"):
  1231. i = line.find("-")
  1232. if not listInd or i > listInd[-1]:
  1233. lines.insert(index + indexDiff, " "*len(listInd) + "<ul>")
  1234. indexDiff += 1
  1235. listInd.append(i);
  1236. lines.insert(index + indexDiff, " "*len(listInd) + "<li>")
  1237. indexDiff += 1
  1238. elif i == listInd[-1]:
  1239. lines.insert(index + indexDiff, " "*len(listInd) + "</li>")
  1240. indexDiff += 1
  1241. lines.insert(index + indexDiff, " "*len(listInd) + "<li>")
  1242. indexDiff += 1
  1243. elif len(listInd) > 1 and i == listInd[-2]:
  1244. lines.insert(index + indexDiff, " "*len(listInd) + "</li>")
  1245. indexDiff += 1
  1246. del listInd[-1]
  1247. lines.insert(index + indexDiff, " "*len(listInd) + "</ul>")
  1248. indexDiff += 1
  1249. lines.insert(index + indexDiff, " "*len(listInd) + "<li>")
  1250. indexDiff += 1
  1251. else:
  1252. lines.insert(index + indexDiff, " "*len(listInd) + "</li>")
  1253. indexDiff += 1
  1254. del listInd[-1]
  1255. lines.insert(index + indexDiff, " "*len(listInd) + "</ul>")
  1256. indexDiff += 1
  1257. lines.insert(index + indexDiff, " "*len(listInd) + "<ul>")
  1258. indexDiff += 1
  1259. listInd.append(i);
  1260. lines.insert(index + indexDiff, " "*len(listInd) + "<li>")
  1261. indexDiff += 1
  1262. lines[index + indexDiff] = lines[index + indexDiff][0:i] + lines[index + indexDiff][i + 1:]
  1263. else:
  1264. if listInd and (not line or line == "*" or line.strip().startswith("@note") or line.strip().startswith("@param")):
  1265. lines.insert(index + indexDiff, " "*len(listInd) + "</li>")
  1266. indexDiff += 1
  1267. del listInd[-1]
  1268. lines.insert(index + indexDiff, " "*len(listInd) + "</ul>")
  1269. indexDiff += 1
  1270. i = len(listInd) - 1
  1271. for value in enumerate(listInd):
  1272. lines.append(" "*i + " </li>")
  1273. lines.append(" "*i + "</ul>")
  1274. i -= 1;
  1275. lines = list(map(lambda x: "* " + x[1:].strip() if x.startswith("*") and x != "*" else x, lines))
  1276. lines = list(map(lambda x: x if x.startswith("*") else "* " + x if x and x != "*" else "*", lines))
  1277. lines = list(map(lambda x: x
  1278. .replace("@note", "<b>Note:</b>")
  1279. , lines))
  1280. lines = list(map(lambda x: re.sub('@b ([\\w:]+?)\\b', '<b>' + r'\1' + '</b>', x), lines))
  1281. lines = list(map(lambda x: re.sub('@c ([\\w:]+?)\\b', '<tt>' + r'\1' + '</tt>', x), lines))
  1282. lines = list(map(lambda x: re.sub('`(.*?)`', "{@code " + r'\1' + '}', x), lines))
  1283. lines = list(map(lambda x: re.sub('@p ([\\w:]+?)\\b', '{@code ' + r'\1' + '}', x), lines))
  1284. hasValues = False
  1285. for line in lines:
  1286. if line != "*":
  1287. hasValues = True
  1288. break
  1289. return "/**\n " + "\n ".join(lines) + "\n */" if hasValues else ""
  1290. if __name__ == "__main__":
  1291. # initialize logger
  1292. logging.basicConfig(filename='gen_java.log', format=None, filemode='w', level=logging.INFO)
  1293. handler = logging.StreamHandler()
  1294. handler.setLevel(os.environ.get('LOG_LEVEL', logging.WARNING))
  1295. logging.getLogger().addHandler(handler)
  1296. # parse command line parameters
  1297. import argparse
  1298. arg_parser = argparse.ArgumentParser(description='OpenCV Java Wrapper Generator')
  1299. arg_parser.add_argument('-p', '--parser', required=True, help='OpenCV header parser')
  1300. arg_parser.add_argument('-c', '--config', required=True, help='OpenCV modules config')
  1301. args=arg_parser.parse_args()
  1302. # import header parser
  1303. hdr_parser_path = os.path.abspath(args.parser)
  1304. if hdr_parser_path.endswith(".py"):
  1305. hdr_parser_path = os.path.dirname(hdr_parser_path)
  1306. sys.path.append(hdr_parser_path)
  1307. import hdr_parser
  1308. with open(args.config) as f:
  1309. config = json.load(f)
  1310. ROOT_DIR = config['rootdir']; assert os.path.exists(ROOT_DIR)
  1311. FILES_REMAP = { os.path.realpath(os.path.join(ROOT_DIR, f['src'])): f['target'] for f in config['files_remap'] }
  1312. logging.info("\nRemapped configured files (%d):\n%s", len(FILES_REMAP), pformat(FILES_REMAP))
  1313. USE_CLEANERS = config['support_cleaners']
  1314. if (USE_CLEANERS):
  1315. logging.info("\nUse Java 9+ cleaners\n")
  1316. else:
  1317. logging.info("\nUse old style Java finalize()\n")
  1318. dstdir = "./gen"
  1319. jni_path = os.path.join(dstdir, 'cpp'); mkdir_p(jni_path)
  1320. java_base_path = os.path.join(dstdir, 'java'); mkdir_p(java_base_path)
  1321. java_test_base_path = os.path.join(dstdir, 'test'); mkdir_p(java_test_base_path)
  1322. for (subdir, target_subdir) in [('src/java', 'java'), ('android/java', None),
  1323. ('android-21/java', None), ('android-24/java', None)]:
  1324. if target_subdir is None:
  1325. target_subdir = subdir
  1326. java_files_dir = os.path.join(SCRIPT_DIR, subdir)
  1327. if os.path.exists(java_files_dir):
  1328. target_path = os.path.join(dstdir, target_subdir); mkdir_p(target_path)
  1329. copy_java_files(java_files_dir, target_path)
  1330. # launch Java Wrapper generator
  1331. generator = JavaWrapperGenerator()
  1332. gen_dict_files = []
  1333. print("JAVA: Processing OpenCV modules: %d" % len(config['modules']))
  1334. preprocessor_definitions = config.get('preprocessor_definitions', None)
  1335. for e in config['modules']:
  1336. (module, module_location) = (e['name'], os.path.join(ROOT_DIR, e['location']))
  1337. logging.info("\n=== MODULE: %s (%s) ===\n" % (module, module_location))
  1338. java_path = os.path.join(java_base_path, 'org/opencv')
  1339. mkdir_p(java_path)
  1340. module_imports = []
  1341. module_j_code = None
  1342. module_jn_code = None
  1343. srcfiles = []
  1344. common_headers = []
  1345. misc_location = os.path.join(module_location, 'misc/java')
  1346. srcfiles_fname = os.path.join(misc_location, 'filelist')
  1347. if os.path.exists(srcfiles_fname):
  1348. with open(srcfiles_fname) as f:
  1349. srcfiles = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
  1350. else:
  1351. re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.details.hpp$|_winrt.hpp$|/cuda/|/legacy/)')
  1352. # .h files before .hpp
  1353. h_files = []
  1354. hpp_files = []
  1355. for root, dirnames, filenames in os.walk(os.path.join(module_location, 'include')):
  1356. h_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.h')]
  1357. hpp_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.hpp')]
  1358. srcfiles = h_files + hpp_files
  1359. srcfiles = [f for f in srcfiles if not re_bad.search(f.replace('\\', '/'))]
  1360. logging.info("\nFiles (%d):\n%s", len(srcfiles), pformat(srcfiles))
  1361. common_headers_fname = os.path.join(misc_location, 'filelist_common')
  1362. if os.path.exists(common_headers_fname):
  1363. with open(common_headers_fname) as f:
  1364. common_headers = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
  1365. logging.info("\nCommon headers (%d):\n%s", len(common_headers), pformat(common_headers))
  1366. gendict_fname = os.path.join(misc_location, 'gen_dict.json')
  1367. if os.path.exists(gendict_fname):
  1368. with open(gendict_fname) as f:
  1369. gen_type_dict = json.load(f)
  1370. class_ignore_list += gen_type_dict.get("class_ignore_list", [])
  1371. const_ignore_list += gen_type_dict.get("const_ignore_list", [])
  1372. const_private_list += gen_type_dict.get("const_private_list", [])
  1373. missing_consts.update(gen_type_dict.get("missing_consts", {}))
  1374. type_dict.update(gen_type_dict.get("type_dict", {}))
  1375. ManualFuncs.update(gen_type_dict.get("ManualFuncs", {}))
  1376. func_arg_fix.update(gen_type_dict.get("func_arg_fix", {}))
  1377. namespaces_dict.update(gen_type_dict.get("namespaces_dict", {}))
  1378. if 'module_j_code' in gen_type_dict:
  1379. module_j_code = read_contents(checkFileRemap(os.path.join(misc_location, gen_type_dict['module_j_code'])))
  1380. if 'module_jn_code' in gen_type_dict:
  1381. module_jn_code = read_contents(checkFileRemap(os.path.join(misc_location, gen_type_dict['module_jn_code'])))
  1382. module_imports += gen_type_dict.get("module_imports", [])
  1383. java_files_dir = os.path.join(misc_location, 'src/java')
  1384. if os.path.exists(java_files_dir):
  1385. copy_java_files(java_files_dir, java_base_path, 'org/opencv/' + module)
  1386. java_test_files_dir = os.path.join(misc_location, 'test')
  1387. if os.path.exists(java_test_files_dir):
  1388. copy_java_files(java_test_files_dir, java_test_base_path, 'org/opencv/test/' + module)
  1389. if len(srcfiles) > 0:
  1390. generator.gen(srcfiles, module, dstdir, jni_path, java_path, common_headers,
  1391. preprocessor_definitions)
  1392. else:
  1393. logging.info("No generated code for module: %s", module)
  1394. # Copy Cleaner / finalize() related files
  1395. if USE_CLEANERS:
  1396. cleaner_src = os.path.join(SCRIPT_DIR, "src", "java9", "CleanableMat.java")
  1397. else:
  1398. cleaner_src = os.path.join(SCRIPT_DIR, "src", "java_classic", "CleanableMat.java")
  1399. cleaner_dst = os.path.join(java_base_path, "org", "opencv", "core", "CleanableMat.java")
  1400. print("cleaner_dst: ", cleaner_dst)
  1401. copyfile(cleaner_src, cleaner_dst)
  1402. generator.finalize(jni_path)
  1403. print('Generated files: %d (updated %d)' % (total_files, updated_files))