--- /dev/null
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.util.*;
+
+/** A JavaScript JSArray */
+public class JSArray extends JS {
+ private static final Object NULL = new Object();
+
+ public JSArray() { }
+ public JSArray(int size) { setSize(size); }
+
+ private static int intVal(Object o) {
+ if (o instanceof Number) {
+ int intVal = ((Number)o).intValue();
+ if (intVal == ((Number)o).doubleValue()) return intVal;
+ return Integer.MIN_VALUE;
+ }
+ if (!(o instanceof String)) return Integer.MIN_VALUE;
+ String s = (String)o;
+ for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
+ return Integer.parseInt(s);
+ }
+
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ //#switch(method)
+ case "pop": {
+ int oldSize = size();
+ if(oldSize == 0) return null;
+ return removeElementAt(oldSize-1);
+ }
+ case "reverse": return reverse();
+ case "toString": return join(",");
+ case "shift":
+ if(length() == 0) return null;
+ return removeElementAt(0);
+ case "join":
+ return join(nargs == 0 ? "," : JS.toString(a0));
+ case "sort":
+ return sort(nargs < 1 ? null : a0);
+ case "slice":
+ int start = toInt(nargs < 1 ? null : a0);
+ int end = nargs < 2 ? length() : toInt(a1);
+ return slice(start, end);
+ case "push": {
+ int oldSize = size();
+ for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],oldSize+i);
+ return N(oldSize + nargs);
+ }
+ case "unshift":
+ for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],i);
+ return N(size());
+ case "splice":
+ JSArray array = new JSArray();
+ for(int i=0; i<nargs; i++) array.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
+ return splice(array);
+ //#end
+ return super.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+
+ public Object get(Object key) throws JSExn {
+ int i = intVal(key);
+ if (i != Integer.MIN_VALUE) {
+ if (i < 0 || i >= size()) return null;
+ return elementAt(i);
+ }
+ //#switch(key)
+ case "pop": return METHOD;
+ case "reverse": return METHOD;
+ case "toString": return METHOD;
+ case "shift": return METHOD;
+ case "join": return METHOD;
+ case "sort": return METHOD;
+ case "slice": return METHOD;
+ case "push": return METHOD;
+ case "unshift": return METHOD;
+ case "splice": return METHOD;
+ case "length": return N(size());
+ //#end
+ return super.get(key);
+ }
+
+ public void put(Object key, Object val) throws JSExn {
+ if (key.equals("length")) setSize(toInt(val));
+ int i = intVal(key);
+ if (i == Integer.MIN_VALUE)
+ super.put(key, val);
+ else {
+ int oldSize = size();
+ if(i < oldSize) {
+ setElementAt(val,i);
+ } else {
+ if(i > oldSize) setSize(i);
+ insertElementAt(val,i);
+ }
+ }
+ }
+
+ public Enumeration keys() {
+ return new Enumeration() {
+ int cur = 0;
+ public boolean hasMoreElements() { return cur >= size(); }
+ public Object nextElement() {
+ if (cur >= size()) throw new NoSuchElementException();
+ return new Integer(cur++);
+ }
+ };
+ }
+
+ public final void setSize(int newSize) {
+ // FEATURE: This could be done a lot more efficiently in BalancedTree
+ int oldSize = size();
+ for(int i=oldSize;i<newSize;i++) insertElementAt(null,i);
+ for(int i=oldSize-1;i>=newSize;i--) removeElementAt(i);
+ }
+
+ public final int length() { return size(); }
+ public final Object elementAt(int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ Object o = getNode(i);
+ return o == NULL ? null : o;
+ }
+ public final void addElement(Object o) {
+ insertNode(size(),o==null ? NULL : o);
+ }
+ public final void setElementAt(Object o, int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ replaceNode(i,o==null ? NULL : o);
+ }
+ public final void insertElementAt(Object o, int i) {
+ if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
+ insertNode(i,o==null ? NULL : o);
+ }
+ public final Object removeElementAt(int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ Object o = deleteNode(i);
+ return o == NULL ? null : o;
+ }
+
+ public final int size() { return treeSize(); }
+ public String typeName() { return "array"; }
+
+ private Object join(String sep) {
+ int length = size();
+ if(length == 0) return "";
+ StringBuffer sb = new StringBuffer(64);
+ int i=0;
+ while(true) {
+ Object o = elementAt(i);
+ if(o != null) sb.append(JS.toString(o));
+ if(++i == length) break;
+ sb.append(sep);
+ }
+ return sb.toString();
+ }
+
+ // FEATURE: Implement this more efficiently
+ private Object reverse() {
+ int size = size();
+ if(size < 2) return this;
+ Vec vec = toVec();
+ clear();
+ for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt(vec.elementAt(i),j);
+ return this;
+ }
+
+ private Object slice(int start, int end) {
+ int length = length();
+ if(start < 0) start = length+start;
+ if(end < 0) end = length+end;
+ if(start < 0) start = 0;
+ if(end < 0) end = 0;
+ if(start > length) start = length;
+ if(end > length) end = length;
+ JSArray a = new JSArray(end-start);
+ for(int i=0;i<end-start;i++)
+ a.setElementAt(elementAt(start+i),i);
+ return a;
+ }
+
+ private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() {
+ public int compare(Object a, Object b) {
+ return JS.toString(a).compareTo(JS.toString(b));
+ }
+ };
+ private Object sort(Object tmp) throws JSExn {
+ Vec vec = toVec();
+ if(tmp instanceof JS) {
+ final JSArray funcArgs = new JSArray(2);
+ final JS jsFunc = (JS) tmp;
+ vec.sort(new Vec.CompareFunc() {
+ public int compare(Object a, Object b) {
+ try {
+ funcArgs.setElementAt(a,0);
+ funcArgs.setElementAt(b,1);
+ return JS.toInt(jsFunc.call(a, b, null, null, 2));
+ } catch (Exception e) {
+ // FIXME
+ throw new JSRuntimeExn(e.toString());
+ }
+ }
+ });
+ } else {
+ vec.sort(defaultSort);
+ }
+ setFromVec(vec);
+ return this;
+ }
+
+ private Object splice(JSArray args) {
+ int oldLength = length();
+ int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
+ int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
+ int newCount = args.length() - 2;
+ if(newCount < 0) newCount = 0;
+ if(start < 0) start = oldLength+start;
+ if(start < 0) start = 0;
+ if(start > oldLength) start = oldLength;
+ if(deleteCount < 0) deleteCount = 0;
+ if(deleteCount > oldLength-start) deleteCount = oldLength-start;
+ int newLength = oldLength - deleteCount + newCount;
+ int lengthChange = newLength - oldLength;
+ JSArray ret = new JSArray(deleteCount);
+ for(int i=0;i<deleteCount;i++)
+ ret.setElementAt(elementAt(start+i),i);
+ if(lengthChange > 0) {
+ setSize(newLength);
+ for(int i=newLength-1;i>=start+newCount;i--)
+ setElementAt(elementAt(i-lengthChange),i);
+ } else if(lengthChange < 0) {
+ for(int i=start+newCount;i<newLength;i++)
+ setElementAt(elementAt(i-lengthChange),i);
+ setSize(newLength);
+ }
+ for(int i=0;i<newCount;i++)
+ setElementAt(args.elementAt(i+2),start+i);
+ return ret;
+ }
+
+ protected Vec toVec() {
+ int count = size();
+ Vec vec = new Vec();
+ vec.setSize(count);
+ for(int i=0;i<count;i++) {
+ Object o = getNode(i);
+ vec.setElementAt(o == NULL ? null : o,i);
+ }
+ return vec;
+ }
+
+ protected void setFromVec(Vec vec) {
+ int count = vec.size();
+ clear();
+ for(int i=0;i<count;i++) {
+ Object o = vec.elementAt(i);
+ insertNode(i,o==null ? NULL : o);
+ }
+ }
+
+ public String toString() { return JS.toString(join(",")); }
+}