java: 'namespace_' class prefix, support inner classes

- support multi-level namespaces
- support inner classes (Params)
- reduce scope of 'using namespace' in JNI C++ code
This commit is contained in:
Alexander Alekhin 2020-11-13 06:27:18 +00:00
parent a7c150ec66
commit 3826158547

View File

@ -123,7 +123,8 @@ T_CPP_MODULE = Template(read_contents(os.path.join(SCRIPT_DIR, 'templates/cpp_mo
class GeneralInfo(): class GeneralInfo():
def __init__(self, type, decl, namespaces): def __init__(self, type, decl, namespaces):
self.symbol_id, self.namespace, self.classpath, self.classname, self.name = self.parseName(decl[0], namespaces) self.symbol_id, self.parent_id, self.namespace, self.classpath, self.classname, self.name = self.parseName(decl[0], namespaces)
self.cname = get_cname(self.symbol_id)
# parse doxygen comments # parse doxygen comments
self.params={} self.params={}
@ -150,6 +151,9 @@ class GeneralInfo():
returns: (namespace, classpath, classname, name) returns: (namespace, classpath, classname, name)
''' '''
name = name[name.find(" ")+1:].strip() # remove struct/class/const prefix name = name[name.find(" ")+1:].strip() # remove struct/class/const prefix
parent = name[:name.rfind('.')].strip()
if len(parent) == 0:
parent = None
spaceName = "" spaceName = ""
localName = name # <classes>.<name> localName = name # <classes>.<name>
for namespace in sorted(namespaces, key=len, reverse=True): for namespace in sorted(namespaces, key=len, reverse=True):
@ -159,31 +163,44 @@ class GeneralInfo():
break break
pieces = localName.split(".") pieces = localName.split(".")
if len(pieces) > 2: # <class>.<class>.<class>.<name> if len(pieces) > 2: # <class>.<class>.<class>.<name>
return name, spaceName, ".".join(pieces[:-1]), pieces[-2], pieces[-1] return name, parent, spaceName, ".".join(pieces[:-1]), pieces[-2], pieces[-1]
elif len(pieces) == 2: # <class>.<name> elif len(pieces) == 2: # <class>.<name>
return name, spaceName, pieces[0], pieces[0], pieces[1] return name, parent, spaceName, pieces[0], pieces[0], pieces[1]
elif len(pieces) == 1: # <name> elif len(pieces) == 1: # <name>
return name, spaceName, "", "", pieces[0] return name, parent, spaceName, "", "", pieces[0]
else: else:
return name, spaceName, "", "" # error?! return name, parent, spaceName, "", "" # error?!
def fullName(self, isCPP=False): def fullNameOrigin(self):
result = ".".join([self.fullClass(), self.name]) result = self.symbol_id
return result if not isCPP else get_cname(result) return result
def fullClass(self, isCPP=False): def fullNameJAVA(self):
result = '.'.join([self.fullParentNameJAVA(), self.jname])
return result
def fullNameCPP(self):
result = self.cname
return result
def fullParentNameJAVA(self):
result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f)>0]) result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f)>0])
return result if not isCPP else get_cname(result) return result
def fullParentNameCPP(self):
result = get_cname(self.parent_id)
return result
class ConstInfo(GeneralInfo): class ConstInfo(GeneralInfo):
def __init__(self, decl, addedManually=False, namespaces=[], enumType=None): def __init__(self, decl, addedManually=False, namespaces=[], enumType=None):
GeneralInfo.__init__(self, "const", decl, namespaces) GeneralInfo.__init__(self, "const", decl, namespaces)
self.cname = get_cname(self.name)
self.value = decl[1] self.value = decl[1]
self.enumType = enumType self.enumType = enumType
self.addedManually = addedManually self.addedManually = addedManually
if self.namespace in namespaces_dict: if self.namespace in namespaces_dict:
self.name = '%s_%s' % (namespaces_dict[self.namespace], self.name) prefix = namespaces_dict[self.namespace]
if prefix:
self.name = '%s_%s' % (prefix, self.name)
def __repr__(self): def __repr__(self):
return Template("CONST $name=$value$manual").substitute(name=self.name, return Template("CONST $name=$value$manual").substitute(name=self.name,
@ -227,7 +244,6 @@ class ClassPropInfo():
class ClassInfo(GeneralInfo): class ClassInfo(GeneralInfo):
def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ] def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ]
GeneralInfo.__init__(self, "class", decl, namespaces) GeneralInfo.__init__(self, "class", decl, namespaces)
self.cname = get_cname(self.name)
self.methods = [] self.methods = []
self.methods_suffixes = {} self.methods_suffixes = {}
self.consts = [] # using a list to save the occurrence order self.consts = [] # using a list to save the occurrence order
@ -242,6 +258,18 @@ class ClassInfo(GeneralInfo):
for m in decl[2]: for m in decl[2]:
if m.startswith("="): if m.startswith("="):
self.jname = m[1:] self.jname = m[1:]
if self.classpath:
prefix = self.classpath.replace('.', '_')
self.name = '%s_%s' % (prefix, self.name)
self.jname = '%s_%s' % (prefix, self.jname)
if self.namespace in namespaces_dict:
prefix = namespaces_dict[self.namespace]
if prefix:
self.name = '%s_%s' % (prefix, self.name)
self.jname = '%s_%s' % (prefix, self.jname)
self.base = '' self.base = ''
if decl[1]: if decl[1]:
#self.base = re.sub(r"\b"+self.jname+r"\b", "", decl[1].replace(":", "")).strip() #self.base = re.sub(r"\b"+self.jname+r"\b", "", decl[1].replace(":", "")).strip()
@ -358,11 +386,26 @@ class FuncInfo(GeneralInfo):
self.isconstructor = self.name == self.classname self.isconstructor = self.name == self.classname
if "[" in self.name: if "[" in self.name:
self.jname = "getelem" self.jname = "getelem"
if self.namespace in namespaces_dict:
self.jname = '%s_%s' % (namespaces_dict[self.namespace], self.jname)
for m in decl[2]: for m in decl[2]:
if m.startswith("="): if m.startswith("="): # alias from WRAP_AS
self.jname = m[1:] self.jname = m[1:]
if self.classpath and self.classname != self.classpath:
prefix = self.classpath.replace('.', '_')
self.classname = prefix #'%s_%s' % (prefix, self.classname)
if self.isconstructor:
self.name = prefix #'%s_%s' % (prefix, self.name)
self.jname = prefix #'%s_%s' % (prefix, self.jname)
if self.namespace in namespaces_dict:
prefix = namespaces_dict[self.namespace]
if prefix:
if self.classname:
self.classname = '%s_%s' % (prefix, self.classname)
if self.isconstructor:
self.jname = '%s_%s' % (prefix, self.jname)
else:
self.jname = '%s_%s' % (prefix, self.jname)
self.static = ["","static"][ "/S" in decl[2] ] self.static = ["","static"][ "/S" in decl[2] ]
self.ctype = re.sub(r"^CvTermCriteria", "TermCriteria", decl[1] or "") self.ctype = re.sub(r"^CvTermCriteria", "TermCriteria", decl[1] or "")
self.args = [] self.args = []
@ -374,6 +417,12 @@ class FuncInfo(GeneralInfo):
arg[3] = arg_fix_map.get('attrib', arg[3]) #fixing arg attrib arg[3] = arg_fix_map.get('attrib', arg[3]) #fixing arg attrib
self.args.append(ArgInfo(arg)) self.args.append(ArgInfo(arg))
def fullClassJAVA(self):
return self.fullParentNameJAVA()
def fullClassCPP(self):
return self.fullParentNameCPP()
def __repr__(self): def __repr__(self):
return Template("FUNC <$ctype $namespace.$classpath.$name $args>").substitute(**self.__dict__) return Template("FUNC <$ctype $namespace.$classpath.$name $args>").substitute(**self.__dict__)
@ -388,7 +437,8 @@ class JavaWrapperGenerator(object):
def clear(self): def clear(self):
self.namespaces = ["cv"] self.namespaces = ["cv"]
self.classes = { "Mat" : ClassInfo([ 'class Mat', '', [], [] ], self.namespaces) } classinfo_Mat = ClassInfo([ 'class cv.Mat', '', [], [] ], self.namespaces)
self.classes = { "Mat" : classinfo_Mat }
self.module = "" self.module = ""
self.Module = "" self.Module = ""
self.ported_func_list = [] self.ported_func_list = []
@ -411,7 +461,7 @@ class JavaWrapperGenerator(object):
type_dict.setdefault(name, {}).update( type_dict.setdefault(name, {}).update(
{ "j_type" : classinfo.jname, { "j_type" : classinfo.jname,
"jn_type" : "long", "jn_args" : (("__int64", ".nativeObj"),), "jn_type" : "long", "jn_args" : (("__int64", ".nativeObj"),),
"jni_name" : "(*("+classinfo.fullName(isCPP=True)+"*)%(n)s_nativeObj)", "jni_type" : "jlong", "jni_name" : "(*("+classinfo.fullNameCPP()+"*)%(n)s_nativeObj)", "jni_type" : "jlong",
"suffix" : "J", "suffix" : "J",
"j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname) "j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname)
} }
@ -419,7 +469,7 @@ class JavaWrapperGenerator(object):
type_dict.setdefault(name+'*', {}).update( type_dict.setdefault(name+'*', {}).update(
{ "j_type" : classinfo.jname, { "j_type" : classinfo.jname,
"jn_type" : "long", "jn_args" : (("__int64", ".nativeObj"),), "jn_type" : "long", "jn_args" : (("__int64", ".nativeObj"),),
"jni_name" : "("+classinfo.fullName(isCPP=True)+"*)%(n)s_nativeObj", "jni_type" : "jlong", "jni_name" : "("+classinfo.fullNameCPP()+"*)%(n)s_nativeObj", "jni_type" : "jlong",
"suffix" : "J", "suffix" : "J",
"j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname) "j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname)
} }
@ -446,7 +496,7 @@ class JavaWrapperGenerator(object):
type_dict.setdefault("Ptr_"+name, {}).update( type_dict.setdefault("Ptr_"+name, {}).update(
{ "j_type" : classinfo.jname, { "j_type" : classinfo.jname,
"jn_type" : "long", "jn_args" : (("__int64", ".getNativeObjAddr()"),), "jn_type" : "long", "jn_args" : (("__int64", ".getNativeObjAddr()"),),
"jni_name" : "*((Ptr<"+classinfo.fullName(isCPP=True)+">*)%(n)s_nativeObj)", "jni_type" : "jlong", "jni_name" : "*((Ptr<"+classinfo.fullNameCPP()+">*)%(n)s_nativeObj)", "jni_type" : "jlong",
"suffix" : "J", "suffix" : "J",
"j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname) "j_import" : "org.opencv.%s.%s" % (self.module, classinfo.jname)
} }
@ -489,14 +539,15 @@ class JavaWrapperGenerator(object):
def add_func(self, decl): def add_func(self, decl):
fi = FuncInfo(decl, namespaces=self.namespaces) fi = FuncInfo(decl, namespaces=self.namespaces)
classname = fi.classname or self.Module classname = fi.classname or self.Module
class_symbol_id = classname if self.isWrapped(classname) else fi.classpath.replace('.', '_') #('.'.join([fi.namespace, fi.classpath])[3:])
if classname in class_ignore_list: if classname in class_ignore_list:
logging.info('ignored: %s', fi) logging.info('ignored: %s', fi)
elif classname in ManualFuncs and fi.jname in ManualFuncs[classname]: elif classname in ManualFuncs and fi.jname in ManualFuncs[classname]:
logging.info('manual: %s', fi) logging.info('manual: %s', fi)
elif not self.isWrapped(classname): elif not self.isWrapped(class_symbol_id):
logging.warning('not found: %s', fi) logging.warning('not found: %s', fi)
else: else:
self.getClass(classname).addMethod(fi) self.getClass(class_symbol_id).addMethod(fi)
logging.info('ok: %s', fi) logging.info('ok: %s', fi)
# calc args with def val # calc args with def val
cnt = len([a for a in fi.args if a.defval]) cnt = len([a for a in fi.args if a.defval])
@ -521,7 +572,7 @@ class JavaWrapperGenerator(object):
# TODO: support UMat versions of declarations (implement UMat-wrapper for Java) # TODO: support UMat versions of declarations (implement UMat-wrapper for Java)
parser = hdr_parser.CppHeaderParser(generate_umat_decls=False) parser = hdr_parser.CppHeaderParser(generate_umat_decls=False)
self.add_class( ['class ' + self.Module, '', [], []] ) # [ 'class/struct cname', ':bases', [modlist] [props] ] self.add_class( ['class cv.' + self.Module, '', [], []] ) # [ 'class/struct cname', ':bases', [modlist] [props] ]
# scan the headers and build more descriptive maps of classes, consts, functions # scan the headers and build more descriptive maps of classes, consts, functions
includes = [] includes = []
@ -582,9 +633,9 @@ class JavaWrapperGenerator(object):
report.write("\n%i def args - %i funcs" % (i, self.def_args_hist[i])) report.write("\n%i def args - %i funcs" % (i, self.def_args_hist[i]))
return report.getvalue() return report.getvalue()
def fullTypeName(self, t): def fullTypeNameCPP(self, t):
if self.isWrapped(t): if self.isWrapped(t):
return self.getClass(t).fullName(isCPP=True) return self.getClass(t).fullNameCPP()
else: else:
return cast_from(t) return cast_from(t)
@ -897,7 +948,7 @@ class JavaWrapperGenerator(object):
default = "" default = ""
elif not fi.ctype: # c-tor elif not fi.ctype: # c-tor
if self.isSmartClass(ci): if self.isSmartClass(ci):
ret = "return (jlong)(new Ptr<%(ctype)s>(_retval_));" % { 'ctype': fi.fullClass(isCPP=True) } ret = "return (jlong)(new Ptr<%(ctype)s>(_retval_));" % { 'ctype': fi.fullClassCPP() }
else: else:
ret = "return (jlong) _retval_;" ret = "return (jlong) _retval_;"
elif "v_type" in type_dict[fi.ctype]: # c-tor elif "v_type" in type_dict[fi.ctype]: # c-tor
@ -907,9 +958,9 @@ class JavaWrapperGenerator(object):
ret = "return env->NewStringUTF(_retval_.c_str());" ret = "return env->NewStringUTF(_retval_.c_str());"
default = 'return env->NewStringUTF("");' default = 'return env->NewStringUTF("");'
elif self.isWrapped(fi.ctype): # wrapped class: elif self.isWrapped(fi.ctype): # wrapped class:
ret = "return (jlong) new %s(_retval_);" % self.fullTypeName(fi.ctype) ret = "return (jlong) new %s(_retval_);" % self.fullTypeNameCPP(fi.ctype)
elif fi.ctype.startswith('Ptr_'): elif fi.ctype.startswith('Ptr_'):
c_prologue.append("typedef Ptr<%s> %s;" % (self.fullTypeName(fi.ctype[4:]), fi.ctype)) c_prologue.append("typedef Ptr<%s> %s;" % (self.fullTypeNameCPP(fi.ctype[4:]), fi.ctype))
ret = "return (jlong)(new %(ctype)s(_retval_));" % { 'ctype':fi.ctype } ret = "return (jlong)(new %(ctype)s(_retval_));" % { 'ctype':fi.ctype }
elif self.isWrapped(ret_type): # pointer to wrapped class: elif self.isWrapped(ret_type): # pointer to wrapped class:
ret = "return (jlong) _retval_;" ret = "return (jlong) _retval_;"
@ -924,12 +975,12 @@ class JavaWrapperGenerator(object):
else: else:
name = prop_name + ";//" name = prop_name + ";//"
cvname = fi.fullName(isCPP=True) cvname = fi.fullNameCPP()
retval = self.fullTypeName(fi.ctype) + " _retval_ = " if ret else "return " retval = self.fullTypeNameCPP(fi.ctype) + " _retval_ = " if ret else "return "
if fi.ctype == "void": if fi.ctype == "void":
retval = "" retval = ""
elif fi.ctype == "String": elif fi.ctype == "String":
retval = "cv::" + self.fullTypeName(fi.ctype) + " _retval_ = " retval = "cv::" + self.fullTypeNameCPP(fi.ctype) + " _retval_ = "
elif fi.ctype == "string": elif fi.ctype == "string":
retval = "std::string _retval_ = " retval = "std::string _retval_ = "
elif "v_type" in type_dict[fi.ctype]: # vector is returned elif "v_type" in type_dict[fi.ctype]: # vector is returned
@ -945,18 +996,18 @@ class JavaWrapperGenerator(object):
if fi.classname: if fi.classname:
if not fi.ctype: # c-tor if not fi.ctype: # c-tor
if self.isSmartClass(ci): if self.isSmartClass(ci):
retval = self.smartWrap(ci, fi.fullClass(isCPP=True)) + " _retval_ = " retval = self.smartWrap(ci, fi.fullClassCPP()) + " _retval_ = "
cvname = "makePtr<" + fi.fullClass(isCPP=True) +">" cvname = "makePtr<" + fi.fullClassCPP() +">"
else: else:
retval = fi.fullClass(isCPP=True) + "* _retval_ = " retval = fi.fullClassCPP() + "* _retval_ = "
cvname = "new " + fi.fullClass(isCPP=True) cvname = "new " + fi.fullClassCPP()
elif fi.static: elif fi.static:
cvname = fi.fullName(isCPP=True) cvname = fi.fullNameCPP()
else: else:
cvname = ("me->" if not self.isSmartClass(ci) else "(*me)->") + name cvname = ("me->" if not self.isSmartClass(ci) else "(*me)->") + name
c_prologue.append( c_prologue.append(
"%(cls)s* me = (%(cls)s*) self; //TODO: check for NULL" "%(cls)s* me = (%(cls)s*) self; //TODO: check for NULL"
% { "cls" : self.smartWrap(ci, fi.fullClass(isCPP=True))} % { "cls" : self.smartWrap(ci, fi.fullClassCPP())}
) )
cvargs = [] cvargs = []
for a in args: for a in args:
@ -981,13 +1032,12 @@ class JavaWrapperGenerator(object):
clazz = ci.jname clazz = ci.jname
cpp_code.write ( Template( cpp_code.write ( Template(
""" """
${namespace}
JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname ($argst); JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname ($argst);
JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname
($args) ($args)
{ {
${namespace}
static const char method_name[] = "$module::$fname()"; static const char method_name[] = "$module::$fname()";
try { try {
LOGD("%s", method_name);$prologue LOGD("%s", method_name);$prologue
@ -1014,7 +1064,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname
cvargs = " " + ", ".join(cvargs) + " " if cvargs else "", cvargs = " " + ", ".join(cvargs) + " " if cvargs else "",
default = "\n " + default if default else "", default = "\n " + default if default else "",
retval = retval, retval = retval,
namespace = ('using namespace ' + ci.namespace.replace('.', '::') + ';') if ci.namespace else '' namespace = ('using namespace ' + ci.namespace.replace('.', '::') + ';') if ci.namespace and ci.namespace != 'cv' else ''
) ) ) )
# adding method signature to dictionary # adding method signature to dictionary
@ -1081,13 +1131,14 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname
self.gen_func(ci, fi) self.gen_func(ci, fi)
# props # props
for pi in ci.props: for pi in ci.props:
basename = ci.fullNameOrigin()
# getter # getter
getter_name = ci.fullName() + ".get_" + pi.name getter_name = basename + ".get_" + pi.name
fi = FuncInfo( [getter_name, pi.ctype, [], []], self.namespaces ) # [ funcname, return_ctype, [modifiers], [args] ] fi = FuncInfo( [getter_name, pi.ctype, [], []], self.namespaces ) # [ funcname, return_ctype, [modifiers], [args] ]
self.gen_func(ci, fi, pi.name) self.gen_func(ci, fi, pi.name)
if pi.rw: if pi.rw:
#setter #setter
setter_name = ci.fullName() + ".set_" + pi.name setter_name = basename + ".set_" + pi.name
fi = FuncInfo( [ setter_name, "void", [], [ [pi.ctype, pi.name, "", [], ""] ] ], self.namespaces) fi = FuncInfo( [ setter_name, "void", [], [ [pi.ctype, pi.name, "", [], ""] ] ], self.namespaces)
self.gen_func(ci, fi, pi.name) self.gen_func(ci, fi, pi.name)
@ -1131,7 +1182,7 @@ JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete
delete (%(cls)s*) self; delete (%(cls)s*) self;
} }
""" % {"module" : module.replace('_', '_1'), "cls" : self.smartWrap(ci, ci.fullName(isCPP=True)), "j_cls" : ci.jname.replace('_', '_1')} """ % {"module" : module.replace('_', '_1'), "cls" : self.smartWrap(ci, ci.fullNameCPP()), "j_cls" : ci.jname.replace('_', '_1')}
) )
def getClass(self, classname): def getClass(self, classname):