renamed Script to JSU
[org.ibex.js.git] / src / org / ibex / js / JSArray.java
index 07cdc40..8e2fdd2 100644 (file)
-// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] 
+// Copyright 2000-2005 the Contributors, as shown in the revision logs.
+// Licensed under the Apache Public Source License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+
 package org.ibex.js; 
 
+import java.io.InputStream;
 import org.ibex.util.*; 
-import java.util.*;
 
 /** A JavaScript JSArray */
-public class JSArray extends JS {
-
-    private static final Object NULL = new Object();
-    private Vec vec = new Vec();
-
-    public JSArray() { }
-    public JSArray(int size) { setSize(size); }
-    
-    private static int intVal(Object o) {
-        if (o instanceof Number) {
-            int intVal = ((Number)o).intValue();
-            if (intVal == ((Number)o).doubleValue()) return intVal;
-            return Integer.MIN_VALUE;
+class JSArray extends Basket.Array implements JS, Basket.CompareFunc {
+    private static final JS.Method METHOD = new JS.Method();
+    private static final String[] empty = new String[0];
+
+    JSArray() { }
+    JSArray(int size) { super(size); }
+    JSArray(JS[] args) { super(args.length); addAll(args); }
+    JSArray(JS arg) { super(1); add(arg); }
+
+    public JS unclone() { return this; }
+    public JS.Enumeration keys() throws JSExn {
+        return new Enumeration(null) {
+            private int n = 0;
+            public boolean _hasNext() { return n < size(); }
+            public JS _next() { return JSU.N(n++); }
+        };
+    }
+    public JS get(JS key) throws JSExn {
+        if (key == null || !(key instanceof JSNumber.I)) {
+            //#switch(JSU.str(key))
+            case "pop": return METHOD;
+            case "reverse": return METHOD;
+            case "toString": return METHOD;
+            case "shift": return METHOD;
+            case "join": return METHOD;
+            case "sort": return METHOD;
+            case "slice": return METHOD;
+            case "push": return METHOD;
+            case "unshift": return METHOD;
+            case "splice": return METHOD;
+            case "length": return JSU.N(size());
+            //#end
+            throw new JSExn("arrays only support positive integer keys, can not use: "+JSU.str(key));
         }
-        if (!(o instanceof String)) return Integer.MIN_VALUE;
-        String s = (String)o;
-        for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
-        return Integer.parseInt(s);
+        return (JS)get(((JSNumber.I)key).toInt());
     }
-    
-    public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
-        //#switch(method)
-        case "pop": {
-            int oldSize = size();
-            if(oldSize == 0) return null;
-            return removeElementAt(oldSize-1);
-        }
-        case "reverse": return reverse();
+    public void put(JS key, JS val) throws JSExn {
+        if (JSU.str(key).equals("length")) { setSize(JSU.toInt(val)); }
+
+        if (key == null || !(key instanceof JSNumber.I)) throw new JSExn(
+            "arrays only support positive integer keys, can not use: "+JSU.str(key));
+        int i = ((JSNumber.I)key).toInt();
+        if (i < 0) throw new JSExn("arrays can not use negative integer keys "+i);
+        size(i + 1); while (size() < i) add(null);
+        set(i, val);
+    }
+    public InputStream getInputStream() { return null; }
+
+    public String[] getFormalArgs() { return empty; }
+    public String coerceToString() { return "array"; } // FIXME
+
+    public boolean hasKey(JS key) {
+        if (key == null || !(key instanceof JSNumber.I)) return false;
+        int i = ((JSNumber.I)key).toInt();
+        return 0 <= i && i < size();
+    }
+
+    public Object run(Object o) throws JSExn { return call(null); }
+    public void pause() throws NotPausableException { throw new NotPausableException(); }
+    public JS call(JS[] args) throws JSExn { throw new JSExn("can not call an array as a function"); }
+    public JS call(JS method, JS[] args) throws JSExn {
+        //#switch(JSU.str(method))
+        case "pop": return size() == 0 ? null : (JS)remove(size() - 1);
+        case "push": addAll(args); return JSU.N(size());
+        case "reverse": reverse(); return this;
         case "toString": return join(",");
-        case "shift":
-            if(length() == 0) return null;
-            return removeElementAt(0);
-        case "join":
-            return join(nargs == 0 ? "," : JS.toString(a0));
-        case "sort":
-            return sort(nargs < 1 ? null : a0);
+        case "shift": return size() == 0 ? null : (JS)remove(0);
+        case "join": return join(args.length == 0 ? "," : JSU.str(args[0]));
+        case "sort": return sort(args.length == 0 ? null : args[0]);
         case "slice":
-            int start = toInt(nargs < 1 ? null : a0);
-            int end = nargs < 2 ? length() : toInt(a1);
+            int start = JSU.toInt(args.length < 1 ? null : args[0]);
+            int end = args.length < 2 ? size() : JSU.toInt(args[1]);
             return slice(start, end);
-        case "push": {
-            int oldSize = size();
-            for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],oldSize+i);
-            return N(oldSize + nargs);
-        }
         case "unshift":
-            for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],i);
-            return N(size());
-        case "splice":
-            JSArray array = new JSArray();
-            for(int i=0; i<nargs; i++) array.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
-            return splice(array);
+            for (int i=0; i < args.length; i++) add(i, args[i]);
+            return JSU.N(size());
+        case "splice": return splice(args);
         //#end
-        return super.callMethod(method, a0, a1, a2, rest, nargs);
-    }
-        
-    public Object get(Object key) throws JSExn {
-        int i = intVal(key);
-        if (i != Integer.MIN_VALUE) {
-            if (i < 0 || i >= size()) return null;
-            return elementAt(i);
-        }
-        //#switch(key)
-        case "pop": return METHOD;
-        case "reverse": return METHOD;
-        case "toString": return METHOD;
-        case "shift": return METHOD;
-        case "join": return METHOD;
-        case "sort": return METHOD;
-        case "slice": return METHOD;
-        case "push": return METHOD;
-        case "unshift": return METHOD;
-        case "splice": return METHOD;
-        case "length": return N(size());
-        //#end
-        return super.get(key);
-    }
 
