mirror of https://github.com/kivy/pyjnius.git
issue 465 - revised reflect
This commit is contained in:
parent
5088487c6e
commit
049c97d55e
123
jnius/reflect.py
123
jnius/reflect.py
|
@ -12,6 +12,7 @@ from .jnius import (
|
|||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class Class(with_metaclass(MetaJavaClass, JavaClass)):
|
||||
|
@ -195,6 +196,20 @@ def log_method(method, name, signature):
|
|||
Modifier.isStrict(mods)
|
||||
)
|
||||
|
||||
def identifyHierarchy(cls, level, classList, concrete=True):
|
||||
classList.append((cls,level))
|
||||
supercls = cls.getSuperclass()
|
||||
if supercls is not None:
|
||||
identifyHierarchy(supercls, level+1, classList, concrete)
|
||||
intfcs = cls.getInterfaces()
|
||||
if intfcs is not None:
|
||||
for intf in intfcs:
|
||||
identifyHierarchy(intf, level+1, classList, concrete)
|
||||
if not concrete and cls.isInterface() and len(intfcs) ==0:
|
||||
classList.append((find_javaclass('java.lang.Object'),level+1))
|
||||
#return classList
|
||||
|
||||
|
||||
|
||||
def autoclass(clsname):
|
||||
jniname = clsname.replace('.', '/')
|
||||
|
@ -217,58 +232,82 @@ def autoclass(clsname):
|
|||
constructors.append((sig, constructor.isVarArgs()))
|
||||
classDict['__javaconstructor__'] = constructors
|
||||
|
||||
cls = c
|
||||
level = -1
|
||||
while cls is not None:
|
||||
level += 1
|
||||
if cls is c:
|
||||
methods = cls.getDeclaredMethods()
|
||||
else:
|
||||
methods = cls.getMethods()
|
||||
methods_name = [x.getName() for x in methods]
|
||||
classHierachy=[]
|
||||
identifyHierarchy(c, 0, classHierachy, not c.isInterface())
|
||||
classHierachy.reverse()
|
||||
|
||||
print("autoclass(%s) intf %r hierarchy is %s" % (clsname,c.isInterface(),classHierachy))
|
||||
clsDone=set()
|
||||
|
||||
clsMethods=defaultdict(list)
|
||||
|
||||
#we now walk the hierarchy, from top of the tree, identifying methods
|
||||
#hopefully we start at java.lang.Object
|
||||
for cls,level in classHierachy:
|
||||
#dont analyse a given class more than once.
|
||||
#many interfaces can lead to java.lang.Object
|
||||
if cls in clsDone:
|
||||
continue
|
||||
clsDone.add(cls)
|
||||
#as we are walking the entire hierarchy, we only need getDeclaredMethods()
|
||||
#to get what is in this class; other parts of the hierarchy will be found
|
||||
#in those respective classes.
|
||||
methods = cls.getDeclaredMethods()
|
||||
methods_name = [x.getName() for x in methods]
|
||||
for index, method in enumerate(methods):
|
||||
name = methods_name[index]
|
||||
if name in classDict:
|
||||
continue
|
||||
|
||||
# only one method available
|
||||
if methods_name.count(name) == 1:
|
||||
static = Modifier.isStatic(method.getModifiers())
|
||||
varargs = method.isVarArgs()
|
||||
sig = '({0}){1}'.format(
|
||||
''.join([get_signature(x) for x in method.getParameterTypes()]),
|
||||
get_signature(method.getReturnType()))
|
||||
if log.level <= logging.DEBUG:
|
||||
log_method(method, name, sig)
|
||||
classDict[name] = (JavaStaticMethod if static else JavaMethod)(sig, varargs=varargs)
|
||||
if name != 'getClass' and bean_getter(name) and len(method.getParameterTypes()) == 0:
|
||||
lowername = lower_name(name[2 if name.startswith('is') else 3:])
|
||||
classDict[lowername] = (lambda n: property(lambda self: getattr(self, n)()))(name)
|
||||
continue
|
||||
#print("%s %s" % (str(cls), name))
|
||||
|
||||
clsMethods[name].append((cls, method, level))
|
||||
|
||||
#having collated the mthods, identify if there are any with the same name
|
||||
for name in clsMethods:
|
||||
if len(clsMethods[name]) == 1:
|
||||
#uniquely named method
|
||||
owningCls, method, level = clsMethods[name][0]
|
||||
static = Modifier.isStatic(method.getModifiers())
|
||||
varargs = method.isVarArgs()
|
||||
sig = '({0}){1}'.format(
|
||||
''.join([get_signature(x) for x in method.getParameterTypes()]),
|
||||
get_signature(method.getReturnType()))
|
||||
if log.level <= logging.DEBUG:
|
||||
log_method(method, name, sig)
|
||||
classDict[name] = (JavaStaticMethod if static else JavaMethod)(sig, varargs=varargs)
|
||||
if name != 'getClass' and bean_getter(name) and len(method.getParameterTypes()) == 0:
|
||||
lowername = lower_name(name[2 if name.startswith('is') else 3:])
|
||||
classDict[lowername] = (lambda n: property(lambda self: getattr(self, n)()))(name)
|
||||
else:
|
||||
# multiple signatures
|
||||
signatures = []
|
||||
for index, subname in enumerate(methods_name):
|
||||
if subname != name:
|
||||
continue
|
||||
method = methods[index]
|
||||
sig = '({0}){1}'.format(
|
||||
''.join([get_signature(x) for x in method.getParameterTypes()]),
|
||||
get_signature(method.getReturnType()))
|
||||
#print("method with %d multiple signatures is %s in cls %s" % (len(clsMethods[name]), name, c))
|
||||
|
||||
|
||||
#assume there can be no more than 10000 levels of inheritance
|
||||
paramsig_to_level=defaultdict(lambda: 10000)
|
||||
#we now identify if any have the same signature, as we will call the _lowest_, ie min level
|
||||
for owningCls, method, level in clsMethods[name]:
|
||||
param_sig = ''.join([get_signature(x) for x in method.getParameterTypes()])
|
||||
#print("\t owner %s level %d param_sig %s" % (str(owningCls), level, param_sig))
|
||||
if level < paramsig_to_level[param_sig]:
|
||||
paramsig_to_level[param_sig] = level
|
||||
|
||||
for owningCls, method, level in clsMethods[name]:
|
||||
param_sig = ''.join([get_signature(x) for x in method.getParameterTypes()])
|
||||
#only accept the parameter signature at the deepest level of hierarchy (i.e. min level)
|
||||
if level > paramsig_to_level[param_sig]:
|
||||
#print("discarding %s name from %s at level %d" % (name, str(owningCls), level))
|
||||
continue
|
||||
|
||||
return_sig = get_signature(method.getReturnType())
|
||||
sig = '({0}){1}'.format(param_sig, return_sig)
|
||||
|
||||
if log.level <= logging.DEBUG:
|
||||
log_method(method, name, sig)
|
||||
signatures.append((sig, Modifier.isStatic(method.getModifiers()), method.isVarArgs()))
|
||||
|
||||
#print("method selected %d multiple signatures of %s" % (len(signatures), str(signatures)))
|
||||
classDict[name] = JavaMultipleMethod(signatures)
|
||||
|
||||
_cls = cls.getSuperclass()
|
||||
if not _cls and cls.isInterface():
|
||||
cls = find_javaclass('java.lang.Object')
|
||||
else:
|
||||
cls = _cls
|
||||
|
||||
def _getitem(self, index):
|
||||
try:
|
||||
return self.get(index)
|
||||
|
@ -295,12 +334,8 @@ def autoclass(clsname):
|
|||
classDict[field.getName()] = cls(sig)
|
||||
|
||||
classDict['__javaclass__'] = clsname.replace('.', '/')
|
||||
#print(classDict)
|
||||
#if "newInstance" in classDict:
|
||||
# print("newInstncae returns " + str(classDict["newInstance"].signatures()))
|
||||
#print(clsname)
|
||||
return MetaJavaClass.__new__(
|
||||
MetaJavaClass,
|
||||
clsname, # .replace('.', '_'),
|
||||
clsname,
|
||||
(JavaClass, ),
|
||||
classDict)
|
||||
|
|
|
@ -3,6 +3,13 @@ package org.jnius;
|
|||
import org.jnius.Parent;
|
||||
|
||||
public class Child extends Parent {
|
||||
|
||||
public int CHILD_FIELD = 1;
|
||||
|
||||
public int doCall(int child) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static public Child newInstance(){
|
||||
return new Child();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
package org.jnius;
|
||||
|
||||
public class Parent {
|
||||
|
||||
public int doCall(Object o) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static public Parent newInstance(){
|
||||
return new Parent();
|
||||
}
|
||||
|
||||
public int PARENT_FIELD = 0;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
from jnius import autoclass
|
||||
|
||||
|
||||
def test_methodcalls():
|
||||
Parent = autoclass('org.jnius.Parent')
|
||||
Child = autoclass('org.jnius.Child')
|
||||
child = Child.newInstance()
|
||||
parent = Parent.newInstance()
|
||||
assert parent.doCall(parent) == 0
|
||||
assert child.doCall(0) == 1
|
||||
assert child.doCall(child) == 0
|
||||
|
||||
def test_fields():
|
||||
Parent = autoclass('org.jnius.Parent')
|
||||
Child = autoclass('org.jnius.Child')
|
||||
child = Child.newInstance()
|
||||
parent = Parent.newInstance()
|
||||
assert parent.PARENT_FIELD == 0
|
||||
assert child.CHILD_FIELD == 1
|
||||
assert child.PARENT_FIELD == 0
|
||||
|
||||
|
||||
def test_newinstance():
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
Parent = autoclass('org.jnius.Parent')
|
||||
Child = autoclass('org.jnius.Child')
|
||||
|
||||
child = Child.newInstance()
|
||||
|
||||
assert isinstance(child, Child)
|
||||
assert isinstance(child, Parent)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_newinstance()
|
|
@ -3,6 +3,7 @@ from __future__ import division
|
|||
from __future__ import absolute_import
|
||||
import unittest
|
||||
from jnius.reflect import autoclass
|
||||
from jnius import cast
|
||||
|
||||
class ReflectTest(unittest.TestCase):
|
||||
|
||||
|
@ -14,6 +15,34 @@ class ReflectTest(unittest.TestCase):
|
|||
stack.push('world')
|
||||
self.assertEqual(stack.pop(), 'world')
|
||||
self.assertEqual(stack.pop(), 'hello')
|
||||
|
||||
def test_list_interface(self):
|
||||
ArrayList = autoclass('java.util.ArrayList')
|
||||
words = ArrayList()
|
||||
words.add('hello')
|
||||
words.add('world')
|
||||
self.assertIsNotNone(words.stream())
|
||||
self.assertIsNotNone(words.iterator())
|
||||
|
||||
def test_super_interface(self):
|
||||
LinkedList = autoclass('java.util.LinkedList')
|
||||
words = LinkedList()
|
||||
words.add('hello')
|
||||
words.add('world')
|
||||
q = cast('java.util.Queue', words)
|
||||
self.assertEqual(2, q.size())
|
||||
self.assertIsNotNone(q.iterator())
|
||||
|
||||
def test_super_object(self):
|
||||
LinkedList = autoclass('java.util.LinkedList')
|
||||
words = LinkedList()
|
||||
words.hashCode()
|
||||
|
||||
def test_super_interface_object(self):
|
||||
LinkedList = autoclass('java.util.LinkedList')
|
||||
words = LinkedList()
|
||||
q = cast('java.util.Queue', words)
|
||||
q.hashCode()
|
||||
|
||||
def test_list_iteration(self):
|
||||
ArrayList = autoclass('java.util.ArrayList')
|
||||
|
@ -21,3 +50,4 @@ class ReflectTest(unittest.TestCase):
|
|||
words.add('hello')
|
||||
words.add('world')
|
||||
self.assertEqual(['hello', 'world'], [word for word in words])
|
||||
|
||||
|
|
Loading…
Reference in New Issue