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