new js api
[org.ibex.core.git] / src / org / ibex / js / JSArray.java
1 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] 
2 package org.ibex.js; 
3
4 import org.ibex.util.*; 
5 import java.util.*;
6
7 /** A JavaScript JSArray */
8 public class JSArray extends JS.BT {
9     private static final Object NULL = new Object();
10     
11     public JSArray() { }
12     public JSArray(int size) { setSize(size); }
13     
14     /*private static int intVal(Object o) {
15         if (o instanceof Number) {
16             int intVal = ((Number)o).intValue();
17             if (intVal == ((Number)o).doubleValue()) return intVal;
18             return Integer.MIN_VALUE;
19         }
20         if (!(o instanceof String)) return Integer.MIN_VALUE;
21         String s = (String)o;
22         for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
23         return Integer.parseInt(s);
24     }*/
25     
26     public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
27         if(isString(method)) {
28             //#switch(JS.toString(method))
29             case "pop": {
30                 int oldSize = size();
31                 if(oldSize == 0) return null;
32                 return removeElementAt(oldSize-1);
33             }
34             case "reverse": return reverse();
35             case "toString": return join(",");
36             case "shift":
37                 if(length() == 0) return null;
38                 return removeElementAt(0);
39             case "join":
40                 return join(nargs == 0 ? "," : JS.toString(a0));
41             case "sort":
42                 return sort(nargs < 1 ? null : a0);
43             case "slice":
44                 int start = toInt(nargs < 1 ? null : a0);
45                 int end = nargs < 2 ? length() : toInt(a1);
46                 return slice(start, end);
47             case "push": {
48                 int oldSize = size();
49                 for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],oldSize+i);
50                 return N(oldSize + nargs);
51             }
52             case "unshift":
53                 for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],i);
54                 return N(size());
55             case "splice":
56                 JSArray array = new JSArray();
57                 for(int i=0; i<nargs; i++) array.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
58                 return splice(array);
59             //#end
60         }
61         return super.callMethod(method, a0, a1, a2, rest, nargs);
62     }
63         
64     public JS get(JS key) throws JSExn {
65         if (isInt(key)) {
66             int i = toInt(key);
67             if (i < 0 || i >= size()) return null;
68             return elementAt(i);
69         }
70         if(isString(key)) {
71             //#switch(JS.toString(key))
72             case "pop": return METHOD;
73             case "reverse": return METHOD;
74             case "toString": return METHOD;
75             case "shift": return METHOD;
76             case "join": return METHOD;
77             case "sort": return METHOD;
78             case "slice": return METHOD;
79             case "push": return METHOD;
80             case "unshift": return METHOD;
81             case "splice": return METHOD;
82             case "length": return N(size());
83             //#end
84         }
85         return super.get(key);
86     }
87
88     public void put(JS key, JS val) throws JSExn {
89         if (isInt(key)) {
90             int i = toInt(key);
91             int oldSize = size();
92             if(i < oldSize) {
93                 setElementAt(val,i);
94             } else {
95                 if(i > oldSize) setSize(i);
96                 insertElementAt(val,i);
97             }
98         }
99         if(isString(key)) {
100             if (JS.toString(key).equals("length")) {
101                 setSize(JS.toInt(val));
102                 return;
103             }
104         }
105         super.put(key,val);
106     }
107
108     // FIXME: Needs to include super's keys
109     public Enumeration keys() {
110         return new Enumeration() {
111                 private int n = size();
112                 public boolean hasMoreElements() { return n > 0; }
113                 public Object nextElement() {
114                     if(n == 0) throw new NoSuchElementException();
115                     return new Integer(--n);
116                 }
117             };
118     }
119
120     public final void setSize(int newSize) {
121         // FEATURE: This could be done a lot more efficiently in BalancedTree
122         int oldSize = size();
123         for(int i=oldSize;i<newSize;i++) insertElementAt(null,i);
124         for(int i=oldSize-1;i>=newSize;i--) removeElementAt(i);
125     }
126     
127     public final int length() { return size(); }
128     public final JS elementAt(int i) { 
129         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
130         JS o = (JS) getNode(i);
131         return o == NULL ? null : o;
132     }
133     public final void addElement(JS o) { 
134         insertNode(size(),o==null ? NULL : o);
135     }
136     public final void setElementAt(JS o, int i) {
137         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
138         replaceNode(i,o==null ? NULL : o);
139     }
140     public final void insertElementAt(JS o, int i) {
141         if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
142         insertNode(i,o==null ? NULL : o);
143     }
144     public final JS removeElementAt(int i) {
145         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
146         JS o = (JS) deleteNode(i);
147         return o == NULL ? null : o;
148     }
149     
150     public final int size() { return treeSize(); }
151     
152     private JS join(String sep) throws JSExn {
153         int length = size();
154         if(length == 0) return JS.S("");
155         StringBuffer sb = new StringBuffer(64);
156         int i=0;
157         while(true) {
158             JS o = elementAt(i);
159             if(o != null) sb.append(JS.toString(o));
160             if(++i == length) break;
161             sb.append(sep);
162         }
163         return JS.S(sb.toString());
164     }
165     
166     // FEATURE: Implement this more efficiently
167     private JS reverse() {
168         int size = size();
169         if(size < 2) return this;
170         Vec vec = toVec();
171         clear();
172         for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt((JS)vec.elementAt(i),j);
173         return this;
174     }
175     
176     private JS slice(int start, int end) {
177         int length = length();
178         if(start < 0) start = length+start;
179         if(end < 0) end = length+end;
180         if(start < 0) start = 0;
181         if(end < 0) end = 0;
182         if(start > length) start = length;
183         if(end > length) end = length;
184         JSArray a = new JSArray(end-start);
185         for(int i=0;i<end-start;i++)
186             a.setElementAt(elementAt(start+i),i);
187         return a;
188     }
189     
190     private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() {
191         public int compare(Object a, Object b) {
192             try {
193                 return JS.toString((JS)a).compareTo(JS.toString((JS)b));
194             } catch(JSExn e) {
195                 // FIXME: See emca about this
196                 throw new RuntimeException(e.toString());
197             }
198         }
199     };
200     private JS sort(JS tmp) throws JSExn {
201         Vec vec = toVec();
202         if(tmp instanceof JS) {
203             final JS jsFunc = (JS) tmp;
204             vec.sort(new Vec.CompareFunc() {
205                 public int compare(Object a, Object b) {
206                     try {
207                         return JS.toInt(jsFunc.call((JS)a, (JS)b, null, null, 2));
208                     } catch (JSExn e) {
209                         // FIXME: Check ecma to see what we should do here
210                         throw new RuntimeException(e.toString());
211                     }
212                 }
213             });
214         } else {
215             vec.sort(defaultSort);
216         }
217         setFromVec(vec);
218         return this;
219     }
220     
221     private JS splice(JSArray args) throws JSExn {
222         int oldLength = length();
223         int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
224         int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
225         int newCount = args.length() - 2;
226         if(newCount < 0) newCount = 0;
227         if(start < 0) start = oldLength+start;
228         if(start < 0) start = 0;
229         if(start > oldLength) start = oldLength;
230         if(deleteCount < 0) deleteCount = 0;
231         if(deleteCount > oldLength-start) deleteCount = oldLength-start;
232         int newLength = oldLength - deleteCount + newCount;
233         int lengthChange = newLength - oldLength;
234         JSArray ret = new JSArray(deleteCount);
235         for(int i=0;i<deleteCount;i++)
236             ret.setElementAt(elementAt(start+i),i);
237         if(lengthChange > 0) {
238             setSize(newLength);
239             for(int i=newLength-1;i>=start+newCount;i--)
240                 setElementAt(elementAt(i-lengthChange),i);
241         } else if(lengthChange < 0) {
242             for(int i=start+newCount;i<newLength;i++)
243                 setElementAt(elementAt(i-lengthChange),i);
244             setSize(newLength);
245         }
246         for(int i=0;i<newCount;i++)
247             setElementAt(args.elementAt(i+2),start+i);
248         return ret;
249     }
250     
251     protected Vec toVec() {
252         int count = size();
253         Vec vec = new Vec();
254         vec.setSize(count);
255         for(int i=0;i<count;i++) {
256             Object o = getNode(i);
257             vec.setElementAt(o == NULL ? null : o,i);
258         }
259         return vec;
260     }
261     
262     protected void setFromVec(Vec vec) {
263         int count = vec.size();
264         clear();
265         for(int i=0;i<count;i++) {
266             Object o = vec.elementAt(i);
267             insertNode(i,o==null ? NULL : o);
268         }
269     }
270     
271     String coerceToString() throws JSExn { return JS.toString(join(",")); }
272 }