2003/11/18 10:47:26
[org.ibex.core.git] / src / org / xwt / js / JSArray.java
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL] 
2 package org.xwt.js; 
3
4 import org.xwt.util.*; 
5 import java.io.*;
6 import java.util.*;
7
8 // FIXME: review, use redblacktrees
9 /** A JavaScript JSArray */
10 public class JSArray extends JS {
11     private Vec vec = new Vec();
12     public JSArray() { }
13     public JSArray(int size) { vec.setSize(size); }
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) {
27         //#switch(method)
28         case "pop": return vec.pop();
29         case "reverse": return reverse();
30         case "toString": return join(",");
31         case "shift":
32             if(length() == 0) return null;
33             Object o = vec.elementAt(0);
34             vec.removeElementAt(0);
35             return o;
36         case "join":
37             return join(nargs == 0 ? "," : JS.toString(a0));
38         case "sort":
39             return sort(nargs < 1 ? null : a0);
40         case "slice":
41             int start = toInt(nargs < 1 ? null : a0);
42             int end = nargs < 2 ? length() : toInt(a1);
43             return slice(start, end);
44         case "push":
45             for(int i=0; i<nargs; i++) vec.push(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
46             return N(vec.size());
47         case "unshift":
48             for(int i=0; i<nargs; i++) vec.insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3], i);
49             return N(vec.size());
50         case "splice":
51             JSArray array = new JSArray();
52             for(int i=0; i<nargs; i++) array.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
53             return splice(array);
54         //#end
55         return super.callMethod(method, a0, a1, a2, rest, nargs);
56     }
57         
58     public Object get(Object key) throws JSExn {
59         if (key instanceof Number) {
60             int i = intVal(key);
61             if (i == Integer.MIN_VALUE) return super.get(key);
62             try {
63                 return vec.elementAt(i);
64             } catch (ArrayIndexOutOfBoundsException e) {
65                 return null;
66             }
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(vec.size());
80         //#end
81         return super.get(key);
82     }
83
84     public void put(Object key, Object val) {
85         if (key.equals("length")) vec.setSize(toNumber(val).intValue());
86         int i = intVal(key);
87         if (i == Integer.MIN_VALUE) super.put(key, val);
88         else {
89             if (i >= vec.size()) vec.setSize(i+1);
90             vec.setElementAt(val, i);
91         }
92     }
93
94     public Enumeration keys() {
95         return new Enumeration() {
96                 int cur = 0;
97                 public boolean hasMoreElements() { return cur >= vec.size(); }
98                 public Object nextElement() {
99                     if (cur >= vec.size()) throw new NoSuchElementException();
100                     return new Integer(cur++);
101                 }
102             };
103     }
104
105     public void setSize(int i) { vec.setSize(i); }
106     public int length() { return vec.size(); }
107     public Object elementAt(int i) { return vec.elementAt(i); }
108     public void addElement(Object o) { vec.addElement(o); }
109     public void setElementAt(Object o, int i) { vec.setElementAt(o, i); }
110     public int size() { return vec.size(); }
111     public String typeName() { return "array"; }
112         
113     private Object join(String sep) {
114         int length = vec.size();
115         if(length == 0) return "";
116         StringBuffer sb = new StringBuffer(64);
117         int i=0;
118         while(true) {
119             Object o = elementAt(i);
120             if(o != null) sb.append(JS.toString(o));
121             if(++i == length) break;
122             sb.append(sep);
123         }
124         return sb.toString();
125     }
126     
127     private Object reverse() {
128         Vec oldVec = vec;
129         int size = oldVec.size();
130         if(size < 2) return this;
131         vec = new Vec(size);
132         for(int i=size-1;i>=0;i--) vec.addElement(oldVec.elementAt(i));
133         return this;
134     }
135     
136     private Object slice(int start, int end) {
137         int length = length();
138         if(start < 0) start = length+start;
139         if(end < 0) end = length+end;
140         if(start < 0) start = 0;
141         if(end < 0) end = 0;
142         if(start > length) start = length;
143         if(end > length) end = length;
144         JSArray a = new JSArray(end-start);
145         for(int i=0;i<end-start;i++)
146             a.setElementAt(elementAt(start+i),i);
147         return a;
148     }
149     
150     private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() {
151         public int compare(Object a, Object b) {
152             return JS.toString(a).compareTo(JS.toString(b));
153         }
154     };
155     private Object sort(Object tmp) {
156         if(tmp instanceof JS) {
157             final JSArray funcArgs = new JSArray(2);
158             final JS jsFunc = (JS) tmp;
159             vec.sort(new Vec.CompareFunc() {
160                 public int compare(Object a, Object b) {
161                     funcArgs.setElementAt(a,0);
162                     funcArgs.setElementAt(b,1);
163                     return JS.toInt(jsFunc.call(a, b, null, null, 2));
164                 }
165             });
166         } else {
167             vec.sort(defaultSort);
168         }
169         return this;
170     }
171     
172     private Object splice(JSArray args) {
173         int oldLength = length();
174         int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
175         int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
176         int newCount = args.length() - 2;
177         if(newCount < 0) newCount = 0;
178         if(start < 0) start = oldLength+start;
179         if(start < 0) start = 0;
180         if(start > oldLength) start = oldLength;
181         if(deleteCount < 0) deleteCount = 0;
182         if(deleteCount > oldLength-start) deleteCount = oldLength-start;
183         int newLength = oldLength - deleteCount + newCount;
184         int lengthChange = newLength - oldLength;
185         JSArray ret = new JSArray(deleteCount);
186         for(int i=0;i<deleteCount;i++)
187             ret.setElementAt(elementAt(start+i),i);
188         if(lengthChange > 0) {
189             setSize(newLength);
190             for(int i=newLength-1;i>=start+newCount;i--)
191                 setElementAt(elementAt(i-lengthChange),i);
192         } else if(lengthChange < 0) {
193             for(int i=start+newCount;i<newLength;i++)
194                 setElementAt(elementAt(i-lengthChange),i);
195             setSize(newLength);
196         }
197         for(int i=0;i<newCount;i++)
198             setElementAt(args.elementAt(i+2),start+i);
199         return ret;
200     }
201     
202      public String coerceToString() { return JS.toString(join(",")); }
203 }