diff --git a/jnius/reflect.py b/jnius/reflect.py index 0b4f813..73c128e 100644 --- a/jnius/reflect.py +++ b/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) diff --git a/tests/java-src/org/jnius/Child.java b/tests/java-src/org/jnius/Child.java index 363de82..8d6fc62 100644 --- a/tests/java-src/org/jnius/Child.java +++ b/tests/java-src/org/jnius/Child.java @@ -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(); } diff --git a/tests/java-src/org/jnius/Parent.java b/tests/java-src/org/jnius/Parent.java index 836fdf5..912f693 100644 --- a/tests/java-src/org/jnius/Parent.java +++ b/tests/java-src/org/jnius/Parent.java @@ -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; } diff --git a/tests/test_inheritance.py b/tests/test_inheritance.py index 979b71b..dafc30c 100644 --- a/tests/test_inheritance.py +++ b/tests/test_inheritance.py @@ -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() \ No newline at end of file diff --git a/tests/test_reflect.py b/tests/test_reflect.py index 778614d..4c686bf 100644 --- a/tests/test_reflect.py +++ b/tests/test_reflect.py @@ -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]) +