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