diff --git a/jnius/signatures.py b/jnius/signatures.py new file mode 100644 index 0000000..9bc1830 --- /dev/null +++ b/jnius/signatures.py @@ -0,0 +1,99 @@ +''' +signatures.py +============= + +A handy API for writing JNI signatures easily + +Author: chrisjrn + +This module aims to provide a more human-friendly API for +wiring up Java proxy methods in PyJnius. + +You can use the signature function to produce JNI method +signautures for methods; passing PyJnius JavaClass classes +as return or argument types; provided here are annotations +representing Java's primitive and array times. + +Methods can return just a standard primitive type: + +>>> signature(jint, ()) +'()I' + +>>> s.signature(jvoid, [jint]) +'(I)V' + +Or you can use autoclass proxies to specify Java classes +for return types. + +>>> from jnius import autoclass +>>> String = autoclass("java.lang.String") +>>> signature(String, ()) +'()Ljava/lang/String;' + +''' + +__version__ = '0.0.1' + +from . import JavaClass +from . import java_method + + +''' Type specifiers for primitives ''' + +class _JavaSignaturePrimitive(object): + _spec = "" + +def _MakeSignaturePrimitive(name, spec): + class __Primitive(_JavaSignaturePrimitive): + ''' PyJnius signature for Java %s type ''' % name + _name = name + _spec = spec + __Primitive.__name__ = "j" + name + + return __Primitive + +jboolean = _MakeSignaturePrimitive("boolean", "Z") +jbyte = _MakeSignaturePrimitive("byte", "B") +jchar = _MakeSignaturePrimitive("char", "C") +jdouble = _MakeSignaturePrimitive("double", "D") +jfloat = _MakeSignaturePrimitive("float", "F") +jint = _MakeSignaturePrimitive("int", "I") +jlong = _MakeSignaturePrimitive("long", "J") +jshort = _MakeSignaturePrimitive("short", "S") +jvoid = _MakeSignaturePrimitive("void", "V") + +def JArray(of_type): + ''' Signature helper for identifying arrays of a given object or + primitive type. ''' + + spec = "[" + _jni_type_spec(of_type) + return _MakeSignaturePrimitive("array", spec) + +def with_signature(returns, takes): + ''' Alternative version of @java_method that takes JavaClass + objects to produce the method signature. ''' + + sig = signature(returns, takes) + return java_method(sig) + +def signature(returns, takes): + ''' Produces a JNI method signature, taking the provided arguments + and returning the given return type. ''' + + out_takes = [] + for arg in takes: + out_takes.append(_jni_type_spec(arg)) + + return "(" + "".join(out_takes) + ")" + _jni_type_spec(returns) + +def _jni_type_spec(jclass): + ''' Produces a JNI type specification string for the given argument. + If the argument is a jnius.JavaClass, it produces the JNI type spec + for the class. Signature primitives return their stored type spec. + ''' + + if issubclass(jclass, JavaClass): + return "L" + jclass.__javaclass__ + ";" + elif issubclass(jclass, _JavaSignaturePrimitive): + return jclass._spec + \ No newline at end of file diff --git a/tests/test_signature.py b/tests/test_signature.py new file mode 100644 index 0000000..07c9422 --- /dev/null +++ b/tests/test_signature.py @@ -0,0 +1,174 @@ +import unittest + +from jnius import autoclass, java_method, PythonJavaClass, cast + +from jnius.signatures import * + +JObject = autoclass('java/lang/Object') +JString = autoclass('java/lang/String') +JListIterator = autoclass("java.util.ListIterator") + +class TestImplemIterator(PythonJavaClass): + __javainterfaces__ = [ + 'java/util/ListIterator', ] + + def __init__(self, collection, index=0): + super(TestImplemIterator, self).__init__() + self.collection = collection + self.index = index + + @with_signature(jboolean, []) + def hasNext(self): + return self.index < len(self.collection.data) - 1 + + @with_signature(JObject, []) + def next(self): + obj = self.collection.data[self.index] + self.index += 1 + return obj + + @with_signature(jboolean, []) + def hasPrevious(self): + return self.index >= 0 + + @with_signature(JObject, []) + def previous(self): + self.index -= 1 + obj = self.collection.data[self.index] + return obj + + @with_signature(jint, []) + def previousIndex(self): + return self.index - 1 + + @with_signature(JString, []) + def toString(self): + return repr(self) + + @with_signature(JObject, [jint]) + def get(self, index): + return self.collection.data[index - 1] + + @with_signature(jvoid, [JObject]) + def set(self, obj): + self.collection.data[self.index - 1] = obj + + +class TestImplem(PythonJavaClass): + __javainterfaces__ = ['java/util/List'] + + def __init__(self, *args): + super(TestImplem, self).__init__(*args) + self.data = list(args) + + @with_signature(autoclass("java.util.Iterator"), []) + def iterator(self): + it = TestImplemIterator(self) + return it + + @with_signature(JString, []) + def toString(self): + return repr(self) + + @with_signature(jint, []) + def size(self): + return len(self.data) + + @with_signature(JObject, [jint]) + def get(self, index): + return self.data[index] + + @with_signature(JObject, [jint, JObject]) + def set(self, index, obj): + old_object = self.data[index] + self.data[index] = obj + return old_object + + @with_signature(JArray(JObject), []) + def toArray(self): + return self.data + + @with_signature(JListIterator, []) + def listIterator(self): + it = TestImplemIterator(self) + return it + + # TODO cover this case of listIterator. + @java_method(signature(JListIterator, [jint]), + name='ListIterator') + def listIteratorI(self, index): + it = TestImplemIterator(self, index) + return it + + +from jnius.reflect import autoclass + +class SignaturesTest(unittest.TestCase): + + def test_construct_stack_from_testimplem(self): + Stack = autoclass("java.util.Stack") + pyjlist = TestImplem(1, 2, 3, 4, 5, 6, 7) + stack = Stack() + stack.addAll(pyjlist) + self.assertEquals(7, pyjlist.size()) + self.assertEquals(stack.size(), pyjlist.size()) + array = pyjlist.toArray() + + def test_return_types(self): + + # Void + sig = signature(jvoid, []) + self.assertEquals(sig, "()V") + + # Boolean + sig = signature(jboolean, []) + self.assertEquals(sig, "()Z") + + # Byte + sig = signature(jbyte, []) + self.assertEquals(sig, "()B") + + # Char + sig = signature(jchar, []) + self.assertEquals(sig, "()C") + + # Double + sig = signature(jdouble, []) + self.assertEquals(sig, "()D") + + # Float + sig = signature(jfloat, []) + self.assertEquals(sig, "()F") + + # Int + sig = signature(jint, []) + self.assertEquals(sig, "()I") + + # Long + sig = signature(jlong, []) + self.assertEquals(sig, "()J") + + # Short + sig = signature(jshort, []) + self.assertEquals(sig, "()S") + + # Object return method + String = autoclass("java.lang.String") + sig = signature(String, []) + self.assertEquals(sig, "()Ljava/lang/String;") + + # Array return + sig = signature(JArray(jint), []) + self.assertEquals(sig, "()[I") + + def test_params(self): + String = autoclass("java.lang.String") + + # Return void, takes objects as parameters + sig = signature(jvoid, [String, String]) + self.assertEquals(sig, "(Ljava/lang/String;Ljava/lang/String;)V") + + # Multiple array parameter types + sig = signature(jvoid, [JArray(jint), JArray(jboolean)]) + self.assertEquals(sig, "([I[Z)V") +