pyjnius/jnius/jnius_proxy.pxi

163 lines
5.6 KiB
Cython
Raw Normal View History

2013-03-13 20:38:10 +00:00
class java_method(object):
def __init__(self, signature, name=None):
super(java_method, self).__init__()
self.signature = signature
self.name = name
def __get__(self, instance, instancetype):
return partial(self.__call__, instance)
def __call__(self, f):
f.__javasignature__ = self.signature
f.__javaname__ = self.name
return f
cdef class PythonJavaClass(object):
'''
Base class to create a java class from python
'''
cdef JNIEnv *j_env
cdef jclass j_cls
cdef public object j_self
def __cinit__(self, *args):
self.j_env = get_jnienv()
self.j_cls = NULL
self.j_self = None
def __init__(self, *args, **kwargs):
self.j_self = create_proxy_instance(self.j_env, self,
self.__javainterfaces__)
# discover all the java method implemented
self.__javamethods__ = {}
for x in dir(self):
attr = getattr(self, x)
if not callable(attr):
continue
if not hasattr(attr, '__javasignature__'):
continue
signature = parse_definition(attr.__javasignature__)
self.__javamethods__[(attr.__javaname__ or x, signature)] = attr
def invoke(self, method, *args):
try:
ret = self._invoke(method, *args)
return ret
except Exception as e:
traceback.print_exc(e)
return None
def _invoke(self, method, *args):
from .reflect import get_signature
# search the java method
ret_signature = get_signature(method.getReturnType())
args_signature = tuple([get_signature(x) for x in method.getParameterTypes()])
method_name = method.getName()
key = (method_name, (ret_signature, args_signature))
py_method = self.__javamethods__.get(key, None)
if not py_method:
print(''.join(
'\n===== Python/java method missing ======',
'\nPython class:', self,
'\nJava method name:', method_name,
'\nSignature: ({}){}'.format(''.join(args_signature), ret_signature),
'\n=======================================\n'))
raise NotImplemented('The method {} is not implemented'.format(key))
return py_method(*args)
cdef jobject invoke0(JNIEnv *j_env, jobject j_this, jobject j_proxy, jobject
j_method, jobjectArray args) except *:
from .reflect import get_signature, Method
# get the python object
cdef jfieldID ptrField = j_env[0].GetFieldID(j_env,
j_env[0].GetObjectClass(j_env, j_this), "ptr", "J")
cdef jlong jptr = j_env[0].GetLongField(j_env, j_this, ptrField)
cdef object py_obj = <object>jptr
# extract the method information
cdef JavaClass method = Method(noinstance=True)
cdef LocalRef ref = create_local_ref(j_env, j_method)
method.instanciate_from(create_local_ref(j_env, j_method))
ret_signature = get_signature(method.getReturnType())
args_signature = [get_signature(x) for x in method.getParameterTypes()]
# convert java argument to python object
# native java type are given with java.lang.*, even if the signature say
# it's a native type.
cdef jobject j_arg
py_args = []
convert_signature = {
'Z': 'Ljava/lang/Boolean;',
'B': 'Ljava/lang/Byte;',
'C': 'Ljava/lang/Character;',
'S': 'Ljava/lang/Short;',
'I': 'Ljava/lang/Integer;',
'J': 'Ljava/lang/Long;',
'F': 'Ljava/lang/Float;',
'D': 'Ljava/lang/Double;'}
for index, arg_signature in enumerate(args_signature):
arg_signature = convert_signature.get(arg_signature, arg_signature)
j_arg = j_env[0].GetObjectArrayElement(j_env, args, index)
py_arg = convert_jobject_to_python(j_env, arg_signature, j_arg)
py_args.append(py_arg)
# really invoke the python method
name = method.getName()
ret = py_obj.invoke(method, *py_args)
# convert back to the return type
# use the populate_args(), but in the reverse way :)
t = ret_signature[:1]
# did python returned a "native" type ?
jtype = None
if ret_signature == 'Ljava/lang/Object;':
# generic object, try to manually convert it
tp = type(ret)
if tp == int:
jtype = 'J'
elif tp == float:
jtype = 'D'
elif tp == bool:
jtype = 'Z'
elif len(ret_signature) == 1:
jtype = ret_signature
try:
return convert_python_to_jobject(j_env, jtype or ret_signature, ret)
except Exception as e:
traceback.print_exc(e)
# now we need to create a proxy and pass it an invocation handler
cdef create_proxy_instance(JNIEnv *j_env, py_obj, j_interfaces):
from .reflect import autoclass
Proxy = autoclass('java.lang.reflect.Proxy')
NativeInvocationHandler = autoclass('jnius.NativeInvocationHandler')
ClassLoader = autoclass('java.lang.ClassLoader')
# convert strings to Class
j_interfaces = [find_javaclass(x) for x in j_interfaces]
cdef JavaClass nih = NativeInvocationHandler(<long><void *>py_obj)
cdef JNINativeMethod invoke_methods[1]
invoke_methods[0].name = 'invoke0'
invoke_methods[0].signature = '(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;'
invoke_methods[0].fnPtr = <void *>&invoke0
j_env[0].RegisterNatives(j_env, nih.j_cls, <JNINativeMethod *>invoke_methods, 1)
# create the proxy and pass it the invocation handler
cdef JavaClass j_obj = Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(), j_interfaces, nih)
return j_obj