package org.python.core; import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; /** * Implements type checking and return type coercion for a data descriptor. A * subclass must at least implement invokeGet which is called in __get__ * operations. If the descriptor supports setting and deleting, the subclass * must also override invokeSet and invokeDel respectively. When implementing * those methods, their respective implementsDescr* methods should be overriden * as well. */ @ExposedType(name = "getset_descriptor", base = PyObject.class, isBaseType = false) public abstract class PyDataDescr extends PyDescriptor { protected Class ofType; private String doc; /** * @param onType - * the type the descriptor belongs to * @param name - * the name of the descriptor on descriptor type * @param ofType - * the type returned by the descriptor */ public PyDataDescr(PyType onType, String name, Class ofType, String doc) { this(name, ofType, doc); setType(onType); } /** * This constructor does not initialize the type the descriptor belongs to. setType must be * called before this descriptor can be used. * * @param name - * the name of the descriptor on descriptor type * @param ofType - * the type returned by the descriptor */ public PyDataDescr(String name, Class ofType, String doc) { this.name = name; this.ofType = ofType; this.doc = doc; } /** * Sets the type the descriptor belongs to. */ public void setType(PyType onType) { dtype = onType; } @Override public PyObject __get__(PyObject obj, PyObject type) { return getset_descriptor___get__(obj, type); } @ExposedMethod(defaults = "null") public PyObject getset_descriptor___get__(PyObject obj, PyObject type) { if(obj != null) { checkGetterType(obj.getType()); return Py.java2py(invokeGet(obj)); } return this; } public abstract Object invokeGet(PyObject obj); @Override public void __set__(PyObject obj, PyObject value) { getset_descriptor___set__(obj, value); } @ExposedMethod public void getset_descriptor___set__(PyObject obj, PyObject value) { checkGetterType(obj.getType()); // XXX: We may want to special case value being PyUnicode and ofType being String // (then explicitly value.encode() first) Object converted = value.__tojava__(ofType); if(converted == Py.NoConversion) { throw Py.TypeError(String.format("unsupported type for assignment to %s: '%.200s'", name, value.getType().fastGetName())); } invokeSet(obj, converted); } public void invokeSet(PyObject obj, Object converted) { throw new UnsupportedOperationException("Must be overriden by a subclass"); } @Override public void __delete__(PyObject obj) { getset_descriptor___delete__(obj); } @ExposedMethod public void getset_descriptor___delete__(PyObject obj) { if(obj != null) { checkGetterType(obj.getType()); invokeDelete(obj); } } public void invokeDelete(PyObject obj) { throw new UnsupportedOperationException("Must be overriden by a subclass"); } @Override public boolean isDataDescr() { return true; } @Override public String toString() { return String.format("<attribute '%s' of '%s' objects>", name, dtype.fastGetName()); } @ExposedGet(name = "__doc__") public String getDoc() { return doc; } /** * Return the name this descriptor is exposed as. * * @return a name String */ @ExposedGet(name = "__name__") public String getName() { return name; } /** * Return the owner class of this descriptor. * * @return this descriptor's owner */ @ExposedGet(name = "__objclass__") public PyObject getObjClass() { return dtype; } }