move to JS interface
[org.ibex.js.git] / src / org / ibex / js / JSArray.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.js; 
6
7 import java.io.InputStream;
8 import java.util.*;
9 import org.ibex.util.*; 
10 import org.ibex.util.Collections;
11
12 /** A JavaScript JSArray */
13 class JSArray extends ArrayList implements JS, Comparator {
14     private static final JS.Method METHOD = new JS.Method();
15     private static final String[] empty = new String[0];
16
17     JSArray() { }
18     JSArray(int size) { super(size); }
19     JSArray(JS[] args) { super(args.length); addAll(args); }
20     JSArray(JS arg) { super(1); add(arg); }
21
22     public JS unclone() { return this; }
23     public JS.Enumeration keys() throws JSExn { return new Enumeration.RandomAccessList(null, this); }
24     public JS get(JS key) throws JSExn {
25         if (key == null || !(key instanceof JSNumber.I)) {
26             //#switch(Script.str(key))
27             case "pop": return METHOD;
28             case "reverse": return METHOD;
29             case "toString": return METHOD;
30             case "shift": return METHOD;
31             case "join": return METHOD;
32             case "sort": return METHOD;
33             case "slice": return METHOD;
34             case "push": return METHOD;
35             case "unshift": return METHOD;
36             case "splice": return METHOD;
37             case "length": return Script.N(size());
38             //#end
39             throw new JSExn("arrays only support positive integer keys, can not use: "+Script.str(key));
40         }
41         return (JS)get(((JSNumber.I)key).toInt());
42     }
43     public void put(JS key, JS val) throws JSExn {
44         if (Script.str(key).equals("length")) { setSize(Script.toInt(val)); }
45
46         if (key == null || !(key instanceof JSNumber.I)) throw new JSExn(
47             "arrays only support positive integer keys, can not use: "+Script.str(key));
48         int i = ((JSNumber.I)key).toInt();
49         if (i < 0) throw new JSExn("arrays can not use negative integer keys "+i);
50         ensureCapacity(i + 1); while (size() < i) add(null);
51         set(i, val);
52     }
53     public InputStream getInputStream() { return null; }
54
55     public String[] getFormalArgs() { return empty; }
56     public String coerceToString() { return "array"; } // FIXME
57
58     public boolean hasKey(JS key) {
59         if (key == null || !(key instanceof JSNumber.I)) return false;
60         int i = ((JSNumber.I)key).toInt();
61         return 0 <= i && i < size();
62     }
63
64     public Object run(Object o) throws JSExn { return call(null); }
65     public void pause() throws NotPausableException { throw new NotPausableException(); }
66     public JS call(JS[] args) throws JSExn { throw new JSExn("can not call an array as a function"); }
67     public JS call(JS method, JS[] args) throws JSExn {
68         //#switch(Script.str(method))
69         case "pop": return size() == 0 ? null : (JS)remove(size() - 1);
70         case "push": addAll(args); return Script.N(size());
71         case "reverse": Collections.reverse(this); return this;
72         case "toString": return join(",");
73         case "shift": return size() == 0 ? null : (JS)remove(0);
74         case "join": return join(args.length == 0 ? "," : Script.str(args[0]));
75         case "sort": return sort(args.length == 0 ? null : args[0]);
76         case "slice":
77             int start = Script.toInt(args.length < 1 ? null : args[0]);
78             int end = args.length < 2 ? size() : Script.toInt(args[1]);
79             return slice(start, end);
80         case "unshift":
81             for (int i=0; i < args.length; i++) add(i, args[i]);
82             return Script.N(size());
83         case "splice": return splice(args);
84         //#end
85
86         throw new JSExn("arrays have no function: "+Script.str(method));
87     }
88
89     public JS putAndTriggerTraps(JS key, JS val) throws JSExn { put(key, val); return val; }
90     public JS getAndTriggerTraps(JS key) throws JSExn { return get(key); }
91     public JS justTriggerTraps(JS key, JS val) throws JSExn { return val; }
92
93     public void addTrap(JS k, JS f) throws JSExn { throw new JSExn("arrays do not support traps"); }
94     public void delTrap(JS k, JS f) throws JSExn { throw new JSExn("arrays do not support traps"); }
95     public JS.Trap getTrap(JS k) throws JSExn { throw new JSExn("arrays do not support traps"); }
96
97     /** FIXME: move to specialised ArrayStore superclass. */
98     public void addAll(JS[] entries) { for (int i=0; i < entries.length; i++) add(entries[i]); }
99
100     public void setSize(int newSize) {
101         ensureCapacity(newSize);
102         for (int i=size(); i < newSize; i++) add(null);
103         for (int i=size() - 1; i >= newSize; i--) remove(i);
104     }
105
106
107     // ECMA Implementation ////////////////////////////////////////////////////
108
109     private JS join(String sep) throws JSExn {
110         int length = size();
111         if(length == 0) return JS.S("");
112         StringBuffer sb = new StringBuffer(64);
113         int i=0;
114         while(true) {
115             JS o = (JS)get(i);
116             if(o != null) sb.append(Script.toString(o));
117             if(++i == length) break;
118             sb.append(sep);
119         }
120         return JS.S(sb.toString());
121     }
122  
123     private JS slice(int start, int end) {
124         int length = size();
125         if(start < 0) start = length+start;
126         if(end < 0) end = length+end;
127         if(start < 0) start = 0;
128         if(end < 0) end = 0;
129         if(start > length) start = length;
130         if(end > length) end = length;
131         JSArray a = new JSArray(end-start);
132         for(int i=0;i<end-start;i++) // FIXME: with ArrayStore do System.arraycopy()
133             a.set(i, get(start+i));
134         return a;
135     }
136
137     private JS splice(JS[] args) throws JSExn {
138         int oldLength = size();
139         int start = Script.toInt(args.length < 1 ? null : args[0]);
140         int deleteCount = Script.toInt(args.length < 2 ? null : args[1]);
141         int newCount = args.length - 2;
142         if(newCount < 0) newCount = 0;
143         if(start < 0) start = oldLength+start;
144         if(start < 0) start = 0;
145         if(start > oldLength) start = oldLength;
146         if(deleteCount < 0) deleteCount = 0;
147         if(deleteCount > oldLength-start) deleteCount = oldLength-start;
148         int newLength = oldLength - deleteCount + newCount;
149         int lengthChange = newLength - oldLength;
150         JSArray ret = new JSArray(deleteCount);
151         for(int i=0;i<deleteCount;i++) // FIXME: ArrayStore System.arraycopy()
152             ret.set(i, get(start+i));
153         if(lengthChange > 0) {
154             setSize(newLength);
155             for(int i=newLength-1;i>=start+newCount;i--)
156                 set(i, get(i-lengthChange));
157         } else if(lengthChange < 0) {
158             for(int i=start+newCount;i<newLength;i++)
159                 set(i, get(i-lengthChange));
160             setSize(newLength);
161         }
162         for(int i=0;i<newCount;i++)
163             set(start+i, args[i+2]);
164         return ret;
165     }
166
167     private JS sort(JS comparator) throws JSExn {
168         try {
169             if (comparator == null) Collections.sort(this);
170             else { sort = comparator; Collections.sort(this, this); }
171             return this;
172         } catch (JSExn.Wrapper w) { throw w.unwrap(); }
173     }
174
175     private JS sort = null;
176     private final JS[] sortargs = new JS[2];
177     public int compare(java.lang.Object a, java.lang.Object b) throws JSExn.Wrapper {
178         try {
179             sortargs[0] = (JS)a; sortargs[1] = (JS)b;
180             return Script.toInt(sort.call(sortargs));
181         } catch (JSExn e) { throw new JSExn.Wrapper(e);
182         } finally { sortargs[0] = null; sortargs[1] = null; }
183     }
184
185 }