From f4c4ef73bbe7d39dc68b9f8b0941a6d4ec104934 Mon Sep 17 00:00:00 2001 From: Abhinav Sarkar Date: Sat, 31 Oct 2009 00:16:42 +0530 Subject: [PATCH] exclusion from guessing for non-annotation member types support for PyClass support for name pattern for INIT minor cleanup --- .classpath | 3 +- .project | 40 +- .../abhinavsarkar/jywrapper/JyWrapper.java | 181 +-- .../net/abhinavsarkar/jywrapper/Messages.java | 56 +- .../jywrapper/PyAttributeType.java | 35 +- .../jywrapper/PyImportLoader.java | 124 +- .../abhinavsarkar/jywrapper/PyMethodType.java | 40 +- .../jywrapper/PyObjectProxy.java | 1181 +++++++++-------- .../net/abhinavsarkar/jywrapper/Util.java | 161 +-- .../jywrapper/messages.properties | 34 +- 10 files changed, 946 insertions(+), 909 deletions(-) diff --git a/.classpath b/.classpath index f80d362..4445cfe 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,7 @@ - + + diff --git a/.project b/.project index 13b2083..b84c1fe 100644 --- a/.project +++ b/.project @@ -1,17 +1,23 @@ - - - jywrapper - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - + + + jywrapper + + + + + + org.python.pydev.PyDevBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + org.python.pydev.pythonNature + + diff --git a/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java b/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java index bf4de37..28f9e46 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java @@ -1,89 +1,92 @@ -package net.abhinavsarkar.jywrapper; - -import static net.abhinavsarkar.jywrapper.Messages._; - - -import net.abhinavsarkar.jywrapper.annotation.Wraps; -import net.abhinavsarkar.jywrapper.exception.PythonImportNotFoundException; - -import org.python.core.PyFunction; -import org.python.core.PyModule; -import org.python.core.PyObject; -import org.python.core.PyType; - -/** - * @author Abhinav Sarkar - * - * @param The type of the java class to wrap the Python class/module with. - */ -public final class JyWrapper { - - private JyWrapper() { - } - - public static T wrap(final Class javaClass) { - final Wraps annotation = javaClass.getAnnotation(Wraps.class); - if (annotation == null) { - throw new PythonImportNotFoundException(_("JyWrapper.7", javaClass)); //$NON-NLS-1$ - } - - return wrap(javaClass, annotation.value()); - } - - /** - * @param pyImportName The full import name of the Python class/module - * to wrap. - * @return An instance of {@link UninitedPyObjectWrapper}, ready to be - * initialized. - * @throws IllegalStateException Thrown if the java Class to be used to - * wrap the Python module/class, has not been supplied by earlier - * calling {@link JyWrapper#with(Class)}. - * @throws IllegalArgumentException Thrown if the pyImportName parameter - * is null. - */ - public static T wrap(final Class javaClass, final String pyImportName) { - if (javaClass == null) { - throw new IllegalStateException(_("JyWrapper.6", "javaClass")); //$NON-NLS-1$ //$NON-NLS-2$ - } - if (pyImportName == null) { - throw new IllegalArgumentException(_("JyWrapper.6", "pyImportName")); //$NON-NLS-1$ //$NON-NLS-2$ - } - - final PyObject pyImport = PyImportLoader.loadPyImport(pyImportName); - if (!(pyImport instanceof PyType || pyImport instanceof PyModule)) { - throw new IllegalArgumentException(_("JyWrapper.5", pyImportName)); //$NON-NLS-1$ - } - return Util.py2Java(pyImport, javaClass); - } - - /** - * @param The return type of the {@link PyCallable} instance. - * @param pyImportName The full import name of the Python function to wrap. - * @param returnType The class of the return type. - * @return An instance of {@link PyCallable} which wraps the - * Python function given in parameter. - * @throws IllegalArgumentException Thrown if the any of the parameters - * supplied are null or if the pyImportName parameter supplied does not - * correspond to a Python function. - */ - public static PyCallable wrapPyFunction( - final String pyImportName, final Class returnType) { - if (pyImportName == null) { - throw new IllegalArgumentException(_("JyWrapper.6", "pyImportName")); //$NON-NLS-1$ //$NON-NLS-2$ - } - if (returnType == null) { - throw new IllegalArgumentException(_("JyWrapper.6", "returnType")); //$NON-NLS-1$ //$NON-NLS-2$ - } - - final PyObject pyImport = PyImportLoader.loadPyImport(pyImportName); - if (!(pyImport instanceof PyFunction)) { - throw new IllegalArgumentException(_("JyWrapper.0", pyImportName)); //$NON-NLS-1$ - } - - @SuppressWarnings("unchecked") - final PyCallable newInstance = PyObjectProxy.newInstance( - pyImport, PyCallable.class); - return newInstance; - } - -} +package net.abhinavsarkar.jywrapper; + +import static net.abhinavsarkar.jywrapper.Messages._; + + +import net.abhinavsarkar.jywrapper.annotation.Wraps; +import net.abhinavsarkar.jywrapper.exception.PythonImportNotFoundException; + +import org.python.core.PyClass; +import org.python.core.PyFunction; +import org.python.core.PyModule; +import org.python.core.PyObject; +import org.python.core.PyType; + +/** + * @author Abhinav Sarkar + * + * @param The type of the java class to wrap the Python class/module with. + */ +public final class JyWrapper { + + private JyWrapper() { + } + + public static T wrap(final Class javaClass) { + final Wraps annotation = javaClass.getAnnotation(Wraps.class); + if (annotation == null) { + throw new PythonImportNotFoundException(_("JyWrapper.7", javaClass)); //$NON-NLS-1$ + } + + return wrap(javaClass, annotation.value()); + } + + /** + * @param pyImportName The full import name of the Python class/module + * to wrap. + * @return An instance of {@link UninitedPyObjectWrapper}, ready to be + * initialized. + * @throws IllegalStateException Thrown if the java Class to be used to + * wrap the Python module/class, has not been supplied by earlier + * calling {@link JyWrapper#with(Class)}. + * @throws IllegalArgumentException Thrown if the pyImportName parameter + * is null. + */ + public static T wrap(final Class javaClass, final String pyImportName) { + if (javaClass == null) { + throw new IllegalStateException(_("JyWrapper.6", "javaClass")); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (pyImportName == null) { + throw new IllegalArgumentException(_("JyWrapper.6", "pyImportName")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + final PyObject pyImport = PyImportLoader.loadPyImport(pyImportName); + if (!(pyImport instanceof PyType + || pyImport instanceof PyModule + || pyImport instanceof PyClass)) { + throw new IllegalArgumentException(_("JyWrapper.5", pyImportName)); //$NON-NLS-1$ + } + return Util.py2Java(pyImport, javaClass); + } + + /** + * @param The return type of the {@link PyCallable} instance. + * @param pyImportName The full import name of the Python function to wrap. + * @param returnType The class of the return type. + * @return An instance of {@link PyCallable} which wraps the + * Python function given in parameter. + * @throws IllegalArgumentException Thrown if the any of the parameters + * supplied are null or if the pyImportName parameter supplied does not + * correspond to a Python function. + */ + public static PyCallable wrapPyFunction( + final String pyImportName, final Class returnType) { + if (pyImportName == null) { + throw new IllegalArgumentException(_("JyWrapper.6", "pyImportName")); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (returnType == null) { + throw new IllegalArgumentException(_("JyWrapper.6", "returnType")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + final PyObject pyImport = PyImportLoader.loadPyImport(pyImportName); + if (!(pyImport instanceof PyFunction)) { + throw new IllegalArgumentException(_("JyWrapper.0", pyImportName)); //$NON-NLS-1$ + } + + @SuppressWarnings("unchecked") + final PyCallable newInstance = PyObjectProxy.newInstance( + pyImport, PyCallable.class); + return newInstance; + } + +} diff --git a/src/main/java/net/abhinavsarkar/jywrapper/Messages.java b/src/main/java/net/abhinavsarkar/jywrapper/Messages.java index b51c668..755184d 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/Messages.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/Messages.java @@ -1,28 +1,28 @@ -package net.abhinavsarkar.jywrapper; - -import java.text.MessageFormat; -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -final class Messages { - private static final String BUNDLE_NAME = "net.abhinavsarkar.jywrapper.messages"; //$NON-NLS-1$ - - private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle - .getBundle(BUNDLE_NAME); - - private Messages() { - } - - public static String getString(String key) { - try { - return RESOURCE_BUNDLE.getString(key); - } catch (MissingResourceException e) { - return '!' + key + '!'; - } - } - - public static String _(final String messageKey, final Object... arguments) { - return new MessageFormat(getString(messageKey)) - .format(arguments, new StringBuffer(), null).toString(); - } -} +package net.abhinavsarkar.jywrapper; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +final class Messages { + private static final String BUNDLE_NAME = "net.abhinavsarkar.jywrapper.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = + ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() { + } + + public static String getString(final String key) { + try { + return RESOURCE_BUNDLE.getString(key); + } catch (final MissingResourceException e) { + return '!' + key + '!'; + } + } + + public static String _(final String messageKey, final Object... arguments) { + return new MessageFormat(getString(messageKey)) + .format(arguments, new StringBuffer(), null).toString(); + } +} diff --git a/src/main/java/net/abhinavsarkar/jywrapper/PyAttributeType.java b/src/main/java/net/abhinavsarkar/jywrapper/PyAttributeType.java index 497bf12..6af6f75 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/PyAttributeType.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/PyAttributeType.java @@ -1,17 +1,18 @@ -package net.abhinavsarkar.jywrapper; - -import net.abhinavsarkar.jywrapper.PyObjectProxy.MemberType; - -public enum PyAttributeType { - GETTER(MemberType.GETTER), SETTER(MemberType.SETTER), CONST(MemberType.CONST); - - private final MemberType memberType; - - private PyAttributeType(MemberType memberType) { - this.memberType = memberType; - } - - public MemberType getMemberType() { - return memberType; - } -} +package net.abhinavsarkar.jywrapper; + +import net.abhinavsarkar.jywrapper.PyObjectProxy.MemberType; + +public enum PyAttributeType { + + GETTER(MemberType.GETTER), SETTER(MemberType.SETTER), CONST(MemberType.CONST); + + private final MemberType memberType; + + private PyAttributeType(final MemberType memberType) { + this.memberType = memberType; + } + + MemberType getMemberType() { + return memberType; + } +} diff --git a/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java b/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java index 8298af9..1a9e05a 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java @@ -1,62 +1,62 @@ -package net.abhinavsarkar.jywrapper; - -import static net.abhinavsarkar.jywrapper.Messages._; - -import java.util.concurrent.ConcurrentHashMap; - -import net.abhinavsarkar.jywrapper.exception.PythonImportNotFoundException; - -import org.python.core.Py; -import org.python.core.PyException; -import org.python.core.PyObject; -import org.python.core.PySystemState; - -/** - * @author AbhinavSarkar - * - */ -public final class PyImportLoader { - - private static final PyObject importer = new PySystemState().getBuiltins() - .__getitem__(Py.newString("__import__")); //$NON-NLS-1$ - - private static final ConcurrentHashMap loadedPyImports = - new ConcurrentHashMap(); - - private PyImportLoader() { - } - - /** - * @param fullImportName - * @return - * @throws PythonImportNotFoundException - */ - public static PyObject loadPyImport(String fullImportName) - throws PythonImportNotFoundException { - if (!loadedPyImports.containsKey(fullImportName)) { - int i = fullImportName.lastIndexOf('.'); - String errorMsg = _("PyImportLoader.1", fullImportName); //$NON-NLS-1$ - PyObject pyImport; - if (i == -1) { - String pyModuleName = fullImportName; - try { - pyImport = importer.__call__(Py.newString(pyModuleName)); - } catch (PyException pye) { - throw new PythonImportNotFoundException(errorMsg, pye); - } - } else { - String pyModuleName = fullImportName.substring(0, i); - String pyClassName = fullImportName.substring(i + 1); - - try { - pyImport = importer.__call__(Py.newString(pyModuleName)) - .__getattr__(pyClassName); - } catch (PyException pye) { - throw new PythonImportNotFoundException(errorMsg, pye); - } - } - loadedPyImports.putIfAbsent(fullImportName, pyImport); - } - return loadedPyImports.get(fullImportName); - } -} +package net.abhinavsarkar.jywrapper; + +import static net.abhinavsarkar.jywrapper.Messages._; + +import java.util.concurrent.ConcurrentHashMap; + +import net.abhinavsarkar.jywrapper.exception.PythonImportNotFoundException; + +import org.python.core.Py; +import org.python.core.PyException; +import org.python.core.PyObject; +import org.python.core.PySystemState; + +/** + * @author AbhinavSarkar + * + */ +public final class PyImportLoader { + + private static final PyObject importer = new PySystemState().getBuiltins() + .__getitem__(Py.newString("__import__")); //$NON-NLS-1$ + + private static final ConcurrentHashMap loadedPyImports = + new ConcurrentHashMap(); + + private PyImportLoader() { + } + + /** + * @param fullImportName + * @return + * @throws PythonImportNotFoundException + */ + public static PyObject loadPyImport(final String fullImportName) + throws PythonImportNotFoundException { + if (!loadedPyImports.containsKey(fullImportName)) { + final int i = fullImportName.lastIndexOf('.'); + final String errorMsg = _("PyImportLoader.1", fullImportName); //$NON-NLS-1$ + PyObject pyImport; + if (i == -1) { + final String pyModuleName = fullImportName; + try { + pyImport = importer.__call__(Py.newString(pyModuleName)); + } catch (final PyException pye) { + throw new PythonImportNotFoundException(errorMsg, pye); + } + } else { + final String pyModuleName = fullImportName.substring(0, i); + final String pyClassName = fullImportName.substring(i + 1); + + try { + pyImport = importer.__call__(Py.newString(pyModuleName)) + .__getattr__(pyClassName); + } catch (final PyException pye) { + throw new PythonImportNotFoundException(errorMsg, pye); + } + } + loadedPyImports.putIfAbsent(fullImportName, pyImport); + } + return loadedPyImports.get(fullImportName); + } +} diff --git a/src/main/java/net/abhinavsarkar/jywrapper/PyMethodType.java b/src/main/java/net/abhinavsarkar/jywrapper/PyMethodType.java index 0782b06..c35c721 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/PyMethodType.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/PyMethodType.java @@ -1,20 +1,20 @@ -package net.abhinavsarkar.jywrapper; - -import net.abhinavsarkar.jywrapper.PyObjectProxy.MemberType; - -public enum PyMethodType { - INIT(MemberType.INIT), - DIRECT(MemberType.DIRECT), - UNDERSCORED(MemberType.UNDERSCORED), - NUMERIC(MemberType.NUMERIC); - - private final MemberType memberType; - - private PyMethodType(MemberType memberType) { - this.memberType = memberType; - } - - public MemberType getMemberType() { - return memberType; - } -} +package net.abhinavsarkar.jywrapper; + +import net.abhinavsarkar.jywrapper.PyObjectProxy.MemberType; + +public enum PyMethodType { + INIT (MemberType.INIT), + DIRECT (MemberType.DIRECT), + UNDERSCORED (MemberType.UNDERSCORED), + NUMERIC (MemberType.NUMERIC); + + private final MemberType memberType; + + private PyMethodType(final MemberType memberType) { + this.memberType = memberType; + } + + MemberType getMemberType() { + return memberType; + } +} diff --git a/src/main/java/net/abhinavsarkar/jywrapper/PyObjectProxy.java b/src/main/java/net/abhinavsarkar/jywrapper/PyObjectProxy.java index 70bafc3..6c9efe6 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/PyObjectProxy.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/PyObjectProxy.java @@ -1,584 +1,599 @@ -/** - * - */ -package net.abhinavsarkar.jywrapper; - -import static net.abhinavsarkar.jywrapper.Messages._; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.concurrent.ConcurrentHashMap; - -import net.abhinavsarkar.jywrapper.annotation.PyAttribute; -import net.abhinavsarkar.jywrapper.annotation.PyMethod; -import net.abhinavsarkar.jywrapper.exception.PythonImportInstantiationError; - -import org.python.core.Py; -import org.python.core.PyException; -import org.python.core.PyFunction; -import org.python.core.PyModule; -import org.python.core.PyObject; -import org.python.core.PyProperty; -import org.python.core.PyProxy; -import org.python.core.PyType; - -final class PyObjectProxy implements InvocationHandler { - - private static final String JAVA_COMPARE_METHOD_NAME = "compareTo"; //$NON-NLS-1$ - - private static final String GETTER_METHOD_PREFIX = "get"; //$NON-NLS-1$ - - private static final String SETTER_METHOD_PREFIX = "set"; //$NON-NLS-1$ - - private static final String CONST_METHOD_PREFIX = "const"; //$NON-NLS-1$ - - private static enum JavaSpecialMethod { - equals("__eq__"), hashCode("__hash__"), toString("__str__"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - private final String pyEquiv; - - private JavaSpecialMethod(final String pyEquiv) { - this.pyEquiv = pyEquiv; - } - - public static Object invoke(final PyObject pyObject, final Method method, - final Object[] args) throws NoSuchMethodException { - try { - return invokePyMethod(pyObject, method.getReturnType(), - JavaSpecialMethod.valueOf(method.getName()).pyEquiv, args); - } catch (final IllegalArgumentException ex) { - throw new NoSuchMethodException(); - } - } - } - - private static enum NumericMethod { - add ("__add__"), //$NON-NLS-1$ - subtract ("__sub__"), //$NON-NLS-1$ - multiply ("__mul__"), //$NON-NLS-1$ - divide ("__div__"), //$NON-NLS-1$ - divideAndRemainder ("__divmod__"), //$NON-NLS-1$ - remainder ("__mod__"), //$NON-NLS-1$ - pow ("__pow__"), //$NON-NLS-1$ - negate ("__neg__"), //$NON-NLS-1$ - plus ("__pos__"), //$NON-NLS-1$ - abs ("__abs__"), //$NON-NLS-1$ - and ("__and__"), //$NON-NLS-1$ - or ("__or__"), //$NON-NLS-1$ - xor ("__xor__"), //$NON-NLS-1$ - invert ("__invert__"), //$NON-NLS-1$ - intValue ("__int__"), //$NON-NLS-1$ - longValue ("__long__"), //$NON-NLS-1$ - floatValue ("__float__"); //$NON-NLS-1$ - - private final String pyEquiv; - - private NumericMethod(final String pyEquiv) { - this.pyEquiv = pyEquiv; - } - - public static Object invoke(final PyObject pyObject, final Method method, - final Object[] args) throws NoSuchMethodException { - try { - return findAndInvokePyMethod(pyObject, - method.getReturnType(), - NumericMethod.valueOf(method.getName()).pyEquiv, args); - } catch (final IllegalArgumentException ex) { - throw new NoSuchMethodException(); - } - } - } - - private static enum PyProxyInterfaceMethod { - _setPyInstance, _setPySystemState, __initProxy__, _getPySystemState, - _getPyInstance { - @Override - public Object invoke(final PyObject pyObject) { - return new Throwable().getStackTrace()[4].getClassName() - .startsWith("org.python") ? pyObject : null; - } - }; - - public Object invoke(final PyObject pyObject) { - return Void.TYPE; - } - } - - static enum MemberType { - INIT { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - final PyMethod annotation = method.getAnnotation(PyMethod.class); - - if (annotation != null - && annotation.type() == PyMethodType.INIT) { - synchronized (pyObject) { - return initialize(pyObject, pyImportName, javaClass, args); - } - } - throw new NoSuchMethodException( - _("JyWrapper.15", "initialization method", //$NON-NLS-1$ //$NON-NLS-2$ - method.getName(), pyImportName)); - } - }, - CALL { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - if (javaClass.equals(PyCallable.class) - && pyObject instanceof PyFunction - && "call".equals(method.getName())) { //$NON-NLS-1$ - return invokePyMethod(pyObject, method.getReturnType(), - "__call__", (Object[]) args[0]); //$NON-NLS-1$ - } - throw new NoSuchMethodException(); - } - }, - DIRECT { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - String methodName = method.getName(); - final PyMethod annotation = method.getAnnotation(PyMethod.class); - if (annotation != null - && annotation.type() == PyMethodType.DIRECT) { - final String name = annotation.method(); - if (!"".equals(name)) { //$NON-NLS-1$ - methodName = name; - } - } - try { - return findAndInvokePyMethod(pyObject, - method.getReturnType(), methodName, args); - } catch (final NoSuchMethodException e) { - throw new NoSuchMethodException( - _("JyWrapper.15", "direct method", //$NON-NLS-1$ //$NON-NLS-2$ - methodName, pyImportName)); - } - } - }, - UNDERSCORED { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - try { - return findAndInvokePyMethod(pyObject, - method.getReturnType(), - Util.camelCase2UnderScore(method.getName()), args); - } catch (final NoSuchMethodException e) { - throw new NoSuchMethodException( - _("JyWrapper.15", "underscored method", //$NON-NLS-1$ //$NON-NLS-2$ - method.getName(), pyImportName)); - } - } - }, - COMPARE { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - for (final RichComparisonOperator compOp : RichComparisonOperator.values()) { - try { - if ((Boolean) findAndInvokePyMethod(pyObject, - Boolean.class, compOp.name(), args)) { - return compOp.returnValue(); - } - } catch (final PyException e) { - if (e.type.equals(Py.NotImplementedError)) { - // the comparison operator is not implemented. move on. - continue; - } - throw e; - } - } - - try { - return (Integer) findAndInvokePyMethod( - pyObject, Integer.class, "__cmp__", args); //$NON-NLS-1$ - } catch (final PyException e) { - if (!e.type.equals(Py.NotImplementedError)) { - throw e; - } - } - throw new NoSuchMethodException(_("JyWrapper.11")); //$NON-NLS-1$ - } - }, - SPECIAL { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - return JavaSpecialMethod.invoke(pyObject, method, args); - } - }, - PYPROXY_INTERFACE { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - try { - return PyProxyInterfaceMethod.valueOf(method.getName()) - .invoke(pyObject); - } catch (final IllegalArgumentException ex) { - throw new NoSuchMethodException(); - } - } - }, - GETTER { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - final PyAttribute annotation = method.getAnnotation(PyAttribute.class); - final String methodName = method.getName(); - PyObject attrValue = null; - String attrName = methodName; - - if (annotation != null - && annotation.type() == PyAttributeType.GETTER) { - attrName = annotation.attribute(); - if (!"".equals(attrName)) { //$NON-NLS-1$ - synchronized (pyObject) { - attrValue = pyObject.__findattr__(attrName); - } - } - } - - if (attrValue == null && methodName.startsWith(GETTER_METHOD_PREFIX)) { - attrName = Util.camelCase2UnderScore(methodName) - .substring(GETTER_METHOD_PREFIX.length() + 1); - synchronized (pyObject) { - attrValue = pyObject.__findattr__(attrName); - } - } - - if (attrValue != null) { - if (PyProperty.class.isAssignableFrom(attrValue.getClass())) { - throw new IllegalArgumentException(_("JyWrapper.12", attrName)); //$NON-NLS-1$ - } - return Util.py2Java(attrValue, method.getReturnType()); - } - - throw new NoSuchMethodException( - _("JyWrapper.15", "instance attribute", //$NON-NLS-1$ //$NON-NLS-2$ - methodName, pyImportName)); - } - }, - SETTER { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException, IllegalAccessException { - final String methodName = method.getName(); - final PyAttribute annotation = method.getAnnotation(PyAttribute.class); - String attrName = null; - - if (annotation != null - && annotation.type() == PyAttributeType.SETTER) { - attrName = annotation.attribute(); - if ("".equals(attrName)) { //$NON-NLS-1$ - attrName = null; - } - } - - if (attrName == null - && methodName.startsWith(SETTER_METHOD_PREFIX) - && args.length == 1) { - attrName = Util.camelCase2UnderScore(methodName).substring( - SETTER_METHOD_PREFIX.length() + 1); - } - - if (attrName != null) { - PyObject attrValue; - synchronized (pyObject) { - attrValue = pyObject.__findattr__(attrName); - } - if (attrValue != null) { - if (PyProperty.class.isAssignableFrom(attrValue.getClass())) { - throw new IllegalArgumentException( - _("JyWrapper.14", attrName)); //$NON-NLS-1$ - } - } - try { - final PyObject pyArgs = Py.java2py(args[0]); - synchronized (pyObject) { - pyObject.__setattr__(attrName, pyArgs); - } - return Void.TYPE; - } catch (final PyException e) { - if (e.type.equals(Py.AttributeError) - && e.value.toString().equals( - "can't set attribute")) { //$NON-NLS-1$ - throw new IllegalAccessException( - _("JyWrapper.13", attrName)); //$NON-NLS-1$ - } - throw e; - } - } - - throw new NoSuchMethodException( - _("JyWrapper.15", "instance attribute", //$NON-NLS-1$ //$NON-NLS-2$ - methodName, pyImportName)); - } - }, - CONST { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - final PyAttribute annotation = method.getAnnotation(PyAttribute.class); - final String methodName = method.getName(); - PyObject attrValue = null; - - if (annotation != null - && annotation.type() == PyAttributeType.CONST) { - final String attrName = annotation.attribute(); - if (!"".equals(attrName)) { //$NON-NLS-1$ - synchronized (pyObject) { - attrValue = pyObject.__findattr__(attrName); - } - } else { - synchronized (pyObject) { - attrValue = pyObject.__findattr__(methodName); - } - } - } - - if (attrValue == null - && methodName.startsWith(CONST_METHOD_PREFIX)) { - final String attrName = methodName - .substring(CONST_METHOD_PREFIX.length()); - synchronized (pyObject) { - attrValue = pyObject.__findattr__(attrName); - } - } - - if (attrValue != null) { - return Util.py2Java(attrValue, method.getReturnType()); - } - - throw new NoSuchMethodException( - _("JyWrapper.15", "constant", //$NON-NLS-1$ //$NON-NLS-2$ - methodName, pyImportName)); - } - }, - NUMERIC { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - try { - return NumericMethod.invoke(pyObject, method, args); - } catch (final NoSuchMethodException e) { - throw new NoSuchMethodException( - _("JyWrapper.15", "eqivalent numeric method", //$NON-NLS-1$ //$NON-NLS-2$ - method.getName(), pyImportName)); - } - } - }, - NO_SUCH_METHOD { - @Override - public Object invoke(final PyObject pyObject, final Class javaClass, - final Method method, final Object[] args, final String pyImportName) - throws NoSuchMethodException { - throw new NoSuchMethodException( - _("JyWrapper.15", "method", method.getName(), pyImportName)); //$NON-NLS-1$ //$NON-NLS-2$ - } - }; - - public abstract Object invoke(PyObject pyObject, Class javaClass, - Method method, Object[] args, String pyImportName) - throws NoSuchMethodException, IllegalAccessException; - } - - private static enum RichComparisonOperator { - __eq__(0), __lt__(-1), __gt__(1); - - private int returnValue; - - public int returnValue() { return returnValue; } - - private RichComparisonOperator(final int returnValue) { - this.returnValue = returnValue; - } - } - - private final PyObject pyObject; - - private final Class javaClass; - - private final String pyImportName; - - private static final ConcurrentHashMap METHOD_JUMP_TABLE = - new ConcurrentHashMap(); - - private PyObjectProxy(final PyObject pyObject, final Class javaClass) { - this.pyObject = pyObject; - this.javaClass = javaClass; - this.pyImportName = Util.getPyImportName(this.pyObject); - } - - public static T newInstance(final PyObject pyObject, - final Class javaClass) { - if (javaClass.isInterface()) { - ClassLoader classLoader = javaClass.getClassLoader(); - if (classLoader == null) { - classLoader = ClassLoader.getSystemClassLoader(); - } - - @SuppressWarnings("unchecked") - final T newProxyInstance = (T) Proxy.newProxyInstance( - classLoader, - new Class[] { javaClass, PyProxy.class }, - new PyObjectProxy(pyObject, javaClass)); - return newProxyInstance; - } else { - throw new IllegalArgumentException(_("JyWrapper.8", javaClass.getName())); //$NON-NLS-1$ - } - } - - public Object invoke(final Object proxy, final Method method, final Object[] args) - throws NoSuchMethodException, IllegalAccessException { - final PyMethod pyMethodAnnotation = method.getAnnotation(PyMethod.class); - if (pyMethodAnnotation != null) { - return pyMethodAnnotation.type().getMemberType() - .invoke(pyObject, javaClass, method, args, pyImportName); - } - - final PyAttribute pyAttributeAnnotation = method.getAnnotation(PyAttribute.class); - if (pyAttributeAnnotation != null) { - return pyAttributeAnnotation.type().getMemberType() - .invoke(pyObject, javaClass, method, args, pyImportName); - } - - final String methodName = method.getName(); - if (!METHOD_JUMP_TABLE.containsKey(method)) { - if (Comparable.class.isAssignableFrom(javaClass) - && JAVA_COMPARE_METHOD_NAME.equals(methodName)) { - //check if the method is "compareTo" - METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.COMPARE); - } else { - //check if the method is one of the special methods - try { - JavaSpecialMethod.valueOf(methodName); - METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.SPECIAL); - } catch (final IllegalArgumentException ex) { - //the method is not one of the special methods. move on. - - //check if the method is one of the PyProxy interface methods - try { - PyProxyInterfaceMethod.valueOf(methodName); - METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.PYPROXY_INTERFACE); - } catch (final IllegalArgumentException e) { - //the method is not one of the PyProxy interface methods. - //move on. - } - } - } - } - - //check if the method has already been registered in the jump table - //if so invoke it using the method type registered in the table - if (METHOD_JUMP_TABLE.containsKey(method)) { - return METHOD_JUMP_TABLE.get(method) - .invoke(pyObject, javaClass, method, args, pyImportName); - } - - // try each method types one by one to guess the correct one - for (final MemberType methodType : MemberType.values()) { - try { - final Object retVal = methodType.invoke( - pyObject, javaClass, method, args, pyImportName); - METHOD_JUMP_TABLE.putIfAbsent(method, methodType); - return retVal; - } catch (final NoSuchMethodException ex) { - continue; - } - } - - //all checks failed. throw NoSuchMethodException - METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.NO_SUCH_METHOD); - throw new NoSuchMethodException( - _("JyWrapper.15", "method", methodName, pyImportName)); //$NON-NLS-1$ //$NON-NLS-2$ - } - - /** - * @param args Arguments to initialize the underlying Python class. - * @param pyImport - * @param pyImportName - * @param javaClass - * @return An initialized (wrapped) Python class instance, ready to be used. - */ - private static T initialize(final PyObject pyImport, final String pyImportName, - final Class javaClass, final Object... args) { - if (javaClass == null) { - throw new IllegalStateException(_("JyWrapper.6", "javaClass")); //$NON-NLS-1$ //$NON-NLS-2$ - } - if (pyImportName == null) { - throw new IllegalArgumentException(_("JyWrapper.6", "pyImportName")); //$NON-NLS-1$ //$NON-NLS-2$ - } - - if (pyImport instanceof PyModule) { - throw new PythonImportInstantiationError(_("JyWrapper.4", pyImportName)); //$NON-NLS-1$ - } - if (!(pyImport instanceof PyType)) { - throw new PythonImportInstantiationError(_("JyWrapper.9")); //$NON-NLS-1$ - } - - try { - return Util.py2Java(pyImport.__call__(Util.convertArgs(args)), javaClass); - } catch (final PyException e) { - if (e.type.equals(Py.TypeError)) { - throw new PythonImportInstantiationError( - _("JyWrapper.3", pyImportName), e); //$NON-NLS-1$ - } - throw e; - } catch (final IllegalArgumentException e) { - throw new IllegalArgumentException( - _("JyWrapper.2", pyImportName, javaClass.getName())); //$NON-NLS-1$ - } - } - - private static Object findAndInvokePyMethod(final PyObject pyObject, - final Class javaReturnType, final String methodName, final Object[] args) - throws NoSuchMethodException { - PyObject pyAttr; - synchronized (pyObject) { - pyAttr = pyObject.__findattr__(methodName); - } - if (pyAttr instanceof org.python.core.PyMethod || pyAttr instanceof PyFunction) { - return invokePyMethod(pyObject, javaReturnType, methodName, args); - } else { - throw new NoSuchMethodException(); - } - } - - private static Object invokePyMethod(final PyObject pyObject, - final Class javaReturnType, final String methodName, final Object[] args) { - PyObject pyReturnValue; - try { - final PyObject[] pyArgs = Util.convertArgs(args); - synchronized (pyObject) { - pyReturnValue = pyObject.invoke(methodName, pyArgs); - } - } catch (final PyException e) { - if (e.type.equals(Py.TypeError)) { - throw new IllegalArgumentException( - _("JyWrapper.10", methodName), e); //$NON-NLS-1$ - } - throw e; - } - - if (pyReturnValue == Py.None) { - return "void".equals(javaReturnType.getName()) ? Void.TYPE : null; //$NON-NLS-1$ - } - return Util.py2Java(pyReturnValue, javaReturnType); - } - +/** + * + */ +package net.abhinavsarkar.jywrapper; + +import static net.abhinavsarkar.jywrapper.Messages._; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.EnumSet; +import java.util.concurrent.ConcurrentHashMap; + +import net.abhinavsarkar.jywrapper.annotation.PyAttribute; +import net.abhinavsarkar.jywrapper.annotation.PyMethod; +import net.abhinavsarkar.jywrapper.exception.PythonImportInstantiationError; + +import org.python.core.Py; +import org.python.core.PyClass; +import org.python.core.PyException; +import org.python.core.PyFunction; +import org.python.core.PyModule; +import org.python.core.PyObject; +import org.python.core.PyProperty; +import org.python.core.PyProxy; +import org.python.core.PyType; + +final class PyObjectProxy implements InvocationHandler { + + private static final String JAVA_COMPARE_METHOD_NAME = "compareTo"; //$NON-NLS-1$ + + private static final String INIT_METHOD_NAME = "initialize"; //$NON-NLS-1$ + + private static final String GETTER_METHOD_PREFIX = "get"; //$NON-NLS-1$ + + private static final String SETTER_METHOD_PREFIX = "set"; //$NON-NLS-1$ + + private static final String CONST_METHOD_PREFIX = "const"; //$NON-NLS-1$ + + private static final EnumSet NON_ANNOTATED_MEMBERS = + EnumSet.of(MemberType.COMPARE, MemberType.SPECIAL, MemberType.PYPROXY_INTERFACE); + + private static enum JavaSpecialMethod { + equals("__eq__"), hashCode("__hash__"), toString("__str__"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + private final String pyEquiv; + + private JavaSpecialMethod(final String pyEquiv) { + this.pyEquiv = pyEquiv; + } + + public static Object invoke(final PyObject pyObject, final Method method, + final Object[] args) throws NoSuchMethodException { + try { + return invokePyMethod(pyObject, method.getReturnType(), + JavaSpecialMethod.valueOf(method.getName()).pyEquiv, args); + } catch (final IllegalArgumentException ex) { + throw new NoSuchMethodException(); + } + } + } + + private static enum NumericMethod { + add ("__add__"), //$NON-NLS-1$ + subtract ("__sub__"), //$NON-NLS-1$ + multiply ("__mul__"), //$NON-NLS-1$ + divide ("__div__"), //$NON-NLS-1$ + divideAndRemainder ("__divmod__"), //$NON-NLS-1$ + remainder ("__mod__"), //$NON-NLS-1$ + pow ("__pow__"), //$NON-NLS-1$ + negate ("__neg__"), //$NON-NLS-1$ + plus ("__pos__"), //$NON-NLS-1$ + abs ("__abs__"), //$NON-NLS-1$ + and ("__and__"), //$NON-NLS-1$ + or ("__or__"), //$NON-NLS-1$ + xor ("__xor__"), //$NON-NLS-1$ + invert ("__invert__"), //$NON-NLS-1$ + intValue ("__int__"), //$NON-NLS-1$ + longValue ("__long__"), //$NON-NLS-1$ + floatValue ("__float__"); //$NON-NLS-1$ + + private final String pyEquiv; + + private NumericMethod(final String pyEquiv) { + this.pyEquiv = pyEquiv; + } + + public static Object invoke(final PyObject pyObject, final Method method, + final Object[] args) throws NoSuchMethodException { + try { + return findAndInvokePyMethod( + pyObject, method.getReturnType(), + NumericMethod.valueOf(method.getName()).pyEquiv, args); + } catch (final IllegalArgumentException ex) { + throw new NoSuchMethodException(); + } + } + } + + private static enum PyProxyInterfaceMethod { + _setPyInstance, _setPySystemState, __initProxy__, _getPySystemState, + _getPyInstance { + @Override + public Object invoke(final PyObject pyObject) { + return new Throwable().getStackTrace()[4].getClassName() + .startsWith("org.python") ? pyObject : null; + } + }; + + public Object invoke(final PyObject pyObject) { + return null; + } + } + + static enum MemberType { + INIT { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + final PyMethod annotation = method.getAnnotation(PyMethod.class); + + if (annotation != null && annotation.type() == PyMethodType.INIT) { + synchronized (pyObject) { + return initialize(pyObject, pyImportName, javaClass, args); + } + } + + final String methodName = method.getName(); + if (INIT_METHOD_NAME.equals(methodName)) { + synchronized (pyObject) { + return initialize(pyObject, pyImportName, javaClass, args); + } + } + + throw new NoSuchMethodException( + _("JyWrapper.15", "initialization method", //$NON-NLS-1$ //$NON-NLS-2$ + methodName, pyImportName)); + } + }, + CALL { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + if (javaClass.equals(PyCallable.class) + && pyObject instanceof PyFunction + && "call".equals(method.getName())) { //$NON-NLS-1$ + return invokePyMethod(pyObject, method.getReturnType(), + "__call__", (Object[]) args[0]); //$NON-NLS-1$ + } + throw new NoSuchMethodException(); + } + }, + DIRECT { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + String methodName = method.getName(); + + final PyMethod annotation = method.getAnnotation(PyMethod.class); + if (annotation != null && annotation.type() == PyMethodType.DIRECT) { + final String name = annotation.method(); + if (!"".equals(name)) { //$NON-NLS-1$ + methodName = name; + } + } + try { + return findAndInvokePyMethod( + pyObject, method.getReturnType(), methodName, args); + } catch (final NoSuchMethodException e) { + throw new NoSuchMethodException( + _("JyWrapper.15", "direct method", //$NON-NLS-1$ //$NON-NLS-2$ + methodName, pyImportName)); + } + } + }, + UNDERSCORED { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + try { + return findAndInvokePyMethod( + pyObject, method.getReturnType(), + Util.camelCase2UnderScore(method.getName()), args); + } catch (final NoSuchMethodException e) { + throw new NoSuchMethodException( + _("JyWrapper.15", "underscored method", //$NON-NLS-1$ //$NON-NLS-2$ + method.getName(), pyImportName)); + } + } + }, + COMPARE { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + for (final RichComparisonOperator compOp : RichComparisonOperator.values()) { + try { + if ((Boolean) findAndInvokePyMethod( + pyObject, Boolean.class, compOp.name(), args)) { + return compOp.returnValue(); + } + } catch (final PyException e) { + if (e.type.equals(Py.NotImplementedError)) { + // the comparison operator is not implemented. move on. + continue; + } + throw e; + } + } + + try { + return findAndInvokePyMethod( + pyObject, Integer.class, "__cmp__", args); //$NON-NLS-1$ + } catch (final PyException e) { + if (!e.type.equals(Py.NotImplementedError)) { + throw e; + } + } + throw new NoSuchMethodException(_("JyWrapper.11")); //$NON-NLS-1$ + } + }, + SPECIAL { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + return JavaSpecialMethod.invoke(pyObject, method, args); + } + }, + PYPROXY_INTERFACE { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + try { + return PyProxyInterfaceMethod.valueOf(method.getName()) + .invoke(pyObject); + } catch (final IllegalArgumentException ex) { + throw new NoSuchMethodException(); + } + } + }, + GETTER { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + final PyAttribute annotation = method.getAnnotation(PyAttribute.class); + final String methodName = method.getName(); + PyObject attrValue = null; + String attrName = methodName; + + if (annotation != null && annotation.type() == PyAttributeType.GETTER) { + attrName = annotation.attribute(); + if (!"".equals(attrName)) { //$NON-NLS-1$ + synchronized (pyObject) { + attrValue = pyObject.__findattr__(attrName); + } + } + } + + if (attrValue == null && methodName.startsWith(GETTER_METHOD_PREFIX)) { + attrName = Util.camelCase2UnderScore(methodName) + .substring(GETTER_METHOD_PREFIX.length() + 1); + synchronized (pyObject) { + attrValue = pyObject.__findattr__(attrName); + } + } + + if (attrValue != null) { + if (PyProperty.class.isAssignableFrom(attrValue.getClass())) { + throw new IllegalArgumentException(_("JyWrapper.12", attrName)); //$NON-NLS-1$ + } + return Util.py2Java(attrValue, method.getReturnType()); + } + + throw new NoSuchMethodException( + _("JyWrapper.15", "instance attribute", //$NON-NLS-1$ //$NON-NLS-2$ + methodName, pyImportName)); + } + }, + SETTER { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException, IllegalAccessException { + final String methodName = method.getName(); + final PyAttribute annotation = method.getAnnotation(PyAttribute.class); + String attrName = null; + + if (annotation != null && annotation.type() == PyAttributeType.SETTER) { + attrName = annotation.attribute(); + if ("".equals(attrName)) { //$NON-NLS-1$ + attrName = null; + } + } + + if (attrName == null + && methodName.startsWith(SETTER_METHOD_PREFIX) + && args.length == 1) { + attrName = Util.camelCase2UnderScore(methodName) + .substring(SETTER_METHOD_PREFIX.length() + 1); + } + + if (attrName != null) { + PyObject attrValue; + synchronized (pyObject) { + attrValue = pyObject.__findattr__(attrName); + } + if (attrValue != null) { + if (PyProperty.class.isAssignableFrom(attrValue.getClass())) { + throw new IllegalArgumentException( + _("JyWrapper.14", attrName)); //$NON-NLS-1$ + } + } + try { + final PyObject pyArgs = Py.java2py(args[0]); + synchronized (pyObject) { + pyObject.__setattr__(attrName, pyArgs); + } + return Void.TYPE; + } catch (final PyException e) { + if (e.type.equals(Py.AttributeError) + && e.value.toString().equals( "can't set attribute")) { //$NON-NLS-1$ + throw new IllegalAccessException( + _("JyWrapper.13", attrName)); //$NON-NLS-1$ + } + throw e; + } + } + + throw new NoSuchMethodException( + _("JyWrapper.15", "instance attribute", //$NON-NLS-1$ //$NON-NLS-2$ + methodName, pyImportName)); + } + }, + CONST { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + final PyAttribute annotation = method.getAnnotation(PyAttribute.class); + final String methodName = method.getName(); + PyObject attrValue = null; + + if (annotation != null + && annotation.type() == PyAttributeType.CONST) { + final String attrName = annotation.attribute(); + if (!"".equals(attrName)) { //$NON-NLS-1$ + synchronized (pyObject) { + attrValue = pyObject.__findattr__(attrName); + } + } else { + synchronized (pyObject) { + attrValue = pyObject.__findattr__(methodName); + } + } + } + + if (attrValue == null + && methodName.startsWith(CONST_METHOD_PREFIX)) { + final String attrName = methodName + .substring(CONST_METHOD_PREFIX.length()); + synchronized (pyObject) { + attrValue = pyObject.__findattr__(attrName); + } + } + + if (attrValue != null) { + return Util.py2Java(attrValue, method.getReturnType()); + } + + throw new NoSuchMethodException( + _("JyWrapper.15", "constant", //$NON-NLS-1$ //$NON-NLS-2$ + methodName, pyImportName)); + } + }, + NUMERIC { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + try { + return NumericMethod.invoke(pyObject, method, args); + } catch (final NoSuchMethodException e) { + throw new NoSuchMethodException( + _("JyWrapper.15", "eqivalent numeric method", //$NON-NLS-1$ //$NON-NLS-2$ + method.getName(), pyImportName)); + } + } + }, + NO_SUCH_METHOD { + @Override + public Object invoke(final PyObject pyObject, final Class javaClass, + final Method method, final Object[] args, final String pyImportName) + throws NoSuchMethodException { + throw new NoSuchMethodException( + _("JyWrapper.15", "method", method.getName(), pyImportName)); //$NON-NLS-1$ //$NON-NLS-2$ + } + }; + + public abstract Object invoke(PyObject pyObject, Class javaClass, + Method method, Object[] args, String pyImportName) + throws NoSuchMethodException, IllegalAccessException; + } + + private static enum RichComparisonOperator { + __eq__(0), __lt__(-1), __gt__(1); + + private int returnValue; + + public int returnValue() { return returnValue; } + + private RichComparisonOperator(final int returnValue) { + this.returnValue = returnValue; + } + } + + private final PyObject pyObject; + + private final Class javaClass; + + private final String pyImportName; + + private static final ConcurrentHashMap METHOD_JUMP_TABLE = + new ConcurrentHashMap(); + + private PyObjectProxy(final PyObject pyObject, final Class javaClass) { + this.pyObject = pyObject; + this.javaClass = javaClass; + this.pyImportName = Util.getPyImportName(this.pyObject); + } + + public static T newInstance(final PyObject pyObject, + final Class javaClass) { + if (javaClass.isInterface()) { + ClassLoader classLoader = javaClass.getClassLoader(); + if (classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + + @SuppressWarnings("unchecked") + final T newProxyInstance = (T) Proxy.newProxyInstance( + classLoader, + new Class[] { javaClass, PyProxy.class }, + new PyObjectProxy(pyObject, javaClass)); + return newProxyInstance; + } else { + throw new IllegalArgumentException(_("JyWrapper.8", javaClass.getName())); //$NON-NLS-1$ + } + } + + public Object invoke(final Object proxy, final Method method, final Object[] args) + throws NoSuchMethodException, IllegalAccessException { + final PyMethod pyMethodAnnotation = method.getAnnotation(PyMethod.class); + if (pyMethodAnnotation != null) { + return pyMethodAnnotation.type().getMemberType() + .invoke(pyObject, javaClass, method, args, pyImportName); + } + + final PyAttribute pyAttributeAnnotation = method.getAnnotation(PyAttribute.class); + if (pyAttributeAnnotation != null) { + return pyAttributeAnnotation.type().getMemberType() + .invoke(pyObject, javaClass, method, args, pyImportName); + } + + final String methodName = method.getName(); + if (!METHOD_JUMP_TABLE.containsKey(method)) { + //check if the method is "compareTo" + if (Comparable.class.isAssignableFrom(javaClass) + && JAVA_COMPARE_METHOD_NAME.equals(methodName)) { + METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.COMPARE); + } else { + //check if the method is one of the special methods + try { + JavaSpecialMethod.valueOf(methodName); + METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.SPECIAL); + } catch (final IllegalArgumentException ex) { + //the method is not one of the special methods. move on. + + //check if the method is one of the PyProxy interface methods + try { + PyProxyInterfaceMethod.valueOf(methodName); + METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.PYPROXY_INTERFACE); + } catch (final IllegalArgumentException e) { + //the method is not one of the PyProxy interface methods. + //move on. + } + } + } + } + + //check if the method has already been registered in the jump table + //if so invoke it using the method type registered in the table + if (METHOD_JUMP_TABLE.containsKey(method)) { + return METHOD_JUMP_TABLE.get(method) + .invoke(pyObject, javaClass, method, args, pyImportName); + } + + // try each method types one by one to guess the correct one + for (final MemberType methodType : MemberType.values()) { + if (NON_ANNOTATED_MEMBERS.contains(methodType)) { + continue; + } + + try { + final Object retVal = methodType.invoke( + pyObject, javaClass, method, args, pyImportName); + METHOD_JUMP_TABLE.putIfAbsent(method, methodType); + return retVal; + } catch (final NoSuchMethodException ex) { + continue; + } + } + + //all checks failed. throw NoSuchMethodException + METHOD_JUMP_TABLE.putIfAbsent(method, MemberType.NO_SUCH_METHOD); + throw new NoSuchMethodException( + _("JyWrapper.15", "method", methodName, pyImportName)); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * @param args Arguments to initialize the underlying Python class. + * @param pyImport + * @param pyImportName + * @param javaClass + * @return An initialized (wrapped) Python class instance, ready to be used. + */ + private static T initialize(final PyObject pyImport, final String pyImportName, + final Class javaClass, final Object... args) { + if (javaClass == null) { + throw new IllegalStateException(_("JyWrapper.6", "javaClass")); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (pyImportName == null) { + throw new IllegalArgumentException(_("JyWrapper.6", "pyImportName")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (pyImport instanceof PyModule) { + throw new PythonImportInstantiationError(_("JyWrapper.4", pyImportName)); //$NON-NLS-1$ + } + if (!(pyImport instanceof PyType || pyImport instanceof PyClass)) { + throw new PythonImportInstantiationError(_("JyWrapper.9")); //$NON-NLS-1$ + } + + try { + return Util.py2Java(pyImport.__call__(Util.convertArgs(args)), javaClass); + } catch (final PyException e) { + if (e.type.equals(Py.TypeError)) { + throw new PythonImportInstantiationError( + _("JyWrapper.3", pyImportName), e); //$NON-NLS-1$ + } + throw e; + } catch (final IllegalArgumentException e) { + throw new IllegalArgumentException( + _("JyWrapper.2", pyImportName, javaClass.getName())); //$NON-NLS-1$ + } + } + + private static Object findAndInvokePyMethod(final PyObject pyObject, + final Class javaReturnType, final String methodName, final Object[] args) + throws NoSuchMethodException { + PyObject pyAttr; + synchronized (pyObject) { + pyAttr = pyObject.__findattr__(methodName); + } + if (pyAttr instanceof org.python.core.PyMethod || pyAttr instanceof PyFunction) { + return invokePyMethod(pyObject, javaReturnType, methodName, args); + } else { + throw new NoSuchMethodException(); + } + } + + private static Object invokePyMethod(final PyObject pyObject, + final Class javaReturnType, final String methodName, final Object[] args) { + PyObject pyReturnValue; + try { + final PyObject[] pyArgs = Util.convertArgs(args); + synchronized (pyObject) { + pyReturnValue = pyObject.invoke(methodName, pyArgs); + } + } catch (final PyException e) { + if (e.type.equals(Py.TypeError)) { + throw new IllegalArgumentException( + _("JyWrapper.10", methodName), e); //$NON-NLS-1$ + } + throw e; + } + + if (pyReturnValue == Py.None) { + return "void".equals(javaReturnType.getName()) ? Void.TYPE : null; //$NON-NLS-1$ + } + return Util.py2Java(pyReturnValue, javaReturnType); + } + } \ No newline at end of file diff --git a/src/main/java/net/abhinavsarkar/jywrapper/Util.java b/src/main/java/net/abhinavsarkar/jywrapper/Util.java index a61db5c..220659a 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/Util.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/Util.java @@ -1,75 +1,86 @@ -package net.abhinavsarkar.jywrapper; - -import static net.abhinavsarkar.jywrapper.Messages._; - -import org.python.core.Py; -import org.python.core.PyFunction; -import org.python.core.PyModule; -import org.python.core.PyObject; -import org.python.core.PyString; -import org.python.core.PyType; - -final class Util { - - private Util() { - } - - static T py2Java(final PyObject pyObject, final Class javaClass) { - final Object javaWrapper = pyObject.__tojava__(javaClass); - if (javaWrapper == Py.NoConversion) { - if (javaClass.isInterface()) { - return PyObjectProxy.newInstance(pyObject, javaClass); - } else { - throw new IllegalArgumentException( - _("JyWrapper.1", pyObject, javaClass.getName())); //$NON-NLS-1$ - } - } else { - @SuppressWarnings("unchecked") - final T t = (T) javaWrapper; - return t; - } - } - - static PyObject[] convertArgs(final Object[] args) { - if (args == null || args.length == 0) { - return new PyObject[0]; - } - if (args.length == 1) { - return new PyObject[] { Py.java2py(args[0]) }; - } - - final PyObject[] pyArgs = new PyObject[args.length]; - for (int i = args.length; --i >= 0;) { - pyArgs[i] = Py.java2py(args[i]); - } - return pyArgs; - } - - static String getPyImportName(final PyObject pyObject) { - synchronized (pyObject) { - if (pyObject instanceof PyType) { - final PyType pyType = (PyType) pyObject; - final PyObject module = pyType.getModule(); - if (module instanceof PyString - && !module.toString().equals("__builtin__")) { //$NON-NLS-1$ - return String.format("%s.%s", module.toString(), //$NON-NLS-1$ - pyType.getName()); - } - return pyType.getName(); - } else if (pyObject instanceof PyModule) { - return ((PyModule) pyObject).toString(); - } else if (pyObject instanceof PyFunction) { - final PyFunction pyFunction = (PyFunction) pyObject; - return String.format("%s.%s", pyFunction.__module__, //$NON-NLS-1$ - pyFunction.__name__); - } else { - return getPyImportName(pyObject.getType()); - } - } - } - - static String camelCase2UnderScore(final String word) { - return word.replaceAll("([A-Z])", "_$1").toLowerCase(); //$NON-NLS-1$ //$NON-NLS-2$ - } - -} +package net.abhinavsarkar.jywrapper; + +import static net.abhinavsarkar.jywrapper.Messages._; + +import org.python.core.Py; +import org.python.core.PyClass; +import org.python.core.PyFunction; +import org.python.core.PyModule; +import org.python.core.PyObject; +import org.python.core.PyString; +import org.python.core.PyType; + +final class Util { + + private Util() { + } + + static T py2Java(final PyObject pyObject, final Class javaClass) { + final Object javaWrapper = pyObject.__tojava__(javaClass); + if (javaWrapper == Py.NoConversion) { + if (javaClass.isInterface()) { + return PyObjectProxy.newInstance(pyObject, javaClass); + } else { + throw new IllegalArgumentException( + _("JyWrapper.1", pyObject, javaClass.getName())); //$NON-NLS-1$ + } + } else { + @SuppressWarnings("unchecked") + final T t = (T) javaWrapper; + return t; + } + } + + static PyObject[] convertArgs(final Object[] args) { + if (args == null || args.length == 0) { + return new PyObject[0]; + } + if (args.length == 1) { + return new PyObject[] { Py.java2py(args[0]) }; + } + + final PyObject[] pyArgs = new PyObject[args.length]; + for (int i = args.length; --i >= 0;) { + pyArgs[i] = Py.java2py(args[i]); + } + return pyArgs; + } + + static String getPyImportName(final PyObject pyObject) { + synchronized (pyObject) { + if (pyObject instanceof PyType) { + final PyType pyType = (PyType) pyObject; + final PyObject module = pyType.getModule(); + if (module instanceof PyString + && !module.toString().equals("__builtin__")) { //$NON-NLS-1$ + return String.format("%s.%s", module.toString(), //$NON-NLS-1$ + pyType.getName()); + } + return pyType.getName(); + } else if (pyObject instanceof PyClass) { + final PyClass pyClass = (PyClass) pyObject; + final PyObject mod = pyClass.__dict__.__finditem__("__module__"); + String smod; + if (mod == null || !(mod instanceof PyString)) { + smod = ""; + } else { + smod = ((PyString) mod).toString(); + } + return smod + "." + pyClass.__name__; + } else if (pyObject instanceof PyModule) { + return ((PyModule) pyObject).toString(); + } else if (pyObject instanceof PyFunction) { + final PyFunction pyFunction = (PyFunction) pyObject; + return String.format( + "%s.%s", pyFunction.__module__, pyFunction.__name__); //$NON-NLS-1$ + } else { + return getPyImportName(pyObject.getType()); + } + } + } + + static String camelCase2UnderScore(final String word) { + return word.replaceAll("([A-Z])", "_$1").toLowerCase(); //$NON-NLS-1$ //$NON-NLS-2$ + } + +} diff --git a/src/main/java/net/abhinavsarkar/jywrapper/messages.properties b/src/main/java/net/abhinavsarkar/jywrapper/messages.properties index b1a4bab..559e274 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/messages.properties +++ b/src/main/java/net/abhinavsarkar/jywrapper/messages.properties @@ -1,17 +1,17 @@ -JyWrapper.0="{0}" is not a Python Function -JyWrapper.1=Cannot convert Python object: {0} to a Java object of class {1} -JyWrapper.2=Cannot convert Python Type {0} to Java Class {1} -JyWrapper.3=Cannot not instantiate the Python type: {0} -JyWrapper.4=Python module {0} can be instantiated statically only -JyWrapper.5={0} is not a Python Type or Module -JyWrapper.6=parameter "{0}" is null -JyWrapper.7=No Python import name annotated on the Java Class {0} -JyWrapper.8={0} is not an Interface -JyWrapper.9=Cannot instantiate a Python non type -JyWrapper.10=Instance method "{0}" called on a Python Type -JyWrapper.11=No comparison methods found in the backing Python object -JyWrapper.12=Trying to get instance attribute "{0}" from a Python Type -JyWrapper.13=Can''t set attribute: {0} -JyWrapper.14=Trying to set instance attribute "{0}" in a python class -JyWrapper.15=No {0} named "{1}" in the backing Python Type {2} -PyImportLoader.1=Python Import "{0}" not found in pythonpath +JyWrapper.0="{0}" is not a Python Function +JyWrapper.1=Cannot convert Python object: {0} to a Java object of class {1} +JyWrapper.2=Cannot convert Python Type {0} to Java Class {1} +JyWrapper.3=Cannot not instantiate the Python type: {0} +JyWrapper.4=Python module {0} can be instantiated statically only +JyWrapper.5={0} is not a Python Type, Class or Module +JyWrapper.6=parameter "{0}" is null +JyWrapper.7=No Python import name annotated on the Java Class {0} +JyWrapper.8={0} is not an Interface +JyWrapper.9=Cannot instantiate a non Python Type or Class +JyWrapper.10=Instance method "{0}" called on a Python Type +JyWrapper.11=No comparison methods found in the backing Python object +JyWrapper.12=Trying to get instance attribute "{0}" from a Python Type +JyWrapper.13=Can''t set attribute: {0} +JyWrapper.14=Trying to set instance attribute "{0}" in a python class +JyWrapper.15=No {0} named "{1}" in the backing Python Type {2} +PyImportLoader.1=Python Import "{0}" not found in pythonpath