This is continuation of my earlier post on script beans. On rethinking, I've made few (minor) changes to ScriptObject and Callable. The changes are:
invoke method in ScriptObject
-- this is used to directly invoke a method on a script object instead
of getting a method/function valued property using get
and then invoking Callable.call method. Why? Many dynamically typed
languages have Smalltalk's doesNotUnderstand equivalent. i.e., if a
method is not found, then a method missing handler is invoked.
- like JRuby's method_missing
and Groovy's invokeMethod.
ScriptObject.invoke
may be implemented by calling ScriptObject.get and Callable.call on result and
possibly incorporating language specific method missing method as well.
ScriptObject.put returns boolean to tell whether the put
was successful or not (may be read-only property was assigned or property addition
is not possible and so on).
import javax.script.ScriptException;
/**
* Any Java object supporting this interface can be
* accessed from scripts with "simpler" access pattern.
* For example, a script engine may support natural
* property/field access syntax for the properties exposed
* via this interface. We use this interface so that we
* can dynamically add/delete/modify fields exposed to
* scripts. Also, script engines may expose this interface for
* it's own objects.
*/
public interface ScriptObject {
// special value to denote no-result -- so that
// null could be used as proper result value
public static final Object UNDEFINED = new Object();
// empty object array
public static final Object[] EMPTY_ARRAY = new Object[0];
/*
* Returns all property names supported by this object.
* Property "name" is either a String or an Integer".
*/
public Object[] getIds();
/**
* Get the value of the named property.
*/
public Object get(String name);
/**
* Get the value of the "indexed" property.
* Returns UNDEFINED if the property does not exist.
*/
public Object get(int index);
/**
* Set the value of the named property. Returns
* whether the put was successful or not.
*/
public boolean put(String name, Object value);
/**
* Set the value of the indexed property. Returns
* whether the put was successful or not.
*/
public boolean put(int index, Object value);
/**
* Returns whether the named property exists or not.
*/
public boolean has(String name);
/**
* Returns whether the indexed property exists or not.
*/
public boolean has(int index);
/**
* Deletes the named property. Returns true on success.
*/
public boolean delete(String name);
/**
* Deletes the indexed property. Returns true on success.
*/
public boolean delete(int index);
/**
* Call the named method on this script object.
*/
public Object invoke(String name, Object... arguments)
throws NoSuchMethodException, ScriptException;
}
import javax.script.ScriptException;
/**
* This interface is used to represent "function/method" valued
* properties in ScriptObjects.
*/
public interface Callable {
/**
* Call the underlying function passing the given
* arguments and return the result.
*/
public Object call(Object... args) throws ScriptException;
}
import javax.script.ScriptException;
/**
* Simple dummy implementation of ScriptObject.
*/
public abstract class AbstractScriptObject
implements ScriptObject {
public Object[] getIds() {
return EMPTY_ARRAY;
}
public Object get(String name) {
return UNDEFINED;
}
public Object get(int index) {
return UNDEFINED;
}
public boolean put(String name, Object value) {
return false;
}
public boolean put(int index, Object value) {
return false;
}
public boolean has(String name) {
return false;
}
public boolean has(int index) {
return false;
}
public boolean delete(String name) {
return false;
}
public boolean delete(int index) {
return false;
}
public Object invoke(String name, Object... arguments)
throws NoSuchMethodException, ScriptException {
Object value = get(name);
if (value instanceof Callable) {
return ((Callable)value).call(arguments);
} else {
throw new NoSuchMethodException(name);
}
}
}
The invoke(...) method can be helpful for interface usability reasons, but I don't see why a MethodMissing kind of feature could not be implemented by having a Callable subclass following the Special Case pattern.
Excellent blog, BTW. I specially liked the Scala summary posts, I'm hoping that they will help to make the language more popular.
Posted by Rafael Ferreira on March 25, 2007 at 02:36 AM IST #
Posted by A. Sundararajan on March 25, 2007 at 12:52 PM IST #
Posted by Jochen "blackdrag" Theodorou on March 26, 2007 at 05:00 PM IST #