Skip to content

Commit 3628416

Browse files
committed
add support for array arguments (native or any Java object)
+ rewrote the signature parsing + few typo fixes, moar tests needed.
1 parent ee425bd commit 3628416

File tree

4 files changed

+216
-30
lines changed

4 files changed

+216
-30
lines changed

recipes/android/src/java.pyx

Lines changed: 178 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,37 @@ include "jni.pxi"
3838
from libc.stdlib cimport malloc, free
3939

4040

41-
cdef tuple parse_definition(definition):
42-
args, ret = definition[1:].split(')')
43-
if args:
44-
args = args.split(';')
45-
if args[-1] == '':
46-
args.pop(-1)
47-
else:
48-
args = []
49-
return ret, args
41+
cdef parse_definition(definition):
42+
# not a function, just a field
43+
if definition[0] != '(':
44+
return definition, None
45+
46+
# it's a function!
47+
argdef, ret = definition[1:].split(')')
48+
args = []
49+
50+
while len(argdef):
51+
c = argdef[0]
52+
53+
# read the array char
54+
prefix = ''
55+
if c == '[':
56+
prefix = c
57+
argdef = argdef[1:]
58+
c = argdef[0]
59+
60+
# native type
61+
if c in 'ZBCSIJFD':
62+
args.append(prefix + c)
63+
argdef = argdef[1:]
64+
continue
65+
66+
# java class
67+
if c == 'L':
68+
c, argdef = argdef.split(';', 1)
69+
args.append(prefix + c + ';')
5070

71+
return ret, args
5172

