From: <cg...@us...> - 2007-05-06 06:09:28
|
Revision: 3209 http://svn.sourceforge.net/jython/?rev=3209&view=rev Author: cgroves Date: 2007-05-05 23:09:25 -0700 (Sat, 05 May 2007) Log Message: ----------- added PyObjectAdapters to allow Py.py2java to be extended with custom object wrappers Modified Paths: -------------- trunk/jython/src/org/python/core/Py.java trunk/jython/src/org/python/core/PySystemState.java Added Paths: ----------- trunk/jython/src/org/python/core/adapter/ trunk/jython/src/org/python/core/adapter/ClassAdapter.java trunk/jython/src/org/python/core/adapter/ClassicPyObjectAdapter.java trunk/jython/src/org/python/core/adapter/ExtensiblePyObjectAdapter.java trunk/jython/src/org/python/core/adapter/PyObjectAdapter.java Modified: trunk/jython/src/org/python/core/Py.java =================================================================== --- trunk/jython/src/org/python/core/Py.java 2007-05-05 23:33:26 UTC (rev 3208) +++ trunk/jython/src/org/python/core/Py.java 2007-05-06 06:09:25 UTC (rev 3209) @@ -14,6 +14,8 @@ import java.lang.reflect.InvocationTargetException; import org.python.compiler.Module; +import org.python.core.adapter.ClassicPyObjectAdapter; +import org.python.core.adapter.ExtensiblePyObjectAdapter; import org.python.parser.ast.modType; public final class Py @@ -1480,49 +1482,27 @@ } return letters[c]; } - - // Needs rewriting for efficiency and extensibility - public static PyObject java2py(Object o) { - if (o instanceof PyObject) - return (PyObject)o; - - if (o instanceof PyProxy) - return ((PyProxy)o)._getPyInstance(); - - if (o instanceof Number) { - if (o instanceof Double || o instanceof Float) { - return new PyFloat(((Number)o).doubleValue()); - } - else if (o instanceof Long) { - return new PyLong(((Number)o).longValue()); - } - else if (o instanceof Integer || - o instanceof Byte || - o instanceof Short) - { - return new PyInteger(((Number)o).intValue()); - } - } - if (o instanceof Boolean) { - return ((Boolean)o).booleanValue() ? Py.One : Py.Zero; - } - if (o == null) return Py.None; - if (o instanceof String) return new PyString((String)o); - if (o instanceof Character) return makeCharacter((Character)o); - if (o instanceof Class) { - Class cls = (Class)o; - if (PyObject.class.isAssignableFrom(cls)) { - return PyType.fromClass(cls); - } - return PyJavaClass.lookup(cls); - } - - Class c = o.getClass(); - if (c.isArray()) { - return new PyArray(c.getComponentType(), o); - } - return new PyJavaInstance(o); + + /** + * Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn o into a PyObject. + * + * @see ClassicPyObjectAdapter - default PyObjectAdapter type + */ + public static PyObject java2py(Object o) { + return adapter.adapt(o); + } + + /** + * @return the ExtensiblePyObjectAdapter used by java2py. + */ + public static ExtensiblePyObjectAdapter getAdapter(){ + return adapter; } + + /** + * Handles wrapping Java objects in PyObject to expose them to jython. + */ + protected static ExtensiblePyObjectAdapter adapter; public static PyObject makeClass(String name, PyObject[] bases, PyCode code, PyObject doc) Modified: trunk/jython/src/org/python/core/PySystemState.java =================================================================== --- trunk/jython/src/org/python/core/PySystemState.java 2007-05-05 23:33:26 UTC (rev 3208) +++ trunk/jython/src/org/python/core/PySystemState.java 2007-05-06 06:09:25 UTC (rev 3209) @@ -19,6 +19,8 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; +import org.python.core.adapter.ClassicPyObjectAdapter; +import org.python.core.adapter.ExtensiblePyObjectAdapter; import org.python.modules.Setup; /** @@ -433,6 +435,14 @@ String[] argv, ClassLoader classLoader) { + initialize(preProperties, postProperties, argv, classLoader, new ClassicPyObjectAdapter()); + } + public static synchronized void initialize(Properties preProperties, + Properties postProperties, + String[] argv, + ClassLoader classLoader, + ExtensiblePyObjectAdapter adapter) + { //System.err.println("initializing system state"); //Thread.currentThread().dumpStack(); @@ -447,6 +457,7 @@ } initialized = true; + Py.adapter = adapter; boolean standalone = false; String jarFileName = getJarFileName(); if (jarFileName != null) { Added: trunk/jython/src/org/python/core/adapter/ClassAdapter.java =================================================================== --- trunk/jython/src/org/python/core/adapter/ClassAdapter.java (rev 0) +++ trunk/jython/src/org/python/core/adapter/ClassAdapter.java 2007-05-06 06:09:25 UTC (rev 3209) @@ -0,0 +1,19 @@ +package org.python.core.adapter; + +public abstract class ClassAdapter implements PyObjectAdapter { + + public ClassAdapter(Class adaptedClass) { + this.adaptedClass = adaptedClass; + } + + public Class getAdaptedClass() { + return adaptedClass; + } + + public boolean canAdapt(Object o) { + return adaptedClass.getClass().equals(adaptedClass); + } + + private Class adaptedClass; + +} Property changes on: trunk/jython/src/org/python/core/adapter/ClassAdapter.java ___________________________________________________________________ Name: svn:executable + * Added: trunk/jython/src/org/python/core/adapter/ClassicPyObjectAdapter.java =================================================================== --- trunk/jython/src/org/python/core/adapter/ClassicPyObjectAdapter.java (rev 0) +++ trunk/jython/src/org/python/core/adapter/ClassicPyObjectAdapter.java 2007-05-06 06:09:25 UTC (rev 3209) @@ -0,0 +1,154 @@ +package org.python.core.adapter; + +import org.python.core.Py; +import org.python.core.PyArray; +import org.python.core.PyFloat; +import org.python.core.PyInteger; +import org.python.core.PyJavaClass; +import org.python.core.PyJavaInstance; +import org.python.core.PyLong; +import org.python.core.PyObject; +import org.python.core.PyProxy; +import org.python.core.PyString; +import org.python.core.PyType; + +/** + * Implements the algorithm originally used in {@link Py#java2py} to adapt objects. + * + * Pre-class adapters are added to handle instances of PyObject, PyProxy and + * null values. Class adapters are added to handle builtin Java classes: String, + * Integer, Float, Double, Byte, Long, Short, Character, Class and Boolean. An + * adapter is added to the post-class adapters to handle wrapping arrays + * properly. Finally, if all of the added adapters can handle an object, it's + * wrapped in a PyJavaInstance. + * + */ +public class ClassicPyObjectAdapter extends ExtensiblePyObjectAdapter { + + public ClassicPyObjectAdapter() { + addPreClass(new PyObjectAdapter() { + + public PyObject adapt(Object o) { + return (PyObject) o; + } + + public boolean canAdapt(Object o) { + return o instanceof PyObject; + } + }); + addPreClass(new PyObjectAdapter() { + + public PyObject adapt(Object o) { + return ((PyProxy) o)._getPyInstance(); + } + + public boolean canAdapt(Object o) { + return o instanceof PyProxy; + } + }); + addPreClass(new PyObjectAdapter() { + + public boolean canAdapt(Object o) { + return o == null; + } + + public PyObject adapt(Object o) { + return Py.None; + } + }); + + add(new ClassAdapter(String.class) { + + public PyObject adapt(Object o) { + return new PyString((String) o); + } + + }); + add(new ClassAdapter(Character.class) { + + public PyObject adapt(Object o) { + return Py.makeCharacter((Character) o); + } + + }); + add(new ClassAdapter(Class.class) { + + public PyObject adapt(Object o) { + Class cls = (Class) o; + if (PyObject.class.isAssignableFrom(cls)) { + return PyType.fromClass(cls); + } + return PyJavaClass.lookup(cls); + } + + }); + add(new NumberToPyFloat(Double.class)); + add(new NumberToPyFloat(Float.class)); + add(new NumberToPyInteger(Integer.class)); + add(new NumberToPyInteger(Byte.class)); + add(new NumberToPyInteger(Short.class)); + add(new ClassAdapter(Long.class) { + + public PyObject adapt(Object o) { + return new PyLong(((Number) o).longValue()); + } + + }); + add(new ClassAdapter(Boolean.class) { + + public PyObject adapt(Object o) { + return ((Boolean) o).booleanValue() ? Py.One : Py.Zero; + } + + }); + addPostClass(new PyObjectAdapter() { + + public PyObject adapt(Object o) { + return new PyArray(o.getClass().getComponentType(), o); + } + + public boolean canAdapt(Object o) { + return o.getClass().isArray(); + } + }); + } + + /** + * Always returns true as we just return new PyJavaInstance(o) if the + * adapters added to the superclass can't handle o. + */ + public boolean canAdapt(Object o) { + return true; + } + + public PyObject adapt(Object o) { + PyObject result = super.adapt(o); + if (result != null) { + return result; + } + return new PyJavaInstance(o); + } + + private static class NumberToPyInteger extends ClassAdapter { + + public NumberToPyInteger(Class c) { + super(c); + } + + public PyObject adapt(Object o) { + return new PyInteger(((Number) o).intValue()); + } + + } + + private static class NumberToPyFloat extends ClassAdapter { + public NumberToPyFloat(Class c) { + super(c); + } + + public PyObject adapt(Object o) { + return new PyFloat(((Number) o).doubleValue()); + } + + } +} Property changes on: trunk/jython/src/org/python/core/adapter/ClassicPyObjectAdapter.java ___________________________________________________________________ Name: svn:executable + * Added: trunk/jython/src/org/python/core/adapter/ExtensiblePyObjectAdapter.java =================================================================== --- trunk/jython/src/org/python/core/adapter/ExtensiblePyObjectAdapter.java (rev 0) +++ trunk/jython/src/org/python/core/adapter/ExtensiblePyObjectAdapter.java 2007-05-06 06:09:25 UTC (rev 3209) @@ -0,0 +1,94 @@ +package org.python.core.adapter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.python.core.PyObject; + +/** + * A PyObjectAdapter attempts to adapt a Java Object with three user fillable + * groups of adapters: preClass, class and postClass. + * + */ +public class ExtensiblePyObjectAdapter implements PyObjectAdapter { + + /** + * @return true if a preClass, postClass or class adapter can handle this + */ + public boolean canAdapt(Object o) { + return findAdapter(preClassAdapters, o) != null || classAdapters.containsKey(o.getClass()) + || findAdapter(postClassAdapters, o) != null; + } + + /** + * Attempts to adapt o using the preClass, class and postClass adapters. + * + * First each of the preClass adapters is asked in the order of addition if + * they can adapt o. If so, they adapt it. Otherwise, if o.getClass() is + * equal to one of the classes from the added ClassAdapters, that class + * adapter is used. Finally, each of the post class adapters are asked in + * turn if they can adapt o. If so, that adapter handles it. If none can, + * null is returned. + */ + public PyObject adapt(Object o) { + PyObjectAdapter adapter = findAdapter(preClassAdapters, o); + if (adapter != null) { + return adapter.adapt(o); + } + + adapter = (PyObjectAdapter) classAdapters.get(o.getClass()); + if (adapter != null) { + return adapter.adapt(o); + } + + adapter = findAdapter(postClassAdapters, o); + if (adapter != null) { + return adapter.adapt(o); + } + return null; + } + + /** + * Adds an adapter to the list of adapters to be tried before the + * ClassAdapters. + */ + public void addPreClass(PyObjectAdapter adapter) { + preClassAdapters.add(adapter); + } + + /** + * Adds a Class handling adapter that will adapt any objects of its Class if + * that object hasn't already been handled by one of the pre class adapters. + */ + public void add(ClassAdapter adapter) { + classAdapters.put(adapter.getAdaptedClass(), adapter); + } + + /** + * Adds an adapter to the list of adapters to be tried after the + * ClassAdapters. + */ + public void addPostClass(PyObjectAdapter converter) { + postClassAdapters.add(converter); + } + + private static PyObjectAdapter findAdapter(List l, Object o) { + for (Iterator iter = l.iterator(); iter.hasNext();) { + PyObjectAdapter adapter = (PyObjectAdapter) iter.next(); + if (adapter.canAdapt(o)) { + return adapter; + } + } + return null; + } + + private List preClassAdapters = new ArrayList(); + + private List postClassAdapters = new ArrayList(); + + private Map classAdapters = new HashMap(); + +} Property changes on: trunk/jython/src/org/python/core/adapter/ExtensiblePyObjectAdapter.java ___________________________________________________________________ Name: svn:executable + * Added: trunk/jython/src/org/python/core/adapter/PyObjectAdapter.java =================================================================== --- trunk/jython/src/org/python/core/adapter/PyObjectAdapter.java (rev 0) +++ trunk/jython/src/org/python/core/adapter/PyObjectAdapter.java 2007-05-06 06:09:25 UTC (rev 3209) @@ -0,0 +1,20 @@ +package org.python.core.adapter; + +import org.python.core.PyObject; + +/** + * PyObjectAdapters turn Java Objects into PyObjects. + */ +public interface PyObjectAdapter { + + /** + * @return true if o can be adapted by this adapter. + */ + public abstract boolean canAdapt(Object o); + + /** + * @return the PyObject version of o or null if canAdapt(o) returns false. + */ + public abstract PyObject adapt(Object o); + +} \ No newline at end of file Property changes on: trunk/jython/src/org/python/core/adapter/PyObjectAdapter.java ___________________________________________________________________ Name: svn:executable + * This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |