a9483d0dbc490b13972a060cdf02fd545f354921
[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.O {
9     private static final Object NULL = new Object();
10     private final BalancedTree bt = new BalancedTree();
11     
12     public JSArray() { }
13     public JSArray(int size) { setSize(size); }
14     
15     /*private static int intVal(Object o) {
16         if (o instanceof Number) {
17             int intVal = ((Number)o).intValue();
18             if (intVal == ((Number)o).doubleValue()) return intVal;
19             return Integer.MIN_VALUE;
20         }
21         if (!(o instanceof String)) return Integer.MIN_VALUE;
22         String s = (String)o;
23         for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
24         return Integer.parseInt(s);
25     }*/
26     
27     public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
28         //#jswitch(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         return super.callMethod(method, a0, a1, a2, rest, nargs);
61     }
62         
63     public JS get(JS key) throws JSExn {
64         if (isInt(key)) {
65             int i = toInt(key);
66             if (i < 0 || i >= size()) return null;
67             return elementAt(i);
68         }
69         //#jswitch(key)
70         case "pop": return METHOD;
71         case "reverse": return METHOD;
72         case "toString": return METHOD;
73         case "shift": return METHOD;
74         case "join": return METHOD;
75         case "sort": return METHOD;
76         case "slice": return METHOD;
77         case "push": return METHOD;
78         case "unshift": return METHOD;
79         case "splice": return METHOD;
80         case "length": return N(size());
81         //#end
82         return super.get(key);
83     }
84
85     public void put(JS key, JS val) throws JSExn {
86         if (isInt(key)) {
87             int i = toInt(key);
88             int oldSize = size();
89             if(i < oldSize) {
90                 setElementAt(val,i);
91             } else {
92                 if(i > oldSize) setSize(i);
93                 insertElementAt(val,i);
94             }
95             return;
96         }
97         if(isString(key)) {
98             if (JS.toString(key).equals("length")) {
99                 setSize(JS.toInt(val));
100                 return;
101             }
102         }
103         super.put(key,val);
104     }
105
106     public Enumeration keys() throws JSExn {
107         return new Enumeration(super.keys()) {
108                 private int n = 0;
109                 public boolean _hasMoreElements() { return n < size(); }
110                 public JS _nextElement() {
111                     return n >= size() ? null : JS.N(n++);
112                 }
113             };
114     }
115
116     public final void setSize(int newSize) {
117         // FEATURE: This could be done a lot more efficiently in BalancedTree
118         int oldSize = size();
119         for(int i=oldSize;i<newSize;i++) insertElementAt(null,i);
120         for(int i=oldSize-1;i>=newSize;i--) removeElementAt(i);
121     }
122     
123     public final int length() { return size(); }
124     public final JS elementAt(int i) { 
125         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
126         Object o = bt.getNode(i);
127         return o == NULL ? (JS)null : (JS)o;
128     }
129     public final void addElement(JS o) { 
130         bt.insertNode(size(),o==null ? NULL : o);
131     }
132     public final void setElementAt(JS o, int i) {
133         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
134         bt.replaceNode(i,o==null ? NULL : o);
135     }
136     public final void insertElementAt(JS o, int i) {
137         if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
138         bt.insertNode(i,o==null ? NULL : o);
139     }
140     public final JS removeElementAt(int i) {
141         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
142         Object o = bt.deleteNode(i);
143         return o == NULL ? (JS)null : (JS)o;
144     }
145     
146     public final int size() { return bt.treeSize(); }
147     
148     private JS join(String sep) throws JSExn {
149         int length = size();
150         if(length == 0) return JS.S("");
151         StringBuffer sb = new StringBuffer(64);
152         int i=0;
153         while(true) {
154             JS o = elementAt(i);
155             if(o != null) sb.append(JS.toString(o));
156             if(++i == length) break;
157             sb.append(sep);
158         }
159         return JS.S(sb.toString());
160     }
161     
162     // FEATURE: Implement this more efficiently
163     private JS reverse() {
164         int size = size();
165         if(size < 2) return this;
166         Vec vec = toVec();
167         bt.clear();
168         for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt((JS)vec.elementAt(i),j);
169         return this;
170     }
171     
172     private JS slice(int start, int end) {
173         int length = length();
174         if(start < 0) start = length+start;
175         if(end < 0) end = length+end;
176         if(start < 0) start = 0;
177         if(end < 0) end = 0;
178         if(start > length) start = length;
179         if(end > length) end = length;
180         JSArray a = new JSArray(end-start);
181         for(int i=0;i<end-start;i++)
182             a.setElementAt(elementAt(start+i),i);
183         return a;
184     }
185         
186     private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() {
187         public int compare(Object a, Object b) {
188             try {
189                 return JS.toString((JS)a).compareTo(JS.toString((JS)b));
190             } catch(JSExn e) { throw new JSExn.Wrapper(e); }
191         }
192     };
193     private JS sort(JS tmp) throws JSExn {
194         Vec vec = toVec();
195         try {
196             if(tmp instanceof JS) {
197                 final JS jsFunc = (JS) tmp;
198                 vec.sort(new Vec.CompareFunc() {
199                     public int compare(Object a, Object b) {
200                         try {
201                             return JS.toInt(jsFunc.call((JS)a, (JS)b, null, null, 2));
202                         } catch(JSExn e) { throw new JSExn.Wrapper(e); }
203                     }
204                 });
205             } else {
206                 vec.sort(defaultSort);
207             }
208         } catch(JSExn.Wrapper e) {
209             throw e.refill();
210         }
211         setFromVec(vec);
212         return this;
213     }
214     
215     private JS splice(JSArray args) throws JSExn {
216         int oldLength = length();
217         int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
218         int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
219         int newCount = args.length() - 2;
220         if(newCount < 0) newCount = 0;
221         if(start < 0) start = oldLength+start;
222         if(start < 0) start = 0;
223         if(start > oldLength) start = oldLength;
224         if(deleteCount < 0) deleteCount = 0;
225         if(deleteCount > oldLength-start) deleteCount = oldLength-start;
226         int newLength = oldLength - deleteCount + newCount;
227         int lengthChange = newLength - oldLength;
228         JSArray ret = new JSArray(deleteCount);
229         for(int i=0;i<deleteCount;i++)
230             ret.setElementAt(elementAt(start+i),i);
231         if(lengthChange > 0) {
232             setSize(newLength);
233             for(int i=newLength-1;i>=start+newCount;i--)
234                 setElementAt(elementAt(i-lengthChange),i);
235         } else if(lengthChange < 0) {
236             for(int i=start+newCount;i<newLength;i++)
237                 setElementAt(elementAt(i-lengthChange),i);
238             setSize(newLength);
239         }
240         for(int i=0;i<newCount;i++)
241             setElementAt(args.elementAt(i+2),start+i);
242         return ret;
243     }
244     
245     protected Vec toVec() {
246         int count = size();
247         Vec vec = new Vec();
248         vec.setSize(count);
249         for(int i=0;i<count;i++) {
250             Object o = bt.getNode(i);
251             vec.setElementAt(o == NULL ? null : o,i);
252         }
253         return vec;
254     }
255     
256     protected void setFromVec(Vec vec) {
257         int count = vec.size();
258         bt.clear();
259         for(int i=0;i<count;i++) {
260             Object o = vec.elementAt(i);
261             bt.insertNode(i,o==null ? NULL : o);
262         }
263     }
264     
265     String coerceToString() throws JSExn { return JS.toString(join(",")); }
266 }