1    // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] 
2    package org.xwt.js; 
3    
4    import org.xwt.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       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                  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