-    public void put(Object key, Object val) throws JSExn {
-        if (key.equals("length")) setSize(toInt(val));
-        int i = intVal(key);
-        if (i == Integer.MIN_VALUE)
-            super.put(key, val);
-        else {
-            int oldSize = size();
-            if(i < oldSize) {
-                setElementAt(val,i);
-            } else {
-                if(i > oldSize) setSize(i);
-                insertElementAt(val,i);
-            }
-        }
+        throw new JSExn("arrays have no function: "+JSU.str(method));
     }
 
-    public Enumeration keys() {
-        return new Enumeration() {
-                private int n = size();
-                public boolean hasMoreElements() { return n > 0; }
-                public Object nextElement() {
-                    if(n == 0) throw new NoSuchElementException();
-                    return new Integer(--n);
-                }
-            };
-    }
+    public JS putAndTriggerTraps(JS key, JS val) throws JSExn { put(key, val); return val; }
+    public JS getAndTriggerTraps(JS key) throws JSExn { return get(key); }
+    public JS justTriggerTraps(JS key, JS val) throws JSExn { return val; }
 
-    public final void setSize(int newSize) {
-        // FEATURE: This could be done a lot more efficiently in BalancedTree
-        int oldSize = size();
-        for(int i=oldSize;i<newSize;i++) insertElementAt(null,i);
-        for(int i=oldSize-1;i>=newSize;i--) removeElementAt(i);
-    }
-    
-    public final int length() { return size(); }
-    public final Object elementAt(int i) { 
-        if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
-        Object o = vec.elementAt(i);
-        return o == NULL ? null : o;
-    }
-    public final void addElement(Object o) { 
-        vec.insertElementAt(o==null ? NULL : o, size());
-    }
-    public final void setElementAt(Object o, int i) {
-        if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
-        vec.setElementAt(o==null ? NULL : o,i);
-    }
-    public final void insertElementAt(Object o, int i) {
-        if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
-        vec.insertElementAt(o==null ? NULL : o, i);
-    }
-    public final Object removeElementAt(int i) {
-        if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
-        Object o = vec.elementAt(i);
-        vec.removeElementAt(i);
-        return o == NULL ? null : o;
+    public void addTrap(JS k, JS f) throws JSExn { throw new JSExn("arrays do not support traps"); }
+    public void delTrap(JS k, JS f) throws JSExn { throw new JSExn("arrays do not support traps"); }
+    public JS.Trap getTrap(JS k) throws JSExn { throw new JSExn("arrays do not support traps"); }
+
+    /** FIXME: move to specialised ArrayStore superclass. */
+    public void addAll(JS[] entries) { for (int i=0; i < entries.length; i++) add(entries[i]); }
+
+    public void setSize(int newSize) {
+        size(newSize);
+        for (int i=size(); i < newSize; i++) add(null);
+        for (int i=size() - 1; i >= newSize; i--) remove(i);
     }
-    
-    public final int size() { return vec.size(); }
-    public String typeName() { return "array"; }
-        
-    private Object join(String sep) {
+
+
+    // ECMA Implementation ////////////////////////////////////////////////////
+
+    private JS join(String sep) throws JSExn {
         int length = size();
-        if(length == 0) return "";
+        if(length == 0) return JSU.S("");
         StringBuffer sb = new StringBuffer(64);
         int i=0;
         while(true) {
-            Object o = elementAt(i);
-            if(o != null) sb.append(JS.toString(o));
+            JS o = (JS)get(i);
+            if(o != null) sb.append(JSU.toString(o));
             if(++i == length) break;
             sb.append(sep);
         }
-        return sb.toString();
-    }
-    
-    // FEATURE: Implement this more efficiently
-    private Object reverse() {
-        int size = size();
-        if(size < 2) return this;
-        Vec vec = toVec();
-        vec.removeAllElements();
-        for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt(vec.elementAt(i),j);
-        return this;
+        return JSU.S(sb.toString());
     }
-    
-    private Object slice(int start, int end) {
-        int length = length();
+    private JS slice(int start, int end) {
+        int length = size();
         if(start < 0) start = length+start;
         if(end < 0) end = length+end;
         if(start < 0) start = 0;
@@ -177,45 +133,16 @@ public class JSArray extends JS {
         if(start > length) start = length;
         if(end > length) end = length;
         JSArray a = new JSArray(end-start);
-        for(int i=0;i<end-start;i++)
-            a.setElementAt(elementAt(start+i),i);
+        for(int i=0;i<end-start;i++) // FIXME: with ArrayStore do System.arraycopy()
+            a.set(i, get(start+i));
         return a;
     }
-    
-    private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() {
-        public int compare(Object a, Object b) {
-            return JS.toString(a).compareTo(JS.toString(b));
-        }
-    };
-    private Object sort(Object tmp) throws JSExn {
-        Vec vec = toVec();
-        if(tmp instanceof JS) {
-            final JSArray funcArgs = new JSArray(2);
-            final JS jsFunc = (JS) tmp;
-            vec.sort(new Vec.CompareFunc() {
-                public int compare(Object a, Object b) {
-                    try {
-                        funcArgs.setElementAt(a,0);
-                        funcArgs.setElementAt(b,1);
-                        return JS.toInt(jsFunc.call(a, b, null, null, 2));
-                    } catch (Exception e) {
-                        // FIXME
-                        throw new JSRuntimeExn(e.toString());
-                    }
-                }
-            });
-        } else {
-            vec.sort(defaultSort);
-        }
-        setFromVec(vec);
-        return this;
-    }
-    
-    private Object splice(JSArray args) {
-        int oldLength = length();
-        int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
-        int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
-        int newCount = args.length() - 2;
+
+    private JS splice(JS[] args) throws JSExn {
+        int oldLength = size();
+        int start = JSU.toInt(args.length < 1 ? null : args[0]);
+        int deleteCount = JSU.toInt(args.length < 2 ? null : args[1]);
+        int newCount = args.length - 2;
         if(newCount < 0) newCount = 0;
         if(start < 0) start = oldLength+start;
         if(start < 0) start = 0;
@@ -225,23 +152,45 @@ public class JSArray extends JS {
         int newLength = oldLength - deleteCount + newCount;
         int lengthChange = newLength - oldLength;
         JSArray ret = new JSArray(deleteCount);
-        for(int i=0;i<deleteCount;i++)
-            ret.setElementAt(elementAt(start+i),i);
+        for(int i=0;i<deleteCount;i++) // FIXME: ArrayStore System.arraycopy()
+            ret.set(i, get(start+i));
         if(lengthChange > 0) {
             setSize(newLength);
             for(int i=newLength-1;i>=start+newCount;i--)
-                setElementAt(elementAt(i-lengthChange),i);
+                set(i, get(i-lengthChange));
         } else if(lengthChange < 0) {
             for(int i=start+newCount;i<newLength;i++)
-                setElementAt(elementAt(i-lengthChange),i);
+                set(i, get(i-lengthChange));
             setSize(newLength);
         }
         for(int i=0;i<newCount;i++)
-            setElementAt(args.elementAt(i+2),start+i);
+            set(start+i, args[i+2]);
         return ret;
     }
-    
-    public Vec toVec() { return (Vec)vec.clone(); }
-    public void setFromVec(Vec vec) { this.vec = (Vec)vec.clone(); }
-    public String toString() { return JS.toString(join(",")); }
+
+    private static final Basket.CompareFunc defaultSort = new Basket.CompareFunc() {
+        public int compare(Object a, Object b) {
+            try { return JSU.toString((JS)a).compareTo(JSU.toString((JS)b)); }
+            catch (JSExn e) { throw new JSExn.Wrapper(e); }
+        }
+    };
+    private JS sort(JS comparator) throws JSExn {
+        try {
+            if (comparator == null) sort(defaultSort);
+            else { sort = comparator; sort((CompareFunc)this); }
+            return this;
+        } catch (JSExn.Wrapper w) { throw w.unwrap();
+        } finally { sort = null; }
+    }
+
+    private JS sort = null;
+    private final JS[] sortargs = new JS[2];
+    public int compare(Object a, Object b) throws JSExn.Wrapper {
+        try {
+            sortargs[0] = (JS)a; sortargs[1] = (JS)b;
+            return JSU.toInt(sort.call(sortargs));
+        } catch (JSExn e) { throw new JSExn.Wrapper(e);
+        } finally { sortargs[0] = null; sortargs[1] = null; }
+    }
+
 }