1
2 package org.xwt.js;
3
4 import org.xwt.util.*;
5 import java.util.*;
6
7
8 public class JSArray extends JS {
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
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 int cur = 0;
103 public boolean hasMoreElements() { return cur >= size(); }
104 public Object nextElement() {
105 if (cur >= size()) throw new NoSuchElementException();
106 return new Integer(cur++);
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 }
263