1    // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL] 
2    
3    package org.xwt.js; 
4    import org.xwt.util.*; 
5    import java.io.*;
6    import java.util.*;
7    
8    /** A JavaScript Array */
9    class ArrayImpl extends JS.Obj {
10       private Vec vec = new Vec();
11       public ArrayImpl() { }
12       public ArrayImpl(int size) { vec.setSize(size); }
13       private static int intVal(Object o) {
14           if (o instanceof Number) {
15               int intVal = ((Number)o).intValue();
16               if (intVal == ((Number)o).doubleValue()) return intVal;
17               return Integer.MIN_VALUE;
18           }
19           if (!(o instanceof String)) return Integer.MIN_VALUE;
20           String s = (String)o;
21           for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
22           return Integer.parseInt(s);
23       }
24       
25       public Object callMethod(Object method, JS.Array args,boolean justChecking) {
26           if(method.equals("push")) {
27               if(justChecking) return Boolean.TRUE;
28               for(int i=0;i<args.length();i++)
29                   vec.push(args.elementAt(i));
30               return new Integer(vec.size());
31           }
32           if(method.equals("pop")) {
33               if(justChecking) return Boolean.TRUE;
34               return vec.pop(); // this'll return null on size()==0 
35           }
36           if(method.equals("shift")) {
37               if(justChecking) return Boolean.TRUE;
38               if(length() > 0) {
39                   Object o = vec.elementAt(0);
40                   vec.removeElementAt(0);
41                   return o;
42               } else {
43                   return null;
44               }
45           }
46           if(method.equals("unshift")) {
47               if(justChecking) return Boolean.TRUE;
48               // FEATURE: could be optimized a bit with some help from Vec
49               for(int i=0;i<args.length();i++)
50                   vec.insertElementAt(args.elementAt(i),i);
51               return new Integer(vec.size());
52           }
53           if(method.equals("slice")) return justChecking ? Boolean.TRUE : slice(args);
54           if(method.equals("join")) return justChecking ? Boolean.TRUE : join(args);
55           if(method.equals("reverse")) return justChecking ? Boolean.TRUE : reverse(args);
56           if(method.equals("toString")) return justChecking ? Boolean.TRUE : join(",");
57           if(method.equals("sort")) return justChecking ? Boolean.TRUE : sort(args);
58           if(method.equals("splice")) return justChecking ? Boolean.TRUE : splice(args);
59           return super.callMethod(method,args,justChecking);
60       }
61           
62           
63       // we use _get instead of get solely to work around a GCJ bug
64       public Object _get(Object key) throws JS.Exn {
65           if (key.equals("length")) return new Long(vec.size());
66                   
67           int i = intVal(key);
68           if (i == Integer.MIN_VALUE) return super.get(key);
69           try {
70               return vec.elementAt(i);
71           } catch (ArrayIndexOutOfBoundsException e) {
72               return null;
73           }
74       }
75       // we use _put instead of put solely to work around a GCJ bug
76       public void _put(Object key, Object val) {
77           if (key.equals("length")) vec.setSize(toNumber(val).intValue());
78           int i = intVal(key);
79           if (i == Integer.MIN_VALUE) super.put(key, val);
80           else {
81               if (i >= vec.size()) vec.setSize(i+1);
82               vec.setElementAt(val, i);
83           }
84       }
85       public Object[] keys() {
86           Object[] sup = super.keys();
87           Object[] ret = new Object[vec.size() + 1 + sup.length];
88           System.arraycopy(sup, 0, ret, vec.size(), sup.length);
89           for(int i=0; i<vec.size(); i++) ret[i] = new Integer(i);
90           ret[vec.size()] = "length";
91           return ret;
92       }
93       public void setSize(int i) { vec.setSize(i); }
94       public int length() { return vec.size(); }
95       public Object elementAt(int i) { return vec.elementAt(i); }
96       public void addElement(Object o) { vec.addElement(o); }
97       public void setElementAt(Object o, int i) { vec.setElementAt(o, i); }
98       
99       public String typeName() { return "array"; }
100          
101      private Object join(JS.Array args) {
102          return join(args.length() == 0 ? "," : JS.toString(args.elementAt(0)));
103      }
104      
105      private Object join(String sep) {
106          int length = vec.size();
107          if(length == 0) return "";
108          StringBuffer sb = new StringBuffer(64);
109          int i=0;
110          while(true) {
111              Object o = elementAt(i);
112              if(o != null) sb.append(JS.toString(o));
113              if(++i == length) break;
114              sb.append(sep);
115          }
116          return sb.toString();
117      }
118      
119      private Object reverse(JS.Array args) {
120          Vec oldVec = vec;
121          int size = oldVec.size();
122          if(size < 2) return this;
123          vec = new Vec(size);
124          for(int i=size-1;i>=0;i--)
125              vec.addElement(oldVec.elementAt(i));
126          return this;
127      }
128      
129      private Object slice(JS.Array args) {
130          int length = length();
131          int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
132          int end = args.length() < 2 ? length : JS.toInt(args.elementAt(1));
133          if(start < 0) start = length+start;
134          if(end < 0) end = length+end;
135          if(start < 0) start = 0;
136          if(end < 0) end = 0;
137          if(start > length) start = length;
138          if(end > length) end = length;
139          JS.Array a = new JS.Array(end-start);
140          for(int i=0;i<end-start;i++)
141              a.setElementAt(elementAt(start+i),i);
142          return a;
143      }
144      
145      private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() {
146          public int compare(Object a, Object b) {
147              return JS.toString(a).compareTo(JS.toString(b));
148          }
149      };
150      private Object sort(JS.Array args) {
151          Object tmp = args.length() < 1 ? null : args.elementAt(0);
152          if(tmp instanceof JS.Callable) {
153              final JS.Array funcArgs = new JS.Array(2);
154              final JS.Callable jsFunc = (JS.Callable) tmp;
155              vec.sort(new Vec.CompareFunc() {
156                  public int compare(Object a, Object b) {
157                      funcArgs.setElementAt(a,0);
158                      funcArgs.setElementAt(b,1);
159                      return JS.toInt(jsFunc.call(funcArgs));
160                  }
161              });
162          } else {
163              vec.sort(defaultSort);
164          }
165          return this;
166      }
167      
168      private Object splice(JS.Array args) {
169          int oldLength = length();
170          int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
171          int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
172          int newCount = args.length() - 2;
173          if(newCount < 0) newCount = 0;
174          if(start < 0) start = oldLength+start;
175          if(start < 0) start = 0;
176          if(start > oldLength) start = oldLength;
177          if(deleteCount < 0) deleteCount = 0;
178          if(deleteCount > oldLength-start) deleteCount = oldLength-start;
179          int newLength = oldLength - deleteCount + newCount;
180          int lengthChange = newLength - oldLength;
181          JS.Array ret = new JS.Array(deleteCount);
182          for(int i=0;i<deleteCount;i++)
183              ret.setElementAt(elementAt(start+i),i);
184          if(lengthChange > 0) {
185              setSize(newLength);
186              for(int i=newLength-1;i>=start+newCount;i--)
187                  setElementAt(elementAt(i-lengthChange),i);
188          } else if(lengthChange < 0) {
189              for(int i=start+newCount;i<newLength;i++)
190                  setElementAt(elementAt(i-lengthChange),i);
191              setSize(newLength);
192          }
193          for(int i=0;i<newCount;i++)
194              setElementAt(args.elementAt(i+2),start+i);
195          return ret;
196      }
197      
198       public String coerceToString() { return JS.toString(join(",")); }
199  }
200