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