Skip to content

Commit 4c24455

Browse files
committed
add java.pyx module!
1 parent 1a9f6ce commit 4c24455

File tree

3 files changed

+340
-1
lines changed

3 files changed

+340
-1
lines changed

recipes/android/recipe.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ function build_android() {
3232
# cythonize
3333
try cython android.pyx
3434
try cython android_sound.pyx
35+
try cython java.pyx
3536
try $BUILD_PATH/python-install/bin/python.host setup.py build_ext -i
3637

3738
# copy files
38-
try cp android.so android_sound.so \
39+
try cp android.so android_sound.so java.so \
3940
$BUILD_PATH/python-install/lib/python2.7/lib-dynload/
4041
try cp android_mixer.py \
4142
$BUILD_PATH/python-install/lib/python2.7/

recipes/android/src/java.pyx

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
include "jni.pxi"
2+
from libc.stdlib cimport malloc, free
3+
4+
cdef class JavaObject(object):
5+
cdef jobject obj
6+
def __cinit__(self):
7+
self.obj = NULL
8+
9+
cdef class JavaClass(object):
10+
cdef JNIEnv *j_env
11+
cdef jclass j_cls
12+
cdef jobject j_self
13+
14+
def __cinit__(self, *args):
15+
self.j_env = NULL
16+
self.j_cls = NULL
17+
self.j_self = NULL
18+
19+
def __init__(self, *args):
20+
"""Create java object, with arguments, arguments need to be in the order
21+
of the java method signature
22+
"""
23+
super(JavaClass, self).__init__()
24+
self.resolve_class()
25+
self.resolve_methods()
26+
self.call_constructor(args)
27+
28+
cdef parse_definition(self, definition):
29+
self.definition = definition
30+
args, ret = definition[1:].split(')')
31+
if args:
32+
args = args.split(';')
33+
else:
34+
args = []
35+
return ret, args
36+
37+
cdef void call_constructor(self, args):
38+
cdef jvalue *j_args = NULL
39+
# get the constructor definition if exist
40+
definition = '()V'
41+
if hasattr(self, 'j_constructor'):
42+
definition = self.j_constructor
43+
d_ret, d_args = self.parse_definition(definition)
44+
if len(args) != len(d_args):
45+
raise Exception("Invalid call, number of argument mismatch for constructor")
46+
# convert python arguments to java arguments
47+
if len(args):
48+
j_args = <jvalue *>malloc(sizeof(jvalue) * len(d_args))
49+
assert(j_args != NULL)
50+
try:
51+
if self.populate_args(d_args, j_args, args) < 0:
52+
return
53+
finally:
54+
free(j_args)
55+
# get the java constructor
56+
cdef jmethodID constructor = self.j_env[0].GetMethodID(
57+
self.j_env, self.j_cls, "<init>", <char *><bytes><str>definition)
58+
# create the object
59+
self.j_self = self.j_env[0].NewObjectA(self.j_env, self.j_cls, constructor, j_args)
60+
if j_args != NULL:
61+
free(j_args)
62+
63+
cdef void resolve_class(self):
64+
# search the Java class, and bind to our object
65+
assert(hasattr(self, 'j_classname'))
66+
self.j_env = SDL_ANDROID_GetJNIEnv()
67+
assert(self.j_env != NULL)
68+
self.j_cls = self.j_env[0].FindClass(self.j_env, <char *><bytes>self.j_classname)
69+
assert(self.j_cls != NULL)
70+
71+
cdef void resolve_methods(self):
72+
# search all the JavaMethod within our class.
73+
for name, value in self.__dict__.iteritems():
74+
if not isinstance(value, JavaMethod):
75+
continue
76+
value.resolve_method(self, name)
77+
78+
cdef int populate_args(self, list definition_args, jvalue *j_args, args):
79+
cdef JavaObject j_object
80+
for index, argtype in enumerate(definition_args):
81+
py_arg = args[index]
82+
if argtype == 'Z':
83+
j_args[index].z = py_arg
84+
elif argtype == 'B':
85+
j_args[index].b = py_arg
86+
elif argtype == 'C':
87+
j_args[index].c = py_arg
88+
elif argtype == 'S':
89+
j_args[index].s = py_arg
90+
elif argtype == 'I':
91+
j_args[index].i = py_arg
92+
elif argtype == 'J':
93+
j_args[index].j = py_arg
94+
elif argtype == 'F':
95+
j_args[index].f = py_arg
96+
elif argtype == 'D':
97+
j_args[index].d = py_arg
98+
elif argtype[0] == 'L':
99+
if argtype == 'Ljava/lang/String;':
100+
if isinstance(py_arg, basestring):
101+
j_args[index].l = self.j_env[0].NewStringUTF(self.j_env, <char *><bytes>py_arg)
102+
else:
103+
raise Exception("Not a correct type of string, must be an instance of str or unicode")
104+
else:
105+
if not isinstance(py_arg, JavaObject):
106+
raise Exception('JavaObject needed for argument {0}'.format(index))
107+
j_object = py_arg
108+
j_args[index].l = j_object.obj
109+
elif argtype[0] == '[':
110+
raise Exception("List arguments not accepted")
111+
return 0
112+
113+
cdef class JavaMethod(object):
114+
cdef jmethodID j_method
115+
cdef JavaClass jc
116+
cdef JNIEnv *j_env
117+
cdef jclass j_cls
118+
cdef jobject j_self
119+
cdef char *definition
120+
cdef object is_static
121+
cdef object definition_return
122+
cdef object definition_args
123+
124+
def __cinit__(self, definition, **kwargs):
125+
self.j_method = NULL
126+
self.j_env = NULL
127+
self.j_cls = NULL
128+
129+
def __init__(self, definition, **kwargs):
130+
super(JavaMethod, self).__init__()
131+
self.parse_definition(definition)
132+
self.is_static = kwargs.get('static', False)
133+
134+
cdef resolve_method(self, JavaClass jc, bytes name):
135+
# called by JavaClass when we want to resolve the method name
136+
self.jc = jc
137+
self.j_env = jc.j_env
138+
self.j_cls = jc.j_cls
139+
self.j_self = jc.j_self
140+
self.j_method = self.j_env[0].GetMethodID(self.j_env, self.j_cls, <char *>name, self.definition)
141+
assert(self.j_method != NULL)
142+
143+
cdef void parse_definition(self, definition):
144+
self.definition = <char *><bytes>definition
145+
args, ret = definition[1:].split(')')
146+
if args:
147+
args = args.split(';')
148+
else:
149+
args = []
150+
self.definition_return = ret
151+
self.definition_args = args
152+
153+
def error_if_null(self, value):
154+
if not value:
155+
raise Exception("Return value was NULL")
156+
157+
def __call__(self, *args):
158+
# argument array to pass to the method
159+
cdef jvalue *j_args = NULL
160+
cdef list d_args = self.definition_args
161+
if len(args) != len(d_args):
162+
raise Exception("Invalid call, number of argument mismatch")
163+
if len(args):
164+
j_args = <jvalue *>malloc(sizeof(jvalue) * len(d_args))
165+
assert(j_args != NULL)
166+
try:
167+
if self.jc.populate_args(self.definition_args, j_args, args) < 0:
168+
return
169+
finally:
170+
free(j_args)
171+
172+
try:
173+
if self.is_static:
174+
return self.call_staticmethod(j_args)
175+
return self.call_method(j_args)
176+
finally:
177+
if j_args != NULL:
178+
free(j_args)
179+
180+
cdef call_method(self, jvalue *j_args):
181+
cdef jboolean j_boolean
182+
cdef jbyte j_byte
183+
cdef jchar j_char
184+
cdef jshort j_short
185+
cdef jint j_int
186+
cdef jlong j_long
187+
cdef jfloat j_float
188+
cdef jdouble j_double
189+
cdef jobject j_object
190+
cdef char *c_str
191+
cdef bytes py_str
192+
cdef object ret = None
193+
cdef JavaObject ret_jobject
194+
195+
# return type of the java method
196+
r = self.definition_return[0]
197+
198+
# now call the java method
199+
if r == 'V':
200+
self.j_env[0].CallVoidMethodA(self.j_env, self.j_self, self.j_method, j_args)
201+
elif r == 'Z':
202+
j_boolean = self.j_env[0].CallBooleanMethodA(self.j_env, self.j_self, self.j_method, j_args)
203+
ret = True if j_boolean else False
204+
elif r == 'B':
205+
j_byte = self.j_env[0].CallByteMethodA(self.j_env, self.j_self, self.j_method, j_args)
206+
self.error_if_null(ret)
207+
ret = <char>j_byte
208+
elif r == 'C':
209+
j_char = self.j_env[0].CallCharMethodA(self.j_env, self.j_self, self.j_method, j_args)
210+
self.error_if_null(ret)
211+
ret = <char>j_char
212+
elif r == 'S':
213+
j_short = self.j_env[0].CallShortMethodA(self.j_env, self.j_self, self.j_method, j_args)
214+
self.error_if_null(ret)
215+
ret = <short>j_short
216+
elif r == 'I':
217+
j_int = self.j_env[0].CallIntMethodA(self.j_env, self.j_self, self.j_method, j_args)
218+
self.error_if_null(ret)
219+
ret = <int>j_int
220+
elif r == 'J':
221+
j_long = self.j_env[0].CallLongMethodA(self.j_env, self.j_self, self.j_method, j_args)
222+
self.error_if_null(ret)
223+
ret = <long>j_long
224+
elif r == 'F':
225+
j_float = self.j_env[0].CallFloatMethodA(self.j_env, self.j_self, self.j_method, j_args)
226+
self.error_if_null(ret)
227+
ret = <float>j_float
228+
elif r == 'D':
229+
j_double = self.j_env[0].CallDoubleMethodA(self.j_env, self.j_self, self.j_method, j_args)
230+
self.error_if_null(ret)
231+
ret = <double>j_double
232+
elif r == 'L':
233+
# accept only string for the moment
234+
j_object = self.j_env[0].CallObjectMethodA(self.j_env, self.j_self, self.j_method, j_args)
235+
self.error_if_null(ret)
236+
if r == 'Ljava/lang/String;':
237+
c_str = <char *>self.j_env[0].GetStringUTFChars(self.j_env, j_object, NULL)
238+
py_str = <bytes>c_str
239+
self.j_env[0].ReleaseStringUTFChars(self.j_env, j_object, c_str)
240+
ret = py_str
241+
else:
242+
ret_jobject = JavaObject()
243+
ret_jobject.obj = j_object
244+
ret = ret_jobject
245+
elif r == '[':
246+
# TODO array support
247+
raise NotImplementedError("Array arguments not implemented")
248+
else:
249+
raise Exception('Invalid return definition?')
250+
251+
# free args
252+
if j_args != NULL:
253+
free(j_args)
254+
255+
return ret
256+
257+
cdef call_staticmethod(self, jvalue *j_args):
258+
cdef jboolean j_boolean
259+
cdef jbyte j_byte
260+
cdef jchar j_char
261+
cdef jshort j_short
262+
cdef jint j_int
263+
cdef jlong j_long
264+
cdef jfloat j_float
265+
cdef jdouble j_double
266+
cdef jobject j_object
267+
cdef char *c_str
268+
cdef bytes py_str
269+
cdef object ret = None
270+
cdef JavaObject ret_jobject
271+
272+
# return type of the java method
273+
r = self.definition_return[0]
274+
275+
# now call the java method
276+
if r == 'V':
277+
self.j_env[0].CallStaticVoidMethodA(self.j_env, self.j_cls, self.j_method, j_args)
278+
elif r == 'Z':
279+
j_boolean = self.j_env[0].CallStaticBooleanMethodA(self.j_env, self.j_cls, self.j_method, j_args)
280+
ret = True if j_boolean else False
281+
elif r == 'B':
282+
j_byte = self.j_env[0].CallStaticByteMethodA(self.j_env, self.j_cls, self.j_method, j_args)
283+
self.error_if_null(ret)
284+
ret = <char>j_byte
285+
elif r == 'C':
286+
j_char = self.j_env[0].CallStaticCharMethodA(self.j_env, self.j_cls, self.j_method, j_args)
287+
self.error_if_null(ret)
288+
ret = <char>j_char
289+
elif r == 'S':
290+
j_short = self.j_env[0].CallStaticShortMethodA(self.j_env, self.j_cls, self.j_method, j_args)
291+
self.error_if_null(ret)
292+
ret = <short>j_short
293+
elif r == 'I':
294+
j_int = self.j_env[0].CallStaticIntMethodA(self.j_env, self.j_cls, self.j_method, j_args)
295+
self.error_if_null(ret)
296+
ret = <int>j_int
297+
elif r == 'J':
298+
j_long = self.j_env[0].CallStaticLongMethodA(self.j_env, self.j_cls, self.j_method, j_args)
299+
self.error_if_null(ret)
300+
ret = <long>j_long
301+
elif r == 'F':
302+
j_float = self.j_env[0].CallStaticFloatMethodA(self.j_env, self.j_cls, self.j_method, j_args)
303+
self.error_if_null(ret)
304+
ret = <float>j_float
305+
elif r == 'D':
306+
j_double = self.j_env[0].CallStaticDoubleMethodA(self.j_env, self.j_cls, self.j_method, j_args)
307+
self.error_if_null(ret)
308+
ret = <double>j_double
309+
elif r == 'L':
310+
# accept only string for the moment
311+
j_object = self.j_env[0].CallStaticObjectMethodA(self.j_env, self.j_cls, self.j_method, j_args)
312+
self.error_if_null(ret)
313+
if r == 'Ljava/lang/String;':
314+
c_str = <char *>self.j_env[0].GetStringUTFChars(self.j_env, j_object, NULL)
315+
py_str = <bytes>c_str
316+
self.j_env[0].ReleaseStringUTFChars(self.j_env, j_object, c_str)
317+
ret = py_str
318+
else:
319+
ret_jobject = JavaObject()
320+
ret_jobject.obj = j_object
321+
ret = ret_jobject
322+
elif r == '[':
323+
# TODO array support
324+
raise NotImplementedError("Array arguments not implemented")
325+
else:
326+
raise Exception('Invalid return definition?')
327+
328+
# free args
329+
if j_args != NULL:
330+
free(j_args)
331+
332+
return ret
333+

recipes/android/src/setup.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
libraries=[ 'sdl', 'log' ],
1111
library_dirs=[ 'libs/'+os.environ['ARCH'] ],
1212
),
13+
Extension(
14+
'java', ['java.c'],
15+
libraries=[ 'sdl', 'log' ],
16+
library_dirs=[ 'libs/'+os.environ['ARCH'] ],
17+
),
1318
Extension(
1419
'android_sound', ['android_sound.c', 'android_sound_jni.c',],
1520
libraries=[ 'sdl', 'log' ],

0 commit comments

Comments
 (0)