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