// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.lang.InstantiationException;
public class PyReflectedConstructor extends PyReflectedFunction {
public PyReflectedConstructor(String name) {
super(name);
}
public PyReflectedConstructor(Constructor<?> c) {
this(c.getDeclaringClass().getName());
addConstructor(c);
}
private ReflectedArgs makeArgs(Constructor<?> m) {
return new ReflectedArgs(m, m.getParameterTypes(), m.getDeclaringClass(), true);
}
public void addConstructor(Constructor<?> m) {
int mods = m.getModifiers();
// Only add public methods unless we're overriding
if (!Modifier.isPublic(mods) && Options.respectJavaAccessibility) {
return;
}
addArgs(makeArgs(m));
}
// xxx temporary solution, type ctr will go through __new__ ...
PyObject make(PyObject[] args, String[] keywords) {
ReflectedCallData callData = new ReflectedCallData();
Object method = null;
boolean consumes_keywords = false;
PyObject[] allArgs = null;
// Check for a matching constructor to call
if (nargs > 0) { // PyArgsKeywordsCall signature, if present, is the first
if (argslist[0].matches(null, args, keywords, callData)) {
method = argslist[0].data;
consumes_keywords = argslist[0].flags == ReflectedArgs.PyArgsKeywordsCall;
} else {
allArgs = args;
int i = 1;
if (keywords.length > 0) {
args = new PyObject[allArgs.length - keywords.length];
System.arraycopy(allArgs, 0, args, 0, args.length);
i = 0;
}
for (; i < nargs; i++) {
if (argslist[i].matches(null, args, Py.NoKeywords, callData)) {
method = argslist[i].data;
break;
}
}
}
}
// Throw an error if no valid set of arguments
if (method == null) {
throwError(callData.errArg, args.length, true /* xxx? */, false);
}
// Do the actual constructor call
PyObject obj;
try {
obj = (PyObject)((Constructor<?>)method).newInstance(callData.getArgsArray());
} catch (Throwable t) {
throw Py.JavaError(t);
}
if (!consumes_keywords) {
int offset = args.length;
for (int i = 0; i < keywords.length; i++) {
obj.__setattr__(keywords[i], allArgs[i + offset]);
}
}
return obj;
}
@Override
public PyObject __call__(PyObject self, PyObject[] args, String[] keywords) {
if (self == null) {
throw Py.TypeError("invalid self argument to constructor");
}
Class<?> javaClass = self.getType().getProxyType();
if (javaClass == null) {
throw Py.TypeError("self invalid - must be a Java subclass [self=" + self + "]");
}
Class<?> declaringClass = argslist[0] == null ? null : argslist[0].declaringClass;
// If the declaring class is a pure Java type but we're instantiating a Python proxy,
// grab the proxy version of the constructor to instantiate the proper type
if ((declaringClass == null || !PyProxy.class.isAssignableFrom(declaringClass))
&& !(self.getType() instanceof PyJavaType)) {
return PyType.fromClass(javaClass).lookup("__init__").__call__(self, args, keywords);
}
if (nargs == 0) {
throw Py.TypeError("No visible constructors for class (" + javaClass.getName() + ")");
}
if (!declaringClass.isAssignableFrom(javaClass)) {
throw Py.TypeError("self invalid - must implement: " + declaringClass.getName());
}
int mods = declaringClass.getModifiers();
if (Modifier.isInterface(mods)) {
throw Py.TypeError("can't instantiate interface (" + declaringClass.getName() + ")");
} else if (Modifier.isAbstract(mods)) {
throw Py.TypeError("can't instantiate abstract class (" + declaringClass.getName() + ")");
}
if (self.javaProxy != null) {
Class<?> sup = javaClass;
if (PyProxy.class.isAssignableFrom(sup)) {
sup = sup.getSuperclass();
}
throw Py.TypeError("instance already instantiated for " + sup.getName());
}
ReflectedCallData callData = new ReflectedCallData();
Object method = null;
// If we have keyword args, there are two ways this can be handled;
// a) we find a constructor that takes keyword args, and use it.
// b) we don't, in which case we strip the keyword args, and pass the
// non-keyword args, and then use the keyword args to set bean properties
// If we don't have keyword args; just look for a constructor that
// takes the right number of args.
int nkeywords = keywords.length;
ReflectedArgs rargs = null;
PyObject[] allArgs = args;
boolean usingKeywordArgsCtor = false;
if (nkeywords > 0) {
// We have keyword args.
// Look for a constructor; the ReflectedArgs#matches() method exits early in the case
// where keyword args are used
int n = nargs;
for (int i = 0; i < n; i++) {
rargs = argslist[i];
if (rargs.matches(null, args, keywords, callData)) {
method = rargs.data;
break;
}
}
if (method != null) {
// Constructor found that will accept the keyword args
usingKeywordArgsCtor = true;
} else {
// No constructor found that will take keyword args
// Remove the keyword args
args = new PyObject[allArgs.length - nkeywords];
System.arraycopy(allArgs, 0, args, 0, args.length);
// Look for a constructor with no keyword args
for (int i = 0; i < n; i++) {
rargs = argslist[i];
if (rargs.matches(null, args, Py.NoKeywords, callData)) {
method = rargs.data;
break;
}
}
}
} else {
// Just look for a constructor with no keyword args
int n = nargs;
for (int i = 0; i < n; i++) {
rargs = argslist[i];
if (rargs.matches(null, args, Py.NoKeywords, callData)) {
method = rargs.data;
break;
}
}
}
// Throw an error if no valid set of arguments
if (method == null) {
throwError(callData.errArg, args.length, false, false);
}
// Do the actual constructor call
constructProxy(self, (Constructor<?>)method, callData.getArgsArray(), javaClass);
// Do setattr's for keyword args. This convenience allows Java bean properties to be set in
// by a Python constructor call.
// However, this is not done if the Java constructor accepts (PyObject[], String[]) as its arguments,
// in which case the intention is that the Java constructor will handle the keyword arguments itself.
if (!usingKeywordArgsCtor) {
int offset = args.length;
for (int i = 0; i < nkeywords; i++) {
self.__setattr__(keywords[i], allArgs[i + offset]);
}
}
return Py.None;
}
@Override
public PyObject __call__(PyObject[] args, String[] keywords) {
if (args.length < 1) {
throw Py.TypeError("constructor requires self argument");
}
PyObject[] newArgs = new PyObject[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, newArgs.length);
return __call__(args[0], newArgs, keywords);
}
protected void constructProxy(PyObject obj, Constructor<?> ctor, Object[] args, Class<?> proxy) {
// Do the actual constructor call
Object jself = null;
ThreadState ts = Py.getThreadState();
try {
ts.pushInitializingProxy(obj);
try {
jself = ctor.newInstance(args);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof InstantiationException) {
Class<?> sup = proxy.getSuperclass();
String msg = "Constructor failed for Java superclass";
if (sup != null) {
msg += " " + sup.getName();
}
throw Py.TypeError(msg);
} else
throw Py.JavaError(e);
} catch (Throwable t) {
throw Py.JavaError(t);
}
} finally {
ts.popInitializingProxy();
}
obj.javaProxy = jself;
}
@Override
public PyObject _doget(PyObject container, PyObject wherefound) {
if (container == null) {
return this;
}
return new PyMethod(this, container, wherefound);
}
@Override
public String toString() {
// printArgs();
return "<java constructor " + __name__ + " " + Py.idstr(this) + ">";
}
}