5273
class JavaException(Exception):
5374
'''Can be a real java exception, or just an exception from the wrapper.
@@ -164,15 +185,17 @@ cdef class JavaClass(object):
164185

165186
cdef void populate_args(self, list definition_args, jvalue *j_args, args):
166187
# do the conversion from a Python object to Java from a Java definition
167-
cdef JavaObject j_object
188+
cdef JavaObject jo
189+
cdef JavaClass jc
190+
cdef int index
168191
for index, argtype in enumerate(definition_args):
169192
py_arg = args[index]
170193
if argtype == 'Z':
171194
j_args[index].z = py_arg
172195
elif argtype == 'B':
173196
j_args[index].b = py_arg
174197
elif argtype == 'C':
175-
j_args[index].c = py_arg
198+
j_args[index].c = ord(py_arg)
176199
elif argtype == 'S':
177200
j_args[index].s = py_arg
178201
elif argtype == 'I':
@@ -184,23 +207,150 @@ cdef class JavaClass(object):
184207
elif argtype == 'D':
185208
j_args[index].d = py_arg
186209
elif argtype[0] == 'L':
187-
if argtype == 'Ljava/lang/String':
188-
if isinstance(py_arg, basestring):
189-
j_args[index].l = self.j_env[0].NewStringUTF(
190-
self.j_env, <char *><bytes>py_arg)
191-
elif py_arg is None:
192-
j_args[index].l = NULL
193-
else:
194-
raise JavaException('Not a correct type of string, '
195-
'must be an instance of str or unicode')
210+
if py_arg is None:
211+
j_args[index].l = NULL
212+
elif isinstance(py_arg, basestring) and \
213+
argtype == 'Ljava/lang/String;':
214+
j_args[index].l = self.j_env[0].NewStringUTF(
215+
self.j_env, <char *><bytes>py_arg)
216+
elif isinstance(py_arg, JavaClass):
217+
jc = py_arg
218+
if jc.__javaclass__ != argtype[1:-1]:
219+
raise JavaException('Invalid class argument, want '
220+
'{0!r}, got {1!r}'.format(
221+
argtype[1:-1], jc.__javaclass__))
222+
j_args[index].l = jc.j_self
223+
elif isinstance(py_arg, JavaObject):
224+
jo = py_arg
225+
j_args[index].l = jo.obj
226+
raise JavaException('JavaObject needed for argument '
227+
'{0}'.format(index))
196228
else:
197-
if not isinstance(py_arg, JavaObject):
198-
raise JavaException('JavaObject needed for argument '
199-
'{0}'.format(index))
200-
j_object = py_arg
201-
j_args[index].l = j_object.obj
229+
raise JavaException('Invalid python object for this '
230+
'argument. Want {0!r}, got {1!r}'.format(
231+
argtype[1:-1], py_arg))
202232
elif argtype[0] == '[':
203-
raise NotImplemented('List arguments not accepted')
233+
if not isinstance(py_arg, list) and \
234+
not isinstance(py_arg, tuple):
235+
raise JavaException('Expecting a python list/tuple, got '
236+
'{0!r}'.format(py_arg))
237+
238+
j_args[index].l = self.convert_pyarray_to_java(
239+
argtype[1:], py_arg)
240+
241+
cdef jobject convert_pyarray_to_java(self, definition, pyarray):
242+
cdef jobject ret = NULL
243+
cdef int array_size = len(pyarray)
244+
cdef int i
245+
cdef jboolean j_boolean
246+
cdef jbyte j_byte
247+
cdef jchar j_char
248+
cdef jshort j_short
249+
cdef jint j_int
250+
cdef jlong j_long
251+
cdef jfloat j_float
252+
cdef jdouble j_double
253+
cdef jstring j_string
254+
cdef jclass j_class
255+
cdef JavaObject jo
256+
cdef JavaClass jc
257+
258+
if definition == 'Z':
259+
ret = self.j_env[0].NewBooleanArray(self.j_env, array_size)
260+
for i in range(array_size):
261+
j_boolean = 1 if pyarray[i] else 0
262+
self.j_env[0].SetBooleanArrayRegion(self.j_env,
263+
ret, i, 1, &j_boolean)
264+
265+
elif definition == 'B':
266+
ret = self.j_env[0].NewByteArray(self.j_env, array_size)
267+
for i in range(array_size):
268+
j_byte = pyarray[i]
269+
self.j_env[0].SetByteArrayRegion(self.j_env,
270+
ret, i, 1, &j_byte)
271+
272+
elif definition == 'C':
273+
ret = self.j_env[0].NewCharArray(self.j_env, array_size)
274+
for i in range(array_size):
275+
j_char = ord(pyarray[i])
276+
self.j_env[0].SetCharArrayRegion(self.j_env,
277+
ret, i, 1, &j_char)
278+
279+
elif definition == 'S':
280+
ret = self.j_env[0].NewShortArray(self.j_env, array_size)
281+
for i in range(array_size):
282+
j_short = pyarray[i]
283+
self.j_env[0].SetShortArrayRegion(self.j_env,
284+
ret, i, 1, &j_short)
285+
286+
elif definition == 'I':
287+
ret = self.j_env[0].NewIntArray(self.j_env, array_size)
288+
for i in range(array_size):
289+
j_int = pyarray[i]
290+
self.j_env[0].SetIntArrayRegion(self.j_env,
291+
ret, i, 1, <const_jint *>&j_int)
292+
293+
elif definition == 'J':
294+
ret = self.j_env[0].NewLongArray(self.j_env, array_size)
295+
for i in range(array_size):
296+
j_long = pyarray[i]
297+
self.j_env[0].SetLongArrayRegion(self.j_env,
298+
ret, i, 1, &j_long)
299+
300+
elif definition == 'F':
301+
ret = self.j_env[0].NewFloatArray(self.j_env, array_size)
302+
for i in range(array_size):
303+
j_float = pyarray[i]
304+
self.j_env[0].SetFloatArrayRegion(self.j_env,
305+
ret, i, 1, &j_float)
306+
307+
elif definition == 'D':
308+
ret = self.j_env[0].NewDoubleArray(self.j_env, array_size)
309+
for i in range(array_size):
310+
j_double = pyarray[i]
311+
self.j_env[0].SetDoubleArrayRegion(self.j_env,
312+
ret, i, 1, &j_double)
313+
314+
elif definition[0] == 'L':
315+
j_class = self.j_env[0].FindClass(
316+
self.j_env, <bytes>definition[1:-1])
317+
if j_class == NULL:
318+
raise JavaException('Cannot create array with a class not '
319+
'found {0!r}'.format(definition[1:-1]))
320+
ret = self.j_env[0].NewObjectArray(
321+
self.j_env, array_size, j_class, NULL)
322+
for i in range(array_size):
323+
arg = pyarray[i]
324+
if arg is None:
325+
self.j_env[0].SetObjectArrayElement(
326+
self.j_env, <jobjectArray>ret, i, NULL)
327+
elif isinstance(arg, basestring) and \
328+
definition == 'Ljava/lang/String;':
329+
j_string = self.j_env[0].NewStringUTF(
330+
self.j_env, <bytes>arg)
331+
self.j_env[0].SetObjectArrayElement(
332+
self.j_env, <jobjectArray>ret, i, j_string)
333+
elif isinstance(arg, JavaClass):
334+
jc = arg
335+
if jc.__javaclass__ != definition[1:-1]:
336+
raise JavaException('Invalid class argument, want '
337+
'{0!r}, got {1!r}'.format(
338+
definition[1:-1],
339+
jc.__javaclass__))
340+
self.j_env[0].SetObjectArrayElement(
341+
self.j_env, <jobjectArray>ret, i, jc.j_self)
342+
elif isinstance(arg, JavaObject):
343+
jo = arg
344+
self.j_env[0].SetObjectArrayElement(
345+
self.j_env, <jobjectArray>ret, i, jo.obj)
346+
else:
347+
raise JavaException('Invalid variable used for L array')
348+
349+
else:
350+
raise JavaException('Invalid array definition')
351+
352+
return <jobject>ret
353+
204354

205355
cdef convert_jarray_to_python(self, definition, jobject j_object):
206356
cdef jboolean iscopy
@@ -210,8 +360,8 @@ cdef class JavaClass(object):
210360
cdef jshort *j_shorts
211361
cdef jint *j_ints
212362
cdef jlong *j_longs
213-
cdef jfloat *j_float
214-
cdef jdouble *j_double
363+
cdef jfloat *j_floats
364+
cdef jdouble *j_doubles
215365
cdef object ret = None
216366
cdef jsize array_size
217367

recipes/android/src/jni.pxi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ cdef extern from "jni.h":
7171
jint *GetVersion(JNIEnv *)
7272
jclass (*DefineClass)(JNIEnv*, const_char*, jobject, const_jbyte*,
7373
jsize)
74-
jclass (*FindClass)(JNIEnv*, const_char*)
74+
jclass (*FindClass)(JNIEnv*, char*)
7575

7676
jmethodID (*FromReflectedMethod)(JNIEnv*, jobject)
7777
jfieldID (*FromReflectedField)(JNIEnv*, jobject)
@@ -287,7 +287,7 @@ cdef extern from "jni.h":
287287
jsize (*GetStringLength)(JNIEnv*, jstring)
288288
const_jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*)
289289
void (*ReleaseStringChars)(JNIEnv*, jstring, const_jchar*)
290-
jstring (*NewStringUTF)(JNIEnv*, const_char*)
290+
jstring (*NewStringUTF)(JNIEnv*, char*)
291291
jsize (*GetStringUTFLength)(JNIEnv*, jstring)
292292
# JNI spec says this returns const_jbyte*, but that's inconsistent
293293
const_char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*)

src/tests/javawrapper/javawrapper/Test.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,27 @@ public String[] methodArrayString() {
9393
return x;
9494
};
9595

96+
97+
public boolean methodParamsZBCSIJFD(boolean x1, byte x2, char x3, short x4,
98+
int x5, long x6, float x7, double x8) {
99+
// ADD float / double, but dunno how to do with approx
100+
return (x1 == true && x2 == 127 && x3 == 'k' && x4 == 32767 &&
101+
x5 == 2147483467 && x6 == 2147483467);
102+
}
103+
104+
public boolean methodParamsString(String s) {
105+
return (s.equals("helloworld"));
106+
}
107+
108+
public boolean methodParamsArrayI(int[] x) {
109+
if (x.length != 3)
110+
return false;
111+
return (x[0] == 1 && x[1] == 2 && x[2] == 3);
112+
}
113+
114+
public boolean methodParamsArrayString(String[] x) {
115+
if (x.length != 2)
116+
return false;
117+
return (x[0].equals("hello") && x[1].equals("world"));
118+
}
96119
}

src/tests/javawrapper/python/main.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ class Test(JavaClass):
5454
fieldStaticD = JavaStaticField('D')
5555
fieldStaticString = JavaStaticField('Ljava/lang/String;')
5656

57+
methodParamsZBCSIJFD = JavaMethod('(ZBCSIJFD)Z')
58+
methodParamsString = JavaMethod('(Ljava/lang/String;)Z')
59+
methodParamsArrayI = JavaMethod('([I)Z')
60+
methodParamsArrayString = JavaMethod('([Ljava/lang/String;)Z')
61+
5762
def do(msg, value, want):
5863
print 'Testing', msg, ':', (value, want), '-->', (value == want)
5964

@@ -116,4 +121,12 @@ def do(msg, value, want):
116121
do('method double', test.methodArrayD(), [1.23456789] * 3)
117122
do('method String', test.methodArrayString(), ['helloworld'] * 3)
118123

124+
print '-- test methods with params'
125+
do('method with all natives', test.methodParamsZBCSIJFD(
126+
True, 127, 'k', 32767, 2147483467, 2147483467, 1.23456789, 1.23456789), True)
127+
do('method with String', test.methodParamsString('helloworld'), True)
128+
do('method with int array', test.methodParamsArrayI([1, 2, 3]), True)
129+
do('method with String array', test.methodParamsArrayString([
130+
'hello', 'world']), True)
131+
119132
print '--------------- ENDTESTS ----------------'

0 commit comments

Comments
 (0)