[Jython-checkins] jython: Refactor PyJavaType.init for readability (no material change).

jeff.allen jython-checkins at python.org
Mon Apr 16 17:44:14 EDT 2018


https://hg.python.org/jython/rev/623eaa3d7834
changeset:   8156:623eaa3d7834
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Mon Apr 16 20:56:52 2018 +0100
summary:
  Refactor PyJavaType.init for readability (no material change).

files:
  src/org/python/core/PyJavaType.java |  686 +++++++++------
  1 files changed, 417 insertions(+), 269 deletions(-)


diff --git a/src/org/python/core/PyJavaType.java b/src/org/python/core/PyJavaType.java
--- a/src/org/python/core/PyJavaType.java
+++ b/src/org/python/core/PyJavaType.java
@@ -383,18 +383,25 @@
         if (!Modifier.isPublic(forClass.getModifiers()) && !name.startsWith("org.python.core")
                 && Options.respectJavaAccessibility) {
             handleSuperMethodArgCollisions(forClass);
-            return;
+            return;  // XXX Why is it ok to skip the rest in this case?
         }
 
-        // Add methods and determine bean properties declared on this class
-        Map<String, PyBeanProperty> props = Generic.map();
-        Map<String, PyBeanEvent<?>> events = Generic.map();
+        /*
+         * Compile lists of the methods, fields and constructors to be exposed through this type. If
+         * we respect Java accessibility, this is simple.
+         */
         Method[] methods;
+        Field[] fields;
+        Constructor<?>[] constructors;
+
         if (Options.respectJavaAccessibility) {
-            // returns just the public methods
+            // Just the public methods, fields and constructors
             methods = forClass.getMethods();
+            fields = forClass.getFields();
+            constructors = forClass.getConstructors();
+
         } else {
-            // Grab all methods on this class and all of its superclasses and make them accessible
+            // All methods on this class and all of its super classes
             List<Method> allMethods = Generic.list();
             for (Class<?> c = forClass; c != null; c = c.getSuperclass()) {
                 for (Method meth : c.getDeclaredMethods()) {
@@ -403,16 +410,129 @@
                 }
             }
             methods = allMethods.toArray(new Method[allMethods.size()]);
+            // Methods must be in resolution order. See issue #2391 for detail.
+            Arrays.sort(methods, new MethodComparator(new ClassComparator()));
+
+            // All the fields on just this class
+            fields = forClass.getDeclaredFields();
+            for (Field field : fields) {
+                field.setAccessible(true);
+            }
+
+            // All the constructors (except if this is for class Class)
+            if (forClass == Class.class) {
+                // No matter the security manager, cannot set constructors accessible
+                constructors = forClass.getConstructors();
+            } else {
+                constructors = forClass.getDeclaredConstructors();
+                for (Constructor<?> ctr : constructors) {
+                    ctr.setAccessible(true);
+                }
+            }
+        }
+
+        // Add methods, also accumulating them in reflectedFuncs, and spotting Java Bean members.
+        ArrayList<PyReflectedFunction> reflectedFuncs = new ArrayList<>(methods.length);
+        Map<String, PyBeanProperty> props = Generic.map();
+        Map<String, PyBeanEvent<?>> events = Generic.map();
+        addMethods(baseClass, reflectedFuncs, props, events, methods);
+        addInheritedMethods(reflectedFuncs, methods);
+
+        // Add fields declared on this type
+        addFields(baseClass, fields);
+
+        // Fill in the bean events and properties picked up while going through the methods
+        addBeanEvents(events);
+        addBeanProperties(props);
+
+        // Add constructors declared on this type
+        addConstructors(forClass, constructors);
+
+        // Special handling for Java collection types
+        addCollectionProxies(forClass);
+
+        // Handle classes that use the ClassDictInit pattern for their definition
+        PyObject nameSpecified = null;
+        if (ClassDictInit.class.isAssignableFrom(forClass) && forClass != ClassDictInit.class) {
+            try {
+                Method m = forClass.getMethod("classDictInit", PyObject.class);
+                m.invoke(null, dict);
+                // allow the class to override its name after it is loaded
+                nameSpecified = dict.__finditem__("__name__");
+                if (nameSpecified != null) {
+                    name = nameSpecified.toString();
+                }
+            } catch (Exception exc) {
+                throw Py.JavaError(exc);
+            }
         }
 
