1    // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL] 
2    
3    package org.xwt.js; 
4    import org.xwt.util.*; 
5    
6    /**
7     * Misc static methods used internally by the JS engine
8     */
9    class Internal {
10       // Package Helper Methods //////////////////////////////////////////////////////////////
11   
12       private static Object wrapInt(int i) { return new Integer(i); }
13       static Object callMethodOnPrimitive(Object o, Object method, JSArray args) {
14           int alength = args.length();
15           String s;
16           if(o instanceof Number) {
17               if(method.equals("toFixed")) throw new JS.Exn("toFixed() not implemented");
18               if(method.equals("toExponential")) throw new JS.Exn("toExponential() not implemented");
19               if(method.equals("toPrecision")) throw new JS.Exn("toPrecision() not implemented");
20               if(method.equals("toString")) {
21                   int radix = alength >= 1 ? JS.toInt(args.elementAt(0)) : 10;
22                   return Long.toString(((Number)o).longValue(),radix);
23               }
24           } else if(o instanceof Boolean) {
25               // No methods for Booleans
26           }
27           s = JS.toString(o);
28           int slength = s.length();
29           if(method.equals("substring")) {
30               int a = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
31               int b = alength >= 2 ? JS.toInt(args.elementAt(1)) : slength;
32               if(a > slength) a = slength;
33               if(b > slength) b = slength;
34               if(a < 0) a = 0;
35               if(b < 0) b = 0;
36               if(a > b) { int tmp = a; a = b; b = tmp; }
37               return s.substring(a,b);
38           }
39           if(method.equals("substr")) {
40               int start = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
41               int len = alength >= 2 ? JS.toInt(args.elementAt(1)) : Integer.MAX_VALUE;
42               if(start < 0) start = slength + start;
43               if(start < 0) start = 0;
44               if(len < 0) len = 0;
45               if(len > slength - start) len = slength - start;
46               if(len <= 0) return "";
47               return s.substring(start,start+len);
48           }
49           if(method.equals("charAt")) {
50               int p = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
51               if(p < 0 || p >= slength) return "";
52               return s.substring(p,p+1);
53           }
54           if(method.equals("charCodeAt")) {
55               int p = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
56               if(p < 0 || p >= slength) return new Double(Double.NaN);
57               return wrapInt(s.charAt(p));
58           }
59           if(method.equals("concat")) {
60               StringBuffer sb = new StringBuffer(slength*2).append(s);
61               for(int i=0;i<alength;i++) sb.append(args.elementAt(i));
62               return sb.toString();
63           }
64           if(method.equals("indexOf")) {
65               String search = alength >= 1 ? args.elementAt(0).toString() : "null";
66               int start = alength >= 2 ? JS.toInt(args.elementAt(1)) : 0;
67               // Java's indexOf handles an out of bounds start index, it'll return -1
68               return wrapInt(s.indexOf(search,start));
69           }
70           if(method.equals("lastIndexOf")) {
71               String search = alength >= 1 ? args.elementAt(0).toString() : "null";
72               int start = alength >= 2 ? JS.toInt(args.elementAt(1)) : 0;
73               // Java's indexOf handles an out of bounds start index, it'll return -1
74               return wrapInt(s.lastIndexOf(search,start));            
75           }
76           if(method.equals("match")) {
77               return JSRegexp.stringMatch(s,args);
78           }
79           if(method.equals("replace")) {
80               return JSRegexp.stringReplace(s,args);
81           }
82           if(method.equals("search")) {
83               return JSRegexp.stringSearch(s,args);
84           }
85           if(method.equals("slice")) {
86               int a = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
87               int b = alength >= 2 ? JS.toInt(args.elementAt(1)) : slength;
88               if(a < 0) a = slength + a;
89               if(b < 0) b = slength + b;
90               if(a < 0) a = 0;
91               if(b < 0) b = 0;
92               if(a > slength) a = slength;
93               if(b > slength) b = slength;
94               if(a > b) return "";
95               return s.substring(a,b);
96           }
97           if(method.equals("split")){
98               return JSRegexp.stringSplit(s,args);
99           } 
100          if(method.equals("toLowerCase")) return s.toLowerCase();
101          if(method.equals("toUpperCase")) return s.toUpperCase();
102          if(method.equals("toString")) return s;
103          throw new JS.Exn("Attempted to call non-existent method: " + method);
104      }
105      
106      static Object getFromPrimitive(Object o, Object key) {
107          boolean returnJSCallable = false;
108          if(o instanceof Boolean) {
109              // no properties for Booleans
110          } else if(o instanceof Number) {
111              if(key.equals("toPrecision") || key.equals("toExponential") || key.equals("toFixed"))
112                  returnJSCallable = true;
113          }
114          if(!returnJSCallable) {
115              // the string stuff applies to everything
116              String s = o.toString();
117              
118              if(key.equals("length")) return wrapInt(s.length());
119          
120              // this is sort of ugly, but this list should never change
121              // These should provide a complete (enough) implementation of the ECMA-262 String object
122              if(key.equals("substring") || key.equals("charAt") || key.equals("charCodeAt") || key.equals("concat") ||
123                  key.equals("indexOf") || key.equals("lastIndexOf") || key.equals("match") || key.equals("replace") ||
124                  key.equals("seatch") || key.equals("slice") || key.equals("split") || key.equals("toLowerCase") ||
125                  key.equals("toUpperCase") || key.equals("toString") || key.equals("substr")
126              )
127                  returnJSCallable = true;
128          }
129          if(returnJSCallable) {
130              final Object target = o;
131              final String method = key.toString();
132              return new JSCallable() {
133                      public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
134                          JSArray args = new JSArray();
135                          for(int i=0; i<nargs; i++) args.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
136                          return callMethodOnPrimitive(target,method,args);
137                      }
138              };
139          }
140          return null;
141      }
142      
143      static class JSCallableStub extends JSCallable {
144          private Object method;
145          JS obj;
146          public JSCallableStub(JS obj, Object method) { this.obj = obj; this.method = method; }
147          public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
148              return ((JSCallable)obj).callMethod(method, a0, a1, a2, rest, nargs);
149          }
150      }
151  }
152