-public abstract class JS {
- public static final JS METHOD = new JS() { };
-
- public JS.Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + getClass().getName() +")"); }
- public JS get(JS key) throws JSExn { return null; }
- public void put(JS key, JS val) throws JSExn { throw new JSExn("" + key + " is read only (class=" + getClass().getName() +")"); }
-
- public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
- throw new JSExn("method not found (" + JS.debugToString(method) + ")");
- }
- public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
- throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
- }
- public InputStream getInputStream() throws IOException {
- throw new IOException("this object doesn't have a stream associated with it " + getClass().getName() + ")");
- }
-
- public final JS unclone() { return _unclone(); }
- public final JS jsclone() throws JSExn { return new Clone(this); }
- public final boolean hasTrap(JS key) { return getTrap(key) != null; }
- public final boolean equals(Object o) { return this == o || ((o instanceof JS) && jsequals((JS)o)); }
- // Discourage people from using toString()
- public final String toString() { return "JS Object [class=" + getClass().getName() + "]"; }
-
- // Package private methods
- Trap getTrap(JS key) { return null; }
- void putTrap(JS key, Trap value) throws JSExn { throw new JSExn("traps cannot be placed on this object (class=" + this.getClass().getName() +")"); }
- String coerceToString() throws JSExn { throw new JSExn("can't coerce to a string (class=" + getClass().getName() +")"); }
- boolean jsequals(JS o) { return this == o; }
- JS _unclone() { return this; }
-
- public static class O extends JS implements Cloneable {
- private Hash entries;
-
- public Enumeration keys() throws JSExn { return entries == null ? (Enumeration)EMPTY_ENUMERATION : (Enumeration)new JavaEnumeration(null,entries.keys()); }
- public JS get(JS key) throws JSExn { return entries == null ? null : (JS)entries.get(key, null); }
- public void put(JS key, JS val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); }
-
- /** retrieve a trap from the entries hash */
- final Trap getTrap(JS key) {
- return entries == null ? null : (Trap)entries.get(key, Trap.class);
+public interface JS {
+
+ /** Returns an enumeration of the keys in this object. */
+ public JS.Enumeration keys() throws JSExn;
+
+ /** Return the value associated with the given key. */
+ public JS get(JS key) throws JSExn;
+
+ /** Store a specific value against the given key. */
+ public void put(JS key, JS value) throws JSExn;
+
+ /** Calls a specific method with given arguments on this object.
+ * An exception is thrown if there is no such method or the
+ * arguments are invalid. */
+ public JS call(JS method, JS[] args) throws JSExn;
+
+ /** Returns the names of the formal arguments, if any.
+ * This function must never return a null object. */
+ public String[] getFormalArgs();
+
+ /** Put a value to the given key, calling any write traps that have
+ * been placed on the key.
+ *
+ * This function may block for an indeterminate amount of time.
+ *
+ * Write traps may change the final value before it is put, thus
+ * be careful to read the latest value from this function's return
+ * before doing any more work with it.
+ */
+ public JS putAndTriggerTraps(JS key, JS value) throws JSExn;
+
+ /** Gets the value for a given key, calling any read traps that have
+ * been placed on the key.
+ *
+ * This function may block for an indeterminate amount of time.
+ */
+ public JS getAndTriggerTraps(JS key) throws JSExn;
+
+ /** Calls the write traps placed on a given key with the given value
+ * and returns the result without saving it to this object's key store. */
+ public JS justTriggerTraps(JS key, JS value) throws JSExn;
+
+ public void addTrap(JS key, JS function) throws JSExn;
+ public void delTrap(JS key, JS function) throws JSExn;
+ public Trap getTrap(JS key) throws JSExn;
+
+ // FIXME: consider renaming/removing these
+ public JS unclone();
+ public String coerceToString() throws JSExn;
+
+
+ // Implementations ////////////////////////////////////////////////////////
+
+ /** Provides the lightest possible implementation of JS, throwing
+ * exceptions on every mutable operation. */
+ public static class Immutable implements JS {
+ private static final String[] emptystr = new String[0];
+
+ public JS unclone() { return this; }
+ public JS.Enumeration keys() throws JSExn {
+ throw new JSExn("object has no key set, class ["+ getClass().getName() +"]"); }
+ public JS get(JS key) throws JSExn { return null; }
+ public void put(JS key, JS val) throws JSExn {
+ throw new JSExn("'" + key + "' is read only on class ["+ getClass().getName() +"]"); }
+
+ public JS call(JS method, JS[] args) throws JSExn {
+ if (method == null) throw new JSExn( "object cannot be called, class ["+ getClass().getName() +"]");
+ throw new JSExn("method not found: " + JSU.str(method) + " class="+this.getClass().getName());