-        // Make sure we sort all methods so they resolve in the right order. See #2391 for detail.
-        Arrays.sort(methods, new MethodComparator(new ClassComparator()));
+        // Fill in the __module__ attribute of PyReflectedFunctions.
+        if (reflectedFuncs.size() > 0) {
+            if (nameSpecified == null) {
+                nameSpecified = Py.newString(name);
+            }
+            for (PyReflectedFunction func : reflectedFuncs) {
+                func.__module__ = nameSpecified;
+            }
+        }
+
+        // Handle descriptor classes
+        if (baseClass != Object.class) {
+            hasGet = getDescrMethod(forClass, "__get__", OO) != null
+                    || getDescrMethod(forClass, "_doget", PyObject.class) != null
+                    || getDescrMethod(forClass, "_doget", OO) != null;
+            hasSet = getDescrMethod(forClass, "__set__", OO) != null
+                    || getDescrMethod(forClass, "_doset", OO) != null;
+            hasDelete = getDescrMethod(forClass, "__delete__", PyObject.class) != null
+                    || getDescrMethod(forClass, "_dodel", PyObject.class) != null;
+        }
+
+        /*
+         * Certain types get particular implementations of__lt__, __le__, __ge__, __gt__, __copy__
+         * and __deepcopy__.
+         */
+        if (forClass == Object.class) {
+            addMethodsForObject();
+        } else if (forClass == Comparable.class) {
+            addMethodsForComparable();
+        } else if (forClass == Cloneable.class) {
+            addMethodsForCloneable();
+        } else if (forClass == Serializable.class) {
+            addMethodsForSerializable();
+        } else if (immutableClasses.contains(forClass)) {
+            // __deepcopy__ just works for these objects since it uses serialization instead
+            addMethod(new PyBuiltinMethodNarrow("__copy__") {
+
+                @Override
+                public PyObject __call__() {
+                    return self;
+                }
+            });
+        }
+    }
+
+    /**
+     * Process the given class for methods defined on the target class itself (the
+     * <code>fromClass</code>), rather than inherited.
+     *
+     * This is exclusively a helper method for {@link #init(Set)}.
+     *
+     * @param baseClass ancestor of the target class
+     * @param reflectedFuncs to which reflected functions are added for further processing
+     * @param props to which Java Bean properties are added for further processing
+     * @param events to which Java Bean events are added for further processing
+     * @param methods of the target class
+     */
+    private void addMethods(Class<?> baseClass, List<PyReflectedFunction> reflectedFuncs,
+            Map<String, PyBeanProperty> props, Map<String, PyBeanEvent<?>> events,
+            Method[] methods) {
 
         boolean isInAwt = name.startsWith("java.awt.") && name.indexOf('.', 9) == -1;
-        ArrayList<PyReflectedFunction> reflectedFuncs = new ArrayList<>(methods.length);
-        PyReflectedFunction reflfunc;
+
+        // First pass skip inherited (and certain "ignored") methods.
         for (Method meth : methods) {
-            if (!declaredOnMember(baseClass, meth) || ignore(meth)) {
+            if (!declaredHere(baseClass, meth) || ignore(meth)) {
                 continue;
             }
 
@@ -428,7 +548,8 @@
             }
 
             String nmethname = normalize(methname);
-            reflfunc = (PyReflectedFunction) dict.__finditem__(nmethname);
+
+            PyReflectedFunction reflfunc = (PyReflectedFunction) dict.__finditem__(nmethname);
             if (reflfunc == null) {
                 reflfunc = new PyReflectedFunction(meth);
                 reflectedFuncs.add(reflfunc);
@@ -437,71 +558,100 @@
                 reflfunc.addMethod(meth);
             }
 
-            // Now check if this is a bean method, for which it must be an instance method
-            if (Modifier.isStatic(meth.getModifiers())) {
-                continue;
-            }
+            // Check if this is a Java Bean method, indicating the "bean nature" of the class
+            checkBeanMethod(props, events, meth);
+        }
+    }
 
