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