Skip to content

Commit

Permalink
Added the ability to subclass Java classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed Dec 16, 2015
1 parent f1b99ee commit 068087b
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 40 deletions.
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ What it does:

* Implementing Java interfaces.

* Extending Java classes.

It *doesn't* currently support (this list is incomplete):

* Subclassing Python classes

* Extending native java classes.

* Generators

* with statements
Expand Down
9 changes: 7 additions & 2 deletions python/common/org/python/java/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ public org.python.types.Str __str__() {
__doc__ = "Implement __get__(self)."
)
public org.python.Object __get__(org.python.Object instance, org.python.Object klass) {
// System.out.println("__GET__ on native function " + this + " " + this.getClass());
return new org.python.java.Method(instance.toJava(), this);
}

Expand All @@ -134,8 +135,12 @@ public org.python.Object invoke(org.python.Object [] args, java.util.Map<java.la

public org.python.Object invoke(java.lang.Object instance, org.python.Object [] args, java.util.Map<java.lang.String, org.python.Object> kwargs) {
try {
// System.out.println("Native Function:" + this.name);
// System.out.println(" args:" + args);
// System.out.println("Native Function:" + this);
// System.out.println(" instance: " + instance + " " + instance.getClass());
// System.out.println(" args:");
// for (org.python.Object arg: args) {
// System.out.println(" " + arg);
// }
// System.out.println(" kwargs:" + kwargs);

java.lang.reflect.Method method = org.python.java.Function.selectMethod(this.methods, args, kwargs);
Expand Down
2 changes: 1 addition & 1 deletion python/common/org/python/java/Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public org.python.Object __getattribute__(java.lang.String name) {
if (value == null) {
try {
java.lang.Class java_class = java.lang.Class.forName(java_namespace + "." + name);
value = new org.python.java.Type(java_class);
value = new org.python.java.Type(org.python.types.Type.Origin.JAVA, java_class);
cls.attrs.put(name, value);
} catch (java.lang.ClassNotFoundException e) {
throw new org.python.exceptions.NameError(name);
Expand Down
5 changes: 5 additions & 0 deletions python/common/org/python/java/Object.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public Object(java.lang.Object object) {
this.object = object;
}

public Object(org.python.types.Type.Origin origin, java.lang.Object object) {
super(org.python.types.Type.Origin.JAVA, object.getClass());
this.object = object;
}

@org.python.Method(
__doc__ = ""
)
Expand Down
36 changes: 22 additions & 14 deletions python/common/org/python/java/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
public class Type extends org.python.types.Type {
java.util.Map<java.lang.String, java.lang.reflect.Constructor> constructors;

public Type(java.lang.Class klass) {
super(klass, org.python.types.Type.Origin.JAVA);

// System.out.println("NATIVE TYPE " + klass);
public Type(org.python.types.Type.Origin origin, java.lang.Class klass) {
super(origin, klass);

this.constructors = new java.util.HashMap<java.lang.String, java.lang.reflect.Constructor>();
for (java.lang.reflect.Constructor constructor: klass.getConstructors()) {
Expand Down Expand Up @@ -37,7 +35,9 @@ public org.python.Object __getattribute_null(java.lang.String name) {
// java.lang.Map doesn't differentiate between "doesn't exist"
// and "value is null"; so since we know the value is null, check
// to see if it is an explicit null (i.e., attribute doesn't exist)
// System.out.println("No class attr");
if (!this.attrs.containsKey(name)) {
// System.out.println("doing lookup...");
try {
value = new org.python.java.Function(this.klass, name);
} catch (org.python.exceptions.AttributeError ae) {
Expand All @@ -49,9 +49,13 @@ public org.python.Object __getattribute_null(java.lang.String name) {
value = null;
}
}
// If the field doesn't exist, store a value of null
// so that we don't try to look up the field again.
// If it does, store the field.
this.attrs.put(name, value);
}
}
// System.out.println("GETATTRIBUTE NATIVE TYPE " + this + " " + name + " = " + value);
return value;
}

Expand All @@ -67,7 +71,7 @@ public boolean __setattr_null(java.lang.String name, org.python.Object value) {

public org.python.Object invoke(org.python.Object [] args, java.util.Map<java.lang.String, org.python.Object> kwargs) {
try {
// System.out.println("NATIVE CONSTRUCTOR :" + this.klass);
// System.out.println("NATIVE CONSTRUCTOR :" + this.klass + " " + this.origin);
// System.out.println("ARGS:");
// for (org.python.Object arg: args) {
// System.out.println(" " + arg);
Expand All @@ -78,17 +82,22 @@ public org.python.Object invoke(org.python.Object [] args, java.util.Map<java.la
// System.out.println(" " + argname + " = " + kwargs.get(argname));
// }

java.lang.reflect.Constructor constructor = org.python.java.Function.selectMethod(this.constructors, args, kwargs);

// If there is still no match, raise an error.
if (constructor == null) {
throw new org.python.exceptions.RuntimeError(String.format("Parameter mismatch calling native Java constructor."));
}
java.lang.reflect.Constructor constructor;
if (this.origin == org.python.types.Type.Origin.EXTENSION) {
constructor = this.constructor;

java.lang.Object [] adjusted_args = org.python.java.Function.adjustArguments(constructor, args, kwargs);
return new org.python.java.Object(constructor.newInstance(args, kwargs));
} else {
constructor = org.python.java.Function.selectMethod(this.constructors, args, kwargs);
java.lang.Object [] adjusted_args = org.python.java.Function.adjustArguments(constructor, args, kwargs);

return new org.python.java.Object(constructor.newInstance(adjusted_args));
// If there is still no match, raise an error.
if (constructor == null) {
throw new org.python.exceptions.RuntimeError(String.format("Parameter mismatch calling native Java constructor."));
}

return new org.python.java.Object(constructor.newInstance(adjusted_args));
}
} catch (java.lang.IllegalAccessException e) {
throw new org.python.exceptions.RuntimeError("Illegal access to native Java constructor for " + this.klass);
} catch (java.lang.reflect.InvocationTargetException e) {
Expand All @@ -107,5 +116,4 @@ public org.python.Object invoke(org.python.Object [] args, java.util.Map<java.la
// System.out.println("CONSTRUCTOR DONE");
}
}

}
16 changes: 13 additions & 3 deletions python/common/org/python/types/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ public Function(
__doc__ = ""
)
public org.python.Object __get__(org.python.Object instance, org.python.Object klass) {
// System.out.println("__GET__ on function " + this + " " + this.getClass() + " " + instance + " " + instance.getClass());
if (instance != null && !(instance instanceof org.python.types.Module)) {
return new Method(instance, (org.python.types.Type) klass, this);
}
Expand Down Expand Up @@ -234,7 +235,16 @@ public org.python.Object invoke(org.python.Object [] args, java.util.Map<java.la
public org.python.Object invoke(org.python.Object instance, org.python.Object [] args, java.util.Map<java.lang.String, org.python.Object> kwargs) {
try {
// System.out.println("Function:" + this.method);
// System.out.println(" instance: " + instance);
// java.lang.String klass;
java.lang.Object java_instance;
if (instance == null) {
// klass = "NULL";
java_instance = null;
} else {
// klass = instance.getClass().getName();
java_instance = instance.toJava();
}
// System.out.println(" instance: " + instance + " " + klass);
// System.out.print(" args:");
// for (org.python.Object arg: args) {
// System.out.print(arg + ", ");
Expand All @@ -252,15 +262,15 @@ public org.python.Object invoke(org.python.Object instance, org.python.Object []

java.lang.Object [] adjusted_args = adjustArguments(instance, args, kwargs);
if (adjusted_args == null) {
return (org.python.Object) this.method.invoke(instance);
return (org.python.Object) this.method.invoke(java_instance);
} else {
// System.out.print(" args:");
// for (java.lang.Object arg: adjusted_args) {
// System.out.print(arg + ", ");
// }
// System.out.println();

return (org.python.Object) this.method.invoke(instance, adjusted_args);
return (org.python.Object) this.method.invoke(java_instance, adjusted_args);
}
} catch (java.lang.IllegalAccessException e) {
throw new org.python.exceptions.RuntimeError("Illegal access to Java method " + this.method);
Expand Down
10 changes: 9 additions & 1 deletion python/common/org/python/types/Object.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ public java.lang.String typeName() {
*
* klass is the underlying java class being represented by this object.
* In the case of a Python object, the klass is the Java manifestation of
* the object; when wrapping Java objects, the native class of the object is used.
* the object; when wrapping Java objects, the native class of the object
* is used.
*/
protected Object(org.python.types.Type.Origin origin, java.lang.Class klass) {
this.origin = origin;
if (origin != org.python.types.Type.Origin.PLACEHOLDER) {
this.attrs = new java.util.HashMap<java.lang.String, org.python.Object>();
if (klass == null) {
Expand Down Expand Up @@ -254,23 +256,28 @@ public org.python.Object __getattribute__(java.lang.String name) {

public org.python.Object __getattribute_null(java.lang.String name) {
// Look for local instance attributes first
// System.out.println("GETATTRIBUTE " + name + " on " + this);
// System.out.println("ATTRS " + this.attrs);
org.python.Object value = this.attrs.get(name);
org.python.types.Type cls = (org.python.types.Type) this.attrs.get("__class__");

if (value == null) {
// Look to the class for an attribute
// System.out.println("no instance attribute");
value = cls.__getattribute_null(name);
if (value == null) {
// System.out.println("no class attribute");
// Use the descriptor protocol
value = this.__getattr_null(name);
if (value == null) {
// System.out.println("no descriptor protocol");
// Still nothing - give up, and return a value
// that can be interpreted as an exception.
return null;
}
}
}
// System.out.println("GETATTRIBUTE " + name + " on " + this + " = " + value);
// Post-process the value retrieved.
return value.__get__(this, cls);
}
Expand All @@ -280,6 +287,7 @@ public org.python.Object __getattribute_null(java.lang.String name) {
args = {"instance", "klass"}
)
public org.python.Object __get__(org.python.Object instance, org.python.Object klass) {
// System.out.println("__GET__ on " + this + " " + this.getClass());
return this;
}

Expand Down
2 changes: 1 addition & 1 deletion python/common/org/python/types/PlaceholderType.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class PlaceholderType extends org.python.types.Type {
private java.util.ArrayList<org.python.Object> instances = new java.util.ArrayList<org.python.Object>();

PlaceholderType(java.lang.Class klass) {
super(klass, org.python.types.Type.Origin.PLACEHOLDER);
super(org.python.types.Type.Origin.PLACEHOLDER, klass);
}

public void add_reference(org.python.Object instance) {
Expand Down
75 changes: 61 additions & 14 deletions python/common/org/python/types/Type.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package org.python.types;

public class Type extends org.python.types.Object implements org.python.Callable {
public enum Origin {PLACEHOLDER, BUILTIN, PYTHON, JAVA};
public enum Origin {PLACEHOLDER, BUILTIN, PYTHON, JAVA, EXTENSION};
public java.lang.String PYTHON_TYPE_NAME;
private static java.util.Map<java.lang.Class, org.python.types.Type> known_types = new java.util.HashMap<java.lang.Class, org.python.types.Type>();

java.lang.reflect.Constructor constructor;
public java.lang.reflect.Constructor constructor;
public java.lang.Class klass;

/**
* Factory method to obtain Python classes from their Java counterparts
Expand All @@ -24,12 +25,17 @@ public static org.python.types.Type pythonType(java.lang.Class java_class) {
// otherwise, wrap it as a native Java type.
if (org.python.Object.class.isAssignableFrom(java_class)) {
if (java_class.getName().startsWith("org.python.types.")) {
python_type = new org.python.types.Type(java_class, Origin.BUILTIN);
python_type = new org.python.types.Type(Origin.BUILTIN, java_class);
} else {
python_type = new org.python.types.Type(java_class, Origin.PYTHON);
python_type = new org.python.types.Type(Origin.PYTHON, java_class);
}
} else {
python_type = new org.python.java.Type(java_class);
try {
java_class.getField("__VOC__");
python_type = new org.python.java.Type(Origin.EXTENSION, java_class);
} catch (NoSuchFieldException e) {
python_type = new org.python.java.Type(Origin.JAVA, java_class);
}
}
known_types.put(java_class, python_type);

Expand Down Expand Up @@ -62,16 +68,52 @@ public static org.python.Object toPython(java.lang.Object value) {
if (value == null) {
return org.python.types.NoneType.NONE;
} else {
if (org.python.Object.class.isAssignableFrom(value.getClass())) {
return (org.python.Object) value;
} else {
return new org.python.java.Object(value);
switch (value.getClass().getName()) {
case "boolean":
case "java.lang.Boolean":
return new org.python.types.Bool((boolean) value);

case "byte":
case "java.lang.Byte":
return new org.python.types.Int((byte) value);

case "char":
case "java.lang.Char":
return new org.python.types.Str((char) value);

case "short":
case "java.lang.Short":
return new org.python.types.Int((short) value);

case "int":
case "java.lang.Integer":
return new org.python.types.Int((int) value);

case "long":
case "java.lang.Long":
return new org.python.types.Int((long) value);

case "float":
case "java.lang.Float":
return new org.python.types.Float((float) value);

case "double":
case "java.lang.Double":
return new org.python.types.Float((double) value);

case "java.lang.String":
return new org.python.types.Str((java.lang.String) value);

default:
if (org.python.Object.class.isAssignableFrom(value.getClass())) {
return (org.python.Object) value;
} else {
return new org.python.java.Object(value);
}
}
}
}

public java.lang.Class klass;
public Origin origin;

/**
* A utility method to update the internal value of this object.
Expand All @@ -83,8 +125,9 @@ void setValue(org.python.Object obj) {
this.klass = ((org.python.types.Type) obj).klass;
}

public Type(java.lang.Class klass, Origin origin) {
public Type(Origin origin, java.lang.Class klass) {
super(origin, null);

if (origin != Origin.PLACEHOLDER) {
this.klass = klass;

Expand All @@ -95,7 +138,7 @@ public Type(java.lang.Class klass, Origin origin) {

if (origin == Origin.BUILTIN) {
org.Python.initializeModule(klass, this.attrs);
} else if (origin == Origin.PYTHON) {
} else if (origin == Origin.PYTHON || origin == Origin.EXTENSION) {
try {
this.constructor = this.klass.getConstructor(org.python.Object[].class, java.util.Map.class);
} catch (java.lang.NoSuchMethodException e) {
Expand All @@ -105,7 +148,7 @@ public Type(java.lang.Class klass, Origin origin) {
}

public Type(java.lang.Class klass) {
this(klass, Origin.PYTHON);
this(Origin.PYTHON, klass);
}

public void add_reference(org.python.Object instance) {
Expand All @@ -130,17 +173,21 @@ public org.python.Object __getattribute_null(java.lang.String name) {
// If the key *doesn't* exist in the attributes dictionary,
// try to look it up. If it doesn't exist as a field, then
// store a null to represent this fact, so we won't look again.
// System.out.println("No class attr");
if (!this.attrs.containsKey(name)) {
// System.out.println("doing lookup...");
try {
value = new org.python.java.Field(klass.getField(name));
} catch (java.lang.NoSuchFieldException e) {
value = null;
}
// If the field doesn't exist, store a value of null
// so that we don't try to look up the field again.
// If it does, store the field.
this.attrs.put(name, value);
}
}
// System.out.println("GETATTRIBUTE CLASS " + this + " " + name + " = " + value);
return value;
}

Expand Down
Loading

0 comments on commit 068087b

Please sign in to comment.