-            // First check if this is a bean event addition method
-            int n = meth.getParameterTypes().length;
-            if ((methname.startsWith("add") || methname.startsWith("set"))
-                    && methname.endsWith("Listener") && n == 1 && meth.getReturnType() == Void.TYPE
-                    && EventListener.class.isAssignableFrom(meth.getParameterTypes()[0])) {
-                Class<?> eventClass = meth.getParameterTypes()[0];
-                String ename = eventClass.getName();
-                int idot = ename.lastIndexOf('.');
-                if (idot != -1) {
-                    ename = ename.substring(idot + 1);
-                }
-                ename = normalize(StringUtil.decapitalize(ename));
-                events.put(ename, new PyBeanEvent<>(ename, eventClass, meth));
-                continue;
-            }
+    /**
+     * Consider whether the given method of the current class indicates the existence of a JavaBean
+     * property or event.
+     *
+     * @param props in which to store properties we discover
+     * @param events in which to store events we discover
+     * @param meth under consideration
+     */
+    private void checkBeanMethod(Map<String, PyBeanProperty> props,
+            Map<String, PyBeanEvent<?>> events, Method meth) {
+
+        // If this is a bean method at all, it must be an instance method
+        if (Modifier.isStatic(meth.getModifiers())) {
+            return;
+        }
 
-            // Now check if it's a bean property accessor
-            String beanPropertyName = null;
-            boolean get = true;
-            if (methname.startsWith("get") && methname.length() > 3 && n == 0) {
-                beanPropertyName = methname.substring(3);
-            } else if (methname.startsWith("is") && methname.length() > 2 && n == 0
-                    && meth.getReturnType() == Boolean.TYPE) {
-                beanPropertyName = methname.substring(2);
-            } else if (methname.startsWith("set") && methname.length() > 3 && n == 1) {
-                beanPropertyName = methname.substring(3);
-                get = false;
+        // First check if this is a bean event addition method
+        int n = meth.getParameterTypes().length;
+        String methname = meth.getName();
+        if ((methname.startsWith("add") || methname.startsWith("set"))
+                && methname.endsWith("Listener") && n == 1 && meth.getReturnType() == Void.TYPE
+                && EventListener.class.isAssignableFrom(meth.getParameterTypes()[0])) {
+            // Yes, we have discovered an event type. Save the information for later.
+            Class<?> eventClass = meth.getParameterTypes()[0];
+            String ename = eventClass.getName();
+            int idot = ename.lastIndexOf('.');
+            if (idot != -1) {
+                ename = ename.substring(idot + 1);
             }
-            if (beanPropertyName != null) {
-                beanPropertyName = normalize(StringUtil.decapitalize(beanPropertyName));
-                PyBeanProperty prop = props.get(beanPropertyName);
-                if (prop == null) {
-                    prop = new PyBeanProperty(beanPropertyName, null, null, null);
-                    props.put(beanPropertyName, prop);
-                }
-                if (get) {
-                    prop.getMethod = meth;
-                    prop.myType = meth.getReturnType();
-                } else {
-                    prop.setMethod = meth;
-                    /*
-                     * Needed for readonly properties. Getter will be used instead if there is one.
-                     * Only works if setX method has exactly one param, which is the only reasonable
-                     * case.
-                     */
-                    // XXX: should we issue a warning if setX and getX have different types?
-                    if (prop.myType == null) {
-                        Class<?>[] params = meth.getParameterTypes();
-                        if (params.length == 1) {
-                            prop.myType = params[0];
-                        }
+            ename = normalize(StringUtil.decapitalize(ename));
+            events.put(ename, new PyBeanEvent<>(ename, eventClass, meth));
+            return;
+        }
+
+        // Now check if it's a bean property accessor
+        String beanPropertyName = null;
+        boolean get = true;
+        if (methname.startsWith("get") && methname.length() > 3 && n == 0) {
+            beanPropertyName = methname.substring(3);
+        } else if (methname.startsWith("is") && methname.length() > 2 && n == 0
+                && meth.getReturnType() == Boolean.TYPE) {
+            beanPropertyName = methname.substring(2);
+        } else if (methname.startsWith("set") && methname.length() > 3 && n == 1) {
+            beanPropertyName = methname.substring(3);
+            get = false;
+        }
+
+        if (beanPropertyName != null) {
+            // Ok, its a bean property. Save this information for later.
+            beanPropertyName = normalize(StringUtil.decapitalize(beanPropertyName));
+            PyBeanProperty prop = props.get(beanPropertyName);
+            if (prop == null) {
+                prop = new PyBeanProperty(beanPropertyName, null, null, null);
+                props.put(beanPropertyName, prop);
+            }
+            // getX and setX should be getting and setting the same type of thing.
+            if (get) {
+                prop.getMethod = meth;
+                prop.myType = meth.getReturnType();
+            } else {
+                prop.setMethod = meth;
+                /*
+                 * Needed for readonly properties. Getter will be used instead if there is one. Only
+                 * works if setX method has exactly one param, which is the only reasonable case.
+                 */
+                // XXX: should we issue a warning if setX and getX have different types?
+                if (prop.myType == null) {
+                    Class<?>[] params = meth.getParameterTypes();
+                    if (params.length == 1) {
+                        prop.myType = params[0];
                     }
                 }
             }
         }
+    }
 
-        // Add superclass methods
+    /**
+     * Process the given class for methods inherited from ancestor classes.
+     *
+     * This is exclusively a helper method for {@link #init(Set)}.
+     *
+     * @param reflectedFuncs to which reflected functions are added for further processing
+     * @param methods of the target class
+     */
+    private void addInheritedMethods(List<PyReflectedFunction> reflectedFuncs, Method[] methods) {
+        // Add inherited and previously ignored methods
         for (Method meth : methods) {
             String nmethname = normalize(meth.getName());
-            reflfunc = (PyReflectedFunction) dict.__finditem__(nmethname);
+            PyReflectedFunction reflfunc = (PyReflectedFunction) dict.__finditem__(nmethname);
             if (reflfunc != null) {
                 /*
                  * The superclass method has the same name as one declared on this class, so add the
@@ -521,20 +671,20 @@
                 dict.__setitem__(nmethname, reflfunc);
             }
         }
+    }
 
-        // Add fields declared on this type
-        Field[] fields;
-        if (Options.respectJavaAccessibility) {
-            // returns just the public fields
-            fields = forClass.getFields();
-        } else {
-            fields = forClass.getDeclaredFields();
-            for (Field field : fields) {
-                field.setAccessible(true);
-            }
-        }
+    /**
+     * Process the given fields defined on the target class (the <code>fromClass</code>).
+     *
+     * This is exclusively a helper method for {@link #init(Set)}.
+     *
+     *
+     * @param baseClass
+     * @param fields
+     */
+    private void addFields(Class<?> baseClass, Field[] fields) {
         for (Field field : fields) {
-            if (!declaredOnMember(baseClass, field)) {
+            if (!declaredHere(baseClass, field)) {
                 continue;
             }
             String fldname = field.getName();
@@ -559,7 +709,10 @@
                 dict.__setitem__(normalize(fldname), new PyReflectedField(field));
             }
         }
+    }
 
+    /** Add the methods corresponding to a each discovered JavaBean event. */
+    private void addBeanEvents(Map<String, PyBeanEvent<?>> events) {
         for (PyBeanEvent<?> ev : events.values()) {
             if (dict.__finditem__(ev.__name__) == null) {
                 dict.__setitem__(ev.__name__, ev);
@@ -574,9 +727,13 @@
                         new PyBeanEventProperty(methodName, ev.eventClass, ev.addMethod, meth));
             }
         }
+    }
 
-        // Fill in the bean properties picked up while going through the methods
+    /** Add the methods corresponding to a each discovered JavaBean property. */
+    private void addBeanProperties(Map<String, PyBeanProperty> props) {
         for (PyBeanProperty prop : props.values()) {
+
+            // Check for an existing __dict__ entry with this name
             PyObject prev = dict.__finditem__(prop.__name__);
             if (prev != null) {
                 if (!(prev instanceof PyReflectedField)
@@ -590,11 +747,12 @@
             }
 
             /*
-             * If one of our superclasses has something defined for this name, check if its a bean
+             * If one of our super-classes has something defined for this name, check if it's a bean
              * property, and if so, try to fill in any gaps in our property from there.
              */
             PyObject fromType[] = new PyObject[] {null};
             PyObject superForName = lookup_where_mro(prop.__name__, fromType);
+
             if (superForName instanceof PyBeanProperty) {
                 PyBeanProperty superProp = ((PyBeanProperty) superForName);
                 /*
@@ -618,6 +776,7 @@
                     // If the parent bean is hiding a static field, we need it as well.
                     prop.field = superProp.field;
                 }
+
             } else if (superForName != null && fromType[0] != this
                     && !(superForName instanceof PyBeanEvent)) {
                 /*
@@ -627,6 +786,7 @@
                  */
                 continue;
             }
+
             /*
              * If the return types on the set and get methods for a property don't agree, the get
              * method takes precedence.
@@ -637,25 +797,23 @@
             }
             dict.__setitem__(prop.__name__, prop);
         }
+    }
+
+    /**
+     * Process the given constructors defined on the target class (the <code>fromClass</code>).
+     *
+     * This is exclusively a helper method for {@link #init(Set)}.
+     *
+     * @param forClass
+     * @param constructors
+     */
+    private void addConstructors(Class<?> forClass, Constructor<?>[] constructors) {
 
         final PyReflectedConstructor reflctr = new PyReflectedConstructor(name);
-        Constructor<?>[] constructors;
-        /*
-         * No matter the security manager, trying to set the constructor on class to accessible
-         * blows up.
-         */
-        if (Options.respectJavaAccessibility || Class.class == forClass) {
-            // returns just the public constructors
-            constructors = forClass.getConstructors();
-        } else {
-            constructors = forClass.getDeclaredConstructors();
-            for (Constructor<?> ctr : constructors) {
-                ctr.setAccessible(true);
-            }
-        }
         for (Constructor<?> ctr : constructors) {
             reflctr.addConstructor(ctr);
         }
+
         if (PyObject.class.isAssignableFrom(forClass)) {
             PyObject new_ = new PyNewWrapper(forClass, "__new__", -1, -1) {
 
@@ -669,13 +827,23 @@
         } else {
             dict.__setitem__("__init__", reflctr);
         }
+    }
+
+    /**
+     * Add Python methods corresponding to the API of common Java collection types. This is
+     * exclusively a helper method for {@link #init(Set)}.
+     *
+     * @param forClass the target class
+     */
+    private void addCollectionProxies(Class<?> forClass) {
         PyBuiltinMethod[] collectionProxyMethods = getCollectionProxies().get(forClass);
         if (collectionProxyMethods != null) {
             for (PyBuiltinMethod meth : collectionProxyMethods) {
                 addMethod(meth);
             }
         }
-        // allow for some methods to override the Java type's methods as a late injection
+
+        // Allow for some methods to override the Java type's methods as a late injection
         for (Class<?> type : getPostCollectionProxies().keySet()) {
             if (type.isAssignableFrom(forClass)) {
                 for (PyBuiltinMethod meth : getPostCollectionProxies().get(type)) {
@@ -683,191 +851,147 @@
                 }
             }
         }
+    }
 
-        PyObject nameSpecified = null;
-        if (ClassDictInit.class.isAssignableFrom(forClass) && forClass != ClassDictInit.class) {
-            try {
-                Method m = forClass.getMethod("classDictInit", PyObject.class);
-                m.invoke(null, dict);
-                // allow the class to override its name after it is loaded
-                nameSpecified = dict.__finditem__("__name__");
-                if (nameSpecified != null) {
-                    name = nameSpecified.toString();
-                }
-            } catch (Exception exc) {
-                throw Py.JavaError(exc);
+    /** Add special methods when this PyJavaType represents <code>Object</code>. */
+    private void addMethodsForObject() {
+        addMethod(new PyBuiltinMethodNarrow("__copy__") {
+
+            @Override
+            public PyObject __call__() {
+                throw Py.TypeError(
+                        "Could not copy Java object because it is not Cloneable or known to be immutable. "
+                                + "Consider monkeypatching __copy__ for "
+                                + self.getType().fastGetName());
             }
-        }
+        });
+        addMethod(new PyBuiltinMethodNarrow("__deepcopy__") {
+
+            @Override
+            public PyObject __call__(PyObject memo) {
+                throw Py.TypeError("Could not deepcopy Java object because it is not Serializable. "
+                        + "Consider monkeypatching __deepcopy__ for "
+                        + self.getType().fastGetName());
+            }
+        });
+        addMethod(new PyBuiltinMethodNarrow("__eq__", 1) {
 
-        // Fill __module__ attribute of PyReflectedFunctions...
-        if (reflectedFuncs.size() > 0) {
-            if (nameSpecified == null) {
-                nameSpecified = Py.newString(name);
+            @Override
+            public PyObject __call__(PyObject o) {
+                Object proxy = self.getJavaProxy();
+                Object oProxy = o.getJavaProxy();
+                return proxy.equals(oProxy) ? Py.True : Py.False;
             }
-            for (PyReflectedFunction func : reflectedFuncs) {
-                func.__module__ = nameSpecified;
-            }
-        }
+        });
+        addMethod(new PyBuiltinMethodNarrow("__ne__", 1) {
 
-        if (baseClass != Object.class) {
-            hasGet = getDescrMethod(forClass, "__get__", OO) != null
-                    || getDescrMethod(forClass, "_doget", PyObject.class) != null
-                    || getDescrMethod(forClass, "_doget", OO) != null;
-            hasSet = getDescrMethod(forClass, "__set__", OO) != null
-                    || getDescrMethod(forClass, "_doset", OO) != null;
-            hasDelete = getDescrMethod(forClass, "__delete__", PyObject.class) != null
-                    || getDescrMethod(forClass, "_dodel", PyObject.class) != null;
-        }
+            @Override
+            public PyObject __call__(PyObject o) {
+                Object proxy = self.getJavaProxy();
+                Object oProxy = o.getJavaProxy();
+                return !proxy.equals(oProxy) ? Py.True : Py.False;
+            }
+        });
+        addMethod(new PyBuiltinMethodNarrow("__hash__") {
 
-        if (forClass == Object.class) {
-            addMethod(new PyBuiltinMethodNarrow("__copy__") {
+            @Override
+            public PyObject __call__() {
+                return Py.newInteger(self.getJavaProxy().hashCode());
+            }
+        });
+        addMethod(new PyBuiltinMethodNarrow("__repr__") {
 
-                @Override
-                public PyObject __call__() {
-                    throw Py.TypeError(
-                            "Could not copy Java object because it is not Cloneable or known to be immutable. "
-                                    + "Consider monkeypatching __copy__ for "
-                                    + self.getType().fastGetName());
-                }
-            });
-            addMethod(new PyBuiltinMethodNarrow("__deepcopy__") {
+            @Override
+            public PyObject __call__() {
+                /*
+                 * java.lang.Object.toString returns Unicode: preserve as a PyUnicode, then let the
+                 * repr() built-in decide how to handle it. (Also applies to __str__.)
+                 */
+                String toString = self.getJavaProxy().toString();
+                return toString == null ? Py.EmptyUnicode : Py.newUnicode(toString);
+            }
+        });
+        addMethod(new PyBuiltinMethodNarrow("__unicode__") {
 
-                @Override
-                public PyObject __call__(PyObject memo) {
-                    throw Py.TypeError(
-                            "Could not deepcopy Java object because it is not Serializable. "
-                                    + "Consider monkeypatching __deepcopy__ for "
-                                    + self.getType().fastGetName());
-                }
-            });
-            addMethod(new PyBuiltinMethodNarrow("__eq__", 1) {
+            @Override
+            public PyObject __call__() {
+                return new PyUnicode(self.toString());
+            }
+        });
+    }
+
+    /** Add special methods when this PyJavaType represents interface <code>Comparable</code>. */
+    private void addMethodsForComparable() {
+        addMethod(new ComparableMethod("__lt__", 1) {
+
+            @Override
+            protected boolean getResult(int comparison) {
+                return comparison < 0;
+            }
+        });
+        addMethod(new ComparableMethod("__le__", 1) {
 
-                @Override
-                public PyObject __call__(PyObject o) {
-                    Object proxy = self.getJavaProxy();
-                    Object oProxy = o.getJavaProxy();
-                    return proxy.equals(oProxy) ? Py.True : Py.False;
-                }
-            });
-            addMethod(new PyBuiltinMethodNarrow("__ne__", 1) {
+            @Override
+            protected boolean getResult(int comparison) {
+                return comparison <= 0;
+            }
+        });
+        addMethod(new ComparableMethod("__gt__", 1) {
+
+            @Override
+            protected boolean getResult(int comparison) {
+                return comparison > 0;
+            }
+        });
+        addMethod(new ComparableMethod("__ge__", 1) {
+
+            @Override
+            protected boolean getResult(int comparison) {
+                return comparison >= 0;
+            }
+        });
+    }
 
-                @Override
-                public PyObject __call__(PyObject o) {
-                    Object proxy = self.getJavaProxy();
-                    Object oProxy = o.getJavaProxy();
-                    return !proxy.equals(oProxy) ? Py.True : Py.False;
-                }
-            });
-            addMethod(new PyBuiltinMethodNarrow("__hash__") {
+    /** Add special methods when this PyJavaType represents interface <code>Cloneable</code>. */
+    private void addMethodsForCloneable() {
+        addMethod(new PyBuiltinMethodNarrow("__copy__") {
 
-                @Override
-                public PyObject __call__() {
-                    return Py.newInteger(self.getJavaProxy().hashCode());
-                }
-            });
-            addMethod(new PyBuiltinMethodNarrow("__repr__") {
-
-                @Override
-                public PyObject __call__() {
-                    /*
-                     * java.lang.Object.toString returns Unicode: preserve as a PyUnicode, then let
-                     * the repr() built-in decide how to handle it. (Also applies to __str__.)
-                     */
-                    String toString = self.getJavaProxy().toString();
-                    return toString == null ? Py.EmptyUnicode : Py.newUnicode(toString);
+            @Override
+            public PyObject __call__() {
+                Object obj = self.getJavaProxy();
+                Method clone;
+                /*
+                 * TODO we could specialize so that for well known objects like collections. This
+                 * would avoid needing to use reflection in the general case, because Object#clone
+                 * is protected (but most subclasses are not). Lastly we can potentially cache the
+                 * method handle in the proxy instead of looking it up each time
+                 */
+                try {
+                    clone = obj.getClass().getMethod("clone");
+                    Object copy = clone.invoke(obj);
+                    return Py.java2py(copy);
+                } catch (Exception ex) {
+                    throw Py.TypeError("Could not copy Java object");
                 }
-            });
-            addMethod(new PyBuiltinMethodNarrow("__unicode__") {
-
-                @Override
-                public PyObject __call__() {
-                    return new PyUnicode(self.toString());
-                }
-            });
-        }
-
-        if (forClass == Comparable.class) {
-            addMethod(new ComparableMethod("__lt__", 1) {
-
-                @Override
-                protected boolean getResult(int comparison) {
-                    return comparison < 0;
-                }
-            });
-            addMethod(new ComparableMethod("__le__", 1) {
+            }
+        });
+    }
 
-                @Override
-                protected boolean getResult(int comparison) {
-                    return comparison <= 0;
-                }
-            });
-            addMethod(new ComparableMethod("__gt__", 1) {
-
-                @Override
-                protected boolean getResult(int comparison) {
-                    return comparison > 0;
-                }
-            });
-            addMethod(new ComparableMethod("__ge__", 1) {
-
-                @Override
-                protected boolean getResult(int comparison) {
-                    return comparison >= 0;
-                }
-            });
-        }
-
-        if (immutableClasses.contains(forClass)) {
-            // __deepcopy__ just works for these objects since it uses serialization instead
-            addMethod(new PyBuiltinMethodNarrow("__copy__") {
+    /** Add special methods when this PyJavaType represents interface <code>Serializable</code>. */
+    private void addMethodsForSerializable() {
+        addMethod(new PyBuiltinMethodNarrow("__deepcopy__") {
 
-                @Override
-                public PyObject __call__() {
-                    return self;
+            @Override
+            public PyObject __call__(PyObject memo) {
+                Object obj = self.getJavaProxy();
+                try {
+                    Object copy = cloneX(obj);
+                    return Py.java2py(copy);
+                } catch (Exception ex) {
+                    throw Py.TypeError("Could not copy Java object");
                 }
-            });
-        }
-
-        if (forClass == Cloneable.class) {
-            addMethod(new PyBuiltinMethodNarrow("__copy__") {
-
-                @Override
-                public PyObject __call__() {
-                    Object obj = self.getJavaProxy();
-                    Method clone;
-                    /*
-                     * TODO we could specialize so that for well known objects like collections.
-                     * This would avoid needing to use reflection in the general case, because
-                     * Object#clone is protected (but most subclasses are not). Lastly we can
-                     * potentially cache the method handle in the proxy instead of looking it up
-                     * each time
-                     */
-                    try {
-                        clone = obj.getClass().getMethod("clone");
-                        Object copy = clone.invoke(obj);
-                        return Py.java2py(copy);
-                    } catch (Exception ex) {
-                        throw Py.TypeError("Could not copy Java object");
-                    }
-                }
-            });
-        }
-
-        if (forClass == Serializable.class) {
-            addMethod(new PyBuiltinMethodNarrow("__deepcopy__") {
-
-                @Override
-                public PyObject __call__(PyObject memo) {
-                    Object obj = self.getJavaProxy();
-                    try {
-                        Object copy = cloneX(obj);
-                        return Py.java2py(copy);
-                    } catch (Exception ex) {
-                        throw Py.TypeError("Could not copy Java object");
-                    }
-                }
-            });
-        }
+            }
+        });
     }
 
     /*
@@ -969,6 +1093,14 @@
         }
     }
 
+    /**
+     * From a given class that is an ancestor of the Java class for which this PyJavaType is being
+     * initialised, or that is an interface implemented by it or an ancestor, process each method in
+     * the ancestor so that, if the class or interface that declares the method is public, that
+     * method becomes a method of the Python type we are constructing.
+     *
+     * @param parent class or interface
+     */
     private void mergeMethods(Class<?> parent) {
         for (Method meth : parent.getMethods()) {
             if (!Modifier.isPublic(meth.getDeclaringClass().getModifiers())) {
@@ -1004,9 +1136,24 @@
         }
     }
 
-    private static boolean declaredOnMember(Class<?> base, Member declaring) {
-        return base == null || (declaring.getDeclaringClass() != base
-                && base.isAssignableFrom(declaring.getDeclaringClass()));
+    /**
+     * True iff the target of this <code>PyJavaType</code>'s target (the <code>forClass</code>)
+     * declares the given member of this target. For this, the method is supplied the super-class of
+     * the target.
+     *
+     * @param baseClass super-class of the target of this <code>PyJavaType</code>, or
+     *            <code>null</code>.
+     * @param member of the <code>forClass</code> that might be exposed on this
+     *            <code>PyJavaType</code>
+     * @return true if the member is declared here on the <code>fromClass</code>
+     */
+    private static boolean declaredHere(Class<?> baseClass, Member member) {
+        if (baseClass == null) {
+            return true;
+        } else {
+            Class<?> declaring = member.getDeclaringClass();
+            return declaring != baseClass && baseClass.isAssignableFrom(declaring);
+        }
     }
 
     private static String normalize(String name) {
@@ -1029,6 +1176,7 @@
         return null;
     }
 
+    /** Recognise certain methods as ignored (tagged as <code>throws PyIgnoreMethodTag</code>). */
     private static boolean ignore(Method meth) {
         Class<?>[] exceptions = meth.getExceptionTypes();
         for (Class<?> exception : exceptions) {

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list