[Jython-checkins] jython: Improved usability of Jython from Java-side by adding some utility-methods to

stefan.richthofer jython-checkins at python.org
Fri Sep 4 18:32:54 CEST 2015


https://hg.python.org/jython/rev/9e204456051d
changeset:   7722:9e204456051d
user:        Stefan Richthofer <stefan.richthofer at gmx.de>
date:        Fri Sep 04 18:32:25 2015 +0200
summary:
  Improved usability of Jython from Java-side by adding some utility-methods to org.python.core.Py. These simplify the use of Python-constructors from Java-side.

files:
  src/org/python/core/Py.java |  202 ++++++++++++++++++++++++
  1 files changed, 202 insertions(+), 0 deletions(-)


diff --git a/src/org/python/core/Py.java b/src/org/python/core/Py.java
--- a/src/org/python/core/Py.java
+++ b/src/org/python/core/Py.java
@@ -22,7 +22,9 @@
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import com.google.common.base.CharMatcher;
@@ -2452,6 +2454,206 @@
         }
         return jarFileName;
     }
+
+//------------------------contructor-section---------------------------
+    static class py2JyClassCacheItem {
+        List<Class<?>> interfaces;
+        List<PyObject> pyClasses;
+
+        public py2JyClassCacheItem(Class<?> initClass, PyObject initPyClass) {
+            if (!initClass.isInterface()) throw
+                new IllegalArgumentException("cls must be an interface.");
+            interfaces = new ArrayList<>(1);
+            pyClasses = new ArrayList<>(1);
+            interfaces.add(initClass);
+            pyClasses.add(initPyClass);
+        }
+
+        public PyObject get(Class<?> cls) {
+            for (int i = 0; i < interfaces.size(); ++i) {
+                if (cls.isAssignableFrom(interfaces.get(i)))
+                    return pyClasses.get(i);
+            }
+            return null;
+        }
+
+        public void add(Class<?> cls, PyObject pyCls) {
+            if (!cls.isInterface()) throw
+                new IllegalArgumentException("cls must be an interface.");
+            interfaces.add(0, cls);
+            for (int i = interfaces.size()-1; i > 0; --i) {
+                if (interfaces.get(i).isAssignableFrom(cls))
+                    interfaces.remove(i);
+            }
+            pyClasses.add(pyCls);
+        }
+    }
+
+    protected static Map<PyObject, py2JyClassCacheItem> py2JyClassCache = new HashMap<>();
+
+    protected static PyObject ensureInterface(PyObject cls, Class<?> interfce) {
+        PyObject pjc = PyType.fromClass(interfce);
+        if (Py.isSubClass(cls, pjc)) {
+            return cls;
+        }
+        PyObject[] bases = {cls, pjc};
+        return Py.makeClass(interfce.getName(), bases, new PyStringMap());
+    }
+
+    /**
+     * Returns a Python-class that extends {@code cls} and {@code interfce}.
+     * If {@code cls} already extends {@code interfce}, simply {@code cls}
+     * is returned. Otherwise a new class is created (if not yet cached).
+     * It caches such classes and only creates a new one if no appropriate
+     * class was cached yet.
+     *
+     * @return a Python-class that extends {@code cls} and {@code interfce}
+     */
+    public static PyObject javaPyClass(PyObject cls, Class<?> interfce) {
+        py2JyClassCacheItem cacheItem = py2JyClassCache.get(cls);
+        PyObject result;
+        if (cacheItem == null) {
+            result = ensureInterface(cls, interfce);
+            cacheItem = new py2JyClassCacheItem(interfce, result);
+            py2JyClassCache.put(cls, cacheItem);
+        } else {
+            result = cacheItem.get(interfce);
+            if (result == null) {
+                result = ensureInterface(cls, interfce);
+                cacheItem.add(interfce, result);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * This method is a compact helper to access Python-constructors from Java.
+     * It creates an instance of {@code cls} and retruns it in form of
+     * {@code jcls}, which must be an interface. This method even works if
+     * {@code cls} does not extend {@code jcls} in Python-code. In that case,
+     * it uses {@link #javaPyClass(PyObject, Class)} to create an appropriate
+     * class on the fly.<br>
+     * It automatically converts {@code args} to {@link org.python.core.PyObject}s.<br>
+     * For keyword-support use
+     * {@link #newJavaObject(PyObject, Class, String[], Object...)}.
+     * 
+     * {@see #newJavaObject(PyObject, Class, PyObject[], String[])}
+     * {@see #newJavaObject(PyObject, Class, String[], Object...)}
+     * {@see #newJavaObject(PyModule, Class, Object...)}
+     * {@see #newJavaObject(PyModule, Class, String[], Object...)}
+     *
+     * @param cls - the class to be instanciated
+     * @param jcls - the Java-type to be returned
+     * @param args are automatically converted to Jython-PyObjects
+     * @return an instance of cls in form of the interface jcls
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T newJavaObject(PyObject cls, Class<T> jcls, Object... args) {
+        PyObject cls2 = javaPyClass(cls, jcls);
+        PyObject resultPy = cls2.__call__(Py.javas2pys(args));
+        return (T) resultPy.__tojava__(jcls);
+    }
+
+    /**
+     * This method is a compact helper to access Python-constructors from Java.
+     * It creates an instance of {@code cls} and retruns it in form of
+     * {@code jcls}, which must be an interface. This method even works if
+     * {@code cls} does not extend {@code jcls} in Python-code. In that case,
+     * it uses {@link #javaPyClass(PyObject, Class)} to create an appropriate
+     * class on the fly.<br>
+     * {@code keywordss} are applied to the last {@code args} in the list.
+     *
+     * {@see #newJavaObject(PyObject, Class, Object...)}
+     * {@see #newJavaObject(PyObject, Class, String[], Object...)}
+     * {@see #newJavaObject(PyModule, Class, Object...)}
+     * {@see #newJavaObject(PyModule, Class, String[], Object...)}
+     *
+     * @param cls - the class to be instanciated
+     * @param jcls - the Java-type to be returned
+     * @param keywords are applied to the last args
+     * @param args for the Python-class constructor
+     * @return an instance of cls in form of the interface jcls
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T newJavaObject(PyObject cls, Class<T> jcls, PyObject[] args, String[] keywords) {
+        PyObject cls2 = javaPyClass(cls, jcls);
+        PyObject resultPy = cls2.__call__(args, keywords);
+        return (T) resultPy.__tojava__(jcls);
+    }
+
+    /**
+     * This method is a compact helper to access Python-constructors from Java.
+     * It creates an instance of {@code cls} and retruns it in form of
+     * {@code jcls}, which must be an interface. This method even works if
+     * {@code cls} does not extend {@code jcls} in Python-code. In that case,
+     * it uses {@link #javaPyClass(PyObject, Class)} to create an appropriate
+     * class on the fly.<br>
+     * It automatically converts {@code args} to {@link org.python.core.PyObject}s.<br>
+     * {@code keywordss} are applied to the last {@code args} in the list.
+     *
+     * {@see #newJavaObject(PyObject, Class, PyObject[], String[])}
+     * {@see #newJavaObject(PyObject, Class, Object...)}
+     * {@see #newJavaObject(PyModule, Class, Object...)}
+     * {@see #newJavaObject(PyModule, Class, String[], Object...)}
+     *
+     * @param cls - the class to be instanciated
+     * @param jcls - the Java-type to be returned
+     * @param keywords are applied to the last args
+     * @param args are automatically converted to Jython-PyObjects
+     * @return an instance of cls in form of the interface jcls
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T newJavaObject(PyObject cls, Class<T> jcls, String[] keywords, Object... args) {
+        PyObject cls2 = javaPyClass(cls, jcls);
+        PyObject resultPy = cls2.__call__(Py.javas2pys(args), keywords);
+        return (T) resultPy.__tojava__(jcls);
+    }
+
+    /**
+     * Works like {@link #newJavaObject(PyObject, Class, Object...)}, but looks
+     * up the Python-class in the module-dict using the interface-name, i.e.
+     * {@code jcls.getSimpleName()}.<br>
+     * For keywords-support use {@link #newJavaObject(PyModule, Class, String[], Object...)}.
+     *
+     * {@see #newJavaObject(PyModule, Class, String[], Object...)}
+     * {@see #newJavaObject(PyObject, Class, PyObject[], String[])}
+     * {@see #newJavaObject(PyObject, Class, Object...)}
+     * {@see #newJavaObject(PyObject, Class, String[], Object...)}
+     *
+     * @param module the module containing the desired class
+     * @param jcls Java-type of the desired clas, must have the same name
+     * @param args constructor-arguments
+     * @return a new instance of the desired class
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T newJavaObject(PyModule module, Class<T> jcls, Object... args) {
+        PyObject cls = module.__getattr__(jcls.getSimpleName().intern());
+        return newJavaObject(cls, jcls, args);
+    }
+
+    /**
+     * Works like {@link #newJavaObject(PyObject, Class, String[], Object...)}, but looks
+     * up the Python-class in the module-dict using the interface-name, i.e.
+     * {@code jcls.getSimpleName()}.<br>
+     * {@code keywordss} are applied to the last {@code args} in the list.
+     *
+     * {@see #newJavaObject(PyModule, Class, Object...)}
+     * {@see #newJavaObject(PyObject, Class, PyObject[], String[])}
+     * {@see #newJavaObject(PyObject, Class, Object...)}
+     * {@see #newJavaObject(PyObject, Class, String[], Object...)}
+     *
+     * @param module the module containing the desired class
+     * @param jcls Java-type of the desired class, must have the same name
+     * @param keywords are applied to the last {@code args} in the list
+     * @param args constructor-arguments
+     * @return a new instance of the desired class
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T newJavaObject(PyModule module, Class<T> jcls, String[] keywords, Object... args) {
+        PyObject cls = module.__getattr__(jcls.getSimpleName().intern());
+        return newJavaObject(cls, jcls, keywords, args);
+    }
+//----------------end of constructor-section------------------
 }
 
 class FixedFileWrapper extends StdoutWrapper {

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


More information about the Jython-checkins mailing list