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